11. Security Configuration
다음으로 Security Configuration을 생성하겠습니다.
SecurityConfig
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(
securedEnabled = true,
jsr250Enabled = true,
prePostEnabled = true
)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomUserDetailsService customUserDetailsService;
@Autowired
private CustomOAuth2UserService customOAuth2UserService;
@Autowired
private OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler;
@Autowired
private OAuth2AuthenticationFailureHandler oAuth2AuthenticationFailureHandler;
@Autowired
private HttpCookieOAuth2AuthorizationRequestRepository httpCookieOAuth2AuthorizationRequestRepository;
@Bean
public TokenAuthenticationFilter tokenAuthenticationFilter() {
return new TokenAuthenticationFilter();
}
/*
By default, Spring OAuth2 uses HttpSessionOAuth2AuthorizationRequestRepository to save
the authorization request. But, since our service is stateless, we can't save it in
the session. We'll save the request in a Base64 encoded cookie instead.
*/
@Bean
public HttpCookieOAuth2AuthorizationRequestRepository cookieAuthorizationRequestRepository() {
return new HttpCookieOAuth2AuthorizationRequestRepository();
}
@Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder
.userDetailsService(customUserDetailsService)
.passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean(BeanIds.AUTHENTICATION_MANAGER)
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf()
.disable()
.formLogin()
.disable()
.httpBasic()
.disable()
.exceptionHandling()
.authenticationEntryPoint(new RestAuthenticationEntryPoint())
.and()
.authorizeRequests()
.antMatchers("/",
"/error",
"/favicon.ico",
"/**/*.png",
"/**/*.gif",
"/**/*.svg",
"/**/*.jpg",
"/**/*.html",
"/**/*.css",
"/**/*.js")
.permitAll()
.antMatchers("/auth/**", "/oauth2/**")
.permitAll()
.anyRequest()
.authenticated()
.and()
.oauth2Login()
.authorizationEndpoint()
.baseUri("/oauth2/authorize")
.authorizationRequestRepository(cookieAuthorizationRequestRepository())
.and()
.userInfoEndpoint()
.userService(customOAuth2UserService)
.and()
.successHandler(oAuth2AuthenticationSuccessHandler)
.failureHandler(oAuth2AuthenticationFailureHandler);
// Add our custom Token based authentication filter
http.addFilterBefore(tokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
• WebSecurityConfigureAdapter를 extends하고 @EnableWebSecurity 어노테이션을 선언해 Security Configuration을 정의합니다.
• CustomUserDetailService : 인증시 사용할 custom User Service 입니다. 자세한 내용은 이 글을 참고합니다.
• TokenAuthenticationFilter : 로그인시 JWT Token을 확인해 인가된 사용자 유무를 판별하고 내부 process를 수행합니다. 자세한 내용은 이 글을 참고합니다.
• HttpCookieOAuth2AuthorizationRequestRepository : Spring OAuth2는 기본적으로 HttpSessionOAuth2AuthorizationRequestRepository를 사용해 Authorization Request를 저장합니다.
우리는 JWT를 사용하므로, Session에 이를 저장할 필요가 없습니다. 따라서 custom으로 구현한 HttpCookieOAuth2AuthorizationRequestRepository를 사용해 Authorization Request를 Based64 encoded cookie에 저장합니다. 자세한 내용은 이 글을 참고합니다.
• configure (AuthenticationManagerBuilder) : Authorization에서 사용할 userDetailService와 password Encoder를 정의합니다.
• passwordEncoder : password를 저장할때 사용할 encoding algorithm을 정의합니다. 가장 많이사용되는 BCrypt 방식을 사용하겠습니다.
11-1) configure (HttpSecurity)
이 부분을 해석해보면 다음과 같습니다.
• cors( ) : cors을 허용합니다.
• sessionManagement( ) : session Creation Policy를 STATELESS로 정의해 session을 사용하지 않겠다 선언합니다.
• csrf( ) : 사용하지 않습니다.
• formLogin ( ) : 사용하지 않습니다.
• httpBasic ( ) : 사용하지 않습니다.
• authenticationEntryPoint(RestAuthenticationEntryPoint) : 사용자가 authentication 없이 protected resource에 접근하는 경우에 invoked 되는 entry point를 정의합니다.
• authorizeRequests ( ) : permitAll( )을 선언한 경로의 resource는 모든 사용자에게 접근을 허용합니다. 이외의 anyRequest( )는 authenticated( ) 즉 인증된 사용자에게만 접근을 허용합니다.
• authorizationEndpoint( ) : oauth 로그인시 접근할 end point를 정의합니다. baseUri에 "/ouath2/authorize"라고 선언했으므로, react Client App에서는 다음과 같이 login을 수행합니다.
# server base uri
API_BASE_URL = 'http://localhost:8080';
# oauth2 redirect uri
OAUTH2_REDIRECT_URI = 'http://localhost:3000/oauth2/redirect'
# google login uri
GOOGLE_AUTH_URL = API_BASE_URL + '/oauth2/authorize/google?redirect_uri=' + OAUTH2_REDIRECT_URI;
• userInfoEndpoint( ) : 로그인시 사용할 User Service를 정의합니다.
• successHandler ( ) : 로그인 성공시 invoke 할 Handler를 정의합니다.
• failureHandler ( ) : 로그인 실패시 invoke 할 Handler를 정의합니다.
• addFilterBefore(TokenAuthenticationFilter) : reqeust 요청이 올때마다 UsernamePasswordAuthenticationFilter 이전에 tokenAuthenticaitonFilter를 수행하도록 정의합니다.
12. OAuth2 Login Flow
다시한번.. OAuth2 login flow를 살펴보겠습니다. 😅
• OAuth2 login flow는 client에서 "http://localhost:8080/oauth2/authorize/{provider}?redirect_uri="로 user를 send 하면서 시작됩니다.
• 이 때 위의 provider는 google/naver와 같은 oauth provider가 됩니다.
• 는 user가 oauth provider의 login에 성공하면 redirect 될 uri 입니다. 예를 들어 "http://localhost:3000/oauth2/redirect"와 같이 작성합니다.
• authorization request를 receive하게 되면, Spring Security's OAuth2 client (google/naver)는 그들의 AuthorizaitonUri로 사용자를 redirect 합니다. 즉, 아래와 같은 google login 창으로 이동하게 됩니다.
• 모든 Authorization 관련 state(allow/deny)는 authorizationRequestRepository를 사용해 저장됩니다.
• 위의 login창으로 이동하게된 경우, 이제 user는 해당 도메인에 대한 permission을 allow/denies 할 수 있습니다.
• 예를 들어 위와 같이 velog에 로그인시 이름/이메일주소/사진에 대한 permission을 허용할 것인지를 물어보게 됩니다.
• user가 허용할 경우 authorization code와 함께 "http://localhost:8080/login/oauth2/code/google" 로 redirect 됩니다.
• 만약 user가 허용하지 않을 경우 동일하게 "http://localhost:8080/login/oauth2/code/google" 로 redirect 되지만, error가 발생합니다.
• 만약 OAuth2 callback이 error를 발생시킨경우, Spring Security는 oAuth2AuthenticationFailureHandler를 invoke합니다.
• 만약 OAuth2 callback이 success한 경우, Spring Security는 자동으로 획득한 authorization_code를 사용해 access_token을 발급받는 절차를 수행하게 됩니다. 또한, customOAuth2UserService 를 invoke 하게 됩니다.
• customOAuth2UserService는 DB에서 login한 사용자에 대한 detail 정보를 retrieve 하며, 신규 사용자를 등록하거나 기존의 사용자 정보를 update 하는 절차를 수행합니다.
• 최종적으로 oAuth2AuthenticationSuccessHandler가 invoke 되며, 이때 JWT Token이 생성됩니다. 이 JWT Token은 client의 redirect_uri로 Query String을 통해 전달됩니다.
JWT Token 발급 흐름은 다음과 같습니다.
참고 자료 : https://www.callicoder.com/spring-boot-security-oauth2-social-login-part-2/
추천서적
파트너스 활동을 통해 일정액의 수수료를 제공받을 수 있음
'Spring > Security' 카테고리의 다른 글
[Spring Security] Google Login with Spring Security & JWT (3) (2) | 2020.09.08 |
---|---|
[Spring Security] Google Login with Spring Security & JWT (1) (0) | 2020.09.08 |
[Spring Security] JWT Security - Spring Boot (10) (0) | 2020.09.08 |
[Spring Security] Thymeleaf Integration - Spring Boot (9) (0) | 2020.09.08 |
[Spring Security] Customize Form Control Names - Spring Boot (8) (0) | 2020.09.08 |