Spring Security - OAuth2 && JWT (JSON Web Token) - 2
지난 글에서 https://infondgndg91.blogspot.com/2020/06/spring-security-oauth2-jwt-json-web.html
간단하게 OAuth 2.0 과 JWT (Json Web Token) 에 대해 정리를 해보았다.
아래와 같이 Security 설정에서 JWTAuthenticationTokenFilter 를 추가해주고, 또한 AuthenticationEntryPoint 를 구현하여 등록해주면 된다.
간단하게 OAuth 2.0 과 JWT (Json Web Token) 에 대해 정리를 해보았다.
이번 시간에는 OAuth 2.0 4가지 type 중 간단한, Resource Owner Password Credentials Grant 를 구현하려고 한다. 먼저 인증 서버에 대한 구현이다.
간단하게 개발 스펙을 정리해보자면,
Spring Boot 2.3.1.RELEASE
Java 14
guava 와 commons 는 애용해서 넣었다. 그리고 auth0 의 JWT, DB 는 mysql 을 사용했다.
당연히 security 와 web 을 추가했다.
그밖에, 롬복, 데브툴, 컨피규레이션 프로세서는 개발 시 많은 유용함이 있어서 당연히 사용했다.
다음은 application.yml 이다. 우선 jwt 와 관련된 내용들이며, 다음은 DataSource 내용들이다. DB 는 AWS RDS 를 사용했고, write 와 read 클러스터 구성이다. 따라서 write 와 read 를 각각 설정해주었다. 로깅은 개발 시 로깅을 디테일하게 켜서 보려고 해놓은거라서 중요한 것은 아니다.
그렇다면, 먼저 DataSource 설정이다. JdbcTemplate 을 사용려고 각각의 DataSource 마다 JdbcTemplate 을 Bean 으로 이용했다.
그 다음, 시큐리티 설정을 확인하자. AuthenticationProvider 를 구현한 커스텀 JWTAuthenticationProvider 을 사용했다. OAuth 2.0 Token 기반 인증 방식을 사용하기 때문에, 세션은 당연히 StateLess 무상태이다. csrf, header, formlogin 모두 disabled 했다. AuthenticationEntryPoint 를 구현하는 핸들러와 AccessDeniedHandler 를 구현하는 핸들러를 통해서 401, 403 을 response 을 핸들링했다. /sign-in , /sign-up 을 제외한 모든 요청은 인증을 필요로한다. /member/test 의 경우만 자체적으로 테스트를 위해서 열어놓았다.
다음은 JWT 이다. newToken 메서드를 통해서 인증이 되었을 때 토큰을 발급해준다. JWTVerifier 을 통해서 만료된 토큰에 대해서 검사를 할 수 있다.
그렇다면, 이제 접근자가 자신을 인증하고 토큰을 발급받는 과정의 시작부터 따라가보자.
1. 먼저 접근자는 /sign-in 으로 username / password 를 담아 요청한다. 해당 정보가 담긴 객체가 AuthenticationRequest 이다. 해당 객체를 통해서 AbstractAuthenticationToken 을 상속 받은 JWTAuthenticationToken 을 생성한다. 그리고 AuthenticationManager 의 authenticate() 메서드를 통해서 해당 요청 인증처리를 시작한다. 즉, AuthenticationManager 을 구현한 ProviderManager 의 authenticate() 메서드를 실행하게 되고, 등록된 provider 를 가져와, authenticate() 메서드를 다시 호출한다. 해당 예제에서는 JWTAuthenticationProvider 를 구현했으므로, 해당 컴포넌트의 authenticate() 메서드가 호출되고 인증 로직을 타게 된다.
JWTAuthenticationProvider 에서 MemberService 를 통해서, 접근자의 username / password 를 통해서 정보를 가져오며, 유무에 따라 핸들링한다. 없는 경우 / 비밀 번호가 틀린 경우 / 있는 경우에 따라 각각 처리한다. 있는 경우 JWT 인 access_token 을 발급한다.
아래는 /sign-in 테스트이다. access_token 이 정상적으로 발급되었다.
이제 access_token 을 발급하는 서버를 완성했다. refresh_token 을 추가적으로 발급해도 된다.
인증 전략을 어떻게 취할지에 따라 refresh_token 발급 여부를 결정하면된다.
sliding session 기법을 사용하면 access_token 하나만 발급 해도 된다. 이 기법은 access_token 이 만료되지 않았다면, 자동으로 만료 시간을 갱신해 주는 기법이다.
refresh_token 과 sliding session 의 기법에는 각각 장단점이 있으므로, 상황에 따라 취사 선택을 잘 하면 된다.
다음은 GenericFilterBean 을 상속받는 커스텀 필터를 이용하여 Resource 서버를 구성하려고한다.
resource server 의 핵심은 마찬가지로 security 를 이용한 jwt custom filter 이다. 모든 요청은 해당 필터를 통해서 인증을 하고, 인증이 되지 않을 경우, 지정한 EntryPoint 로 응답을 하게끔 하면 된다.
따라서 아래와 같이 custom filter 를 만든다.
아래와 같이 Security 설정에서 JWTAuthenticationTokenFilter 를 추가해주고, 또한 AuthenticationEntryPoint 를 구현하여 등록해주면 된다.
그리고 Resource Server 가 주문에 관련된 서버라고 가정했을 때 아래와 같이 ,
@AuthenticationPrincipal 어노테이션을 통해서 인증을 받은 접근자에 대한 정보를 사용할 수 있게된다.
만약, Resource Server 가 다양해진다면, 해당 설정을 모듈로써 관리하거나 Api Gateway 를 앞에 둘 수 있을 것 같다. 이는 상황에 따라 더 고민해보고 적용할 수 있을 것 같다.
댓글
댓글 쓰기