Spring Boot 3 & Spring Framework 6 - Section 18 : Spring Security , 보안 기초

이정수·2025년 3월 9일

이론

  • 인증 ( Authentication ) :
    。사용자의 신원을 증명하는 과정 ( ex. ID/PW 로그인 , JWT 등 )
    ▶ 사용자의 로그인 정보를 받아서 ID정보가 일치하는지 확인하는 과정.

    Multiple Authentication : 복수의 인증이 존재하는경우
    Google 같은 경우 ID/PW로 1차 인증 후 SMS로 2차 인증.

  • 인가 ( Authorization ) :
    。로그인을 통해 Authenticated된 사용자가 Server의 특정 Resource에 Access 가능한 권한이 있는지 확인하는 과정.
    ▶ 적절한 Access 권한을 지니고있는지 판단.
  • Encoding :
    。데이터를 특정 Data type으로 변환하는 과정.
    ▶ 주로 미디어 데이터를 압축 및 Streaming하거나, Text 데이터를 안전하게 전송하거나 저장할 수 있도록 Data typeBinary로 변경할 때 사용.

    EncodingDecoding을 통해 원래의 데이터로 복원이 가능.
    。데이터의 호환성을 보장하며, 주로

    Encoding 대표 예시 : ( Base64 , Wav , MP3 )

    • base64
      。이진 Binary Data를 64개로 구성된 ASCII 문자 데이터로 Encoding하여 변환.
      데이터( JSON , XML , Binary Data 등 )를 TEXT로 변환하여 안전하게 전송이 가능.

      。단순 Encoding 방식이므로, 보안성이 없어 암호화 목적으로는 사용하지 않음.
      Basic Authenticationheader를 획득 시 decoding하여 쉽게 사용자의 IDPW를 획득할 수 있으므로.

      양방향변환으로서 Base64EncodingText 데이터는 다시 Decoding하면서 원본으로 복원 할 수 있다.

      Base64 활용

      • image, file Data의 binary dataTextEncoding 후 전송하여 다시 binary dataDecoding하여 활용.

      • HTTP Basic Authentication : Basic Base64인코딩한ID:PW
        JavaScript에서 생성 시 window.btoa(문자열) 사용. ( Binary to ACSII )
        const ba = 'Basic ' + window.btoa(ID + ':' + PW)
        ▶ 현재는 JWT 사용.


  • Hashing
    。Data를 고정된 길이의 고유한 Hash로 변환하는 과정으로 복원이 불가능한 단방향 변환.
    주로 보안 및 데이터 무결성 검증에 사용

    단방향변환으로 원본 입력값으로 복원이 불가능
    。입력값 크기에 관계없이 고정된 길이의 Hash 생성. ( SHA-256 : 64자 )
    。충돌방지를 예방하기 위해 서로 다른 입력값으로 Hasing동일한 Hash를 갖지 않도록 설계.
    Bcrypt , Scrypt을 통한 암호화 알고리즘을 사용하여 보안성 존재.
    ▶ 현재는 SHA-256을 사용하지 않는다.
    Spring Security - BCrypt hashing 활용

    Hashing 활용

    • 비밀번호 저장용도
      DB에 비밀번호를 특정 Hash 알고리즘으로 Hashing하여 Hash값으로 저장 및 로그인 시 DB에 저장된 Hash값을 참조하여 입력된 비밀번호를 Hashing하여 비교를 통해 검증 수행.
      Hash값은 원본으로 복원이 불가능하므로, DB에 저장 시 공격자가 DB에 Access 하더라도 Hash값만 참조가능하고 원본 PW은 확인 불가능.

    • 데이터 무결성 검증
      。파일이 변조되었는지 확인하는 용도로 사용
      데이터와 데이터의 Hash를 함께 전송하여 전송후의 데이터의 Hash가 서로 일치하는지 확인.

    • 디지털 서명 및 인증서


  • Encryption
    。데이터의 기밀성을 보호하도록 데이터를 안전하게 변환하여 보호하는 과정.
    Encryption된 데이터는 암호화 key 없이는 원본데이터를 알 수 없게 구축.

    Encoding과 동일하게 Decryption을 수행하여 원본값으로 복호화가 가능하지만 암호키가 필요.

    。주로 보안목적으로 활용됨.

    Encryption 예시

    • AES ( Advanced Encryption Standard ) :
      。동일한 키로 암호화와 복호화가 수행되는 대칭키 암호화 방식
      • 대칭키 암호화 방식 ( Symmetric Encryption ) : DES , AES , RC4 , ...
        。단일 공유키( Secret Key )로 암호화, 복호화 수행
        Encryption, Decryption에서 발신자와 수신자가 동일한 암호화 키를 사용.

        。구현이 간단하여 빠르고 효율적이지만, 공유키를 안전하게 공유하는 것이 어렵다.
        ▶ 암호문을 타인과 공유 시 공유키를 적절하게 공유할 방법도 찾아야 하므로.


    • RSA ( Rivest-Shamir-Adleman ) : RSA , ECC , DSA
      공개키( Public Key )로 데이터를 암호화하고 개인키( Private Key )로 복호화하는 비대칭키 암호화 방식
      JWT Token에서 활용하는 방식.
      • 비대칭키 암호화 방식 ( Asymmetric Encryption )
        。공개키 ( Public Key )와 개인키( Private Key ) 한 쌍을 사용하여 암호화, 복호화를 수행 방식
        ▶ 발신자가 Encryption 시 공개키를 사용하고, 수신자는 Decryption 시 개인키를 사용

        。대칭키 암호화 방식에 비해 데이터를 안전하게 공유가 가능하지만 복잡한 연산을 수행하므로 연산속도가 느리고, 키 길이가 길어야 보안이 보장.

        공개키 ( Public Key ) : Encryption 시 활용하며 누구나 알 수 있고 사용가능.
        개인키( Private Key ) : Decryption 시 활용하며 본인만 알 수 있음.
        ▶ 개인키는 반드시 안전하게 보관해야한다.

        。데이터를 위조할 수 없도록 보장하므로 디지털 서명이 가능.

보안의 원칙

  • 기본 실패 ( Fail-Safe Defaults ) :
    System이 Fail하더라도 기본적으로 안전한 상태를 유지하도록 설정한다는 원칙.
    ▶ 권한이 명시적으로 허가되지않은 모든 자원에 대한 접근을 차단하는것을 기본으로 설정.

    ex) 방화벽에서 모든트래픽차단 후 허용할 트래픽만 부분적으로 추가하는 방식.

  • 최소권한 ( Least Privilege )
    。각 사용자 또는 Process에게 항상 작업수행에 필요한 수준의 최소한의 접근권한을 할당.
    ▶ 해당 원칙을 따르면 보안사고가 발생해도 피해최소화가 가능

    ex) 일반사용자 수준에는 관리자권한없이 실행되도록 설정 및 관리장계정이 필요한 작업이 있는경우에만 관리자권한을 부여.

  • 메카니즘의 효율성 ( Economy of Mechanism )
    보안 Architecture의 설계는 이해가 쉽도록 최대한 단순해야한다.
    ▶ 복잡한 보안시스템의 경우 Error가 발생할 가능성이 높고 유지보수가 어려우므로.

    ex) 인증시스템을 여러단계로 나누는 대신, 단순하면서도 강력한 단일 인증기법을 적용

  • 완벽한 조율 ( Complete Mediation )
    보안시스템에 대한 모든 접근요청은 항상 검증해야한다.
    ▶ 기존에 캐시된 인증정보를 사용하는 것이 아닌, 매번 새로운 인증절차를 거쳐야함.

    ex) 사용자가 로그인한 후에도, 중요한 작업(예: 계좌 이체)을 수행할 때마다 비밀번호를 다시 입력하게 하는 방식.

  • 개방 설계( Open Design )
    。보안은 비밀유지에 의존해서는 안된다.
    보안 시스템의 설계는 비밀유지가 아닌, 강력한 보안알고리즘과 보안정책에 기반해야하는 원칙.
    ▶ 보안알고리즘이 공개되더라도 안전하게 보안시스템이 유지되어야한다.

    ex) 오픈소스 암호화 알고리즘 (JWT 등)은 코드는 공개되어있더라도 여전히 안전하게 사용가능.

  • 권한분리 ( Separation of Privilege )
    。중요한 작업 수행 시 복수 이상의 독립적인 권한이 필요하도록 설계해야한다는 원칙.
    ▶ 하나의 권한이 노출되더라도 전체 시스템이 위험에 빠지지 않도록 보호하는 역할을 수행.

    ex) 금융 시스템에서 한 사람이 결제를 승인할 수 없고, 다른 사람이 별도로 확인해야 결제가 이루어지도록 설정.

    +
  • 최소 공통 Mechanism ( Least Common Mechanism )
    서로 다른 사용자 또는 프로세스가 공유되는 보안 Mechanism의 수와 사용은 최소화해야한다.
    ▶ 공유하는 보안Mechanism이 많을 경우 하나가 뚫릴 경우, 전체 시스템이 연쇄적으로 위험해질 수 있다.

    ex) 여러 사용자가 동일한 세션을 공유하지 않고, 각 사용자에게 개별 세션을 할당하는 방식.

  • 심리적 수용성( Psycological acceptability / Easy to use )
    사용자가 보안시스템을 사용하는 경우 편리하게 사용할 수 있어야한다는 원칙.
    ▶ 너무 복잡한 보안정책의 경우 사용자가 기피하거나 비밀번호를 다른곳에 저장하는 등 보안취약점을 생성할 수 있으므로.

    ex ) 너무 복잡한 비밀번호 정책 대신, 다중 인증(MFA)을 도입하여 보안성을 유지하면서도 사용자의 편의를 높이는 방식.

Spring Security

Spring 생태계REST APIWeb Application의 보안(인증: Authentication / 인가: Authorization 등)을 제공하는 Spring Framework.
▶ 보안에 필요한 기능을 제공함으로써 개발자가 보안 관련 logic을 따로 작성할 필요는 없음.

Complete Mediation : Applicaiton에 Dependency가 적용되는 경우 Applciation의 모든 Resource가 보호되어 접근하는 모든 API호출에 대해 Security Filter Chain에 의한 Authentication을 수행.
▶ 기존에 Controller Method에 의해 정의된 API 외에도 존재하지않는 API를 호출하는 경우에도 Login Form을 통해 자격증명을 요구.

Spring Security 6.x 부터는 람다기반 설정방식 권장
Spring Filter Chain을 통해 Customize가 가능한 유연한 Security System을 제공하여 사용자에게 적절한 AuthenticationAuthorization이 부여.

  • 기본 로그인페이지와 로그아웃 페이지 URL 제공
    Spring Security dependency 추가 시 기본적으로 제공되는 기능.
    • localhost:8080/login : Spring Login Form 도출

    • localhost:8080/logout : 기존 로그인된 자격증명을 로그아웃
      Session Cookie가 삭제되어 차후 HTTP Request 전송 시 Authentication Header에 포함되지않아서 요청이 거절됨.
      ▶ HTML의 <a class="nav-link" href="/logout">으로 간단하게 로그아웃 버튼을 생성할 수 있다.


  • Spring Security Dependency
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
</dependency>

Spring Security 원리 Spring MVC - Dispatcher Servlet

  • 기존 Spring ApplicationSpring MVC 디자인 패턴에서 REST API 호출을 위한 Dispatcher Servlet으로 전송되는 모든 HTTP RequestSpring Security가 Intercept하여 Spring Filter Chain을 통해 HTTP RequestAuthorizationAuthentication을 검증 후 Dispatcher Servlet으로 전달하여 최종적으로 Contoller로 전달.

  • Spring Filter Chain에 의한 Authentication 검증 순서

    1. HTTP RequestAuthentication Header의 정보를 포함한 Authentication instance를 AuthenticationManager객체.authenticate(Authentication객체)로 전달.
    。해당 Authentication instance는 Credentials 정보만 포함한 미완성된 instance.
    AuthenticationManager에 의해 Authentication Header의 인증방식 ( ex. JWT )에 해당하는 AuthenticationProvider를 식별 및 적용 ( ex. JwtAutheticationProvider )

    2. AuthenticationManager객체.authenticate(Authentication객체)에서 AutheticationProvider에 의해 사용자 인증정보의 검증을 수행
    UserDetailsService를 호출하여 매개변수로 전달된 Authentication instance의 Credentials 정보에서 포함된 username에 해당하는 사용자정보를 DB에서 조회 후 UserDetails instance로 return.
    Authentication instance의 Credentials의 Password와 UserDetails instance의 Password를 서로 비교하여 검증을 수행.

    3. 검증의 결과값 확인
    Password의 비교 후 검증이 완료된 경우, principal, authorities, credentials 정보를 포함한 완전한 Authentication instance를 반환.
    。검증에 실패한 경우 AuthenticationException 발생.
    • Authentication interface :
      org.springframework.security.core.Authentication
      Spring Security에서 Authentication 정보를 지시하는 Interface
      ▶ 사용자의 인증상태 , 권한 , 인증된 사용자정보 등을 포함.

      HTTP RequestAuthentication Header에 포함된 사용자 인증정보를 Controller Method의 매개변수로 사전에 AuthenticationManager객체.authenticate(Authentication객체)에 의해 사용자인증정보가 검증되어 principal, authorities, credentials 정보를 포함한 Authentication instance로서 mapping되어 전달됨.
      • SecurityContextHolder , SecurityContext , Authentication 관계
      SecurityContextHolder
         └── SecurityContext (보안 컨텍스트)
               └── Authentication (인증 정보)
                     ├── Principal (사용자 정보)
                     ├── Credentials (비밀번호, JWT 등)
                     └── Authorities (권한 정보)

      Spring Authentication instance 구성

      ▶ Client에서 전송된 HTTP RequestHTTP Basic Authentication 인증정보가 포함된 Authentication instance

      • principal :
        。인증된 사용자의 세부정보를 의미.
        ▶ 보통 UserDetails instance를 포함.

      • credentials
        。사용자가 인증 시 입력한 정보 ( ex. username, password )
        ▶ 인증이 완료된 경우, 보안을 위해 null로 설정됨.

      • authorities
        principal에 정의된 사용자가 보유한 역할, 권한 목록을 지시.
        ▶ 주로 "ROLE_USER" , "ROLE_ADMIN" 등의 권한정보를 포함.

        。검증전의 credentials 정보만 포함된 Authentication instance를 AuthenticationManager객체.authenticate(Authentication객체)로 전달 시 사용자인증정보를 검증 후 인증성공 시 principal, authorities 정보를 추가로 포함한 완전한 Authentication instance를 return.


    • AuthenticationManager
      Spring Security의 사용자 인증( Authentication )의 검증을 담당하는 Interface.
      AuthenticationManager 수많은 AuthenticationProvider들을 등록 및 관리.

      。특정 인증방식의 수많은 AuthenticationProvider를 등록 시 사용자가 다양한 인증방식( JWT등 )을 통한 로그인을 수행해도 해당 인증방식을 제공하는 등록된 AuthenticationProvider를 자동으로 식별하여 인증을 수행.
    public interface AuthenticationManager {
        Authentication authenticate(Authentication authentication) throws AuthenticationException;
    }
    • AuthenticationManager객체.authenticate(Authentication객체)
      credentials 정보만 포함된 미완성된 Authentication instance를 전달하여 사용자의 Authentication 정보를 검증 후 인증 성공 시 principal, authorities가 추가된 완전한 Authentication instance를 반환하고, 인증에 실패 시 AuthenticationException 발생.



    • AuthenticationProvider
      Spring Security의 특정 사용자인증방식을 처리하는 Interface.
      AuthenticationManager에 등록하여 사용자가 해당 AuthenticationProvider가 제공하는 인증방식의 로그인요청을 보낼 경우 AuthenticationManager에 의해 식별되어 인증을 수행하게됨.

      AuthenticationManager에 의해 식별되어 Authentication instance의 CredentialPasswordUserDetailsService를 호출하여 해당 Credentialusername에 해당하는 사용자정보를 DB에서 UserDetails instance로 가져온 후 해당 Password를 서로 비교하여 사용자인증의 검증을 수행.
      ▶ 검증이 성공할 경우, principal, authorities 정보를 추가로 포함한 완전한 Authentication instance를 return.

      AuthenticationProvider 종류

      • JwtAutheticationProvider :
        JWT 기반 인증을 처리하는 AuthenticationProvider.


    • UserDetailsService :
      Spring Security에서 사용자 인증정보의 조회를 위해 DB에서 저장된 사용자정보를 조회하는 역할을 수행하는 Core Interface.

      。 주로 AutheticationProvider에 의해 호출되어 DB에서 저장된 사용자정보를 조회 및 해당하는 사용자정보를 UserDetails instance로 return하는 역할을 수행.
      AutheticationProvider에 의해 HTTP RequestAuhtentication Header 정보를 포함한 Authentication instance의 CredentialsUserDetails instance의 Password를 비교하여 사용자인증의 검증을 수행.
      • UserDetailsService.loadUserByUsername("username") :
        。사용자가 자격증명을 전달 시 DB에서 해당 자격증명의 ID 문자열 데이터에 해당하는 사용자 정보를 조회 후 UserDetails 객체로 반환.
        AutheticationProvider에 의해 Authentication instance의 CredentialsUserDetails instance의 Password를 비교하여 사용자인증의 검증을 수행.


    • SecurityContextHolder :
      。현재 실행중인 threadSecurityContext를 저장 및 관리하는 Class.
      ThreadLocal을 통해 Thread별로 SecurityContext를 저장.

      。인증이 완료된 Authentication instance가 저장된 SecurityContext 객체를 저장.
      Application 어디에서든지 현재 로그인한 사용자 인증정보를 가져올 수 있음.

    • SecurityContext :
      。현재 사용자의 인증정보를 포함하는 Container역할의 객체
      AuthenticationProvider에 의해 인증이 완료된 Authentication instance를 저장

      SecurityContextHolder를 통해 접근 가능.

      SecurityContextHolder , SecurityContext , Authentication 관계

      SecurityContextHolder
         └── SecurityContext (보안 컨텍스트)
               └── Authentication (인증 정보)
                     ├── Principal (사용자 정보)
                     ├── Credentials (비밀번호, JWT 등)
                     └── Authorities (권한 정보)


  • WebSecurityConfiguration :
    Spring Security의 웹보안 Configuration을 담당하는 @Configuration Class.
    @Bean Method를 생성 후 SecurityFilterChain instance를 활용하여 Authentication , Authorization , CSRF Protection 등의 보안정책을 해당 Configuration Class에서 설정이 가능.

    SpringBootWebSecurityConfiguration
    Servlet Application을 보호하는 역할의 웹보안 Configuration Class.
    ▶ 웹보안을 위한 Default로 설정된 Configuration Class.

    Spring은 기본적으로 FilterChain을 통해 명시적으로 인증된 HTTP Request에 대해서만 허용 및 formLogin()httpBasic()이 기본적으로 활성화 설정되도록 설정됨.
    CsrfFilter도 기본적으로 활성화 설정이 되어있음.



  • SecurityFilterChain :
    import org.springframework.security.web.SecurityFilterChain;
    Spring Security에서 제공하는 보안 filter 모음 역할의 Interface.
    ( Authentication Filter , Authorization Filter , CORS Filter , CSRF Filter , ExceptionTranslation Filter 등)
    Spring Security의 핵심이 되는 기능을 제공하며 사용자에게 적절한 AuthenticationAuthorization이 부여됨

    HttpServletRequest에 대응하는 FilterChain을 정의.

    SecurityFilterChain의 구현 Class로서 HttpSecurity가 존재하며 HttpSecurity객체.build()로 생성된 HttpSecurity instanceSecurityFilterChain의 instance로서 활용이 가능.
    @Configuration이 선언된 Class에서 @Bean Method의 매개변수로 전달한 HttpSecurity객체에서 Custom된 FilterChain를 적용한 HttpSecurity객체의 Instance를 SecurityFilterChain Instance로 반환.

    Filter Chain의 경우 filter에 따른 Validation이 순서대로 이루어짐.
    1. CORS , CSRF 등의 Filter : 기본필터
    2. Authentication Filter : 적절한 자격증명 보유여부 Filtering
    3. Authorization Filter : 접근하려는 Resource에 적절한 접근권한이 있는지 Filtering

      Security Filter Chain 핵심적인 기능

      • Controller에 Mapping된 모든 URL이 보호.
      • 승인되지 않은 요청에 대해서는 로그인 양식이 표현됨.

        ▶ URL로 Http Request가 전송되었을때, SecurityFilterChain의 필터를 통해 사용자가 인증(Authenticate)되지 않은 경우, Login Form을 표시하는 역할을 수행.

실습

  • 실습용 Spring Project 생성
    Gradle Build Tool 설정. Gradle 관련
    。dependency : Spring Web , Spring Security

    Spring Dev Tool dependency 추가 시 다음 구문을 build.gradledependencies { }에 추가.
    implementation 'org.springframework.boot:spring-boot-devtools'

  • 정적 개발용 자격증명 설정 및 실습용 REST API 정의
   // application.properties
spring.security.user.name=user123
spring.security.user.password=pw456

▶ 해당 개발용 자격증명의 경우 In-Memory DB에 저장되어 배포환경으로는 부적합.

import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
@RestController
public class Security_Filter_Chain {
    private List<Todo> TODOS =  new ArrayList<Todo>( List.of(new Todo("wjdtn1", "UOS"),
            new Todo("wjdtn2","KHU")));
    @GetMapping(path="/todos")
    public List<Todo> returnTodos(){
        return TODOS;
    }
}
record Todo (String username, String description) {}

record를 통해 Todo Class instance를 쉽게 생성.
List.of(요소) : immutable List 생성 시 사용하는 Method.

Spring ApplicationREST API에 접근하는 방법

  • Spring의 Login Form을 통한 로그인


    。사용자가 로그인 시 Spring은 해당 사용자에 대해 Session Cookie를 발급하고 이후 사용자가 수행하는 모든 API호출에 대하여 일일이 로그인 할 필요없이 해당 Session CookieHTTP Request에 포함되어 함께 전송.
    ▶ 전송된 Session CookieSpring Security에 의해 자동으로 인증이 수행됨.

  • HTTP Basic AuthenticationAuthentication Header에 포함하여 HTTP Request 전송
    ID:PWBase64로 인코딩하여 앞에 Basic을 명시한 Base64코드 ( Basic Base64코드 )을 HTTP RequestAuthorization Header로 포함하여 Authentication을 수행.


  • CSRF Token을 사용하여 CSRF Protection을 해결하여 POST , PUT , PATCH , DELETE Method 사용하도록 설정.
    Spring Security를 Application에 선언할 경우 기본적으로 CSRF Protection이 활성화.
    GET 이외 HTTP Request Method를 통해 요청 시 발생하는 CSRF Protection이 발생.

    JWT Token을 사용하는 STATELESS의 경우에만 CSRF Protection 비활성화를 수행하여 REST APIPOST , PUT , PATCH , DELETE Method를 수행하며 , 그외의 경우 CSRF Token을 발급 및 활용하여 POST , PUT , PATCH , DELETE Method를 수행
    Server에서 HTTP Request 요청 직전마다 매번 Client에게 발급하는 CSRF Token을 Client에서 HTTP RequestAuthentication Header로서 포함하여 전송

    Thymeleaf를 통해 Web Application을 build 시 Spring Security는 자동으로 모든 FormCSRF Token을 생성하여 추가된다.

    REST API 사용 시 CSRF Token 생성 및 활용
    Server에서 Client의 이전 HTTP Request에서 Session과 연결된 CSRF Token을 발급 및 Client는 매번 Http Request를 전송할때마다 이전 HTTP Request에서 발급된 고유한 CSRF TokenX-CSRF-TOKEN Header로 포함하여 함께 전송하는 방식.

    • CSRF Token 참조 및 전송
      @RestController Class에서 Controller Method의 매개변수의 HttpServletRequest instance를 통해 해당 REST API 호출시의 HTTP Request에서 보통 form에서 type=hidden으로 숨겨진 <input> field의 name="_csrf"를 참조하여 CSRF-TOKENCsrfToken으로 캐스팅하여 반환하는 Controller Method 생성
    import jakarta.servlet.http.HttpServletRequest;
    import org.springframework.security.web.csrf.CsrfToken;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    @RestController
    public class SpringSecurityHttpServlet {
        @GetMapping("/csrf-token")
        public CsrfToken getCsrfToken(HttpServletRequest request) {
            return (CsrfToken) request.getAttribute("_csrf");
        }
    }


    JWT 또는 HTTP Basic AuthenticationAuthentication header를 포함하여 해당 API를 호출할 경우 , Spring에서 발급한 CSRF TOKEN이 포함된 form에서 CSRF TOKEN을 획득하여 반환.

    CSRF Token을 가져 온 후 HTTP Request의 기존 Authentication Header와 추가적으로 X-CSRF-TOKEN Header를 통해 CSRF Token값을 포함하여 POST HTTP Request로 API호출 시 정상적으로 HTTP Response가 반환됨을 관측가능

    • HttpServletRequest :
      Java Servlet API에서 제공하는 Interface.
      ClientWeb Application에 전송한 HTTP Request 정보를 포함.

      。주로 Spring MVC Controller에서 URL Parameter , Header , Session 정보를 가져올 때 사용됨.
      Controller Class에서 Controller Method의 매개변수의 HttpServletRequest instance를 통해 해당 REST API 호출시의 HTTP RequestCSRF Token, Request Parameter , Header , Session 정보를 획득.

      HttpServletRequest Method

      • HttpServletRequest객체.getParameter("Request Parameter명") :
        HTTP RequestRequest Parameter 값 가져오는 Method.
        @RequestParam과 동일한 역할을 수행.

      • HttpServletRequest객체.getParameterValues("중복된 Request Parameter명") :
        HTTP Request의 동일 이름의 여러 Request Parameter의 값을 배열로서 가져오는 Method.

      • HttpServletRequest객체.getHeaders("Header 종류 이름") :
        HTTP Request의 특정 Header을 가져오는 Method.
        ex ) request.getHeader("Content-Type")

      • HttpServletRequest객체.getCookies() :
        HTTP RequestCookie를 가져오는 Method.

      • HttpServletRequest객체.getMethod() :
        HTTP RequestHTTP Request Method의 정보를 가져오는 Method.

      • HttpServletRequest객체.getRequestURI() :
        HTTP Request가 요청한 URI을 가져오는 Method

      • HttpServletRequest객체.getContextPath() :
        Web ApplicationContext 경로를 반환하는 Method.

      • HttpServletRequest객체.getRemoteAddr() :
        HTTP Request를 요청한 Client의 IP 주소를 반환하는 Method.

      • HttpServletRequest객체.getSession() :
        HTTP RequestSession을 가져오는 Method.


  • SecurityFilterChain을 통해 CSRF Protection을 비활성화 설정
    SpringCsrfFilter가 기본적으로 활성화되어있으므로, 비활성화 설정.
    ▶ 이때 , 반드시 Session PolicySession을 사용하지않는 STATELESS로 설정된 경우에만 비활성화.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class BasicAuthSecurityConfig {
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        // 모든 HTTP Request에 대해 인증된 사용자만 접근가능하도록 접근권한을 부여.
        return http.authorizeHttpRequests((auth)->{
            auth.anyRequest().authenticated();
        })
                // HTTP Basic Authentication 활성화 , formLogin() 사용 X
        .httpBasic(Customizer.withDefaults())
                // Session Policy를 Session 비활성화로 설정.
        .sessionManagement((session)->{
            session.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        })
                // CSRF 비활성화
        .csrf((csrf)->{
              csrf.disable();
        })
                // HttpSecurity Instance를 생성
         		// → SecurityFilterChain instance로서 return.
        .build();
    }
}

CsrfFilter를 비활성화 할 경우, 각 HTTP RequestX-CSRF-TOKEN header를 통한 CSRF Token을 추가로 포함할 필요없이 JWT 또는 HTTP Basic AuthenticationAuthentication header에 포함하여 POST HTTP Request를 전송해도 정상적으로 HTTP Response가 반환.

。이때, formLogin()은 정의하지 않았으며 Session이 비활성화 되었으므로, 이후 Authentication header 없이 HTTP Request를 수행할 경우 자격증명 요구 시 로그인 & 로그아웃기능은 작동하지 않고, Basic Authetication Form을 요구하도록 설정됨
Session 비활성화 된 STATELESS의 경우 CSRF Protection를 비활성화하고, Session 기반 인증을 수행하여 Session을 통해 로그인 상태를 유지하는 formLogin()을 사용할 필요가 없으므로 비활성화 설정.

Spring Security에서 사용자의 자격증명 저장하기
。기존에 application.properties에서 정적으로 ID와 PW를 고정하여 정의한 개발용자격증명 삭제.

  • InMemoryUserDetailsManager를 활용하여 In-Memory DB에 사용자 자격증명 생성 및 저장
    InMemoryUserDetailsManager , UserDetails를 활용한 계정 생성
    InMemoryUserDetailsManager , UserDetails , User 사용.

    InMemoryUserDetailsManager는 인메모리 저장방식이므로 , 테스트환경에서만 적합하고, 배포환경에서는 부적합.
    ▶ 차후 JDBC / JPA를 활용한 DB에 자격증명을 저장 및 참조하는 방식을 사용.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
// 생성한 자격증명의 role 지정 용도의 Enum
enum roles { ADMIN,USER }
@Configuration
public class SpringSecurityConfig {
    @Bean
    public InMemoryUserDetailsManager inMemoryUserDetailsManager() {
        // User Class의 User.roles() 설정용 Role은 Enum으로 설정하는게 좋다.
        roles role1 = roles.USER;
        roles role2 = roles.ADMIN;
        return new InMemoryUserDetailsManager(
            getUserDetails("wjdtn1","1234", role1),
                getUserDetails("wjdtn2","4567", role2)
        );
    }
    private UserDetails getUserDetails(String username, String password,roles role){
        return User.builder().passwordEncoder((input)->
            passwordEncoderB().encode(input)
        ).username(username).password(password).roles(role.toString()).build();
    }
    @Bean
    public PasswordEncoder passwordEncoderB() {
        return new BCryptPasswordEncoder();
    }
}

BCryptPasswordEncoder를 instance를 생성하여 PasswordEncoder type으로 Spring Bean으로서 return하는 Method( passwordEncoderB() )를 정의.

passwordEncoderB()이 반환하는 BCryptPasswordEncoder instance의 .encode(문자열) method를 이용해 입력된 문자열을 input하여 BCrypt hashing을 통해 encode 된 문자열을 반환하는 람다식으로 User.passwordEncoder()의 생성자로 설정.

。자격증명 인증 시 BCryptPasswordEncoder의 알고리즘에 의해 사용자가 임의로 입력한 PW가 BCrypt 알고리즘으로 인코딩이 되고, User.builder().passwordEncoder()에 의해 기존에 저장된 "1234" PW가 BCrypt 알고리즘으로 Encoding되어 서로 매칭하여 비교하는 방식임.
Base64와 다르게 Decoding이 불가능하므로 Encoding 후 서로 비교해서 Validation을 수행해야한다.

。 이후 InMemoryUserDetailsManager를 통해 저장된 자격증명을 통해 HTTP Basic Authentication으로 Authentication HeaderID, PW를 입력 후 HTTP Request에 포함하여 API호출 시 다음처럼 성공된 응답값을 가져온다.
▶ 개발용 자격증명이 아닌, 임의로 생성한 자격증명으로 접근권한을 획득.

  • Spring JDBC를 활용하여 DB에 사용자 자격증명 생성 및 저장
    JdbcUserDetailsManager(DataSource객체)를 활용하여 사용자자격증명을 DB에 저장.
    InMemoryUserDetailsManager 대체.
    • Spring JDBC , PostgreSQL DB 관련 dependency 추가하기 PostgreSQL DB DataSource 연결
      implementation 'org.springframework.boot:spring-boot-starter-jdbc'
      build.gradledependencies { }에 정의 한 후 Reload All Gradle Projects 실행.
    implementation 'org.springframework.boot:spring-boot-starter-jdbc'
    implementation group: 'org.postgresql' , name: 'postgresql' , version: '42.2.23'  // PostgreSQL Driver
    runtimeOnly 'org.postgresql:postgresql' 
    • JDBC를 활용한 application.propertiesPostgreSQL DB DataSource 설정 정의
      。Spring Boot가 기본적으로 자동으로 연결할 PostgreSQL DBDataSource를 설정하기위해 application.properties에 postgresql에 관한 JDBC를 정의.
      @Bean을 선언하여 직접 DataSource instance를 생성하는 방법은 아래에서 소개.

      。Spring Boot가 자동으로 연결할 DataSource를 설정 시 application.properties에 기존에 정의된 h2-db 설정을 삭제해야한다
    spring.datasource.url=jdbc:postgresql://localhost:5432/GeoDB
    spring.datasource.username=postgres
    spring.datasource.password=wjd747
    spring.jpa.database-platform=org.hibernate.dialect.PostgresSQLDialect
    spring.jpa.show-sql=true
    spring.jpa.hibernate.ddl-auto=update
    • PostgreSQL DB에 다음 구문을 입력하여 Table 생성
      。해당 SQL 구문은 org/springframework/security/core/userdetails/jdbc/users.ddlSpring SecurityJDBC 기반 사용자인증을 위해 자격증명을 저장하는 기본 테이블을 생성하는 구문.
      users : 사용자 ID, 사용자 PW 저장
      authorities : 역할(권한) 을 저장.
      authorities는 자식으로서 usersusername를 외래키로서 참조
      JdbcDaoImpl.DEFAULT_USER_SCHEMA_DDL_LOCATION
    CREATE TABLE users (
        username VARCHAR(50) NOT NULL PRIMARY KEY,
        password VARCHAR(500) NOT NULL,
        enabled BOOLEAN NOT NULL
    );
    CREATE TABLE authorities (
        username VARCHAR(50) NOT NULL,
        authority VARCHAR(50) NOT NULL,
        CONSTRAINT fk_authorities_users FOREIGN KEY(username) REFERENCES users(username)
    );
    CREATE UNIQUE INDEX ix_auth_username ON authorities (username, authority);
    • Spring JDBCPostgreSQL DB DataSource instance 생성하기 DataSource Instance 생성
      PostgreSQL에서 JdbcUserDetailsManager(DataSource객체)을 통한 DB에 사용자 자격증명을 추가할때의 용도로 활용할 DataSource instance 정의하기.
      ▶ 사용자 자격증명 저장이 필요없는 경우, 설정안해도 application.properties에서 Spring Boot에 자동연결설정된 DataSource를 활용하여 기본적으로 CRUD를 수행 가능.

      @Configuration이 선언된 Configuration Class에서 DataSource instance를 반환하는 @Bean Method를 생성.
      postgresql DataSource를 정의하기위해 application.properties에서 작성된 JDBC 내용과 유사.
    	@Bean
        public DataSource pgdataSource() {
            HikariConfig config = new HikariConfig();
            config.setJdbcUrl("jdbc:postgresql://localhost:5432/GeoDB");
            config.setUsername("postgres");
            config.setPassword("wjd747");
            return new HikariDataSource(config);
        }
      • DataSource : javax.sql.DataSource
        JDBC에서 특정 DBDB Connection을 관리하는 Interface.
        ApplicationDB에 접근 시 DataSource를 통해 Connection을 관리.

        。최적화된 DB Connection Pool을 제공하고, Spring Boot에서 Auto-Configuration이 가능.

      • HikariCP :
        。Spring Boot에서 기본적으로 활용하는 고성능 JDBC Connection Pool을 관리하는 DataSource Interface를 구현한 Class.
        new HikariConfig()를 통해 DB Connection이 구현된 DataSource instance 생성.

        JDBC Connection Pool을 통해 DriverManagerDataSource와 달리 DB Connection을 재사용.
        • Connection Pool :
          DB Connection을 매번 생성 시 성능 저하 발생하는 단점을 보완.
          Connection Pool을 사용 시 일정 수의 DB Connection을 사전에 생성 및 재사용 가능하여 불필요한 DB Connection 생성을 방지 가능.

        • DriverManagerDataSource :
          Spring에서 JDBC Connection을 위한 DataSource Interface를 구현한 Class.
          new DriverManagerDataSource()를 통해 DB Connection이 구현된 DataSource instance 생성.

          。 매번 새로운 DB Connection을 생성하며 연결을 재사용하지 않고 사용하므로 HikariCP에 대체됨.


    • JdbcUserDetailsManager(DataSource객체)를 통해 DB에 사용자 자격증명 생성 및 저장 JdbcUserDetailsManager
      。기존의 InMemoryUserDetailsManager()를 통해 In-Memory DB에 사용자 자격증명을 추가한 코드에서 JdbcUserDetailsManager(DataSource객체)로 Refactoring.

      PostgreSQL DB DataSource instance를 생성하는 @Bean Method 구현 및 해당 method를 통해 생성된 instance로 JdbcUserDetailsManager(DataSource객체)의 instance를 생성하여 UserDetails instance를 추가한 후 Spring Bean으로 반환.

      PasswordEncoder의 해싱알고리즘 구현 Class ( = BCryptPasswordEncoder )의 생성자에 Hashing 작업량의 강도를 설정하는 숫자를 설정.
      ▶ 기본값 : 10
    import com.zaxxer.hikari.HikariConfig;
    import com.zaxxer.hikari.HikariDataSource;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.core.userdetails.User;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.security.provisioning.JdbcUserDetailsManager;
    import javax.sql.DataSource;
    // 생성한 자격증명의 role 지정 용도의 Enum
    enum roles { ADMIN,USER }
    @Configuration
    public class SpringSecurityConfig {
        @Bean
        public JdbcUserDetailsManager inMemoryUserDetailsManager() {
            // User Class의 User.roles() 설정용 Role은 Enum으로 설정하는게 좋다.
            roles role1 = roles.USER;
            roles role2 = roles.ADMIN;
            // PostgreSQL DB의 DataSource를 이용해 JdbcUserDetailsManager instance 생성.
            JdbcUserDetailsManager userDetailsManager = new JdbcUserDetailsManager(pgdataSource());
            // 생성한 JdbcUserDetailsManager instance에 UserDetails Instance 추가
            userDetailsManager.createUser(getUserDetails("wjdtn1","1234", role1));
            userDetailsManager.createUser(getUserDetails("wjdtn2","4567", role2));
            // JdbcUserDetailsManager Instance를 Spring Bean으로서 반환
            return userDetailsManager;
        }
        private UserDetails getUserDetails(String username, String password,roles role){
          	// User.passwordEncoder(람다식)을 통해 input된 문자열을 PasswordEncoder 구현 Class를 통해
            // `Hashing`으로 Encoding을 수행하여 Hash값을 반환.
            return User.builder().passwordEncoder((input)->
                passwordEncoderB().encode(input)
            ).username(username).password(password).roles(role.toString()).build();
        }
        @Bean
        public PasswordEncoder passwordEncoderB() {
          	// PasswordEncoder 구현 Class의 생성자에 `Hashing` 작업량의 강도를 설정하는 숫자를 설정.
            // 10이 기본값.
            return new BCryptPasswordEncoder(10);
        }
        // PostgreSQL DB의 DataSource instance를 생성 후 반환.
        @Bean
        public DataSource pgdataSource() {
            HikariConfig config = new HikariConfig();
            config.setJdbcUrl("jdbc:postgresql://localhost:5432/GeoDB");
            config.setUsername("postgres");
            config.setPassword("wjd747");
            return new HikariDataSource(config);
        }
    }

    UserDetails instance를 통해 사용자 자격증명 생성 시 User.roles("역할")의 경우 Enum으로 고정된 상수로서 참조하여 가져오는게 좋다.


    。DB에서 JDBC를 통한 사용자인증을 위한 기본 테이블로서 생성한 users DB Table에 사용자 자격증명이 성공적으로 추가된 것을 확인 가능.
    。 Password는 BCryptPasswordEncoder의 알고리즘에 의해 Encoding.
    Decoding은 불가능하고 추후 사용자가 로그인 시 입력하여 Encoding된 비밀번호와 비교대상이 된다.

    authoritiesusernameusersusername의 외래키.

    User.roles("역할명")를 통해 설정된 역할의 경우 authorities table에서 ROLE_역할명으로 저장됨.

    User.builder().passwordEncoder((input)-> passwordEncoderB().encode(input) ).username(username).password(password)
            .roles(role.toString(),"DEVELOPER").build();

    ▶ 사용자자격증명 당 복수 이상의 역할을 가질 수 있다.

    。이후 DB에 저장된 자격증명을 통해 HTTP Basic Authentication을 통해 HTTP Request를 전송 시 성공적으로 HTTP Reponse를 반환하여 Application의 Resource에 접근이 가능함을 확인 가능.

    • Spring Security에서 비밀번호 저장 시 주의점
      • SHA-256 같은 Hash 알고리즘은 더이상 안전하지 않음.
        bcrypt , scrypt 등의 Hash 알고리즘 활용.

      • Adaptive One-Way function 적용 및 Work Factor를 최소 1초가 소요되도록 설정.
        • Adaptive One-Way function : bcrypt , scrypt , argon2 ...
          Hashing 알고리즘은 단방향변환으로서 데이터에서 Hash를 생성하지만, 역방향으로 Hash에서 데이터를 생성할 수 없음을 의미.
          Hash값은 원본으로 복원이 불가능하므로, DB에 저장 시 공격자가 DB에 Access 하더라도 Hash값만 참조가능하고 원본 PW은 확인 불가능.

        • Work Factor :
          。사용자가 입력한 PWHashing 후 기존에 DB에 저장된 Hash값과 비교를 수행하는 검증의 소요시간.

          PW 저장의 경우 Hashing이 지나치게 빠르게 수행될 경우 공격자가 Hashing을 수행하면서 brute force algorithm을 적용할 수 있음.
          Hashing Algorithm 이 너무 빠르게 작동하지 않도록 Adaptive One-Way function을 활용하여 Work Factor 과정이 최소 1초가 소요되도록 설정

          Brute Force Algorithm
          。가능한 모든 경우의 수를 단순하게 전부 탐색함으로써 정답을 찾는 알고리즘 기법.
          ▶ 직관적이고 구현이 간단하지만, 경우의 수가 많아지면 연산량이 급격하게 증가하는 단점이 존재.



    • UserDetails :
      。 사용자별 세부 정보를 관리 및 load하는 Core Interface.
      ▶ 개별 사용자의 이름, PW, 권한 등의 세부정보를 해당 UserDetails를 구현한 Class( User Class )의 instance 내에 저장.

      @Configuration이 선언된 Configuration Class에서 @Bean Method를 통해 User Class를 활용하여 사용자자격증명을 포함한 UserDetails instance를 생성 후 JdbcUserDetailsManager(DataSource객체)에 포함하여 Spring Bean으로서 반환함으로써 DataSource를 통해 연결된 DB의 기본 Table에 전달하여 사용자 자격증명을 저장.



    • InMemoryUserDetailsManager
      new InMemoryUserDetailsManager(UserDetails 객체1, UserDetails 객체2....)
      UserDetails를 구현한 Class로서 Spring Security에서 제공하는 Spring Boot Memory 내부에서 사용자 정보를 관리.
      ▶ 해당 Class를 Spring Bean으로 등록하여 Application에서 정보를 입력한 계정을 In-Memory 방식으로 저장이 가능.
      H2-DB와 동일하게 In-Memory방식으로서 Application의 Process가 종료될 경우 정보들이 없어지면서 초기화되므로 JdbcUserDetailsManager(DataSource객체)를 사용한다.

      개별 사용자 계정정보를 담은 UserDetails instance를 해당 InMemoryUserDetailsManager instance에 저장.
      InMemoryUserDetailsManager()의 생성자는 가변매개변수를 받을 수 있으므로, 여러개의 UserDetails instance를 넣을수 있음.



    • JdbcUserDetailsManager(DataSource객체) : 활용
      var JdbcUserDetailsManager객체 = new JdbcUserDetailsManager(DataSource객체)
      Spring Security에서 사용자 자격증명을 DB에서 조회 및 관리할 수 있도록 하는 JDBC 기반 사용자인증 관리 Class.
      InMemoryUserDetailsManagerIn-Memory DB에 저장되는 방식을 DB에 저장할 수 있도록 대체.

      JdbcUserDetailsManager를 사용하기 위해서 특정 DB와 연결을 구현한 DataSource instance를 필요로 함.

      JdbcUserDetailsManager Method

      • JdbcUserDetailsManager객체.loadUserByUsername("username") :
        DB에서 입력된 "username"에 해당하는 사용자 정보를 조회 후 UserDetails instance로서 반환.

      • JdbcUserDetailsManager객체.createUser(UserDetails객체) :
        DB에 새로운 사용자 자격증명 추가.

      • JdbcUserDetailsManager객체.updateUser() :
        DB에서 사용자 정보를 조회

      • JdbcUserDetailsManager객체.deleteUser() :
        DB에서 사용자 정보를 삭제

      • JdbcUserDetailsManager객체.changePassword("이전비밀번호", "새비밀번호") :
        DB에서 사용자 비밀번호 변경


    • User :
      UserDetails를 구현한 구현체 Class 객체.
      UserDetailsInterface이므로 직접 Instance를 생성할 수 없으므로 User Class의 BuilderID, PW, Role를 지정하여 UserDetails의 Instance 생성.

      User Class Method

      • User.username("계정명") , User.password("비밀번호") , User.roles("역할명")
        UserDetailsID, PW, Role 정보를 설정.
        User.roles("역할명")의 역할의 경우 Enum을 통해 고정된 상수 집합을 정의하여 설정하는게 좋다.
        User.roles("역할명1","역할명2",...) 으로 복수의 역할명도 지정 가능.



      • User.build() :
        UserDetails Instance를 생성



      • User.withDefaultPasswordEncoder()
        。여 build()를 통해 UserDetails instance 생성.
        ID, PW, Role 정의 후 User.build()를 통해 UserDetails Instance를 생성하여 반환함으로써 자격증명을 생성 및 저장.

        。현재는
        deprecated된 코드User.builder()를 통해 BCryptPasswordEndcoder()**의 비밀번호 알고리즘으로 대체.
        UserDetails userDetails =
                      User.withDefaultPasswordEncoder()
                      .username("wjdtn")
                      .password("123456")
                      .roles("USER","ADMIN") // 특정 작업을 수행하기 위한 역할 설정
                      .build();
      • User.builder() :
        User.withDefaultPasswordEncoder()에서 비밀번호 알고리즘( .passwordEncoder(매개변수로 문자열을 입력받아 특정PasswordEncoder에 의해 인코딩하여 반환하는 람다식)을 추가 정의하여 UserDetails의 instance를 생성하는 Custom Method.
        User.withDefaultPasswordEncoder()를 대체.



      • User.passwordEncoder(매개변수로 문자열을 입력받아 특정PasswordEncoder에 의해 인코딩하여 반환하는 람다식) :
        。특정 PasswordEncoder( ex. BCryptPasswordEncoder )를 통해 문자열을 인코딩하여 반환하는 람다식을 정의하여 해당 UserDetails instance의 PasswordEncoder를 정의.
        Spring Security 6.x 부터는 람다기반 설정방식 권장
      UserDetails userDetails =
                      User.builder()
                      .passwordEncoder(input -> passwordEncoderB().encode(input))
                      .username("wjdtn")
                      .password("123456")
                      .roles("USER","ADMIN") // 특정 작업을 수행하기 위한 역할 설정
                      .build();
        @Bean
          public PasswordEncoder passwordEncoderB(){
              return new BCryptPasswordEncoder();
          }

      BCryptPasswordEncoder를 instance를 생성하여 PasswordEncoder type으로 Spring Bean으로서 return하는 Method( passwordEncoderB() )를 정의.

      passwordEncoderB()이 반환하는 BCryptPasswordEncoder instance의 .encode(문자열) method를 이용해 입력된 문자열을 input하여 BCrypt hashing을 통해 encode 된 문자열을 반환하는 람다식으로 User.passwordEncoder()의 생성자로 설정.

      。자격증명 인증 시 BCryptPasswordEncoder의 알고리즘에 의해 사용자가 임의로 입력한 PW가 BCrypt 알고리즘으로 인코딩이 되고, User.builder().passwordEncoder()에 의해 기존에 저장된 "1234" PW가 BCrypt 알고리즘으로 Encoding되어 서로 매칭하여 비교하는 방식임.
      Base64와 다르게 Decoding이 불가능하므로 Encoding 후 서로 비교해서 Validation을 수행해야한다.



    • PasswordEncoder :
      。입력된 비밀번호 문자열에 Adaptive One Way Function ( Hash 알고리즘 : scrypt, bcrypt, argon2, ... )을 적용하여 Hashing을 수행하는 기능을 상속하는 Spring SecurityInterface
      PasswordEncoder를 구현한 Class는 문자열에 대해 Hashing을 수행하며 수행된 Hash값DB에 저장됨.

      PasswordEncoder를 구현한 Class는 Hashing을 통한 단방향변환을 수행하므로 Encoding처럼 복원이 불가능.
      Encoder 명칭에 혼동 주의.

      PasswordEncoder 구현 Class : Hash Algorithm
      BCryptPasswordEncoder , Argon2PasswordEncoder , SCryptPasswordEncoder
      ▶ 구현 Class는 User.builder()User.passwordEncoder(람다식)을 활용하여 PW Hashing을 수행.

      	private UserDetails getUserDetails(String username, String password,roles role){
              // User.passwordEncoder(람다식)을 통해 input된 문자열을 PasswordEncoder 구현 Class를 통해
              // `Hashing`으로 Encoding을 수행하여 Hash값을 반환. 
              return User.builder().passwordEncoder((input)->
                  passwordEncoderB().encode(input)
              ).username(username).password(password).roles(role.toString(),"DEVELOPER").build();
          }
          @Bean
          public PasswordEncoder passwordEncoderB() {
              // PasswordEncoder 구현 Class의 생성자에 `Hashing` 작업량의 강도를 설정하는 숫자를 설정.
              return new BCryptPasswordEncoder(10);
          }

      Hash 알고리즘의 Instance를 생성하여 PasswordEncoder의 구현체로서 Spring Bean으로 반환하는 @Bean Method를 활용.

      • BCryptPasswordEncoder(강도) :
        new BCryptPasswordEncoder(강도)
        Spring Security에서 제공하면서 BCrypt hashing Algorithm을 사용하면서 PasswordEncoder Interface를 구현한 Class.
        BCrypt hashing function을 활용해서 비밀번호를 암호화 및 DB의 저장된 비밀번호와 일치 여부를 확인해주는 Method를 가지는 Class.

        。생성자에 Hashing 작업량의 강도를 설정하는 숫자를 반영. ( 기본값 : 10 )
        PasswordEncoder구현Class.encode(), PasswordEncoder구현Class.matchers(), PasswordEncoder구현Class.upgradeEncoding()의 Method가 존재.
          
profile
공부기록 블로그

0개의 댓글