|
| 1 | +package com.WhoIsRoom.WhoIs_Server.domain.auth.filter; |
| 2 | + |
| 3 | +import com.WhoIsRoom.WhoIs_Server.domain.auth.exception.CustomAuthenticationException; |
| 4 | +import com.WhoIsRoom.WhoIs_Server.domain.auth.exception.CustomJwtException; |
| 5 | +import com.WhoIsRoom.WhoIs_Server.domain.auth.model.UserPrincipal; |
| 6 | +import com.WhoIsRoom.WhoIs_Server.domain.auth.service.JwtService; |
| 7 | +import com.WhoIsRoom.WhoIs_Server.domain.auth.util.JwtUtil; |
| 8 | +import com.WhoIsRoom.WhoIs_Server.global.common.response.ErrorCode; |
| 9 | +import io.jsonwebtoken.JwtException; |
| 10 | +import jakarta.servlet.FilterChain; |
| 11 | +import jakarta.servlet.ServletException; |
| 12 | +import jakarta.servlet.http.HttpServletRequest; |
| 13 | +import jakarta.servlet.http.HttpServletResponse; |
| 14 | +import lombok.RequiredArgsConstructor; |
| 15 | +import lombok.extern.slf4j.Slf4j; |
| 16 | +import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; |
| 17 | +import org.springframework.security.authentication.BadCredentialsException; |
| 18 | +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; |
| 19 | +import org.springframework.security.core.Authentication; |
| 20 | +import org.springframework.security.core.GrantedAuthority; |
| 21 | +import org.springframework.security.core.authority.SimpleGrantedAuthority; |
| 22 | +import org.springframework.security.core.context.SecurityContextHolder; |
| 23 | +import org.springframework.stereotype.Component; |
| 24 | +import org.springframework.util.AntPathMatcher; |
| 25 | +import org.springframework.web.filter.GenericFilterBean; |
| 26 | +import org.springframework.web.filter.OncePerRequestFilter; |
| 27 | + |
| 28 | +import javax.security.sasl.AuthenticationException; |
| 29 | +import java.io.IOException; |
| 30 | +import java.util.Arrays; |
| 31 | +import java.util.List; |
| 32 | +import java.util.Optional; |
| 33 | + |
| 34 | +@Slf4j |
| 35 | +@Component |
| 36 | +@RequiredArgsConstructor |
| 37 | +public class JwtAuthenticationFilter extends OncePerRequestFilter { |
| 38 | + private final JwtUtil jwtUtil; |
| 39 | + private final JwtService jwtService; |
| 40 | + |
| 41 | + // 인증을 안해도 되니 토큰이 필요없는 URL들 (에러: 로그인이 필요합니다) |
| 42 | + public final static List<String> PASS_URIS = Arrays.asList( |
| 43 | + "/api/users/signup", |
| 44 | + "/api/auth/**" |
| 45 | + ); |
| 46 | + |
| 47 | + private static final AntPathMatcher ANT = new AntPathMatcher(); |
| 48 | + |
| 49 | + @Override |
| 50 | + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { |
| 51 | + |
| 52 | + if(isPassUri(request.getRequestURI())) { |
| 53 | + log.info("JWT Filter Passed (pass uri) : {}", request.getRequestURI()); |
| 54 | + filterChain.doFilter(request, response); |
| 55 | + return; |
| 56 | + } |
| 57 | + |
| 58 | + // 엑세스 토큰이 없으면 Authentication도 없음 -> EntryPoint (401) |
| 59 | + log.info("Request URI: {}", request.getRequestURI()); // 요청 URI 로깅 |
| 60 | + String accessToken = jwtUtil.extractAccessToken(request) |
| 61 | + .orElseThrow(() -> new CustomAuthenticationException(ErrorCode.SECURITY_UNAUTHORIZED)); |
| 62 | + |
| 63 | + // 토큰 유효성 검사 |
| 64 | + jwtUtil.validateToken(accessToken); |
| 65 | + |
| 66 | + // 토큰 타입 검사 |
| 67 | + if(!"access".equals(jwtUtil.getTokenType(accessToken))) { |
| 68 | + throw new CustomJwtException(ErrorCode.INVALID_TOKEN_TYPE); |
| 69 | + } |
| 70 | + |
| 71 | + // 로그아웃 체크 |
| 72 | + jwtService.checkLogout(accessToken); |
| 73 | + |
| 74 | + // 권한 리스트 생성 |
| 75 | + List<GrantedAuthority> authorities = Arrays.asList(new SimpleGrantedAuthority(jwtUtil.getRole(accessToken))); |
| 76 | + log.info("Granted Authorities : {}", authorities); |
| 77 | + UserPrincipal principal = new UserPrincipal( |
| 78 | + jwtUtil.getUserId(accessToken), |
| 79 | + jwtUtil.getName(accessToken), |
| 80 | + null, // 패스워드는 필요 없음 |
| 81 | + jwtUtil.getProviderId(accessToken), |
| 82 | + authorities |
| 83 | + ); |
| 84 | + log.info("UserPrincipal.userId: {}", principal.getUserId()); |
| 85 | + log.info("UserPrincipal.nickName: {}", principal.getUsername()); |
| 86 | + log.info("UserPrincipal.providerId: {}", principal.getProviderId()); |
| 87 | + log.info("UserPrincipal.role: {}", principal.getAuthorities().stream().findFirst().get().toString()); |
| 88 | + |
| 89 | + Authentication authToken = null; |
| 90 | + if ("localhost".equals(principal.getProviderId())) { |
| 91 | + // 폼 로그인(자체 회원) |
| 92 | + authToken = new UsernamePasswordAuthenticationToken(principal, null, authorities); |
| 93 | + } |
| 94 | +// else { |
| 95 | +// // 소셜 로그인 |
| 96 | +// authToken = new OAuth2AuthenticationToken(principal, authorities, loginProvider); |
| 97 | +// } |
| 98 | + log.info("Authentication set in SecurityContext: {}", SecurityContextHolder.getContext().getAuthentication()); |
| 99 | + log.info("Authorities in SecurityContext: {}", authToken.getAuthorities()); |
| 100 | + |
| 101 | + log.info("JWT Filter Success : {}", request.getRequestURI()); |
| 102 | + SecurityContextHolder.getContext().setAuthentication(authToken); |
| 103 | + filterChain.doFilter(request, response); |
| 104 | + } |
| 105 | + |
| 106 | + private boolean isPassUri(String uri) { |
| 107 | + return PASS_URIS.stream().anyMatch(pattern -> ANT.match(pattern, uri)); |
| 108 | + } |
| 109 | +} |
0 commit comments