728x90
1. SecurityConfig를 작성하자.
JwtAuthorizationFilter 라는 필터를 생성하여 스프링 시큐리티 내부에 여러 인증 필터 중 만만한 UsernamePasswordAuthenticationFilter 앞에 추가하였다.
@Bean
public SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception {
http
.antMatcher("/api/**")
.exceptionHandling(exceptionHandling -> exceptionHandling
.authenticationEntryPoint(authenticationEntryPoint)
)
.httpBasic().disable()
.csrf().disable()
.cors(cors -> cors
.configurationSource(corsConfigurationSource())
)
.authorizeRequests(
authorizeRequests -> authorizeRequests
.antMatchers("/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html").permitAll()
.antMatchers("/api/*/member/login").permitAll()
.anyRequest()
.authenticated() // 최소자격 : 로그인
)
.sessionManagement(sessionManagement -> sessionManagement
.sessionCreationPolicy(STATELESS)
)
.formLogin().disable()
.addFilterBefore(
jwtAuthorizationFilter,
UsernamePasswordAuthenticationFilter.class
)
.logout().disable();
return http.build();
}
2. 추후 REST API를 구현할 떄 CORS 문제를 미리 차단하기 위한 CorsConfigurationSource도 추가하였다.
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
urlBasedCorsConfigurationSource.registerCorsConfiguration("/api/**", corsConfiguration);
return urlBasedCorsConfigurationSource;
}
3. JwtAuthorizationFilter 함수를 어떻게 구현하였는지 확인해보자. OncePerRequestFilter 를 상속받고 doFilterInternal 함수를 오버라이드받아 구현하면 된다. 들어온 요청에 대해 token을 분리하고 이전에 작성한 Jwt 관련 함수들을 활용했다.
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
log.debug("JwtAuthorizationFilter 실행됨");
String bearerToken = request.getHeader("Authorization");
if (bearerToken != null) {
String token = bearerToken.substring("Bearer ".length());
// 1차 체크(정보가 변조되지 않았는지 체크)
if (jwtProvider.verify(token)) {
Map<String, Object> claims = jwtProvider.getClaims(token);
Member member = memberService.getByUsername__cached((String) claims.get("username"));
// 2차 체크(화이트리스트에 포함되는지)
if ( memberService.verifyWithWhiteList(member, token) ) {
forceAuthentication(member);
}
}
}
filterChain.doFilter(request, response);
}
filterChain.doFilter(request, response)를 추가해주지 않으면 다음 필터로 못넘어갈 수 있으니 유의하자.
4. 토큰이 유효한지 확인이 끝났으면 로그인 상태 처리를 위해 forceAuthentication 함수를 실행시켰다.
private void forceAuthentication(Member member) {
MemberContext memberContext = new MemberContext(member);
UsernamePasswordAuthenticationToken authentication =
UsernamePasswordAuthenticationToken.authenticated(
memberContext,
null,
member.getAuthorities()
);
//로그인 상태 처리
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(authentication);
SecurityContextHolder.setContext(context);
}
5. 다시 Security로 돌아와 authenticationEntryPoint는 인증 실패에 대해 커스텀하여 활용하였다.
@Component
@Slf4j
public class ApiAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
RsData rs = RsData.of("F-AccessDenied", "인증 실패", null);
response.setCharacterEncoding("UTF-8");
response.setContentType(APPLICATION_JSON_VALUE);
response.setStatus(403);
response.getWriter().append(Util.json.toStr(rs));
}
}
'IT' 카테고리의 다른 글
JWT와 Security를 활용한 인증, 인가 구현 3 ( REST API 구현) (0) | 2022.11.11 |
---|---|
Spring Boot로 REST API 구현을 위한 ResponseEntity<> 커스텀 (0) | 2022.11.11 |
JWT와 Security를 활용한 인증, 인가 구현 1 ( JWT 토큰 생성) (0) | 2022.11.11 |
퍼사드 패턴(Facade Pattern) (0) | 2022.11.10 |
접근 제어자 (0) | 2022.11.09 |