UserAuthFilter
내용 참고 및 출처 : https://kimchanjung.github.io/programming/2020/07/01/spring-security-01/
(DO FILTER() 수행 시 시큐리티에서 자동으로 등록된 필터도 함께 수행)
UserDetailsService의 구현체에는 일반적으로 회원정보가 DB에 있다고 한다면 사용자의 이름(ID)로 DB를 조회하여 비밀번호가 일치하는지 확인하여 인증을 처리, 인증 마무리 시 토큰에 회원정보 담아 리턴
이떄 service는 유저정보 담아오는 UserDetauls 데리고 오는 것, 이것은 유저정보를 담아낼 UserDetails인터페이스를 구현해야 한다는 것
https://granger.tistory.com/23
=> (아이디와 비밀번호를 사용하는 form 기반 인증) 설정된 로그인 URL로 오는 요청을 감시하며, 유저 인증 처리
AuthenticationManager를 통한 인증 실행
인증 성공 시, 얻은 Authentication 객체를 SecurityContext에 저장 후 AuthenticationSuccessHandler 실행
인증 실패 시, AuthenticationFailureHandler 실행
When the username and password are submitted
: 로그인 시1) 유저가 자신의 username and password 을 제출한다면, AbstractAuthenticationProcessingFilter
가 HttpServletRequest
으로부터 Authentication
을 만들게 된다.
2) UsernamePasswordAuthenticationToken 는 authenticated 되기 위해서 AuthenticationManager
에게 전달된다.
3-1) 만약 authentication 실패 시 SecurityContextHolder is cleared out.
3-2) 만약 성공 시,
@RequiredArgsConstructor
@Slf4j
public class JwtAuthenticationFilter extends GenericFilterBean {
/**
* 1) Authorization 헤더에서 토큰 값을 꺼냄
*
* 2) 핵심 기능 : 액세스 토큰이 유효할 때만, SpringSecurity 관리해주는 컨텍스트에 사용자 정보 저장
*
* 사용자 정보(CustomAuthenticationToken) 등록
* == SecurityContextHolder에 있는 ContextHolder에
* Authentication 인터페이스의 구현체 CustomAuthenticationToken 등록
*
*/
private final TokenService tokenService;
private final CustomUserDetailsService userDetailsService;
private final Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
/*
CORS 처리를 위한 Filter는
반드시 인증 처리하는
Filter 이전에 있어야 한다.
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response1 = (HttpServletResponse) response;
HttpServletRequest request1 = (HttpServletRequest) request;
response1.setHeader("Access-Control-Allow-Origin", "https://localhost:3000");
response1.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response1.setHeader("Access-Control-Max-Age", "3600");
response1.setHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With, remember-me, Origin,Content-Type,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization");
response1.setHeader("Access-Control-Allow-Credentials", "true");
String token = extractToken(request);
if(validateToken(token)) {
// SecurityContext에 Authentication 객체 저장
setAuthentication(token);
}
chain.doFilter(request, response1);
}
private String extractToken(ServletRequest request) {
return ((HttpServletRequest)request).getHeader("Authorization");
}
private boolean validateToken(String token) {
return(token != null && tokenService.validateAccessToken(token));
}
private void setAuthentication(String token) {
String userId = tokenService.extractAccessTokenSubject(token);
if(userId == null){
throw new AccessExpiredException();
}
- 일반적인 과정은 그냥 로그인하면
- 유저의 이름으로 검사하는 것일텐데,
- 우리는 토큰에서 UserDetails 가져올 id 값을 빼오는 것
- 만약 토큰이 잘못됐다면 이걸 수행하지 못하는 것
- 토큰에서 빼 온 유저의 아이디 값으로 UserDetails 빼오고, - Authentication 토큰 만드는 것
// 따라서 나는 authentication 토큰을 만드는
CustomUserDetails userDetails = userDetailsService.loadUserByUsername(userId);
SecurityContextHolder.getContext().setAuthentication(
new CustomAuthenticationToken(
userDetails, userDetails.getAuthorities()
)
);
}
}
(+)
출처 :
1) https://docs.spring.io/spring-security/site/docs/5.4.2/reference/html5/#servlet-filters-review ,
2) https://sungminhong.github.io/spring/security/,
3) https://kimchanjung.github.io/programming/2020/07/01/spring-security-01/
4) https://tech.junhabaek.net/spring-security-usernamepasswordauthenticationfilter%EC%9D%98-%EB%8D%94-%EA%B9%8A%EC%9D%80-%EC%9D%B4%ED%95%B4-8b5927dbc037