- Spring Security 학습 내용
。로그인 양식 하드코딩
。Session , Cookie , Token 관련
。MVC , Spring MVC
。Spring Security - 1 : 로그인 양식 사용 설정 , 계정생성
。Spring Security - 2 : Filter Chain 관련 설정
。Spring Security - 3 : 간단한 활용예제
。Spring-Security - 4 : Token을 활용한 REST API Authentication- Spring Section 18 : Spring Security 기초 및 원리
- Spring Section 19 : Spring Security JWT 관련 설정
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>。
Gradleimplementation '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.0Dependency 추가
。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를 통해JwtDecoderinstance를 생성 후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 Pairinstance를 생성하는@Bean Method정의하기
。RSA암호화 알고리즘을 통해Public Key,Private Key로 구성된Key Pair객체 생성.
▶oepnssl또는java.security.KeyPairGenerator활용.
。@Configuration이 선언된Configuration Class에Key Pairinstance를 생성하여 반환하는@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를 포함한KeyPairinstance가 생성되어Spring Bean으로 return.
RSA암호화 알고리즘으로 생성된KeyPairinstance를 이용하여RSAKeyinstance 생성
。Nimbus JOSE+JWT라이브러리를 활용하여RSAKeyinstance 생성.
。RSAKeyinstance에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를RSAKeyinstance를 새로 생성 및casting후 적용한RSAKeyinstance 생성.
▶JWKSetinstance에 여러개의Public Key를 정의하고자keyID지정.
RSAKeyinstance를 이용하여JWKSource( JSON Web Key Source )을 생성하는@Bean Method정의
。RSAKeyinstance를 통해JWKSetinstance 생성 후JWKSourceinstance를 생성하는@Bean Method생성
▶RSAKey를 return하는@Bean Method로부터AutoWiring하여 공급.
。JWKSetinstance로JWKSourceinstance 생성 시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정의
。JwtDecoderinstance의 경우 구현 Class인NumbusJwtDecoder의NumbusJwtDecoder.withPublicKey(RSAPublicKey객체)Method를 통해 생성하여 반환.
。NimbusJwtDecoder.withPublicKey(RSAPublicKey객체):
RSA암호화 알고리즘을 사용하는JWT의Decrypt및Signature의 유효성 검증을 수행하는NimbusJwtDecoder instance를 특정RSAPublicKeyinstance를 활용하여 생성하는 Method.// RSAKey instance를 return하는 @Bean Method로부터 매개변수로 AutoWiring. @Bean public JwtDecoder jwtDecoder(RSAKey rsaKey) throws JOSEException { return NimbusJwtDecoder.withPublicKey(rsaKey.toRSAPublicKey()).build(); }▶
NimbusJwtDecoder.build()를 통해NimbusJwtDecoderinstance를JwtDecoder의 instance로서 생성하여 반환.
OpenSSL:
。암호화 및 보안통신을 위한 Opensource Library
▶SSL/TLSprotocol을 지원하고 다양한 암호화 알고리즘( 대칭키 :AES,DES/ 비대칭키 :RSA,ECC)를 제공.
JWT생성을 위한JWTResource 구현
。특정 사용자가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의 매개변수에Authenticationinterface의 instance를 정의하여 Client에서 전송된HTTP Request의HTTP Basic Authentication정보를 확인.
▶ 사전에AuthenticationManager객체.authenticate(Authentication객체)에 의해 사용자인증정보의 검증이 완료되어principal,authorities,credentials정보를 포함한Authenticationinstance가 매개변수로 전달됨.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; } }
Authenticationinterface의 instance를 통해HTTP Basic Authentication를 포함하여HTTP Request로서 전송한Client의 권한("authority") , 인증여부 ("authenticated") , Client의 ID ("username")를 알 수 있다.
Authenticationinterface :
org.springframework.security.core.Authentication
。Spring Security에서Authentication정보를 지시하는 Interface
▶ 사용자의 인증상태 , 권한 , 인증된 사용자정보 등을 포함.
。HTTP Request의Authentication Header에 포함된 사용자 인증정보를Controller Method의 매개변수로 사전에AuthenticationManager객체.authenticate(Authentication객체)에 의해 사용자인증정보가 검증되어principal,authorities,credentials정보를 포함한Authenticationinstance로서 mapping되어 전달됨.
SecurityContextHolder,SecurityContext,Authentication관계SecurityContextHolder └── SecurityContext (보안 컨텍스트) └── Authentication (인증 정보) ├── Principal (사용자 정보) ├── Credentials (비밀번호, JWT 등) └── Authorities (권한 정보)Spring
Authenticationinstance 구성
▶ Client에서 전송된HTTP Request의HTTP Basic Authentication인증정보가 포함된Authentication instance
principal:
。인증된 사용자의 세부정보를 의미.
▶ 보통UserDetailsinstance를 포함.
credentials
。사용자가 인증 시 입력한 정보 ( ex.username,password)
▶ 인증이 완료된 경우, 보안을 위해null로 설정됨.
authorities
。principal에 정의된 사용자가 보유한 역할, 권한 목록을 지시.
▶ 주로"ROLE_USER" , "ROLE_ADMIN"등의 권한정보를 포함.
。검증전의credentials정보만 포함된Authenticationinstance를AuthenticationManager객체.authenticate(Authentication객체)로 전달 시 사용자인증정보를 검증 후 인증성공 시principal,authorities정보를 추가로 포함한 완전한Authenticationinstance를 return.
JWT를 생성하여 반환하는Controller Class구현
。Java Record기능을 활용하여JWTToken을 포함하는 Template 역할의Record( =JwtResponse)의tokenfield에 생성자에JWT의 문자열을 전달하여Recordinstance를 생성하여 반환하는Controller Method구현.
。JwtEncoderInterface의 field를 생성 후Constructor-based Dependency Injection을 구현하여JwtEncoderinstance를 생성하여 return하는@Bean Method로 부터Dependency Injection을 수행.
。매개변수의Authenticationinstance를 받아서JwtClaimsSet객체를 생성 후JwtEncoderinstance를 활용해Jwt객체를 생성하여 서명된JWT의 문자열을 반환하는 Method를 정의.
▶JwtEncoderParameters.from(JwtClaimsSet객체)로 전달하여JwtEncoderParametersinstance 생성 및JwtEncoder객체.encode(JwtEncoderParameters객체)로Jwtinstance 생성 및getter Method(Jwt객체.getTokenValue())를 통해 서명된Jwt문자열 획득.
。권한목적의 커스텀Claim의 값 설정 시HTTP Basic Authentication을 통한Authenticationinstance에 포함된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)의tokenfield에 포함하여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의 식별자 ( 제외 가능. )
JoseHeaderMethodJoseHeader.withAlgorithm(SignatureAlgorithm.RS256) .type("JWT") // JWT 타입 지정 .keyId("my-key-id") // 키 ID 설정 (옵션) .build();。
JoseHeader.withAlgorithm(Signature알고리즘 객체): 특정Signature Algorithm(alg) instance를 지정한JoseHeaderinstance 생성.
。JoseHeader객체.type("토큰형식"):JoseHeaderinstance에 특정Token Type(tkp)을 지정.
。JoseHeader객체.keyId("키ID"):JoseHeaderinstance에 특정Key ID(kid)를 지정.
▶ 정의안해도된다.
。JoseHeader객체.build():Configuration이 정의된JoseHeaderinstance 생성.
OAuth를 활용하여Spring Application에서Google Drive의 파일에 Access하여 전달받는 기능 구현
。OAuthProtocol 활용 시 타사의 Application에 사용자 계정정보를 직접 제공하지않아도 타사의 Application의Resource에 안전하게 접근이 가능.
▶Google Drive에 계정정보를 직접 노출하지 않고도 안전하게 연동하여 Resource를 가져올 수 있다.
。OAuth를 통해
。해당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)
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 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의Authenticationinstance가AuthenticationManager을 통해 인증을 거친 후principal, authorities, credentials정보를 return.
Spring Security 인증원리
Spring Application에서
。@Configuration Class에서HttpSecurityinstance의 Configuration을 수행 후SecurityFilterChaininstance를 return하는@Bean Method를 생성.
▶ 해당HttpSecurity객체.oauth2Login(Customizer.withDefaults())를 설정하여Spring Security에서Oauth 2.0로그인 기능을 활성화.
▶ 다음 설정을 끝낸 후localhost:8080/login접속 시 해당 API를 보호하기위해 등록된OAuth2 Provider( ex.@Configuration public class JwtSecurityConfiguration { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { return http.oauth2Login(Customizer.withDefaults()).build(); } }
。OAuth의OAuth2 Provider에 의해Spring Application의 보호된API에 접근 가능.
▶ 다음은localhost:8080/에 mapping된Controller Method에 의해HTTP Response로 return된Authenticationinstance
- 로그아웃 수행
。localhost:8080/logout입력 시 로그아웃을 수행하게 된다.