package com.codesign.base.jwt;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.codesign.base.auth.PrincipalDetails;
import com.codesign.base.model.User;
import com.codesign.base.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.util.ObjectUtils;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
// 시큐리티가 fliter를 가지고 있는데 그 필터중에
// BasicAuthenticationFilter 라는 것이있음
// 해당 필터에서 무조건 토큰이 있는지 없는지를 검사한다.
@Slf4j
public class JwtAuthorizationFilter extends BasicAuthenticationFilter {
private UserService userService;
public JwtAuthorizationFilter(AuthenticationManager authenticationManager, UserService userService) {
super(authenticationManager);
this.userService = userService;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
log.info("토큰 여부 검사 필터");
String authorization = request.getHeader(JwtProperties.HEADER_STRING);
log.info("authorization : {}", authorization);
if (ObjectUtils.isEmpty(authorization) || !authorization.startsWith("Bearer")) {
chain.doFilter(request, response);
return;
}
// jwt 토큰 검증을 해서 정상적인 사용자인지 확인
String jwtToken = authorization.replace(JwtProperties.TOKEN_PREFIX, "");
String username = JWT.require(Algorithm.HMAC512(JwtProperties.SECRET)).build().verify(jwtToken).getClaim("username").asString();
log.info("username : {}", username);
// 정상적으로 서명됨
if (username != null) {
User byUsername = userService.findByUsername(username);
// JWT 토큰 서명을 통해서 서명이 정상이면 Authentication 객첵를 만들어준다.
PrincipalDetails principalDetails = new PrincipalDetails(byUsername);
Authentication authentication = new UsernamePasswordAuthenticationToken(principalDetails, null, principalDetails.getAuthorities());
// 강제로 시큐리티의 세션에 접근하여 Authentication 객체를 저장
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(request, response);
}
}
}
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final UserService userService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable(); // 위변조 방지 미사용
http.cors().configurationSource(corsFilter()) // cors설정
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // session 미사용
.and().formLogin().disable() // formlogin 미사용
.httpBasic().disable() // Baerer 사용으로 Basic방식 미사용
.addFilter(new JwtAuthenticationFilter(authenticationManager()))
.addFilter(new JwtAuthorizationFilter(authenticationManager(), userService))
.authorizeRequests()
.antMatchers("/api/v1/user/**").permitAll()
.antMatchers("/api/v1/manager/**").access("hasRole('ROLE_MANAGER')")
.anyRequest().permitAll();
}
...
}
.addFilter(new JwtAuthorizationFilter(authenticationManager(), userService))
추가
authenticationManager()
는 WebSecurityConfigurerAdapter
에 정의 되어있음userService
는 해당 username의 유효성 확인을 위해참고
https://www.youtube.com/watch?v=GEv_hw0VOxE&list=PL93mKxaRDidERCyMaobSLkvSPzYtIk0Ah