Spring Boot 3 & Spring Framework 6 - Section 19 : Spring Security JWT 관련 설정

TopOfTheHead·2025년 3월 22일

Spring Security에서 Authentication Header에 자격증명을 담아서 로그인하는 방법
。formLogin() 등의 인증방식과 다르게 Session이 필요없는 STATELESS 방식이므로, CSRF Protection을 설정 가능.

  • HTTP Basic Authentication : Basic Base64인코딩한ID:PW
    。REST API의 보안에서 사용되는 가장 간단한 Authentication 방식.
    。session을 사용하지 않는 stateless 특징으로 HTTP Request마다 Authentication 정보를 포함해야한다.
    ▶ ID:PW를 Base64로 인코딩하여 앞에 Basic을 명시한 Base64코드 ( Basic Base64코드 )을 HTTP Request의 Authorization Header로 포함하여 Authentication을 수행.

    。 Javascript에서는 window.btoa(id + ":" + pw)로 ID : PW를 Base64로 인코딩한 Base64코드를 생성한다.

    。Basic Authentication은 expire date이 없으며, 사용자 정보도 포함하지않아 권한을 확인할 수 없다.
    。Base64 Encoding을 사용하여 Decoding이 가능하며 암호화가 되지않아서 보안에 매우 취약.
    ▶ 현재는 REST API보안 시 주로 JWT 사용



  • Custom Token
    。사용자가 임의로 Token System을 구축.
    。 보안결함이 존재할 수 있는 위험성과 서비스 제공자와 소비자가 이해해야하는 단점이 존재.
    ▶ JWT 활용
  • JWT사용 시 Spring에서 정의해야할 Dependency
    • spring-boot-configuration-processor
      。Spring Boot에서 @ConfigurationProperties가 선언된 Class를 자동으로 문서화를 수행하고 IDE에서 자동완성기능을 제공하는 Library.
      ▶ META-INF/spring-configuration-metadata.json 파일을 생성하여 문서를 자동화.
    <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-configuration-processor</artifactId>
    </dependency>
    • spring-boot-starter-oauth2-resource-server
      。Spring Boot Application을 Spring Security와 통합하여 OAuth 2.0 Resource Server로 설정하는 Library.
      。Maven
    <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
    </dependency>

    。Gradle

    implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'

    OAuth 2.0 Resource Server :
    。OAuth2.0 Authentication을 기반으로 Authenticated된 사용자에 대해서만 접근할 수 있도록 보호된 API를 제공하는 Server.
    ▶ 기존 Application의 REST API가 노출되어있으므로, Application을 OAuth 2.0 Resource Server로 설정하여 보호된 REST API를 제공하도록 설정됨.

    。 Client의 HTTP Request에 Authentication Header에 포함된 Access Token을 전달받아서 검증 후 Authentication을 수행하여 API에 접근.
    ▶ Dependency를 추가 시 Access Token ( = JWT , Opaque Token 등 )을 검증하여 Authenticated된 HTTP Request만 허용하도록 설정됨.

    • Resource Server : Access Token을 검증하여 HTTP Request를 허용 또는 거부.

    • Authorization Server : Access Token을 발급 및 관리


  • JWK ( JSON Web Key )
    。Public Key 정보를 JSON Format으로 표현한 데이터구조.
    ▶ JWT Signature의 검증을 수행하기 위해서 Public Key가 필요하므로 주로 RSA방식의 JWT의 Signature를 검증 시 활용.

    JWK Set : 여러개의 Public Key를 포함하는 Public Key 목록
    JWK Set URI : JWK Set을 제공하는 URI
    。JWT의 발행자와 검증자가 Public Key를 안전하게 공유하고 사용하는 용도로 이용됨.
    • JWK 사용 용도
      • JWK Set URI를 통한 JWK Set 제공
        。Authentication Server가 URI 접속 시 JWK Set를 제공하여 Client가 JWK Signature를 검증할 수 있도록 함.
        ex ) https://example.com/oauth2/jwks :
        ▶ OAuth 2.0 Authentication Server가 제공하는 Public Key 목록.

      • JWT Signature 검증
        。JWT를 사용하는경우, 서명검증을 수행하기 위해 Public Key가 필요하므로, JWK를 사용하여 안전하게 Public Key를 참조.

      • 자동화 Key 관리
        。Public Key를 JWK로 제공 시 여러 서버간 Public Key를 쉽게 공유 및 갱신이 가능.


    • JWK 구조
    {
      "kty": "RSA",
      "kid": "1234abcd",
      "use": "sig",
      "alg": "RS256",
      "n": "modulus_in_base64url",
      "e": "exponent_in_base64url"
    }
    • kty ( Key Type ) :
      。Public Key의 Type을 지정 ( ex. RSA, EC )
      ▶ Public Key의 알고리즘 식별 시 필요

    • kid ( Key ID ) :
      。Public Key의 고유 식별자
      ▶ JWK Set으로 여러 개의 Public Key를 제공하는 경우 식별용도로 활용됨.

    • use ( Key Use ) :
      。Public Key의 용도를 지시.
      。"use" : "sig" : 서명용 , "use" : "enc" : 암호화용 등.
      ▶JWT는 주로 Signature의 검증으로 사용하므로 sig가 활용됨.

    • alg ( Algorithm ) :
      。Public Key를 사용할 알고리즘을 지정 ( ex. RS256, RS512, ES256 )
      ▶ JWT Signature를 검증 시 사용할 알고리즘을 지정.

    • n ( Modulus ) :
      。RSA Public Key에서 사용되는 Modulus값을 base64로 인코딩하여 설정.
      ▶ RSA에서 Public Key의 유효성을 결정하는 핵심요소.

    • e ( Exponent ) :
      。RSA Public Key에서 사용되는 지수값을 base64로 인코딩하여 설정.

Spring Boot에서 JWT 실습 수행하기

  • OAuth2.0 Dependency 추가
    。Gradle의 경우 build.gradle에 다음 구문을 정의 후 Reload 수행.
    implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'

  • JWT를 기반으로 Spring Security Configuration 을 수행하는 Class 생성
    。이전의 Spring Security의 Configuration Class ( ex. HTTP Basic Authentication을 수행 )는 모두 삭제하거나 @Configuration을 삭제하여 비활성화.

    。JWT는 Session을 사용하지않는 STATELESS이므로, Session 정책을 Session을 사용하지 않도록 설정 및 CSRF 비활성화.

    。HTTPSecurity객체.oauth2ResourceServer(람다식)를 활용하여 OAuth2.0 Resorce Server를 이용하여 HTTP Request의 Authentication Header에 포함된 Access Token ( ex. JWT, Opaque Token 등 )을 전달받아 JWT Decoder의 Instance를 통해 Decrypt를 수행 및 유효성 검증을 수행하여 Authentication을 설정하여 API에 접근이 가능하도록 설정.

    。JwtDecoder Interface의 구현체( = NimbusJwtDecoder )의 Instance를 생성하는 @Bean Method를 정의하여 Encrypt된 JWT Token을 Decrypt 후 유효성을 검증하는 역할을 수행.
    ▶ JWT를 Decryption 및 검증하는 용도로 활용되므로, JWK URI에서 Pulbic Key를 가져와서 Decrypt를 수행하는 JwtDecoder Instance를 생성하여 반환.

    。해당 @Bean jwtDecoder() Method를 통해 JwtDecoder instance를 생성 후 Spring Context에 저장하며 Spring Security는 OAuth2.0 Resource Server로 동작 시 JWT Token을 자동으로 검증하지만, 기본 설정으로 JWT를 검증하는 JWK Set을 알 수 없으므로, auth server에서 제공하는 공개키인 JWK Set를 통해 비대칭키인 JWT의 서명을 검증.
// JwtSecurityConfiguration.java
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.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class JwtSecurityConfiguration {
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        // 모든 HTTP Request에 대해 인증된 사용자만 접근가능하도록 접근권한을 부여.
        return http.authorizeHttpRequests((auth)->{
                    auth.anyRequest().authenticated();
                })
                // HTTP Basic Authentication 활성화
                .httpBasic(Customizer.withDefaults())
                // Session Policy를 Session 비활성화로 설정.
                .sessionManagement((session)->{
                    session.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
                })
                // CSRF 비활성화
                .csrf((csrf)->{
                    csrf.disable();
                })
            	// Oauth2.0 Resource Server를 통해 HTTP Request의 Authentication Header에 포함된
                // JWT Token을 커스텀 JWT Decoder를 통해 Decrypt 및 검증을 수행.
                .oauth2ResourceServer(oauth2 -> oauth2
                        .jwt(jwt -> jwt
            					// 커스텀 Decoder를 통해 생성된 JwtDecoder Instance를 활용.
                                .decoder(jwtDecoder()) 
                        ))
                // HttpSecurity Instance를 생성
         		// → SecurityFilterChain instance로서 return.
                .build();
    }
    @Bean
    public JwtDecoder jwtDecoder() {
        return NimbusJwtDecoder.withJwkSetUri("https://auth-server-url/.well-known/jwks.json").build();
    }
}
  • JWT 검증을 수행하기위한 RSA 기반의 RSA Public Key, RSA Private Key를 생성 및 해당 RSA Public Key를 기반으로 JWT를 Descrypt하는 JWT Decoder @Bean Method 구축하기
    • RSA 암호화 알고리즘의 Key Pair instance를 생성하는 @Bean Method 정의하기
      。RSA 암호화 알고리즘을 통해 Public Key , Private Key로 구성된 Key Pair 객체 생성.
      ▶ oepnssl 또는 java.security.KeyPairGenerator 활용.

      。@Configuration이 선언된 Configuration Class에 Key Pair instance를 생성하여 반환하는 @Bean Method 정의.
      ▶ @Bean를 선언하여 Spring에 의해 KeyPair을 필요로하는 Method의 매개변수로 자동으로 Auto Wiring.
    import java.security.KeyPair;
    import java.security.KeyPairGenerator;
    import java.security.NoSuchAlgorithmException;
    	@Bean
        public KeyPair generateKeyPair() throws NoSuchAlgorithmException {
        	// RSA 알고리즘을 사용한 KeyPairGenerator instance 생성
            KeyPairGenerator keyPairGenerator =  KeyPairGenerator.getInstance("RSA");
        	// 2048 bit의 키 크기 설정
            keyPairGenerator.initialize(2048);
        	// KeyPair instance 생성
            return keyPairGenerator.generateKeyPair();
        }

    。 RSA 암호화 알고리즘으로 생성된 2048 bit 크기의 Public Key, Private Key를 포함한 KeyPair instance가 생성되어 Spring Bean으로 return.

    • RSA 암호화 알고리즘으로 생성된 KeyPair instance를 이용하여 RSAKey instance 생성
      。 Nimbus JOSE+JWT 라이브러리를 활용하여 RSAKey instance 생성.
      。RSAKey instance에 RSA 암호화 알고리즘의 Private Key, Public Key, Key ID를 정의 및 생성하여 return하는 @Bean Method를 생성.
      。UUID.randomUUID().toString() : 중복되지않는 랜덤한 UUID를 문자열로서 생성.
    import java.security.interfaces.RSAPublicKey;
    import java.util.UUID;
    // KeyPair을 return하는 @Bean Method로부터 Auto Wiring되어 전달됨.
        @Bean
        public RSAKey generateRSAKey(KeyPair keyPair){
            return new RSAKey
      				// RSAKey KeyPair객체의 Public Key instance를 RSAPublicKey로 casting하여 설정.
                    .Builder((RSAPublicKey)keyPair.getPublic())
      				// RSAKey KeyPair객체의 Private Key instance를 RSAPrivateKey로 casting하여 설정.
                    .privateKey((RSAPrivateKey)keyPair.getPrivate())
      				// 중복되지않는 UUID를 통한 kid를 지정.
                    .keyID(UUID.randomUUID().toString())
                    .build();
        }

    。RSA 암호화 알고리즘으로 생성한 KeyPair 객체의 PublicKey와 PrivateKey를 RSAKey instance를 새로 생성 및 casting 후 적용한 RSAKey instance 생성.
    ▶ JWKSet instance에 여러개의 Public Key를 정의하고자 keyID 지정.

    • RSAKey instance를 이용하여 JWKSource ( JSON Web Key Source )을 생성하는 @Bean Method 정의
      。RSAKey instance를 통해 JWKSet instance 생성 후 JWKSource instance를 생성하는 @Bean Method 생성
      ▶ RSAKey를 return하는 @Bean Method로부터 AutoWiring하여 공급.

      。JWKSet instance로 JWKSource instance 생성 시 JWKSource Interface의 instance를 생성 및 Abstract Method get()를 상속하여 JWKSelector.select(JWKSet객체)를 return하도록 구현.
      ▶ Abstract Method get()은 JWKSet으로부터 JWKSelector를 통해 특정조건에 부합하는 JWK를 선택하여 List<JWK>로 반환.
    	// RSAKey를 return하는 @Bean Method로부터 매개변수로 AutoWiring.
    	@Bean
        public JWKSource generateJWKSource(RSAKey rsaKey){
            // RSAKey instance를 활용하여 JWKSet instance 생성
            JWKSet jwkSet = new JWKSet(rsaKey);
            // JWKSource Interface의 instance 생성
            JWKSource jwkSource = new JWKSource(){
                // 생성자에서 JWKSource의 Abstract Method를 상속 및 구현
                @Override // 원본 Interface에서 상속의 명시적 표현
                public List<JWK> get(JWKSelector jwkSelector, SecurityContext securityContext) throws KeySourceException {
                    // JWKSet instance로부터 JWKSelector를 통해 특정조건에 부합하는 JWK를 List<JWK>로 반환.
                    return jwkSelector.select(jwkSet);
                }
            };
            return jwkSource;
        }
      • 람다식을 활용한 간단한 표현
      // RSAKey를 return하는 @Bean Method로부터 매개변수로 AutoWiring.
          @Bean
          public JWKSource<SecurityContext> generateJWKSource(RSAKey rsaKey){
              // RSAKey instance를 활용하여 JWKSet instance 생성
              JWKSet jwkSet = new JWKSet(rsaKey);
              // JWKSet의 특정조건에 부합하는 List<JWK>를 반환하는 get() Abstract Method가
              // 상속되어 구현된 JWKSource Interface의 instance 생성.
              return ((jwkSelector, securityContext) -> jwkSelector.select(jwkSet));
          }
      • Interface는 abstract method만을 포함하며 Interface를 상속 시 Interface 내부의 메소드를 모두 구현해야한다.


    • JWT의 Decryption을 수행하는 JwtDecoder의 instance를 반환하는 @Bean Method 정의
      。JwtDecoder instance의 경우 구현 Class인 NumbusJwtDecoder의 NumbusJwtDecoder.withPublicKey(RSAPublicKey객체) Method를 통해 생성하여 반환.

      。NimbusJwtDecoder.withPublicKey(RSAPublicKey객체) :
      RSA 암호화 알고리즘을 사용하는 JWT의 Decrypt 및 Signature의 유효성 검증을 수행하는 NimbusJwtDecoder instance를 특정 RSAPublicKey instance를 활용하여 생성하는 Method.
    // RSAKey instance를 return하는 @Bean Method로부터 매개변수로 AutoWiring. 
        @Bean
        public JwtDecoder jwtDecoder(RSAKey rsaKey) throws JOSEException {
            return NimbusJwtDecoder.withPublicKey(rsaKey.toRSAPublicKey()).build();
        }

    ▶ NimbusJwtDecoder.build()를 통해 NimbusJwtDecoder instance를 JwtDecoder의 instance로서 생성하여 반환.

OpenSSL :
。암호화 및 보안통신을 위한 Opensource Library
▶ SSL/TLS protocol을 지원하고 다양한 암호화 알고리즘( 대칭키 : AES, DES / 비대칭키 : RSA , ECC )를 제공.

  • JWT 생성을 위한 JWT Resource 구현
    。특정 사용자가 JWT를 활용해 Application에서 OAuth2.0으로 보호된 REST API를 호출하기 위해서는 사용자ID & PW를 포함한 HTTP Basic Authentication을 HTTP Request에 포함하여 @RestController로 선언된 Class에 Controller Method에 Mapping된 URL에 전달하여 Spring Security에 의해 유효한 Signature를 포함한 JWT를 생성 후 HTTP Response로 반환하여 차후 자원을 요청할 HTTP Request의 Authentication Header에 "Bearer JWT토큰코드"로서 포함하여 전송.
    ▶ Client는 해당 Controller Method의 API를 호출하여 HTTP Basic Authentication를 포함한 HTTP Request를 전송하고 Controller Method는 HTTP Basic Authentication의 정보를 기반으로 JWT Token을 생성하여 반환
    • HTTP Basic Authentication을 통한 HTTP Request가 전달하는 Authentication 정보 확인하기
      。 Controller Method의 매개변수에 Authentication interface의 instance를 정의하여 Client에서 전송된 HTTP Request의 HTTP Basic Authentication 정보를 확인.
      ▶ 사전에 AuthenticationManager객체.authenticate(Authentication객체)에 의해 사용자인증정보의 검증이 완료되어 principal, authorities, credentials 정보를 포함한 Authentication instance가 매개변수로 전달됨.
    import org.springframework.security.core.Authentication;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RestController;
    @RestController
    public class JwtAuthenticationResource {
        @PostMapping("/authenticate")
        public Authentication authenticate(Authentication authentication){
            return authentication;
        }
    }

    • Authentication interface의 instance를 통해 HTTP Basic Authentication를 포함하여 HTTP Request로서 전송한 Client의 권한( "authority" ) , 인증여부 ( "authenticated" ) , Client의 ID ( "username" )를 알 수 있다.
      • Authentication interface :
        org.springframework.security.core.Authentication
        。Spring Security에서 Authentication 정보를 지시하는 Interface
        ▶ 사용자의 인증상태 , 권한 , 인증된 사용자정보 등을 포함.

        。HTTP Request의 Authentication 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 Request의 HTTP 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.


    • JWT를 생성하여 반환하는 Controller Class 구현
      。Java Record 기능을 활용하여 JWT Token을 포함하는 Template 역할의 Record( = JwtResponse )의 token field에 생성자에 JWT의 문자열을 전달하여 Record instance를 생성하여 반환하는 Controller Method 구현.

      。 JwtEncoder Interface의 field를 생성 후 Constructor-based Dependency Injection을 구현하여 JwtEncoder instance를 생성하여 return하는 @Bean Method로 부터 Dependency Injection을 수행.

      。매개변수의 Authentication instance를 받아서 JwtClaimsSet객체를 생성 후 JwtEncoder instance를 활용해 Jwt객체를 생성하여 서명된 JWT의 문자열을 반환하는 Method를 정의.
      ▶ JwtEncoderParameters.from(JwtClaimsSet객체)로 전달하여 JwtEncoderParameters instance 생성 및 JwtEncoder객체.encode(JwtEncoderParameters객체)로 Jwt instance 생성 및 getter Method ( Jwt객체.getTokenValue() )를 통해 서명된 Jwt 문자열 획득.

      。권한목적의 커스텀 Claim의 값 설정 시 HTTP Basic Authentication을 통한 Authentication instance에 포함된 authorities 배열을 가져와서 Stream객체로 변환 후 Stream객체.map(람다식)을 통해 순회하면서 authority를 추출하여 Stream객체.collect(Collectors.joining(" "))를 통해 구분자를 " "로 설정하여 하나로 합친 문자열을 반환하여 커스텀 Claim 값으로 설정.
      ▶ 해당 커스텀 Claim 생성 시 JWT내 Payload에서 "scope" : "ROLE_ADMIN ROLE_DEVELOPER"로 생성.
    import org.springframework.security.core.Authentication;
    import org.springframework.security.oauth2.jwt.JwtClaimsSet;
    import org.springframework.security.oauth2.jwt.JwtEncoder;
    import org.springframework.security.oauth2.jwt.JwtEncoderParameters;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RestController;
    import java.time.Instant;
    import java.util.stream.Collectors;
    @RestController
    public class JwtAuthenticationResource {
        // JwtEncoder instance를 생성하여 return하는 @Bean Method로 부터 Dependency Injection.
        private JwtEncoder jwtEncoder;
        public JwtAuthenticationResource(JwtEncoder jwtEncoder) {
            this.jwtEncoder = jwtEncoder;
        }
        // API 호출 시 HTTP Request의 HTTP Basic Authentication 정보를 활용해
        // JWT Token을 생성 후 Record instance에 포함하여 HTTP Response로 반환.
        @PostMapping("/authenticate")
        public JwtResponse authenticate(Authentication authentication) {
            return new JwtResponse(createToken(authentication));
        }
        // JWT Token을 생성하는 Method
        private String createToken(Authentication authentication){
            var claims = JwtClaimsSet.builder()
                    // 발급자 이름 정의
                    .issuer("self")
                    // 발급시간 현재시점으로 설정
                    .issuedAt(Instant.now())
                    // 만료시간 30분 설정
                    .expiresAt(Instant.now().plusSeconds(60*30))
                    // 사용자 ID
                    .subject(authentication.getName())
                    // HTTP Basic Authentication에 포함된 authority 정보를 하나의 문자열로 합쳐서 반환 시
                    // 커스텀 Claim의 값으로 설정하여 권한 목적의 Claim 생성
                    .claim("scope",createScope(authentication))
                    .build();
            // Dependency Injection된 JwtEncoder instance를 활용해
            // JwtClaimSet instance를 포함하여 JWT를 생성하여 반환.
            return jwtEncoder.encode(JwtEncoderParameters.from(claims)).getTokenValue();
        }
        // HTTP Basic Authentication에 포함된 authority 정보를 하나의 문자열로 합쳐서 반환하는 Method.
        private String createScope(Authentication authentication) {
            return authentication.getAuthorities().stream()
                    .map(a->a.getAuthority())
                    // Stream 요소를 " "의 구분자로 하나의 문자열로 합쳐서 반환.
                    .collect(Collectors.joining(" "));
        }
    }
    // JWT Token을 포함하는 Template 역할의 Record
    record JwtResponse(String token){};


    。 해당 Controller Method의 API로 HTTP Basic Authentication을 포함한 HTTP Request를 전달하여 호출 시 HTTP Basic Authentication를 기반으로 생성된 JWT를 Record ( = JwtResponse )의 token field에 포함하여 HTTP Response로 반환.

    。 생성된 JWT는 다음과 같은 유효한 signature를 가진 header.payload.signature를 포함한다.
    ▶ JWT는 발급시점 ( iat )로부터 1742627427-1742625627 = 1800초만큼 유효.

    。생성된 JWT를 Bearer JWT토큰 양식으로 HTTP Request의 Authetication Header에 포함하여 전송 시 성공적으로 Application의 Resource를 반환함을 확인 가능.
    ▶ HTTP Request의 JWT는 위에서 정의된 JwtDecoder에 의해 Decrypt 및 유효성검증이 수행된 후 Resource의 접근권한여부를 검증.

  • JoseHeader
    。JWT Header를 설정하는 역할의 Class
    • JWT header의 header 요소 종류
      。alg : ( algorithm ) : 서명 알고리즘( Signature Algorithm )
      ▶ Signature Algorithm은 주로 Hashing Algorithm인 HS512 , HS256 등을 사용.

      。typ : ( Type ) : Token Type
      。kid : ( Key ID ) : key의 식별자 ( 제외 가능. )

    • JoseHeader Method
      JoseHeader.withAlgorithm(SignatureAlgorithm.RS256)
            .type("JWT") // JWT 타입 지정
            .keyId("my-key-id") // 키 ID 설정 (옵션)
            .build();

    。JoseHeader.withAlgorithm(Signature알고리즘 객체) : 특정 Signature Algorithm (alg) instance를 지정한 JoseHeader instance 생성.

    。JoseHeader객체.type("토큰형식") : JoseHeader instance에 특정 Token Type (tkp)을 지정.

    。JoseHeader객체.keyId("키ID") : JoseHeader instance에 특정 Key ID (kid)를 지정.
    ▶ 정의안해도된다.

    。JoseHeader객체.build() : Configuration이 정의된 JoseHeader instance 생성.

OAuth를 활용하여 Spring Application에서 Google Drive의 파일에 Access하여 전달받는 기능 구현
。OAuth Protocol 활용 시 타사의 Application에 사용자 계정정보를 직접 제공하지않아도 타사의 Application의 Resource에 안전하게 접근이 가능.
▶ Google Drive에 계정정보를 직접 노출하지 않고도 안전하게 연동하여 Resource를 가져올 수 있다.

。OAuth를 통해 Google에서 제공하는 로그인 서비스를 추가.
。해당 Spring Application은 Client의 Google Drive에 저장된 파일에 Access를 수행하는 Client Application.

  • OAuth( Open Authorization ) :
    。사용자의 ID와 PW를 직접 제공하지 않아도, 타 Application의 Resource에 안전하게 접근할 수 있도록 하는 프로토콜.
    ▶ 사용자가 자신의 계정정보를 노출하지 않고도 다른 서비스와 안전하게 연동이 가능.

    OAuth 주요 개념

    • Resource Owner :
      。API를 통해 보호된 Resource를 소유한 주체.
      ex) Google Drive의 파일을 소유하고 있는 Client

    • Client Application :
      。Resource Owner 대신 API를 호출하는 Application
      ex) Google Drive 파일에 접근하고자 하는 Spring Application
      ex) Google 로그인 서비스를 이용하는 웹사이트

    • Resource Server :
      。API를 제공하고, 인증된 Access Token을 포함한 HTTP Request만 허용하는 Server
      ex) 접근하려는 Google Drive 파일을 보관하는 Server( = Google Drive )

    • Authorization Server :
      。Client의 인증을 담당 및 인증된 Client에 대해 Access Token을 발급하는 Server
      ex) Google OAuth Server


  • OAuth 관련 의존성 추가
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' 

。OAuth2 Client , Spring Web 추가.

  • Application의 Test를 위한 Google 개발자 자격증명 생성
    Google Firebase 활용사례
    • Google API Console에 접속하여 Google Cloud Project 생성

      。프로젝트 구성에서 앱정보와 대상 ( 외부 ) 등을 설정 후 생성완료.

      。이후 데이터 액세스에서 범위 추가 또는 삭제 ( ADD OR REMOVE SCOPE )로 접속하여 이메일정보와 프로필정보에 대한 액세스를 허용하도록 SCOPE를 설정.


    • OAuth 클라이언트 ID 생성

      。클라이언트 - 클라이언트만들기 순으로 접속.

      。어플리케이션 유형을 웹 애플리케이션으로 지정 후 승인된 리디렉션 URI에서 로컬서버의 URI( http://localhost:8080/login/oauth2/code/google )를 입력.
      ▶ Application을 Local환경에서 실행 시 지정해야한다.

      。다음처럼 OAuth Client ID가 성공적으로 생성되어 ID와 Secret이 제공됨.


  • Spring Application에 생성한 OAuth Client 계정 등록하기
    。application.properties에서 다음 구문을 기입하여 위 과정에서 생성한 OAuth Client를 등록.
spring.security.oauth2.client.registration.google.client-id=OAuth클라이언트ID
spring.security.oauth2.client.registration.google.client-secret=OAuth클라이언트Secret
  • Spring Application에서 로그인이 수행 될 경우 접근될 기본 Resource 생성
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class BasicResource {
    @GetMapping("/")
    public Authentication home(Authentication authentication) {
        return authentication;
    }
}

▶localhost:8080/ 접속 시 HTTP Request의 Authentication Header의 Authentication instance가 AuthenticationManager을 통해 인증을 거친 후 principal, authorities, credentials 정보를 return.
Spring Security 인증원리

  • Spring Application에서 Google을 이용한 로그인 기능 구현하기
    。@Configuration Class에서 HttpSecurity instance의 Configuration을 수행 후 SecurityFilterChain instance를 return하는 @Bean Method를 생성.
    ▶ 해당 HttpSecurity객체.oauth2Login(Customizer.withDefaults())를 설정하여 Spring Security에서 Oauth 2.0 로그인 기능을 활성화.
    ▶ 다음 설정을 끝낸 후 localhost:8080/login 접속 시 해당 API를 보호하기위해 등록된 OAuth2 Provider ( ex. Google )에 의해 로그인옵션이 자동으로 표시됨.
@Configuration
public class JwtSecurityConfiguration {
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http.oauth2Login(Customizer.withDefaults()).build();
    }   
}





。 Google을 이용한 로그인을 통해 OAuth의 OAuth2 Provider에 의해 Spring Application의 보호된 API에 접근 가능.
▶ 다음은 localhost:8080/에 mapping된 Controller Method에 의해 HTTP Response로 return된 Authentication instance

  • 로그아웃 수행
    。localhost:8080/logout 입력 시 로그아웃을 수행하게 된다.
profile
공부기록 블로그

0개의 댓글