<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>jawoo-97.log</title>
        <link>https://velog.io/</link>
        <description>https://blog.naver.com/jaewoo2_25</description>
        <lastBuildDate>Sat, 13 Jul 2024 15:30:50 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>jawoo-97.log</title>
            <url>https://velog.velcdn.com/images/jawoo-97/profile/3512a557-9386-44af-bbf8-fbc1cfa5c7e5/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. jawoo-97.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/jawoo-97" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[자바스크립트 첫걸음
-MDN ]]></title>
            <link>https://velog.io/@jawoo-97/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%B2%AB%EA%B1%B8%EC%9D%8C-MDN</link>
            <guid>https://velog.io/@jawoo-97/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%B2%AB%EA%B1%B8%EC%9D%8C-MDN</guid>
            <pubDate>Sat, 13 Jul 2024 15:30:50 GMT</pubDate>
            <description><![CDATA[<h1 id="자바스크립트란-무엇인가요">자바스크립트란 무엇인가요?</h1>
<p><code>JavaScript</code>는 동적으로 변경되는 콘텐츠를 만들고, 멀티미디어를 제어하고, 이미지에 애니메이션을 적용하는 등 거의 모든 작업을 수행할 수 있는 스크립팅 언어입니다. (모든 것이 가능한 것은 아니지만 몇 줄의 <code>JavaScript</code> 코드로 달성할 수 있는 것은 놀랍습니다.)</p>
<hr>
<h2 id="그래서-어떤-일을-할-수-있나요">그래서 어떤 일을 할 수 있나요?</h2>
<p>클라이언트 사이느 JavaScript</p>
<ul>
<li>변수에 값을 저장합니다.</li>
<li>문자열을 조작합니다.</li>
<li>웹 페이지에서 특정 이벤트에 대한 응답</li>
</ul>
<p>흥미로운 점은 클라이언트 측 JavaScript 언어 위에 구축된 기능이다. </p>
<ul>
<li><p>DOM API를 사용하면 HTML, CSS를 조작하여 새 스타일을 동적으로 적용</p>
</li>
<li><p>Geolocation API로 지리정보를 가져올 수 있다.</p>
</li>
<li><p>Cavas와 WebGL API를 사용하면 d애니메이션 2D, 3D 그래픽을 만들 수 있다.</p>
</li>
<li><p>HTMLMediaElement와 WebRTC를 포함하는 오디오,비디오 API사용하여 웹 페이지에서 바로 오디오 및 비디오를 재생</p>
</li>
</ul>
<p><strong>3rd party APIs</strong> 
: 기본적으로 브라우저에 내장되어 있지 않으며, 일반적으로 웹 어딘가에서 해당 코드와 정보를 가져와야 합니다.</p>
<ul>
<li>Twitter API로 여러분의 최신 트윗을 웹 사이트가 보여주도록 구현할 수 있습니다.</li>
<li>Google 지도 API와 OpenStreetMap API로 웹 사이트에 지도를 삽입하고, 지도 관련 기능을 추가할 수 있습니다.</li>
</ul>
<h2 id="웹-페이지에서-js는-어떤일을-하나요">웹 페이지에서 Js는 어떤일을 하나요?</h2>
<p>브라우저에서 웹 페이지를 로드 ( DOM API를 통해 HTML, CSS를 동적으로 수정 업데이트한다. 일반적으로 웹 문서의 코드는 위에서 아래로 로드된다. JS를 먼저 불러오면 오류가 생길 수 있다.)</p>
<h3 id="브라우저-보안">브라우저 보안</h3>
<h3 id="js-실행-순서">Js 실행 순서</h3>
<p>일반적으로 순서대로 위에서 아래로 실행합니다. 따라서 코드 배치 순서에도 신경써야 한다. </p>
<pre><code class="language-javascript">const para = document.querySelector(&quot;p&quot;);

para.addEventListener(&quot;click&quot;, updateName);

function updateName() {
  const name = prompt(&quot;Enter a new name&quot;);
  para.textContent = `Player 1: ${name}`;
}
</code></pre>
<p>코드의 처음 두 줄의 순서를 바꾸면 더 이상 작동 하지 않는다.</p>
<h2 id="인터프리터와-컴파일러">인터프리터와 컴파일러</h2>
<p><strong>인터프러터</strong> : 인터프리터를 사용하는 언어(파이썬..)에서는 코드가 위에서 아래로 실행되고 코드 실행 결과가 즉시 반환.</p>
<p>자바스크립트는 인터프리터를 사용하는 프로그래밍 언어입니다. 대부분의 모던 JS 인터프리터들은 실제 성능 향상을 위해 JIT컴파일이라는 기술을 사용하는데, 스크립트의 실행과 동시에 소스코드를 더 빠르게 실행할 수 있는 이진 형태로 변환하여 속도를 높이는 방법이다. 하지만 컴파일이 미리 처리되는 것이 아니라 런타임에 처리되기 때문에 JS는 여전히 인터프리터 언어로 분류된다.</p>
<h2 id="서버-사이드와-클라이언트-사이드-코드">서버 사이드와 클라이언트 사이드 코드</h2>
<p>** 클라이언트 사이드 코드 **
: 사용자의 컴퓨터에서 실행되는 코드로, 웹 페이지를 볼 때 페이지의 클라이언트 측 코드가 다운로드 된 후 실행되어 브라우저에 표시된다.</p>
<h2 id="웹-페이지에-js-넣는-법">웹 페이지에 JS 넣는 법</h2>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en-US&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;utf-8&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width&quot;&gt;
    &lt;title&gt;Apply JavaScript example&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;button&gt;Click me&lt;/button&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>텍스트 편집기에서 <code>&lt;/head&gt;</code> 태그 바로 앞에 넣는다.</p>
<pre><code class="language-javascript">...(생략)
&lt;script&gt;
    // JavaScript goes here  
&lt;/script&gt;

</code></pre>
<p>다음 코드를 script태그 안에 넣는다.</p>
<pre><code class="language-javascript">document.addEventListener(&quot;DOMContentLoaded&quot;, () =&gt; {
  function createParagraph() {
    const para = document.createElement(&quot;p&quot;);
    para.textContent = &quot;You clicked the button!&quot;;
    document.body.appendChild(para);
  }

  const buttons = document.querySelectorAll(&quot;button&quot;);

  for (const button of buttons) {
    button.addEventListener(&quot;click&quot;, createParagraph);
  }
});
</code></pre>
<hr>
<p>또는, 외부 파일로 분리하고 싶다면
<code>script.js</code> 라는 이름을 붙여 새로운 파일에 코드를 넣는다.</p>
<p>그리고 <code>&lt;script&gt;</code> 요소를 다음 코드로 대체합니다.</p>
<pre><code class="language-javascript">&lt;script src=&quot;script.js&quot; defer&gt;&lt;/script&gt;
</code></pre>
<p>일반적으로 코드를 체계적으로 정리하고 여러 HTML 파일에서 재사용할 수 있다는 측면에서 외부로 분리하는 것이 좋습니다.</p>
<h2 id="스크립트-로딩-전략">스크립트 로딩 전략</h2>
<p>JS를 사용해서 페이지 내의 요소(DOM)를 조작하려고 할 때, 해당 요소를 포함한 HTML 코드보다 JS를 먼저 불러와버리면 코드가 올바르게 동작하지 못합니다.</p>
<pre><code class="language-javascript">document.addEventListener(&quot;DOMContentLoaded&quot;, () =&gt; {
  // …
});
</code></pre>
<p>위의 코드는 HTML 본문 전체를 불러와 읽었다는 것을 나타내는 브라우저의 <code>DOMContentLoaded</code>이벤트 수신기입니다. 이 불록 내부의 JS는 이벤트가 끝나기 전에는 실행되지 않으므로 로딩 시점으로 인한 오류를 예방할 수 있다.</p>
<p>외부 분리 파일은 <code>&lt;script&gt;</code> 태그 요소에 속성을 추가하여 HTML 콘텐츠를 계속 다운로드하도록 지시하는 <code>defer</code>속성 이라는 보다 최신 JS 기능을 사용하여 문제를 해결할 수 있습니다.</p>
<pre><code class="language-javascript">&lt;script src=&quot;script.js&quot; defer&gt;&lt;/script&gt;
</code></pre>
<blockquote>
<p><code>defer</code> 특성이 오류를 예방하므로, 외부 파일에서는 <code>DOMContentLoaded</code>이벤트를 사용하지 않는다. <code>defer</code>는 외부 스크립트에서만 작동하기 때문에 내부 JS예제에서는 사용하지 않았습니다.</p>
</blockquote>
<p>! 고전적인 방법 : 스크립트 요소를 본문의 맨 마지막 <code>&lt;/body&gt;</code> 태그 바로앞에 배치. 이 방법의 문제는 HTML DOM을 모두 불러오기 전에는 스크립트의 로딩과 분석이 완전히 중단되버린다. 그래서 많은 스크립트를 포함하는 대형 사이트에서는 이로 인해 사이트 속도가 느려지는 중대한 성능 문제가 발생할 수 있다.</p>
<h2 id="async와-defer">async와 defer</h2>
<p>스크립트 중단 문제를 해결하는 기능 두 가지</p>
<ul>
<li><code>async</code></li>
<li><code>defer</code></li>
</ul>
<p><code>async</code> 특성을 지정하면 스크립트를 가져오는 동안 페이지 로딩을 중단하지 않습니다. 그러나 다운로드가 끝나면 스크립트가 바로 실행되는데, 실행 도중에는 페이지 렌더링이 중단됩니다. 스크립트의 실행 순서를 보장할 방법은 없습니다. 따라서 <code>async</code>는 스크립트가 서로 독립적으로 실행되고, 다른 스크립트에 의존하지 않는 경우에 사용하는 것이 가장 좋습니다.</p>
<p><code>defer</code> 속성으로 로드된 스크립트는 페이지에 표시되는 순서대로 로드됩니다. 또한 페이지 콘텐츠를 모두 불러오기 전까지는 실행하지 않으므로, 스크립트가 DOM의 위치에 의존하는 경우(예: 페이지에서 하나 이상의 요소를 수정하는 경우) 유용합니다.</p>
<p>다음은 다양한 스크립트 로드 방법과 미치는 영향을 시각적으로 표현한 것이다.</p>
<p><img src="https://velog.velcdn.com/images/jawoo-97/post/6b666131-ef3f-41b2-9549-1ce2d866aec4/image.jpg" alt=""></p>
<pre><code class="language-javascript">&lt;script async src=&quot;js/vendor/jquery.js&quot;&gt;&lt;/script&gt;

&lt;script async src=&quot;js/script2.js&quot;&gt;&lt;/script&gt;

&lt;script async src=&quot;js/script3.js&quot;&gt;&lt;/script&gt;
</code></pre>
<p>위 코드로는 스크립트가 HTML의 순서대로 불러와질 것이라고 확실하게 예측할 수 없습니다. 이 경우 스크립트가 실행될 때 <code>jqery</code>가 정의되지 않기 때문에 함수가 오류를 발생시킬 수 있습니다.</p>
<p><code>async</code>는 로드할 백그라운드 스크립트가 많고 가능한 한 빨리 제자리에 배치하고 싶을 때 사용해야 합니다.</p>
<p><code>defer</code> 속성(아래 참조)을 사용하여 로드된 스크립트는 페이지에 표시되는 순서대로 실행되며 스크립트와 콘텐츠가 다운로드되는 즉시 실행됩니다:</p>
<p>-&gt; 페이지 콘텐츠가 모두 로드될 때까지 실행되지 않으므로 스크립트가 DOM의 위치에 의존하는 경우 유용합니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CSRF 설정하기]]></title>
            <link>https://velog.io/@jawoo-97/CSRF-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@jawoo-97/CSRF-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 13 Jul 2024 12:44:22 GMT</pubDate>
            <description><![CDATA[<h1 id="csrf">CSRF?</h1>
<p><code>CSRF</code> Cross-Site Request Forgery는 요청을 위조하여 사용자가 원하지 않아도 서버측으로 특정 요청을 강제로 보내는 방식이다. ( 회원 정보 변경, 게시글 CRUD를 사용자 모르게 요청 )</p>
<p><a href="https://docs.spring.io/spring-security/reference/servlet/exploits/csrf.html#csrf-components">CSRF 공식 홈페이지 </a></p>
<hr>
<h2 id="csrf-disable">csrf disable()</h2>
<pre><code class="language-java">
package com.example.testsecurity.config;


@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{

        http
                .csrf((auth) -&gt; auth.disable());


        return http.build();
    }
}</code></pre>
<p>scurity config 클래스에서 <code>csrf.disable()</code> 설정을 진행하지 않으면 자동으로 enable 설정이 진행된다. enable 설정 시 스프링 시큐리티는 <code>CSRFFilter</code>를 통해 <code>POST</code>, <code>PUT</code>, <code>DELETE</code> 요청에 대해서 토큰 검증을 진행한다.</p>
<pre><code class="language-html">&lt;form action=&quot;/loginReceiver&quot; method=&quot;post&quot; name=&quot;loginForm&quot;&gt;
    &lt;input type=&quot;text&quot; name=&quot;username&quot; placeholder=&quot;아이디&quot;/&gt;
    &lt;input type=&quot;password&quot; name=&quot;password&quot; placeholder=&quot;비밀번호&quot;/&gt;
      &lt;!-- 다음과 같이 csrf 토큰을 담아서 요청을 전달해야한다.--&gt;
    &lt;input type=&quot;hidden&quot; name=&quot;_csrf&quot; value=&quot;{{_csrf.token}}&quot;/&gt;

    &lt;input type=&quot;submit&quot; value=&quot;로그인&quot;/&gt;
&lt;/form&gt;</code></pre>
<ul>
<li>ajax 요청시<pre><code class="language-html">&lt;!-- 다음과 같이 &lt;head&gt;구획 아래에 요소를 추가해야한다. --&gt;
&lt;meta name=&quot;_csrf&quot; content=&quot;{{_csrf.token}}&quot;/&gt;
&lt;meta name=&quot;_csrf_header&quot; content=&quot;{{_csrf.headerName}}&quot;/&gt;
</code></pre>
</li>
</ul>
<pre><code>
- GET 방식 로그아웃을 진행할 경우 설정 방법
csrf 설정 시 POST 요청으로 로그아웃을 진행해야 하지만 아래 방식을 통해 GET 방식으로 진행할 수 있다.

   - security config 클래스 로그아웃 설정


```java
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{

    http
            .logout((auth) -&gt; auth.logoutUrl(&quot;/logout&quot;)
                    .logoutSuccessUrl(&quot;/&quot;));

    return http.build();
}</code></pre><br>

<ul>
<li>LogoutController</li>
</ul>
<pre><code class="language-java">@Controller
public class logoutController {

    @GetMapping(&quot;/logout&quot;)
    public String logout(HttpServletRequest request, HttpServletResponse response) throws Exception {

        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if(authentication != null) {
            new SecurityContextLogoutHandler().logout(request, response, authentication);
        }

        return &quot;redirect:/&quot;;
    }
}</code></pre>
<hr>
<h3 id="오류-발생-시">오류 발생 시</h3>
<p>mustache에서 csrf 토큰 변수 오류 발생 시 아래 구문을 변수 설정 파일에 추가</p>
<ul>
<li>application.properties</li>
</ul>
<pre><code class="language-java"># request 속성 추가
spring.mustache.servlet.expose-request-attributes=true</code></pre>
<hr>
<h3 id="api-서버의-경우-csrfdisable-">API 서버의 경우 csrf.disable() ?</h3>
<p>앱에서 사용하는 API 서버의 경우 보통 세션을 STATELESS로 관리하기 때문에 스프링 시큐리티 csrf enable 설정을 진행하지 않아도 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[세션 정보 확인하기, 설정하기]]></title>
            <link>https://velog.io/@jawoo-97/%EC%84%B8%EC%85%98-%EC%A0%95%EB%B3%B4-%ED%99%95%EC%9D%B8%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@jawoo-97/%EC%84%B8%EC%85%98-%EC%A0%95%EB%B3%B4-%ED%99%95%EC%9D%B8%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 13 Jul 2024 11:24:00 GMT</pubDate>
            <description><![CDATA[<h1 id="현재-사용자-정보를-불러오는-방법">현재 사용자 정보를 불러오는 방법</h1>
<p>*<em>세션 현재 사용자 아이디 *</em></p>
<pre><code class="language-java">SecurityContextHolder.getContext().getAuthentication().getName();</code></pre>
<hr>
<p><strong>세션 현재 사용자 Role</strong></p>
<pre><code class="language-java">
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

Collection&lt;? extends GrantedAuthority&gt; authorities = authentication.getAuthorities();
Iterator&lt;? extends GrantedAuthority&gt; iter = authorities.iterator();
GrantedAuthority auth = iter.next();
String role = auth.getAuthority();</code></pre>
<hr>
<h2 id="사용법">사용법</h2>
<p>세션정보를 확인해야 하는 곳에 코드를 넣어서 값을 가지고 오면 된다.</p>
<p>예를들어 루트 페이지에서 사용자 정보를 보여주려면 다음과 같이 한다.</p>
<pre><code class="language-java">
@Controller
public class MainController {


    @GetMapping(&quot;/&quot;)
    public String mainP(Model model) {

        // 루트 페이지를 response하는 매핑된 메소드에 세션 정보를 불러와 페이지에 넣는다.

       String username =
               SecurityContextHolder.getContext().getAuthentication().getName();

        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

        Collection&lt;? extends GrantedAuthority&gt; authorities = authentication.getAuthorities();
        Iterator&lt;? extends GrantedAuthority&gt; iter = authorities.iterator();
        GrantedAuthority auth = iter.next();
        String role = auth.getAuthority();

        model.addAttribute(&quot;username&quot;,username);
        model.addAttribute(&quot;role&quot;,role);


        return &quot;main&quot;;
    }
}
</code></pre>
<p>템플릿 소스에 들어가 문법에 맞게 데이터를 불러온다.</p>
<hr>
<p><em>결과확인하기</em>
<img src="https://velog.velcdn.com/images/jawoo-97/post/34f3ab2c-460c-4db0-ad9f-c5e6bbda8b36/image.png" alt="">
로그인 전 세션 정보가 없기 때문에 나오지 않는다.</p>
<h1 id="세션-설정">세션 설정</h1>
<hr>
<p><a href="https://docs.spring.io/spring-security/reference/servlet/authentication/session-management.html#understanding-session-management-components">공식 홈페이지 세션 바로가기</a></p>
<p><strong>로그인 정보</strong>
사용자가 로그인을 진행한 뒤 사용자 정보는 SecurityContextHolder에 의해서 서버 세션에 관리된다.</p>
<h2 id="세션-소멸-시간-설정">세션 소멸 시간 설정</h2>
<p>세션 타임아웃 설정을 통해 로그인 이후 세션이 유지되고 소멸하는 시간을 설정한다. 
세션 소멸 시점은 서버에 마지막 특정 요청(GET,POST,...)을 수행한 뒤 설정한 시간 만큼 유지된다.(기본시간 1800초)</p>
<pre><code class="language-java">//초 기반
server.servlet.session.timeout=1800


//분 기반
server.servlet.session.timeout=90m
</code></pre>
<pre><code class="language-java">// 세션 시간 초과 감지 ( 이미 만료된 세션으로 사용자가 요청할 때 특정 엔드포인트로 리디렉션 한다. 다음 예는 /login 특정 페이지로 리디렉션
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
    http
        .sessionManagement(session -&gt; session
            .invalidSessionUrl(&quot;/login&quot;)
        );
    return http.build();
}</code></pre>
<h2 id="다중-로그인-설정">다중 로그인 설정</h2>
<p>동일한 아이디로 다중 로그인을 진행할 경우에 대한 설정 방법은 세션 통제를 통해 진행한다.</p>
<pre><code class="language-java">@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{

    http
            .sessionManagement((auth) -&gt; auth
                    .maximumSessions(1)
                    .maxSessionsPreventsLogin(true));

    return http.build();
}</code></pre>
<p>sessionManagement() 메소드를 통한 설정을 진행한다.</p>
<p><code>maximumSession(정수)</code> : 하나의 아이디에 대한 다중 로그인 허용 개수</p>
<p><code>maxSessionPreventsLogin(불린)</code> : 다중 로그인 개수를 초과하였을 경우 처리 방법 
기본값 : false</p>
<ul>
<li>true : 초과시 새로운 로그인 차단</li>
<li>false : 초과시 기존 세션 하나 삭제</li>
</ul>
<h2 id="세션-고정-보호">세션 고정 보호</h2>
<p><img src="https://velog.velcdn.com/images/jawoo-97/post/b4cb2a7f-68d0-4e0c-9568-a7d7f7547136/image.png" alt=""></p>
<p>세션 고정 공격을 보호하기 위한 로그인 성공시 세션 설정 방법은 sessionManagement() 메소드의 sessionFixation() 메소드를 통해서 설정할 수 있다.</p>
<ul>
<li>sessionManagement().sessionFixation().none() : 로그인 시 세션 정보 변경 안함</li>
<li>sessionManagement().sessionFixation().newSession() : 로그인 시 세션 새로 생성</li>
<li>sessionManagement().sessionFixation().changeSessionId() : 로그인 시 동일한 세션에 대한 id 변경</li>
</ul>
<ul>
<li><p>공식문서 코드</p>
<pre><code class="language-java">@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
  http
      .sessionManagement((session) - session
          .sessionFixation((sessionFixation) -&gt; sessionFixation
              .newSession()
          )
      );

  return http.build();
}</code></pre>
</li>
<li><p>구현 코드</p>
<pre><code class="language-java">@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{

  http
          .sessionManagement((auth) -&gt; auth
                  .sessionFixation().changeSessionId());

  return http.build();
}</code></pre>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[데이터베이스 연결]]></title>
            <link>https://velog.io/@jawoo-97/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%97%B0%EA%B2%B0</link>
            <guid>https://velog.io/@jawoo-97/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EC%97%B0%EA%B2%B0</guid>
            <pubDate>Sat, 13 Jul 2024 05:28:56 GMT</pubDate>
            <description><![CDATA[<h1 id="데이터베이스-종류와-orm">데이터베이스 종류와 ORM</h1>
<p>회원 정보를 저장하기 위한 데이터베이스는 ORACLE 엔진의 데이터베이스를 사용한다. 그리고 접근은 Spring Data JPA를 사용한다.</p>
<hr>
<pre><code class="language-java">
dependencies {

    implementation &#39;org.springframework.boot:spring-boot-starter-data-jpa&#39;
    runtimeOnly &#39;com.oracle.database.jdbc:ojdbc11&#39;
}</code></pre>
<hr>
<h1 id="변수-설정">변수 설정</h1>
<ul>
<li>application.properties</li>
</ul>
<pre><code class="language-java">#DataSource
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.url=jdbc:oracle:thin:@localhost:1521/xepdb1
spring.datasource.username=ace01
spring.datasource.password=me</code></pre>
<p>url과 name, password는 자신의 데이터베이스 설정에 맞게 수정해야한다.</p>
<h1 id="hibernate-ddl">Hibernate ddl</h1>
<ul>
<li><code>application.properties</code><pre><code class="language-java">spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.OracleDialect</code></pre>
</li>
</ul>
<p>레포지터리 구현체인 hibernate 설정을 해준다. 
<code>spring.jpa.hibernate.ddl-auto=update</code> 설정은 none 등등 다양한 설정이 가능하기 때문에 구글링해서 테스트하는 것에 맞게해주면 된다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[시큐리티 버전별 구현 방법]]></title>
            <link>https://velog.io/@jawoo-97/%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0-%EB%B2%84%EC%A0%84%EB%B3%84-%EA%B5%AC%ED%98%84-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@jawoo-97/%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0-%EB%B2%84%EC%A0%84%EB%B3%84-%EA%B5%AC%ED%98%84-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Sat, 13 Jul 2024 04:01:50 GMT</pubDate>
            <description><![CDATA[<p><a href="https://github.com/spring-projects/spring-security/releases">시큐리티버전 확인하기</a></p>
<hr>
<h1 id="시큐리티-버전별-특성">시큐리티 버전별 특성</h1>
<p>스프링은 버전에 따라 구현 방식이 변경되는데 시큐리티의 경우 특히 세부 버전별로 구현 방법이 다르기 때문에 버전 마다 구현 특징을 확인해야 한다.</p>
<h2 id="주요-버전별-구현">주요 버전별 구현</h2>
<ul>
<li><p>스프링부트 2.X.X ~ 2.6.X(스프링 5.X.X ~ 5.6.X)</p>
<pre><code class="language-java">public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

  @Override
  protected void configure(HttpSecurity http) throws Exception {

      http
              .authorizeRequests()
              .antMatchers(&quot;/&quot;).authenticated()
              .anyRequest().permitAll();

  }
}</code></pre>
</li>
<li><p>스프링부트2.7.X ~ 3.0.X (스프링 5.7.X M2 ~ 6.0.X)</p>
<pre><code class="language-java">public class SpringSecurityConfig {

  @Bean
  public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

      http
                      .authorizeHttpRequests()
                      .requestMatchers(&quot;/admin&quot;).hasRole(&quot;ADMIN&quot;)
                      .anyRequest().authenticated();

      return http.build();
  }
}</code></pre>
</li>
<li><p>스프링 부트 3.1.X ~ (스프링 6.1.X ~ )</p>
<pre><code class="language-java">public class SpringSecurityConfig {

  @Bean
  public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

      http
          .authorizeHttpRequests((auth) -&gt; auth
                .requestMatchers(&quot;/login&quot;, &quot;/join&quot;).permitAll()
                .anyRequest().authenticated()
      );

      return http.build();
  }
}</code></pre>
</li>
</ul>
<p>3.1.X 버전 부터 람다형식 표현 필수</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[요청권한 부여]]></title>
            <link>https://velog.io/@jawoo-97/%EC%9A%94%EC%B2%AD%EA%B6%8C%ED%95%9C-%EB%B6%80%EC%97%AC</link>
            <guid>https://velog.io/@jawoo-97/%EC%9A%94%EC%B2%AD%EA%B6%8C%ED%95%9C-%EB%B6%80%EC%97%AC</guid>
            <pubDate>Sat, 13 Jul 2024 02:15:25 GMT</pubDate>
            <description><![CDATA[<p>permitAll - 요청에는 권한 부여가 필요하지 않으며 퍼블릭 엔드포인트입니다. 이 경우 인증은 세션에서 검색되지 않습니다</p>
<ul>
<li><p><code>denyAll</code> - 어떠한 경우에도 요청이 허용되지 않습니다. 이 경우 세션에서 검색되지 않습니다Authentication</p>
</li>
<li><p><code>hasAuthority</code> - 요청에는 지정된 값과 일치하는 GrantedAuthority가 있어야 합니다.Authentication</p>
</li>
<li><p><code>hasRole</code> - 해당 접두사 또는 기본 접두사로 구성된 모든 항목에 대한 바로 가기hasAuthorityROLE_</p>
</li>
<li><p><code>hasAnyAuthority</code> - 요청에는 지정된 값 중 하나와 일치하는 a가 있어야 합니다.AuthenticationGrantedAuthority</p>
</li>
<li><p><code>hasAnyRole</code> - 해당 접두사 또는 기본 접두사로 구성된 모든 항목에 대한 바로 가기hasAnyAuthorityROLE_</p>
</li>
<li><p><code>access</code> - 요청은 이 사용자 지정을 사용하여 액세스를 결정합니다AuthorizationManager</p>
</li>
</ul>
<pre><code class="language-java">import static jakarta.servlet.DispatcherType.*;

import static org.springframework.security.authorization.AuthorizationManagers.allOf;
import static org.springframework.security.authorization.AuthorityAuthorizationManager.hasAuthority;
import static org.springframework.security.authorization.AuthorityAuthorizationManager.hasRole;

@Bean
SecurityFilterChain web(HttpSecurity http) throws Exception {
    http
        // ...
        .authorizeHttpRequests(authorize -&gt; authorize                                  (1)
            .dispatcherTypeMatchers(FORWARD, ERROR).permitAll() (2)
            .requestMatchers(&quot;/static/**&quot;, &quot;/signup&quot;, &quot;/about&quot;).permitAll()         (3)
            .requestMatchers(&quot;/admin/**&quot;).hasRole(&quot;ADMIN&quot;)                             (4)
            .requestMatchers(&quot;/db/**&quot;).access(allOf(hasAuthority(&quot;db&quot;), hasRole(&quot;ADMIN&quot;)))   (5)
            .anyRequest().denyAll()                                                (6)
        );

    return http.build();
}</code></pre>
<ol>
<li><p>여러 권한 부여 규칙이 지정되어 있습니다. 각 규칙은 선언된 순서대로 고려됩니다.</p>
</li>
<li><p>Spring MVC가 뷰를 렌더링하고 Spring Boot가 오류를 렌더링 할 수 있도록 디스패치하고 허용됩니다.FORWARDERROR</p>
</li>
<li><p>모든 사용자가 액세스할 수 있는 여러 URL 패턴을 지정했습니다. 특히 URL이 &quot;/static/&quot;으로 시작하거나, &quot;/signup&quot;과 같거나, &quot;/about&quot;인 경우 모든 사용자가 요청에 액세스할 수 있습니다.</p>
</li>
<li><p>&quot;/admin/&quot;으로 시작하는 모든 URL은 &quot;ROLE_ADMIN&quot; 역할을 가진 사용자로 제한됩니다. 메서드를 호출하기 때문에 &quot;ROLE_&quot; 접두사를 지정할 필요가 없다는 것을 알 수 있습니다.hasRole</p>
</li>
<li><p>&quot;/db/&quot;로 시작하는 모든 URL은 사용자에게 &quot;db&quot; 권한과 &quot;ROLE_ADMIN&quot;가 모두 부여되어야 합니다. 표현식을 사용하고 있으므로 &quot;ROLE_&quot; 접두사를 지정할 필요가 없습니다.hasRole</p>
</li>
<li><p>아직 일치하지 않는 URL은 액세스가 거부됩니다. 이는 실수로 권한 부여 규칙을 업데이트하는 것을 잊지 않으려는 경우에 좋은 전략입니다.</p>
</li>
</ol>
<h2 id="permitallignoring">permitAllignoring</h2>
<p>정적 리소스가 있는 경우 무시하도록 필터체인을 구성하는 방법
<code>permitAll</code></p>
<pre><code class="language-java">http
    .authorizeHttpRequests((authorize) -&gt; authorize
        .requestMatchers(&quot;/css/**&quot;).permitAll()
        .anyRequest().authenticated()
    )</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[인메모리 인증]]></title>
            <link>https://velog.io/@jawoo-97/%EC%9D%B8%EB%A9%94%EB%AA%A8%EB%A6%AC-%EC%9D%B8%EC%A6%9D</link>
            <guid>https://velog.io/@jawoo-97/%EC%9D%B8%EB%A9%94%EB%AA%A8%EB%A6%AC-%EC%9D%B8%EC%A6%9D</guid>
            <pubDate>Sat, 13 Jul 2024 00:39:06 GMT</pubDate>
            <description><![CDATA[<p>Spring Security는 UserDetailsService를 구현하여 메모리에 저장된 사용자 이름/암호 기반 인증을 지원합니다. </p>
<p>다음은 암호 값을 인코딩하고 인코딩된 암호를 가져옵니다.</p>
<pre><code class="language-java">@Bean
public UserDetailsService users() {
    UserDetails user = User.builder()
        .username(&quot;user&quot;)
        .password(&quot;{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW&quot;)
        .roles(&quot;USER&quot;)
        .build();
    UserDetails admin = User.builder()
        .username(&quot;admin&quot;)
        .password(&quot;{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW&quot;)
        .roles(&quot;USER&quot;, &quot;ADMIN&quot;)
        .build();
    return new InMemoryUserDetailsManager(user, admin);
}</code></pre>
<p>다음 예제에서는 Encoder를 사용하여 메모리에 저장된 암호가 보호되도록 합니다.</p>
<pre><code class="language-java">
@Bean
public UserDetailsService users() {
    // The builder will ensure the passwords are encoded before saving in memory
    UserBuilder users = User.withDefaultPasswordEncoder();
    UserDetails user = users
        .username(&quot;user&quot;)
        .password(&quot;password&quot;)
        .roles(&quot;USER&quot;)
        .build();
    UserDetails admin = users
        .username(&quot;admin&quot;)
        .password(&quot;password&quot;)
        .roles(&quot;USER&quot;, &quot;ADMIN&quot;)
        .build();
    return new InMemoryUserDetailsManager(user, admin);
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[로그인]]></title>
            <link>https://velog.io/@jawoo-97/%EB%A1%9C%EA%B7%B8%EC%9D%B8</link>
            <guid>https://velog.io/@jawoo-97/%EB%A1%9C%EA%B7%B8%EC%9D%B8</guid>
            <pubDate>Sat, 13 Jul 2024 00:27:31 GMT</pubDate>
            <description><![CDATA[<p>Spring Security는 HTML Form을 통해 제공되는 사용자 이름 및 암호를 지원합니다.
<img src="https://velog.velcdn.com/images/jawoo-97/post/b46299e9-74e4-4ddf-94a1-a66c05754cf2/image.png" alt=""></p>
<ol>
<li>사용자는 권한이 부여되지 않은 리소스(<code>/private</code>)에 요청을 합니다.</li>
<li>AuthorizationFilter는 인증되지 않은 요청이 AccessDeniedException을 뱉습니다.</li>
<li>인증되지 않았으므로 <code>ExceptionTranslationFilter</code>는  AuthenticationEntryPoint를 사용하여 로그인 페이지로 리디렉션을 보냅니다.</li>
<li>로그인 페이지를 요청합니다.</li>
<li>로그인 페이지를 렌더링합니다.</li>
</ol>
<p>form이 제출 되면 <code>UsernamePasswordAuthenticationFiter</code>로 인증합니다.</p>
<p><img src="https://velog.velcdn.com/images/jawoo-97/post/0945bf8e-7c27-43f5-aba8-89d764fd3b73/image.png" alt="">
이 그림은 <code>SecurityFilterChain</code> 다이어그램을 기반으로 합니다.</p>
<ol>
<li><p><code>UsernamePasswordAuthenticationToken</code> 인스턴스에서 사용자 이름과 암호를 추출하여 인증합니다.</p>
</li>
<li><p>인증에 실패하면</p>
<ul>
<li><code>SecurityContextHolder</code>가 지워집니다.</li>
<li>RememberMeServices.loginFail 가 호출됩니다. </li>
<li>AuthenticationFailureHandler 가 호출됩니다. </li>
</ul>
</li>
<li><p>인증에 성공하면</p>
<ul>
<li>SessionAuthenticationStrategy 새 로그인에 대한 알림을 받습니다.</li>
<li>인증은 SecurityContextHolder에 설정됩니다.</li>
<li>RememberMeServices.loginSuccess 가 호출됩니다. </li>
<li>AplicationEventPublisher 를 게시합니다.</li>
<li>AuthenticationSuccessHandler 가 호출됩니다. 일반적으로 로그인 페이지로 리디렉션할 때 ExceptionTranslationFilter에 의해 저장된 요청으로 리디렉션</li>
</ul>
</li>
</ol>
<hr>
<h2 id="실습">실습</h2>
<p>Config 설정 후 로그인 페이지
Spring Security는 Config 클래스 설정 후 특정 경로에 대한 접근 권한이 없는 경우 자동으로 로그인 페이지로 리다이렉팅 되지 않고 오류 페이지가 발생합니다.</p>
<p>이 문제를 해결하기 위해 Config 클래스를 설정하면 로그인 페이지 설정도 진행해야 합니다.</p>
<hr>
<h3 id="커스텀-로그인-페이지">커스텀 로그인 페이지</h3>
<pre><code class="language-html"> login page
    &lt;hr&gt;
    &lt;form action=&quot;/loginProc&quot; method=&quot;post&quot; name=&quot;loginForm&quot;&gt;
        &lt;input id=&quot;username&quot; type=&quot;text&quot; name=&quot;username&quot; placeholder=&quot;id&quot;/&gt;
        &lt;input id=&quot;password&quot; type=&quot;password&quot; name=&quot;password&quot; placeholder=&quot;password&quot;/&gt;
        &lt;input type=&quot;submit&quot; value=&quot;login&quot;/&gt;
    &lt;/form&gt;</code></pre>
<p>/locginProc 경로로 Post 요청</p>
<hr>
<p>Login Controller</p>
<pre><code class="language-java">@Controller
public class LoginController {

    @GetMapping(&quot;/login&quot;)
    public String loginP() {

        return &quot;login&quot;;
    }
}
</code></pre>
<hr>
<p>Security Config 로그인 페이지 설정 및 로그인 경로</p>
<pre><code class="language-java">package com.example.testsecurity.config;

import jakarta.servlet.FilterChain;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {


    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {


        http
                .authorizeHttpRequests(auth -&gt; auth
                .requestMatchers(&quot;/&quot;, &quot;/login&quot;, &quot;/loginProc&quot;).permitAll()
                .requestMatchers(&quot;/admin&quot;).hasRole(&quot;ADMIN&quot;)
                .requestMatchers(&quot;/my/**&quot;).hasAnyRole(&quot;USER&quot;, &quot;ADMIN&quot;)
                .anyRequest().authenticated()
        );

        http
                .formLogin((auth) -&gt; auth.loginPage(&quot;/login&quot;)
                        .loginProcessingUrl(&quot;/loginProc&quot;)
                        .permitAll()
                );

        http
                .csrf(AbstractHttpConfigurer::disable); // 비활성화
        /*csrf는 스프링 시큐리티에서 구현해두었다.  로그인 시 POST 요청 시 CSRF 토큰을 함께 전송해야 하기 때문에 학습과 테스트를 위해 비 활성화*/

        return http.build();
    }
}
</code></pre>
<p><code>http://localhost:8080/admin</code>로 페이지 요청시 권한이 없기 때문에 <code>AuthorizationFilter</code>는 인증되지 않은 요청이 <code>AccessDeniedException</code>을 뱉습니다.</p>
<p>인증되지 않았으므로 <code>ExceptionTranslationFilter</code>는  <code>AuthenticationEntryPoint</code>를 사용하여 로그인 페이지로 리디렉션을 보냅니다.</p>
<p>위에서 만든 login.html로 리디렉션 하고 화면을 렌더링 합니다.</p>
<p><img src="https://velog.velcdn.com/images/jawoo-97/post/044c7a31-aa6f-4ac7-8040-2d3545bfd2ee/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[사용자 이름/비밀번호 인증]]></title>
            <link>https://velog.io/@jawoo-97/%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%9D%B4%EB%A6%84%EB%B9%84%EB%B0%80%EB%B2%88%ED%98%B8-%EC%9D%B8%EC%A6%9D</link>
            <guid>https://velog.io/@jawoo-97/%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%9D%B4%EB%A6%84%EB%B9%84%EB%B0%80%EB%B2%88%ED%98%B8-%EC%9D%B8%EC%A6%9D</guid>
            <pubDate>Fri, 12 Jul 2024 15:21:21 GMT</pubDate>
            <description><![CDATA[<p>사용자를 인증하는 가장 일반적인 방법 중 하나는 사용자 이름과 암호를 확인하는 것입니다. Spring Security는 사용자 이름 및 암호를 사용한 인증에 대한 지원을 합니다.</p>
<p><em>간단한 사용자 이름/비밀번호 예</em></p>
<pre><code class="language-java">@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests((authorize) -&gt; authorize
                .anyRequest().authenticated()
            )
            .httpBasic(Customizer.withDefaults())
            .formLogin(Customizer.withDefaults());

        return http.build();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails userDetails = User.withDefaultPasswordEncoder()
            .username(&quot;user&quot;)
            .password(&quot;password&quot;)
            .roles(&quot;USER&quot;)
            .build();

        return new InMemoryUserDetailsManager(userDetails);
    }

}</code></pre>
<h2 id="authenticationmanager">AuthenticationManager</h2>
<p><em>사용자 정의 인증을 위한 Bean 공개AuthenticationManager</em></p>
<pre><code class="language-java">@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests((authorize) -&gt; authorize
                .requestMatchers(&quot;/login&quot;).permitAll()
                .anyRequest().authenticated()
            );

        return http.build();
    }

    @Bean
    public AuthenticationManager authenticationManager(
            UserDetailsService userDetailsService,
            PasswordEncoder passwordEncoder) {
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        authenticationProvider.setUserDetailsService(userDetailsService);
        authenticationProvider.setPasswordEncoder(passwordEncoder);

        return new ProviderManager(authenticationProvider);
    }

    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails userDetails = User.withDefaultPasswordEncoder()
            .username(&quot;user&quot;)
            .password(&quot;password&quot;)
            .roles(&quot;USER&quot;)
            .build();

        return new InMemoryUserDetailsManager(userDetails);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

}
</code></pre>
<p>위의 구성을 사용하면 <code>@RestController</code>를 만들 수 있습니다.</p>
<p><em>@RestController</em></p>
<pre><code class="language-java">package com.example.hellosecurity.controller;

import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class LoginController {

    private final AuthenticationManager authenticationManager;

    public LoginController (AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }

    @PostMapping(&quot;/login&quot;)
    public ResponseEntity&lt;Void&gt; login(@RequestBody LoginRequest loginRequest) {
        Authentication authenticationRequest =
                UsernamePasswordAuthenticationToken.unauthenticated(loginRequest.username(), loginRequest.password());
        Authentication authenticationResponse =
                this.authenticationManager.authenticate(authenticationRequest);
        // ...
        return ResponseEntity.ok().build();
    }

    public record LoginRequest(String username, String password) {
    }
}
</code></pre>
<br>
---

<h2 id="사용자-지정-authenticationmanager">사용자 지정 AuthenticationManager</h2>
<p>일반적으로 Spring Security는 사용자 이름/암호 인증을 위해 내부적으로 구성된<code>AuthenticationManager</code>를 빌드합니다.  사용하는 인스턴스를 사용자 정의하는 것이 여전히 필요할 수 있습니다. 예를 들어 캐시된 사용자에 대해 자격 증명 지우기를 사용하지 않도록 설정해야 할 수 있습니다.</p>
<pre><code class="language-java">
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        //...
    }

    @Bean
    public AuthenticationManager authenticationManager(
            UserDetailsService userDetailsService,
            PasswordEncoder passwordEncoder) {
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        authenticationProvider.setUserDetailsService(userDetailsService);
        authenticationProvider.setPasswordEncoder(passwordEncoder);

        ProviderManager providerManager = new ProviderManager(authenticationProvider);
        providerManager.setEraseCredentialsAfterAuthentication(false);

        return providerManager;
    }

    @Bean
    public UserDetailsService userDetailsService() {
        //...
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

}</code></pre>
<p>또는 Spring Security의 전역을 빌드하는 데 다음과 같이 빌더를 구성할 수 있습니다.</p>
<p><em>전역 구성 AuthenticationManagerBuilder</em></p>
<pre><code class="language-java">@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        // ...
        return http.build();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        // Return a UserDetailsService that caches users
        // ...
    }

    @Autowired
    public void configure(AuthenticationManagerBuilder builder) {
        builder.eraseCredentials(false);
    }

}</code></pre>
<hr>
<h1 id="로그인-검증-로직">로그인 검증 로직</h1>
<p><img src="https://velog.velcdn.com/images/jawoo-97/post/76951247-6b3e-4a17-9dda-b83fb9d1c285/image.png" alt=""></p>
<p>DaoAuthenticationProvider는 <code>UserDetailsService</code> 및 <code>PasswordEncoder</code>를 사용하여 사용자 이름과 암호를 인증하는 AuthenticationProvider 구현입니다.</p>
<p>로그인 검증 로직을 작성하기 위해 UserDetailsService를 구현해보겠다.</p>
<pre><code class="language-java">@Service
public class CustomUserDetailsService implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;


    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        UserEntity userData = userRepository.findByUsername(username);

        if (userData != null) {

            return new CustomUserDetails(userData);
        }

        return null;
    }
}</code></pre>
<p> DaoAuthenticationProvider 에서 <code>UserDetails</code>를 조회합니다. <code>UserDetailsService</code></p>
<h2 id="userdetailsservice에서-userdetails를-반환합니다-daoauthenticationprovider는-유효성을-검사한-다음-구성된-보안-주체가-있는-인증을-반환합니다"> <code>UserDetailsService</code>에서 <code>UserDetails</code>를 반환합니다. DaoAuthenticationProvider는 유효성을 검사한 다음 구성된 보안 주체가 있는 인증을 반환합니다.</h2>
<p> <code>UserDetails</code> 인터페이스 구현</p>
<pre><code class="language-java"> package com.example.testsecurity.config;

import com.example.testsecurity.entity.UserEntity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public class CustomUserDetails implements UserDetails {

    private UserEntity userEntity;

    public CustomUserDetails(UserEntity userEntity) {

        this.userEntity = userEntity;
    }

    @Override
    public Collection&lt;? extends GrantedAuthority&gt; getAuthorities() {
        Collection &lt;GrantedAuthority&gt; authorities = new ArrayList&lt;&gt;();
        authorities.add(new GrantedAuthority() {

            @Override
            public String getAuthority() {
                return userEntity.getRole();
            }
        });

        return authorities;
    }

    @Override
    public String getPassword() {
        return userEntity.getPassword();
    }

    @Override
    public String getUsername() {
        return userEntity.getUsername();
    }
}
</code></pre>
<h1 id="정리">정리</h1>
<p> <img src="https://velog.velcdn.com/images/jawoo-97/post/e1e7eb4c-2d4c-45c5-a171-1374339d08d1/image.png" alt=""></p>
<p>로그인 요청이 들어오면 Security Config는 유효한 아이디인지 검사한다.</p>
<ol>
<li>UserDetailsService 에서 user정보를 로드하여 UserDetails 객체를 뱉는다.</li>
<li>생성된 userDetails 객체는 DaoAuthenticationProvider에  유효성을 검사한 다음 구성된 보안 주체가 있는 인증을 반환합니다</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[인증 _ 스프링시큐리티]]></title>
            <link>https://velog.io/@jawoo-97/%EC%9D%B8%EC%A6%9D-%EC%8A%A4%ED%94%84%EB%A7%81%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0</link>
            <guid>https://velog.io/@jawoo-97/%EC%9D%B8%EC%A6%9D-%EC%8A%A4%ED%94%84%EB%A7%81%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0</guid>
            <pubDate>Fri, 12 Jul 2024 14:53:06 GMT</pubDate>
            <description><![CDATA[<h1 id="서블릿-인증-아키텍쳐">서블릿 인증 아키텍쳐</h1>
<ul>
<li>SecurityContextHolder - Spring Security가 인증된 사용자의 세부 정보를 저장하는 위치입니다.</li>
<li>SecurityContext - 현재 인증된 사용자의 SecurityContextHolder및 Authentication를 포함</li>
<li>Authentication - 사용자가 인증하기 위해 제공한 자격 증명을 제공하기 위한 입력 <code>AuthenticationManager</code></li>
<li>GrantedAuthority - <code>Authentication</code>에서 역할, 범위 등 보안 주체에게 부여되는 권한</li>
<li>AuthenticationManager - Spring Security의 필터가 인증을 수행하는 방법을 정의하는 API입니다.</li>
<li>ProviderManager - AuthenticationManager의 가장 일반적인 구현이다.</li>
<li>AuthenticationProvider - 특정 유형의 인증을 수행하는 데 사용</li>
<li>AuthenticationEntryPoint - 클라이언트에서 자격 증명을 요청하는 데 사용(로그인 페이지로 리디렉션, 응답 보내기 등)</li>
<li>AbstractAuthenticationProcessingFilter - 인증에 사용되는 기반</li>
</ul>
<h2 id="securitycontextholder">SecurityContextHolder</h2>
<p><img src="https://velog.velcdn.com/images/jawoo-97/post/ccec7446-4b89-4316-9630-6f89bd456485/image.png" alt="">
Spring Security 인증 모델의 핵심</p>
<p><em>설정 SecurityContextHolder</em></p>
<pre><code class="language-java">SecurityContext context = SecurityContextHolder.createEmptyContext();
Authentication authentication =
    new TestingAuthenticationToken(&quot;username&quot;, &quot;password&quot;, &quot;ROLE_USER&quot;);
context.setAuthentication(authentication);

SecurityContextHolder.setContext(context);</code></pre>
<ul>
<li>여러 스레드에서 경합 상태를 피하기 위해 새 SecurtyContext 인스턴스를 생성</li>
<li>다음으로 새 Authentication 개체를 만듭니다. </li>
<li>생성한 인증개체를 context에 저장합니다.</li>
<li>ContextHolder에 context를 저장합니다.</li>
</ul>
<p><em>현재 인증된 사용자 액세스</em></p>
<pre><code class="language-java">SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
String username = authentication.getName();
Object principal = authentication.getPrincipal();
Collection&lt;? extends GrantedAuthority&gt; authorities = authentication.getAuthorities();</code></pre>
<hr>
<h2 id="보안-컨텍스트">보안 컨텍스트</h2>
<p>Authentication</p>
<ul>
<li>principal : 사용자를 식별합니다. 사용자 이름/ 비밀번호로 인증할 때 UserDetails의 인스턴스입니다.</li>
<li>authorities: GrantedAuthority 인스턴스는 사용자에게 부여되는 권한입니다.</li>
</ul>
<hr>
<h2 id="권한부여">권한부여</h2>
<p><code>Authentication.getAuthorities()</code>메서드에서 인스턴스를 가져올 수 있습니다.
이러한권한 역할은 나중에 웹 권한 부여, 메소드 권한 부여 및 도메인 오브젝트 권한 부여를 위해 구성됩니다.</p>
<hr>
<h2 id="인증-관리자">인증 관리자</h2>
<p><code>AuthenticationManager</code>는 Spring Security의 필터가 인증을 수행하는 방법을 정의하는 API입니다. 직접 설정할 수 있으며 <code>AuthenticationManager</code> 구현은 무엇이든 될 수 있지만 가장 일반적인 구현은 <code>ProviderManager</code>입니다.</p>
<hr>
<h2 id="프로바이더-매니저">프로바이더 매니저</h2>
<p><code>ProviderManager</code>는 가장 일반적으로 사용되는 구현입니다.
<img src="https://velog.velcdn.com/images/jawoo-97/post/cde01cb4-e515-4bb3-bf0f-eb00f7bdacaa/image.png" alt="">
<code>AuthenticationProvider</code> 인스턴스에 위임합니다. 각각의 인증이 성공 또는 실패해야 함을 나타내거나 결정을 내릴 수 없음을 나타내고 다운스트림이 결정할 수 있도록 할 수 있습니다.</p>
<p>인증이 수행될 수 없는 경우 참조되는 부모를 선택할 수 있습니다. 여러 인스턴스가 동일한 부모를 공유할 수 있습니다. 이는 공통된 인증이 있지만 서로 다른 인증 메커니즘이 있는 여러 <code>SecurityFilterChain</code> 인스턴스가 있는 시나리오에서 다소 일반적입니다.</p>
<p><img src="https://velog.velcdn.com/images/jawoo-97/post/66d7fdba-18a8-4b15-8b5e-cfef21ffbee0/image.png" alt="">
<img src="https://velog.velcdn.com/images/jawoo-97/post/226aa908-05db-49db-9d35-2a8090d650a1/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[스프링 시큐리티 암호화 모듈]]></title>
            <link>https://velog.io/@jawoo-97/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0-%EC%95%94%ED%98%B8%ED%99%94-%EB%AA%A8%EB%93%88</link>
            <guid>https://velog.io/@jawoo-97/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0-%EC%95%94%ED%98%B8%ED%99%94-%EB%AA%A8%EB%93%88</guid>
            <pubDate>Fri, 12 Jul 2024 11:34:15 GMT</pubDate>
            <description><![CDATA[<h1 id="암호화">암호화</h1>
<p><code>Encryptors</code> 클래스는 대칭 암호화기를 생성하기 위한 팩터리 메서드를 제공합니다. 이 클래스를 사용하면 <code>BytesEncryptor</code> 인스턴스를 만들어 원시 형식의 데이터를 암호화 할 수 있습니다.</p>
<h2 id="bytesencryptor-바이트-암호화기">BytesEncryptor (바이트 암호화기)</h2>
<p>팩토리 메소드를 사용해서 생성할 수 있습니다.</p>
<pre><code class="language-java">Encryptors.stronger(&quot;password&quot;, &quot;salt&quot;);</code></pre>
<p>암호화 방법은 256비트 AES 암호화를 사용하여 암호화기를 만듭니다. 솔트는 암호화된 데이터가 손상된 경우 키에 대한 사전 공격을 방지하는 데 사용됩니다.</p>
<p>제공된 salt는 16진수로 인코딩된 문자열 형식이어야 하며 임의적이어야 하며 길이가 8바이트 이상이어야 합니다.</p>
<pre><code class="language-java">String salt = KeyGenerators.string().generateKey(); 
// generates a random 8-byte salt that is then hex-encoded</code></pre>
<h2 id="textencryptor-텍스트-암호화기">TextEncryptor (텍스트 암호화기)</h2>
<p>팩토리 메소드를 사용해 표준 TextEncryptor를 생성할 수 있습니다.</p>
<pre><code class="language-java">Encryptors.text(&quot;password&quot;, &quot;salt&quot;);</code></pre>
<hr>
<h1 id="암호-인코딩">암호 인코딩</h1>
<p><code>spring-security-crypto PasswordEncoder</code>는 중앙 서비스 인터페이스이며 다음과 같은 서명이 있습니다.</p>
<pre><code class="language-java">public interface PasswordEncoder {
    String encode(CharSequence rawPassword);

    boolean matches(CharSequence rawPassword, String encodedPassword);

    default boolean upgradeEncoding(String encodedPassword) {
        return false;
    }
}</code></pre>
<p><code>matches</code> 메서드는 <code>rawPassword</code>가 인코딩되면 true를 반환합니다. </p>
<hr>
<p><em>BCryptPassword 인코더</em></p>
<p>이 구현은 널리 지원되는 <code>bcrypt</code>알고리즘을 사용하여 암호를 해시합니다. <code>bcrypt</code>는 임의의 16바이트 솔트 값을 사용하며 암호 크래커를 방해하기 위해 의도적으로 느린 알고리즘입니다.</p>
<pre><code class="language-java">// Create an encoder with strength 16
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(16);
String result = encoder.encode(&quot;myPassword&quot;);
assertTrue(encoder.matches(&quot;myPassword&quot;, result));</code></pre>
<br>

<hr>
<p><em>Pdkdf2PasswordEncoder 인코더</em></p>
<p>PBKDF2 알고리즘을 사용하여 암호를 해시합니다. 암호 크래킹을 무력화하기 위해 의도적으로 느린 알고리즘이며 시스템에서 암호를 확인하는 데 약 .5초가 걸리도록 조정해야 합니다.</p>
<pre><code class="language-java">
// Create an encoder with all the defaults
Pbkdf2PasswordEncoder encoder = Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8();
String result = encoder.encode(&quot;myPassword&quot;);
assertTrue(encoder.matches(&quot;myPassword&quot;, result));</code></pre>
<hr>
<h1 id="정리">정리</h1>
<p>Spring Security는 사용자 인증(로그인)시 비밀번호에 대해 단뱡향 <code>해시 암호화</code> 를 진행하며 저장되어 있는 비밀번호와 대조한다. 따라서 회원가입 시 비밀번호 항목에 대해서 암호화를 진행해야한다. </p>
<p>스프링 시큐리티는 암호화를 위해 <code>BCrypt</code> <strong>Password Encoder</strong>를 제공하고 권장한다. 따라서 해당 클래스를 return하는 메소드를 만들어 @Bean으로 등록하여 사용하면 된다.</p>
<h2 id="security-config-암호화-bean-추가">Security Config 암호화 Bean 추가</h2>
<pre><code class="language-java">public confing {
    ...

    @Bean
    public BcryptPasswordEncoder bcryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[악용으로부터 보호]]></title>
            <link>https://velog.io/@jawoo-97/%EC%95%85%EC%9A%A9%EC%9C%BC%EB%A1%9C%EB%B6%80%ED%84%B0-%EB%B3%B4%ED%98%B8</link>
            <guid>https://velog.io/@jawoo-97/%EC%95%85%EC%9A%A9%EC%9C%BC%EB%A1%9C%EB%B6%80%ED%84%B0-%EB%B3%B4%ED%98%B8</guid>
            <pubDate>Fri, 12 Jul 2024 11:11:53 GMT</pubDate>
            <description><![CDATA[<h1 id="권한-부여">권한 부여</h1>
<br>

<p><strong>권한 부여</strong> : 특정 리소스에 액세스할 수 있는 사용자를 결정하는 것입니다.
Spring Security는 요청 기반 권한 부여 및 메서드 기반 권한 부여를 허용하여 심층 방어를 제공합니다.</p>
<hr>
<h1 id="악용으로부터-보호">악용으로부터 보호</h1>
<h2 id="교차-사이트-요청-위조-csrf">교차 사이트 요청 위조 (CSRF)</h2>
<br>

<p>Spring은 CSRF(Cross Site Request Forgery) 공격으로부터 보호하기 위한 포괄적인 지원을 제공합니다.</p>
<ul>
<li>CSRF 공격이란?</li>
<li>CSRF 공격으로부터 보호</li>
<li>CSRF 고려 사항</li>
</ul>
<h3 id="csrf-공격이란">CSRF 공격이란?</h3>
<p>은행 웹 사이트에서 현재 로그인한 사용자의 돈을 다루는 은행계좌로 이체할 수 있는 양식을 제공한다고 가정합니다. 예를 들어 전송 양식은 다음과 같을 수 있다.</p>
<pre><code class="language-html">&lt;form method=&quot;post&quot; action=&quot;/transfer&quot;&gt;
    &lt;input type=&quot;text&quot; name=&quot;amount&quot;/&gt;
    &lt;input type=&quot;text&quot; name=&quot;routingNumber&quot;/&gt;
    &lt;input type=&quot;text&quot; name=&quot;account&quot;/&gt;
    &lt;input type=&quot;submit&quot; value=&quot;Transfer&quot;/&gt;
&lt;/form&gt;</code></pre>
<br>

<p><strong>HTTP 요청 전송</strong></p>
<blockquote>
<p>POST /transfer HTTP/1.1
Host: bank.example.com
Cookie: JSESSIONID=randomid
Content-Type: application/x-www-form-urlencoded</p>
</blockquote>
<p>amount=100.00&amp;routingNumber=1234&amp;account=9876</p>
<br>

<p><strong>악용 양식</strong></p>
<pre><code class="language-html">&lt;form method=&quot;post&quot; action=&quot;https://bank.example.com/transfer&quot;&gt;
    &lt;input type=&quot;hidden&quot; name=&quot;amount&quot; value=&quot;100.00&quot;/&gt;
    &lt;input type=&quot;hidden&quot; name=&quot;routingNumber&quot;value=&quot;evilsRoutingNumber&quot;/&gt;
    &lt;input type=&quot;hidden&quot;name=&quot;account&quot;value=&quot;evilsAccountNumber&quot;/&gt;
    &lt;input type=&quot;submit&quot;value=&quot;Win Money!&quot;/&gt;
&lt;/form&gt;</code></pre>
<p>이 과정에서 악의적인 사용자에게 돈이 이체됐습니다. 이 전체 프로세스가 JavaScript를 사용하여 자동화 될 수 있습니다. 즉, 버튼을 클릭할 필요조차 없습니다. 또한 XSS 공격의 피해자인 정직한 사이트를 방문할 때도 쉽게 발생할 수 있습니다. </p>
<hr>
<h3 id="csrf-공격으로부터-보호">CSRF 공격으로부터 보호</h3>
<p>CSRF 공격이 가능한 이유는 피해자 웹 사이트의 HTTP 요청과 공격자 요청이 정확히 동일하기 때문입니다. 즉, 악의적인 웹 사이트에서 오는 요청과 은행 웹 사이트에서 오는 요청을 구분하여 허용할 수 있는 방법이 없기 떄문입니다.</p>
<p>Spring 은 CSRF 공격으로부터 보호하기 위해 두 가지 메커니즘을 제공</p>
<ul>
<li>동기화 장치 토큰 패턴</li>
<li>세션 쿠키에 SameSite 특성 지정</li>
</ul>
<p><strong>동기화 장치 토큰 패턴</strong>
HTTP 요청에 세션 쿠키 외에도 CSRF 토큰이라는 안전한 무작위 생성 값이 HTTP 요청에 존재하도록 하는 것입니다.</p>
<p>HTTP 요청이 제출되면 서버는 예상 CSRF 토큰을 조회하고 HTTP 요청의 실제 CSRF 토큰과 비교해야합니다. 값이 일치하지 않으면 거부합니다.</p>
<p>예를 들어 HTTP 매개 변수 또는 HTTP 헤더에 실제 CSRF 토큰을 요구하면 CSRF 공격으로부터 보호할 수 있습니다. </p>
<p>(애플리케이션은 안전한 HTTP 메서드가 읽기 전용인지 확인해야 합니다.)</p>
<pre><code class="language-html">&lt;form method=&quot;post&quot; action=&quot;/transfer&quot;&gt;
    &lt;input type=&quot;hidden&quot; name=&quot;_csrf&quot; value=&quot;4bfd1575-3ad1-4d21-96c7-4ef2d9f86721&quot;/&gt;
    &lt;input type=&quot;text&quot; name=&quot;amount&quot;/&gt;
    &lt;input type=&quot;text&quot; name=&quot;routingNumber&quot;/&gt;
    &lt;input type=&quot;hidden&quot; name=&quot;account&quot;/&gt;
    &lt;input type=&quot;submit&quot; value=&quot;Transfer&quot;/&gt;
&lt;/form&gt;</code></pre>
<p>이제 양식에 CSRF 토큰 값이 있는 숨겨진 입력이 포함됩니다. 악의적인 사이트가 응답을 읽을 수 없으므로 외부사이트는 CSRF 토큰을 읽을 수 없습니다.</p>
<br>

<p><strong>Synchronizer Token Pattern 토큰 요청</strong></p>
<blockquote>
<p>POST /transfer HTTP/1.1
Host: bank.example.com
Cookie: JSESSIONID=randomid
Content-Type: application/x-www-form-urlencoded</p>
</blockquote>
<p>amount=100.00&amp;routingNumber=1234&amp;account=9876&amp;_csrf=4bfd1575-3ad1-4d21-96c7-4ef2d9f86721</p>
<p>이제 요청에 임의 값이 있는 매개변수가 포함되어 있습니다. 악의적인 웹 사이트는  매개 변수에 대한 올바른 값을 제공 할 수 없으며 서버가 실제 CSRF 토큰을 예상 CSRF 토큰과 비교할 때 전송이 실패합니다.</p>
<h2 id="csrf-보호를-사용하는-경우">CSRF 보호를 사용하는 경우</h2>
<p>일반 사용자가 브라우저에서 처리할 수 있는 모든 요청에 대해 CSRF 보호를 사용하는 것이 좋습니다. 브라우저가 아닌 클라이언트에서만 사용되는 서비스를 만드는 경우 CSRF 보호를 비활성화 할 수 있다.</p>
<h3 id="csrf-보호-및-json">CSRF 보호 및 JSON</h3>
<p>&quot;JavaScript에서 만든 JSON 요청을 보호해야 합니까?
의적인 사용자는 다음 양식을 사용하여 JSON으로 CSRF를 만들 수 있습니다.
JSON 양식이 있는 CSRF</p>
<pre><code class="language-html">&lt;form action=&quot;https://bank.example.com/transfer&quot; method=&quot;post&quot; enctype=&quot;text/plain&quot;&gt;
    &lt;input name=&#39;{&quot;amount&quot;:100,&quot;routingNumber&quot;:&quot;evilsRoutingNumber&quot;,&quot;account&quot;:&quot;evilsAccountNumber&quot;, &quot;ignore_me&quot;:&quot;&#39; value=&#39;test&quot;}&#39; type=&#39;hidden&#39;&gt;
    &lt;input type=&quot;submit&quot;
        value=&quot;Win Money!&quot;/&gt;
&lt;/form&gt;</code></pre>
<p>JSON 요청이 있는 CSRF</p>
<pre><code class="language-JSON">{ 
  &quot;amount&quot;: 100,
  &quot;routingNumber&quot;: &quot;evilsRoutingNumber&quot;,
  &quot;account&quot;: &quot;evilsAccountNumber&quot;,
  &quot;ignore_me&quot;: &quot;=test&quot;
}</code></pre>
<p>응용 프로그램이 헤더의 유효성을 검사하지 않으면 이 악용에 노출됩니다.
다음과 같이 URL 접미사를 .json로 끝나도록 합니다.</p>
<pre><code class="language-html">&lt;form action=&quot;https://bank.example.com/transfer.json&quot; method=&quot;post&quot; enctype=&quot;text/plain&quot;&gt;
    &lt;input name=&#39;{&quot;amount&quot;:100,&quot;routingNumber&quot;:&quot;evilsRoutingNumber&quot;,&quot;account&quot;:&quot;evilsAccountNumber&quot;, &quot;ignore_me&quot;:&quot;&#39; value=&#39;test&quot;}&#39; type=&#39;hidden&#39;&gt;
    &lt;input type=&quot;submit&quot;
        value=&quot;Win Money!&quot;/&gt;
&lt;/form&gt;</code></pre>
<h2 id="csrf-고려-사항">CSRF 고려 사항</h2>
<h3 id="로그인">로그인</h3>
<p>로그인 요청 위조를 방지하려면 로그인 HTTP 요청을 CSRF 공격으로부터 보호해야 합니다.</p>
<h3 id="로그아웃">로그아웃</h3>
<p>로그아웃 요청 위조를 방지하려면 로그아웃 HTTP 요청을 CSRF 공격으로부터 보호해야 합니다.</p>
<h3 id="csrf-및-세션-시간-초과">CSRF 및 세션 시간 초과</h3>
<p>애부분 CSRF 토큰이 세션에 저장됩니다. 즉, 세션이 만료되는 즉시 서버는 CSRF 토큰을 찾지 못하고 HTTP 요청을 거부합니다. </p>
<ul>
<li>JavaScript를 사용하여 양식 제출 시 CSRF 토큰을 요청하여 업데이트 되고 제출</li>
<li>사용자에게 세션이 곧 만료될 것임을 알려주는 JavaScript를 사용</li>
<li>쿠키에 저장</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[21년 3회 정처기실기 오답]]></title>
            <link>https://velog.io/@jawoo-97/21%EB%85%84-3%ED%9A%8C-%EC%A0%95%EC%B2%98%EA%B8%B0%EC%8B%A4%EA%B8%B0-%EC%98%A4%EB%8B%B5</link>
            <guid>https://velog.io/@jawoo-97/21%EB%85%84-3%ED%9A%8C-%EC%A0%95%EC%B2%98%EA%B8%B0%EC%8B%A4%EA%B8%B0-%EC%98%A4%EB%8B%B5</guid>
            <pubDate>Fri, 12 Jul 2024 08:27:59 GMT</pubDate>
            <description><![CDATA[<h1 id="2번">2번</h1>
<p>*<em>(  ) *</em>스푸핑은 로컬 네트워크에서 사용하는 *<em>( ) *</em>프로토콜의 취약점을 이용한 공격기법으로 자신의 물리적 주소(MAC)를 변조하여 다른 PC에게 도달해야하는 데이터 패킷을 가로채거나 방해</p>
<br>
정답 : ARP

<hr>
<h1 id="5번">5번</h1>
<p>(  ) 패턴은 객체 생성을 서브 클래스에서 처리하도록 분리하여 캡슐화한 패턴으로, 상위 클래스에서 인터페이스만 정의하고 실제 생성은 서브 클래스가 담당한다. Virtual Constructor 패턴이라고도 불린다.</p>
<p>정답 : Factory Method</p>
<hr>
<h1 id="6번">6번</h1>
<p>결합도(Coupling)의 종류 중 단순 처리 대상인 데이터만 전달되는 것이 아니라 어떻게 처리해야 하는지를 결정하는 제어 요소가 전달되는 경우의 결합도를 영문으로 쓰시오.</p>
<p>정답 : Control</p>
<hr>
<h1 id="7번">7번</h1>
<p>구조체의 멤버를 지정할 때는 [변수명].[멤버이름]으로 지정하지만, 포인터 변수를 이용해 구조체의 멤버를 지정할 때는 [변수명]-&gt;[멤버이름]으로 지정한다.</p>
<pre><code class="language-c">#include &lt;stdio.h&gt;
struct jsu {


    char nae[12];
    int os, db, hab, hhab;
};
int main(){
    struct jsu st[3] = { {&quot;데이터1&quot;, 95, 88 }, {&quot;데이터2&quot;, 84, 91}, {&quot;데이터3&quot;, 86, 75} };
    struct jsu* p;
    p = &amp;st[0];
    (p + 1) -&gt;hab = (P + 1)-&gt;os + (p+2)-&gt;db;
    (p + 1) -&gt; hhab = (p + 1) -&gt;hab + p -&gt; os + p -&gt;db;
    printf(&quot;%d&quot;,(p+1)-&gt;hab+(p+1)-&gt;hhab);
}</code></pre>
<hr>
<h1 id="11번">11번</h1>
<p>파일 구조는 파일을 구성하는 보조기억장치에 편성되는 방식을 의미한다. 크게 순차, ( ), 해싱으로 구분한다. ( )파일 구조는 &lt;값, 주소&gt; 쌍으로 구성되는 데이터 구조를 활용, 접근한다.</p>
<p>정답 : 색인</p>
<hr>
<h1 id="18번">18번</h1>
<p>테스트 기법 중 그래프를 활용하여 영향을 미치는 상황을 분석한 다음 효용성이 높은 테스트 케이스를 선정하는 기법은 ?</p>
<p>정답 : Cause-Effect Graph</p>
<hr>
<h1 id="19번">19번</h1>
<p>키보드 마우스를 통해 선택하여 모든 작업을 수행하는 인터페이스는?</p>
<p>정답 : GUI</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring_Security_03]]></title>
            <link>https://velog.io/@jawoo-97/SpringSecurity03</link>
            <guid>https://velog.io/@jawoo-97/SpringSecurity03</guid>
            <pubDate>Thu, 11 Jul 2024 15:15:36 GMT</pubDate>
            <description><![CDATA[<h1 id="스프링-보안">스프링 보안</h1>
<p>Spring Security는 일반적인 공격에 대한 인증, 권한 부여 및 보호를 제공하는 프레임워크입니다.</p>
<p>명령형 및 반응형 애플리케이션 모두 보호하기 위한 최고 수준의 지원을 제공하는 
Spring기반 애플리케이션을 보호하기위한 표준이다.</p>
<h1 id="필수-구성-요소">필수 구성 요소</h1>
<ul>
<li>java 8 이상의 런타임 환경</li>
</ul>
<blockquote>
<p>Spring Security는 자체 포함 방식으로 작동하는 것이 목표이다. 
특히 특수 JAAS(Java Authentication and Authorization Service) 정책 파일을 구성하거나 Spring Security를 공통 클래스 경로 위치에 배치할 필요가 없다.</p>
</blockquote>
<h1 id="spring-security-63의-새로운기능">Spring Security 6.3의 새로운기능</h1>
<p><a href="https://docs.spring.io/spring-security/reference/whats-new.html">공식홈페이지 새로운 기능 확인하기</a></p>
<h1 id="기능">기능</h1>
<p>Spring Security는 인증, 권한 부여 및 일반적인 악용에 대한 보호에 대한 포괄적인 지원을 제공합니다.</p>
<h2 id="인증">인증</h2>
<p><strong>인증</strong> : 특정 리소스에 액세스하려는 사용자의 ID를 확인하는 방법이다.</p>
<h3 id="비밀번호-저장">비밀번호 저장</h3>
<p><strong>비밀번호 저장</strong>
처음에는 암호가 일반 텍스트로 저장되었습니다. 암호가 안전하다고 가정했으나, 악의적인 사용자는 SQL 인젝션과 같은 공격을 사용하여 &quot;데이터 덤프&quot;를 얻을 수 있는 방법을 찾을 수 있었습니다. 보안 전문가들은 암호를 보호하기 위해 조치를 취해야했습니다.</p>
<p>개발자는 SHA-256rhk 같은 단방향 해시를 통해 암호를 저장하도록 권장했습니다. 사용자가 인증을 시도하면 해시된 암호가 입력한 암호의 해시와 비교됩니다. 이 시스템을 무력화하기 위해 악의적인 사용자는 레인보우 테이블이라는 조회 테이블을 만듭니다.</p>
<p>개발자는 레인보우 테이블의 효과를 완화하기 위해 솔트 암호를 사용하도록 권장되었습니다. 모든 사용자의 암호에 대해 임의의 바이트(솔트)가 생성됩니다. 이를 통해 고유한 해시를 생성합니다. 솔트는 사용자의 암호와 함께 일반 텍스트로 저장됩니다. 그런 다음 인증이 시도되면 해시된 암호가 저장된 솔트의 해시 및 입력한 암호와 비교됩니다.</p>
<p>현대에는 암호화 해시(예: SHA-256)가 더 이상 안전하지 않다는 것을 알고 있습니다. 그 이유는 최신 하드웨어를 사용하면 초당 수십억 개의 해시 계산을 수행 할 수 있기 때문입니다. 즉, 각 암호를 개별적으로 쉽게 해독할 수 있습니다.</p>
<p><strong>결론</strong>
개발자는 이제 적응형 단방향 함수를 활용하여 암호를 저장하는 것이 좋습니다. 적응형 단방향 함수는 사용한 암호 유효성 검사는 의도적으로 리소스를 많이 사용합니다.
시스템에서 암호를 확인하는 데 약 1초가 걸리도록 &quot;작업요소&quot;를 지정합니다. <em>이 절충안은 공격자가 암호를 해독하기 어렵게 만드는 것이지만 자체 시스템에 과도한 부담을 주거나 사용자를 짜증나게 할 정도로 비용이 많이 들지 않습니다.</em></p>
<p>사용해야하는 적응형 단방향 함수의 예로는 bcrypt, PBKDF2, scrypt 및 argon2가 있습니다.</p>
<p>리소스를 많이 사용하기 때문에 모든 요청에 대해 유효성 검사하면 애플리케이션의 성능이 크게 저하될 수 있습니다. 장기 자격증명을 단기 자격 증명(세션, OAuth 토큰 등)으로 교환하는 것이 좋습니다. </p>
<hr>
<h3 id="bcryptpassword-인코더">BCryptPassword 인코더</h3>
<p>암호 트래킹에 대한 저항력을 높이기 위해 bcrypt는 의도적으로 느립니다.
기본 구현은 javadoc에 언급된대로 강도 10을 사용합니다.</p>
<pre><code class="language-java">// Create an encoder with strength 16
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(16);
String result = encoder.encode(&quot;myPassword&quot;);
assertTrue(encoder.matches(&quot;myPassword&quot;, result));</code></pre>
<h3 id="아르곤2패스워드인코더">아르곤2패스워드인코더</h3>
<pre><code class="language-java">// Create an encoder with all the defaults
Argon2PasswordEncoder encoder = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8();
String result = encoder.encode(&quot;myPassword&quot;);
assertTrue(encoder.matches(&quot;myPassword&quot;, result));</code></pre>
<h3 id="pbkdf2암호-인코더">Pbkdf2암호 인코더</h3>
<p> 이 알고리즘은 FIPS 인증이 필요한 경우에 적합합니다.</p>
<pre><code class="language-java"> // Create an encoder with all the defaults
Pbkdf2PasswordEncoder encoder = Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8();
String result = encoder.encode(&quot;myPassword&quot;);
assertTrue(encoder.matches(&quot;myPassword&quot;, result));</code></pre>
<h3 id="scryptpassword인코더">SCryptPassword인코더</h3>
<pre><code class="language-java">
 // Create an encoder with all the defaults
SCryptPasswordEncoder encoder = SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8();
String result = encoder.encode(&quot;myPassword&quot;);
assertTrue(encoder.matches(&quot;myPassword&quot;, result));</code></pre>
<h3 id="다른-spasswordencoder">다른 sPasswordEncoder</h3>
<p> 이전 버전과의 호환성을 위해 전적으로 존재하는 다른 구현이 많이 있습니다. 더 이상 안전하지 않은 것으로 간주됨을 나타내기 위해 모두 더 이상 사용되지 않습니다. 그러나 기존 레거시 시스템을 마이그레이션하기 어렵기 때문에 제거할 계획은 없습니다.PasswordEncoder</p>
<h2 id="비밀번호-구성-변경">비밀번호 구성 변경</h2>
<p> 사용자가 암호를 지정할 수 있는 대부분의 응용 프로그램에는 해당 암호를 업데이트하는 기능도 필요합니다. 비밀번호 변경을 위한 엔드포인트를 제공할 수 있도록 Spring Securiy를 구성할 수 있습니다.
 예를 들어 애플리케이션의 암호 변경 엔드 포인트가 <code>/change-password</code>인 경우 다음과 같이 구성합니다.</p>
<pre><code class="language-java"> http
    .passwordManagement(Customizer.withDefaults())</code></pre>
<p> passwordManager가 Spring Security로 이동하면 엔드포인트를 리디렉션합니다.</p>
<p> 또는 끝점이 아닌 경우 다음과 같이 지정할 수도 있습니다.</p>
<pre><code class="language-java"> http
    .passwordManagement((management) -&gt; management
        .changePasswordPage(&quot;/update-password&quot;)
    )</code></pre>
<h2 id="손상된-비밀번호-확인">손상된 비밀번호 확인</h2>
<p> Spring Security는 CompromisedPasswordChecker 인터페이스의 HaveIBeenPwnedRestApiPasswordChecker 구현을 통해 Have I Been Pwned API와의 통합을 제공합니다.</p>
<pre><code class="language-java"> @Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests(authorize -&gt; authorize
            .anyRequest().authenticated()
        )
        .formLogin(withDefaults())
        .httpBasic(withDefaults());
    return http.build();
}

@Bean
public CompromisedPasswordChecker compromisedPasswordChecker() {
    return new HaveIBeenPwnedRestApiPasswordChecker();
}</code></pre>
<p> 이렇게 하면 취약한 암호를 사용하여 HTTP Basic 또는 Form Login을 통해 인증을 시도할 때 401 응답 상태 코드를 받게 됩니다.
 그러나 이 경우 사용자가 올바른 암호를 제공했지만 여전히 로그인할 수 없기에 약간의 혼란을 일으킬 수 있습니다.</p>
<p> 이러한 경우 사용자 에이전트를 <code>/reset-password</code>로 리디렉션하는 처리할 수 있습니다.</p>
<pre><code class="language-java">
 @ControllerAdvice
public class MyControllerAdvice {

    @ExceptionHandler(CompromisedPasswordException.class)
    public String handleCompromisedPasswordException(CompromisedPasswordException ex, RedirectAttributes attributes) {
        attributes.addFlashAttribute(&quot;error&quot;, ex.message);
        return &quot;redirect:/reset-password&quot;;
    }

}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring_Security]]></title>
            <link>https://velog.io/@jawoo-97/SpringSecurity</link>
            <guid>https://velog.io/@jawoo-97/SpringSecurity</guid>
            <pubDate>Thu, 11 Jul 2024 13:18:58 GMT</pubDate>
            <description><![CDATA[<p><a href="https://spring.io/guides/gs/securing-web">참고사이트</a></p>
<p>만약 사용자가 홈페이지를 보기전에 로그인을 해야 페이지를 볼 수 있도록 하려면 어떻게 해야 하는가?</p>
<p>** The first thing you need to do is add Spring Security to the classpath.**</p>
<p>With Gradle, you need to add three lines</p>
<pre><code>implementation &#39;org.springframework.boot:spring-boot-starter-security&#39;
//  Temporary explicit version to fix Thymeleaf bug
implementation &#39;org.thymeleaf.extras:thymeleaf-extras-springsecurity6:3.1.2.RELEASE&#39;
testImplementation &#39;org.springframework.security:spring-security-test&#39;</code></pre><p>With Maven, you need to add two extra entries (one for the application and one for testing) to the element in , as the following listing shows:</p>
<pre><code>&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
    &lt;artifactId&gt;spring-boot-starter-security&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.thymeleaf.extras&lt;/groupId&gt;
    &lt;artifactId&gt;thymeleaf-extras-springsecurity6&lt;/artifactId&gt;
    &lt;!-- Temporary explicit version to fix Thymeleaf bug --&gt;
    &lt;version&gt;3.1.1.RELEASE&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.security&lt;/groupId&gt;
    &lt;artifactId&gt;spring-security-test&lt;/artifactId&gt;
    &lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;</code></pre><pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;project xmlns=&quot;http://maven.apache.org/POM/4.0.0&quot; xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
    xsi:schemaLocation=&quot;http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd&quot;&gt;
    &lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;
    &lt;parent&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-parent&lt;/artifactId&gt;
        &lt;version&gt;3.3.0&lt;/version&gt;
        &lt;relativePath/&gt; &lt;!-- lookup parent from repository --&gt;
    &lt;/parent&gt;
    &lt;groupId&gt;com.example&lt;/groupId&gt;
    &lt;artifactId&gt;securing-web-complete&lt;/artifactId&gt;
    &lt;version&gt;0.0.1-SNAPSHOT&lt;/version&gt;
    &lt;name&gt;securing-web-complete&lt;/name&gt;
    &lt;description&gt;Demo project for Spring Boot&lt;/description&gt;

    &lt;properties&gt;
        &lt;java.version&gt;17&lt;/java.version&gt;
    &lt;/properties&gt;

    &lt;dependencies&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter-thymeleaf&lt;/artifactId&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter-security&lt;/artifactId&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.thymeleaf.extras&lt;/groupId&gt;
            &lt;artifactId&gt;thymeleaf-extras-springsecurity6&lt;/artifactId&gt;
            &lt;!-- Temporary explicit version to fix Thymeleaf bug --&gt;
            &lt;version&gt;3.1.1.RELEASE&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.security&lt;/groupId&gt;
            &lt;artifactId&gt;spring-security-test&lt;/artifactId&gt;
            &lt;scope&gt;test&lt;/scope&gt;
        &lt;/dependency&gt;

        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
            &lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;
            &lt;scope&gt;test&lt;/scope&gt;
        &lt;/dependency&gt;
    &lt;/dependencies&gt;

&lt;/project&gt;</code></pre><p>다음 보안 구성을 사용하면 인증된 사용자만 비밀 인사말을 볼 수 있습니다.</p>
<pre><code class="language-java"></code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[HTML_img]]></title>
            <link>https://velog.io/@jawoo-97/HTMLimg</link>
            <guid>https://velog.io/@jawoo-97/HTMLimg</guid>
            <pubDate>Tue, 09 Jul 2024 23:06:11 GMT</pubDate>
            <description><![CDATA[<h1 id="이미지-넣기">이미지 넣기</h1>
<pre><code class="language-html">&lt;img src=&quot;(이미지 파일 경로)&quot; alt=&quot;(대체 텍스트)&quot; title=&quot;(툴팁 텍스트)&quot;&gt;</code></pre>
<p>이미지 태그의 속성</p>
<table>
<thead>
<tr>
<th align="left">속성</th>
<th align="left">설명</th>
<th align="left">비고</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><code>src</code></td>
<td align="left">원본파일 경로</td>
<td align="left">절대경로 또는 상대 경로</td>
</tr>
<tr>
<td align="left"><code>alt</code></td>
<td align="left">대체 택스트</td>
<td align="left">스크린 리더, 원본파일 무효시</td>
</tr>
<tr>
<td align="left"><code>title</code></td>
<td align="left">툴팁</td>
<td align="left">alt의 대체제나 반복이 되어서는 안됨</td>
</tr>
<tr>
<td align="left">width</td>
<td align="left">너비</td>
<td align="left">픽셀 단위의 정수</td>
</tr>
<tr>
<td align="left">height</td>
<td align="left">높이</td>
<td align="left"></td>
</tr>
</tbody></table>
<p><strong>무료 이미지 사이트</strong></p>
<p>👉 <a href="https://pixabay.com/ko/">Pixabay</a>
👉 <a href="https://unsplash.com/ko">Unsplash</a></p>
<p><img src="https://velog.velcdn.com/images/jawoo-97/post/47c364e2-6357-4ceb-b3e0-df004640ac50/image.png" alt=""></p>
<p>높이나 너비 하나의 크기를 수정하면 사진의 비율에 맞게 변한다.
❗너비와 높이 크기를 모두 지정하면 사진의 비율과 다르게 나온다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[VS_CODE 환경세팅]]></title>
            <link>https://velog.io/@jawoo-97/VSCODE-%ED%99%98%EA%B2%BD%EC%84%B8%ED%8C%85</link>
            <guid>https://velog.io/@jawoo-97/VSCODE-%ED%99%98%EA%B2%BD%EC%84%B8%ED%8C%85</guid>
            <pubDate>Tue, 09 Jul 2024 22:41:48 GMT</pubDate>
            <description><![CDATA[<ol>
<li>VS code 설치 </li>
</ol>
<h1 id="탭-사이트--자동-줄바꿈-설정">탭 사이트 &amp;&amp; 자동 줄바꿈 설정</h1>
<ol>
<li>VS Code 설정 창 열기</li>
</ol>
<ul>
<li>윈도우 : <code>Ctrl</code> + ,</li>
<li>맥 : <code>Command</code> + ,</li>
</ul>
<ol start="2">
<li><code>Tab Size</code> 항목 <em>2</em>로 설정</li>
<li><code>Word Wrap</code> 항목 <em>on</em>으로 설정
(줄이 길어질 때 화면 밖으로 나가는 것을 자동으로 줄바꿈을 해서 보여준다.)</li>
</ol>
<p>*<em>플러그인 설치 *</em></p>
<table>
<thead>
<tr>
<th align="left">플러그인</th>
<th align="left">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="left">Korean Language Pack</td>
<td align="left">에디터 한글화</td>
</tr>
<tr>
<td align="left">Material Icon Theme</td>
<td align="left">탐색기 아이콘 테마</td>
</tr>
<tr>
<td align="left">One Dark Pro</td>
<td align="left">에디터 색 테마</td>
</tr>
<tr>
<td align="left">Live Server</td>
<td align="left">새로고침 없이 코딩 중인 웹 업데이트</td>
</tr>
<tr>
<td align="left">indent-rainbow</td>
<td align="left">들여쓰기 색으로 구분</td>
</tr>
<tr>
<td align="left">Bracket Pair Colorizer</td>
<td align="left">여는 괄호와 닫는 괄호 짝 지어줌</td>
</tr>
<tr>
<td align="left">Auto Rename Tag</td>
<td align="left">여는 태그 수정시 닫는 태그도 자동수정</td>
</tr>
</tbody></table>
<p><img src="https://velog.velcdn.com/images/jawoo-97/post/8823d29d-0165-4d84-8749-fee07408d11f/image.png" alt=""></p>
<p>Bracket Pair
<code>Ctrl</code> + , 환경설정에 들어가서 bracketpair 검색
<img src="https://velog.velcdn.com/images/jawoo-97/post/d17ffa8d-51f9-4cbe-ae52-07665fc52309/image.png" alt="">
active로 설정 바꾸어주기</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[웹 폰트 사용하기-구글웹폰트]]></title>
            <link>https://velog.io/@jawoo-97/%EC%9B%B9-%ED%8F%B0%ED%8A%B8-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-%EA%B5%AC%EA%B8%80%EC%9B%B9%ED%8F%B0%ED%8A%B8</link>
            <guid>https://velog.io/@jawoo-97/%EC%9B%B9-%ED%8F%B0%ED%8A%B8-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-%EA%B5%AC%EA%B8%80%EC%9B%B9%ED%8F%B0%ED%8A%B8</guid>
            <pubDate>Mon, 08 Jul 2024 17:29:51 GMT</pubDate>
            <description><![CDATA[<p>웹 폰트를 사용하면 사용자 시스템에 없는 글꼴도 사용할 수 있습니다.</p>
<h3 id="웹-폰트-업로드하고-사용하기">웹 폰트 업로드하고 사용하기</h3>
<pre><code class="language-css">@font-face {
    font-family: &lt;글꼴 이름&gt;;
    src: &lt;글꼴 파일&gt;[&lt;글꼴 파일&gt;, &lt;글꼴 파일&gt;,...];
}
</code></pre>
<ul>
<li>font-family 속성을 사용해 글꼴 이름을 만듭니다.</li>
<li>src 속성에서는 사용할 글꼴 파일의 경로를 지정합니다.</li>
<li>local( )문을 사용해서 사용자 시스템에 해당 글꼴이 있는지 먼저 확인합니다.</li>
</ul>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;

&lt;html lang=&quot;ko&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;title&gt;웹 폰트 사용하기&lt;/title&gt;
    &lt;style&gt;
      @font-face {
        font-family: &#39;Ostrich&#39;;  /* 폰트 이름 */
        src: local(&#39;Ostrich Sans&#39;), 
              url(&#39;fonts/ostrich-sans-bold.woff&#39;) format(&#39;woff&#39;), 
              url(&#39;fonts/ostrich-sans-bold.ttf&#39;) format(&#39;truetype&#39;), 
              url(&#39;fonts/ostrich-sans-bold.svg&#39;) format(&#39;svg&#39;);
      }
      .wfont {
        font-family:&#39;Ostrich&#39;, sans-serif; /* 웹 폰트 지정 */
      }
      p {
        font-size:30px; /* 글자 크기 */
      }
    &lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
  &lt;p&gt;Using Default Fonts&lt;/p&gt;
  &lt;p class=&quot;wfont&quot;&gt;Using Web Fonts&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<h3 id="구글-폰트-사용하기">구글 폰트 사용하기</h3>
<ol>
<li><p>구글 폰트 사이트에서 원하는 웹 폰트 찾기
<a href="https://fonts.google.com/">구글 웹 폰트</a>
<img src="https://velog.velcdn.com/images/jawoo-97/post/38ef407d-f8b6-4e78-b145-fa4632e3140c/image.png" alt=""></p>
</li>
<li><p>원하는 폰트를 클릭하여 들어가서 웹 폰트 스타일 복사하기
[+ Select this style]
<img src="https://velog.velcdn.com/images/jawoo-97/post/2c81bc33-8c3f-430c-81d9-b9033c30c5aa/image.png" alt="">
<img src="https://velog.velcdn.com/images/jawoo-97/post/06275baf-77a2-4479-8aae-2df0cd07125d/image.png" alt=""></p>
</li>
</ol>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ko&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;title&gt;구글 폰트 사용하기&lt;/title&gt;
    &lt;style&gt;
      @import url(&#39;https://fonts.googleapis.com/css2?family=Nanum+Pen+Script&amp;display=swap&#39;);

      h1 {
        font-family: &quot;Nanum Pen Script&quot;, cursive;
        font-size:60px;
        font-weight:bold;       
      }
    &lt;/style&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;h1&gt;HTML+CSS+JavaScript&lt;/h1&gt;
  &lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p><img src="https://velog.velcdn.com/images/jawoo-97/post/de8d1845-722d-4af4-b722-629ae18b2046/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CSS 경량화]]></title>
            <link>https://velog.io/@jawoo-97/CSS-%EA%B2%BD%EB%9F%89%ED%99%94</link>
            <guid>https://velog.io/@jawoo-97/CSS-%EA%B2%BD%EB%9F%89%ED%99%94</guid>
            <pubDate>Mon, 08 Jul 2024 15:14:00 GMT</pubDate>
            <description><![CDATA[<p>CSS 소스는 네트워크를 이용해 파일을 내려받으므로 되도록이면 파일 크기가 작은 것이 좋습니다.
주석이나 줄 바꿈, 공백 등을 제거하고 꼭 필요한 정보만 남겨서 파일을 작게만듭니다.
이것을 <code>소스 경량화</code>라고 합니다.
<img src="https://velog.velcdn.com/images/jawoo-97/post/0a88d74a-f14e-460d-bd11-542c7bec748a/image.png" alt=""></p>
<p>소스 경량화 사이트
<a href="https://www.cleancss.com/css-minify/">https://www.cleancss.com/css-minify/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[스프링 시큐리티공부]]></title>
            <link>https://velog.io/@jawoo-97/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0%EA%B3%B5%EB%B6%80</link>
            <guid>https://velog.io/@jawoo-97/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0%EA%B3%B5%EB%B6%80</guid>
            <pubDate>Mon, 08 Jul 2024 12:59:20 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-java">import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

        //csrf disable
        http
            .csrf((auth) -&gt; auth.disable());

        //Form 로그인 방식 disable
        http
             .formLogin((auth) -&gt; auth.disable());


        //http basic 인증 방식 disable
        http
            .httpBasic(auth -&gt; auth.disable());

        //경로별 인가 작업
        http
            .authorizeHttpRequests(auth -&gt; auth
                    .requestMatchers(&quot;/login&quot;, &quot;/&quot; , &quot;/join&quot;).permitAll()
                    .requestMatchers(&quot;/admin&quot;).hasRole(&quot;ADMIN&quot;)
                    .anyRequest().authenticated());

        //세션 설정
        http
            .sessionManagement(sesstion -&gt; sesstion
                    .sessionCreationPolicy(SessionCreationPolicy.STATELESS));


        return http.build();
    }
}</code></pre>
<p><code>@Configuration</code> 어노테이션은 이 클래스가 스프링 설정 클래스임을 나타냅니다.
<code>@EnableWebSecurity</code> 어노테이션은 스프링 시큐리티를 활성화하는 역할을 합니다.
<code>BCryptPasswordEncoder</code> 빈을 생성하는 메서드입니다. 이 빈은 비밀번호를 암호화하는데 사용합니다.
<code>SecurityFilterChain</code>빈을 생성하는 메서드입니다. 이 빈은 보안 필더 체인을 구성합니다.</p>
<ul>
<li><code>csrf</code> 를 비활성화 합니다.</li>
<li>Form 로그인 방식을 비활성화 합니다.</li>
<li>HTTP Basic 인증 방식을 비활성화 합니다.</li>
<li>특정 경로에 대한 인가 작업을 설정합니다.</li>
<li>세션 관리 정책을 설정합니다.</li>
</ul>
<hr>
<p><strong>CSRF</strong></p>
<blockquote>
<p>Cross-Site Request Forgery
웹사이트 취약점 공격 기법. 이 공격은 사용자가 자신의 의지와는 무관하게 공격자가 의도한 행위를 특정 웹사이트에 요청하게 하는 것이다.
<code>CSRF</code> 공격을 방지하기 위한 조치입니다. CSRF 공격을 비활성화하면 해당 공격에 취약하지 않도록 보호할 수 있다.
(클라이언트 기반 인증 방식을 사용하는 경우에는 CSRF를 비활성화 할 수 있다.)</p>
<blockquote>
<p>CSRF 정의와 XSS와 차이
<a href="https://panda-university.tistory.com/19">https://panda-university.tistory.com/19</a></p>
</blockquote>
</blockquote>
]]></description>
        </item>
    </channel>
</rss>