inblog logo
|
LifeLog, DevLog
    Summary

    Spring Security + JWT

    Spring Security + JWT + RefreshToken + Logout 총 정리
    KYJTHEYJ's avatar
    KYJTHEYJ
    Feb 09, 2026
    Spring Security + JWT
    Contents
    Spring Security주요 필터들작동 흐름주요 인터페이스받은 인증 정보 활용하기JWT구성 요소JWT 토큰 생성 단계JWT 인증 Filter 만들기로그인 시 직접검증? 스프링 인증 객체 검증?Refresh Token 의 사용Refresh Token 과 로그아웃Refresh Token 사용하기Refresh Token 을 통한 재발급재발급 과정JWT 사용 했을 때의 로그아웃블랙 리스트 등록 방식

    Spring Security

    스프링 프레임워크의 보안 요소를 위한 별도의 프레임워크

    SpringSecurityFilterChain 이라는 특유의 별도 Filter 들을 적용하여
    보안 요소를 동작시킨다

    주요 필터들

    • UsernamePasswordAuthenticationFilter → 폼 로그인 처리용 필터

    • ExceptionTranslationFilter → 401, 403 핸들링 (인증/인가의 예외처리 담당 필터)

    • FilterSecurityInterceptor = AuthoriztionFilter → 권한 거부 처리용 필터

    작동 흐름

    1. Security Filter Chain 시작

    2. UserPasswordAuthentication Filter 와 커스텀 JWT Filter 작동

    3. AuthenticationManager, Provider 를 거쳐 인증이 통과 된다

    4. UserDetailsService.loadUserByUsername() (DB에서 사용자 조회)

    5. UsernamePasswordAuthenticationToken 객체 생성

    6. SecurityContext 에 저장

    7. Controller 도달

    주요 인터페이스

    https://lifelogdevlog.inblog.io/96797 를 참조

    @Configuration
    @RequiredArgsConstructor
    @EnableWebSecurity
    @EnableMethodSecurity(securedEnabled = true)
    public class SecurityConfig {
        // Security 내 PasswordEncoder Bean 등록
        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    
        // JwtFilter 등록
        private final JwtAuthFilter jwtAuthFilter;
    
        @Bean
        public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
            http
                    .csrf(httpSecurityCsrfConfigurer -> httpSecurityCsrfConfigurer.disable())
                    .sessionManagement(sessionManagementConfigurer -> sessionManagementConfigurer.disable())
                    .formLogin(formLoginConfigurer -> formLoginConfigurer.disable())
                    .httpBasic(httpBasicConfigurer -> httpBasicConfigurer.disable())
                    .authorizeHttpRequests(authorizeRequests ->
                            // 인증 통과 URI 작성 또는 권한 명세
                            authorizeRequests.anyRequest().authenticated())
                    .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
    
            return http.build();
        }
    }
    

    💡

    실질적 코드 작성이 필요한 부분

    1. Security Filter Chain 이 시작 될 때 JWT 사용이라면 JWT Filter 를 추가한다

    2. UserPasswordAuthentication 이나 JWT Filter 에서 UserDetailsService.loadUserByUsername() 을 부른다

    3. SecurityContextHolder 에 인증 객체를 저장한다

    받은 인증 정보 활용하기

    • Controller 레이어

      • @AuthenticationPrincipal Annotation + UserDetails

    @GetMapping("/me")
    public ResponseEntity<DataResponse<GetMyProfileResponse>>  getMyProfile(
                @AuthenticationPrincipal UserDetails loginAdminInfo
        ) { 
    	... 
    }
    
    • Service

      • SecurityContextHolder.getContext().getAuthentication()

    Service
    @RequiredArgsConstructor
    public class ProductService {
        
        private final ProductRepository productRepository;
        
        public void createProduct(ProductRequest request) {
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            
            if (authentication != null && authentication.isAuthenticated()) {
                AdminDetail adminDetail = (AdminDetail) authentication.getPrincipal();
                Long adminId = adminDetail.getAdmin().getId();
    	...
    }
    

    JWT

    Json Web Token

    Header, Payload, Signature 세 부분으로 구성

    이 세 부분은 Base64로 인코딩 된 JSON 문자열을 . 으로 이은 형태

    구성 요소

    • Header

      • JWT를 생성하는데 사용한 알고리즘을 명시한 부분

    • Payload

      • JWT의 내용이 담긴 부분 (Subject, Claim 등)

    • Signature

      • Header 와 Payload 를 서버만 알고 있는 비밀키로 암호화한 부분

      • 서버에 JWT가 도달하면 Signature 와 서버가 자신이 가진 비밀키로 Header 와 Payload를 암호화 하여 비교함

    JWT 토큰 생성 단계

    1. 비밀키 내용 가져오기

    2. 서명에 사용할 비밀키와 검증을 위한 parser 만들기

    3. JWT builder 통한 토큰 생성하기

    4. parser 가 내부 클레임 정보에 접근 가능한지 확인하기 (검증), 예외처리하기

    5. 내부 내용 추출하게 하기

    @Slf4j
    @Component
    @RequiredArgsConstructor
    public class JwtUtil {
        // 만료 시간 가져오기
        @Value("${jwt.secret.accessExpire}")
        private Long accessTokenExpire;
    
        @Value("${jwt.secret.refreshExpire}")
        private Long refreshTokenExpire;
    
        // 비밀키 구문 가져오기
        @Value("${jwt.secret.key}")
        private String secretKeyStr;
    
        // 서명에 사용할 비밀키와 검증을 위한 Parser
        private SecretKey secretKey;
        private JwtParser parser;
    
        @PostConstruct
        public void init() {
            byte[] bytes = Decoders.BASE64.decode(secretKeyStr); // 비밀키의 Base64 변환
            this.secretKey = Keys.hmacShaKeyFor(bytes); // HMAC-SHA-256 알고리즘으로 서명에 필요한 비밀키 생성 (공식문서)
            this.parser = Jwts.parser().verifyWith(this.secretKey).build(); // 비밀키를 사용한 parser 선언
        }
    
        // 토큰 생성
        public String createAccessToken(String userUid) {
            Date now = new Date();
            return Jwts.builder()
                    .subject(userUid)
                    .issuedAt(now)
                    .expiration(new Date(now.getTime() + accessTokenExpire))
                    .signWith(secretKey, Jwts.SIG.HS256)
                    .compact();
        }
    
        public String createRefreshToken(String userUid) {
            Date now = new Date();
            return Jwts.builder()
                    .subject(userUid)
                    .issuedAt(now)
                    .expiration(new Date(now.getTime() + refreshTokenExpire))
                    .signWith(secretKey, Jwts.SIG.HS256)
                    .compact();
        }
    
        // 토큰 검증
        public boolean validateToken(String token) {
            if(token == null || token.isBlank()) return false;
    
            try {
                parser.parseSignedClaims(token);
                return true;
            } catch (JwtException e) {
                log.error("JwtException : {}", e.getMessage());
                return false;
            }
        }
    
        // 토큰 내 클레임 추출
        private Claims extractClaimByToken(String token) {
            return parser.parseSignedClaims(token).getPayload();
        }
    
        // Subject 추출하기
        public String extractSubject(String token) {
            return parser.parseSignedClaims(token).getPayload().getSubject();
        }
    }
    

    JWT 인증 Filter 만들기

    1. OncePerRequestFilter 상속받기 (Filter 의 동작을 한번만 받아야 JWT가 올바르게 작동할 가능성이 큼)

    2. doFilterInternal 오버라이드 하기

    3. 토큰이 헤더에 Authorization 에 담기므로 확인, 없으면 예외처리

    4. 올바른 토큰인지 검증 메서드로 검증하기

    5. 올바르면 UserDetailService 에 담아 SecurityContextHolder 에 UsernamePasswordAuthenticationToken 으로 담아주기

    6. 인증이 필요없는 URI 명세하기, shouldNotFilter 에 등록하기

    @Slf4j
    @Component
    @RequiredArgsConstructor
    public class JwtAuthFilter extends OncePerRequestFilter {
        private final JwtUtil jwtUtil;
        private final ObjectMapper objectMapper;
        private final AdminDetailService adminDetailService;
    
        private static final PathPatternParser patternParser = new PathPatternParser();
    
    		// 인증 제외 패턴 선언
        private static final List<PathPattern> EXCLUDE_PATTERNS = List.of(
                patternParser.parse("/api/auth/login")
        );
    
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
            // 요청
            log.info("JwtAuthFilter IN");
    
            // 올바른 토큰이 있는지?
            // Authorization 토큰
            String authorization = request.getHeader("Authorization");
    
            if(authorization == null || !authorization.startsWith("Bearer ")) {
                MessageResponse errorResponse = MessageResponse.fail(HttpStatus.UNAUTHORIZED.name(),  "인증 정보가 없습니다");
                String json = objectMapper.writeValueAsString(errorResponse);
    
                response.setContentType("application/json; charset=UTF-8");
                response.setStatus(HttpStatus.UNAUTHORIZED.value());
                response.getWriter().write(json);
                return;
            }
    
            // Bearer 검증 잘라내기
            String token = authorization.substring("Bearer ".length());
    
            if(!jwtUtil.validateToken(token)) {
                MessageResponse errorResponse = MessageResponse.fail(HttpStatus.UNAUTHORIZED.name(),  "인증 정보에 대한 확인이 필요합니다");
                String json = objectMapper.writeValueAsString(errorResponse);
    
                response.setContentType("application/json; charset=UTF-8");
                response.setStatus(HttpStatus.UNAUTHORIZED.value());
                response.getWriter().write(json);
                return;
            } else {
                // 토큰 검증 완료 되었으니 정보를 담아서 활용
                // 토큰에서 이메일 추출
                String email = jwtUtil.getEmail(token);
    
                // UserDetailService로 정보 조회
                UserDetails adminDetail = adminDetailService.loadUserByUsername(email);
    
                // 인증 객체 생성
                UsernamePasswordAuthenticationToken authentication
                        = new UsernamePasswordAuthenticationToken(
                                adminDetail // principal (누구인지)
                                , null // credentials
                                , adminDetail.getAuthorities()); // 권한 정보
    
                // SecurityContext에 인증 객체 저장
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
    
            filterChain.doFilter(request, response);
    
            // 응답
            log.info("Test Filter2 OUT");
        }
    
        // URI가 토큰 체크를 해야하는 URI 인지?
        @Override
        protected boolean shouldNotFilter(@NonNull HttpServletRequest request) {
            log.info("JwtFilter IN - request URI : {}",  request.getRequestURI());
    
            PathContainer path = PathContainer.parsePath(request.getRequestURI());
            return EXCLUDE_PATTERNS.stream().anyMatch(pattern -> pattern.matches(path));
        }
    }
    

    로그인 시 직접검증? 스프링 인증 객체 검증?

    [로그인 요청] →

    1. AuthService.login() 호출

    2. AuthenticationManager.authenticate() 호출

    3. AuthenticationManager가 내부적으로 처리:

      1. UserDetailsServiceImpl.loadUserByUsername(email) 호출

      2. DB에서 Member 조회

      3. UserDetails 객체 생성 (email, password, role 포함)

      4. PasswordEncoder로 비밀번호 검증 (입력된 비밀번호 vs UserDetails의 비밀번호)

      5. 검증 실패 시: BadCredentialsException 발생

      6. 검증 성공 시: Authentication 객체 반환

    4. Authentication에서 UserDetails 추출

    5. Member 정보로 JWT 토큰 생성

    6. RefreshToken 저장

    → [AccessToken 반환]

    Refresh Token 의 사용

    기본적으로 Login 시 AccessToken 발급과 비슷하다

    하지만 Cookie 에 담아 사용하게 하는 것이 다르다

    • Controller

    @PostMapping("/login")
        public ResponseEntity<BaseResponse<LoginResponse>> login(
                @RequestBody LoginRequest request
                , HttpServletResponse response
        ) {
            String email = request.email();
    
            try {
                // AuthenticationManager를 통한 인증
                authenticationManager.authenticate(
                        new UsernamePasswordAuthenticationToken(request.email(), request.password())
                );
    
                // JWT 토큰 생성
                TokenInfo tokens = authService.login(request);
    
                // 리프레쉬 토큰은 쿠키로 반환
                Cookie refreshTokenCookie = new Cookie("refreshToken", tokens.refreshToken());
                refreshTokenCookie.setPath("/");
                refreshTokenCookie.setHttpOnly(true);
                refreshTokenCookie.setMaxAge(3600 * 24 * 7);
                //refreshTokenCookie.setHttpOnly(true); // 현재는 환경상 주석처리
                response.addCookie(refreshTokenCookie);
    
                // 액세스 토큰은 헤더로 반환
                return ResponseEntity.ok()
                    .header("Authorization", "Bearer " + tokens.accessToken())
                    .body(BaseResponse.success(HttpStatus.OK.name(), null, new LoginResponse(true, email)));
    
            } catch (AuthenticationException e) {
                log.error("Login Error : {}", e.getMessage());
                return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(BaseResponse.fail(HttpStatus.UNAUTHORIZED.name(), MSG_NOT_MATCH_LOGIN, new LoginResponse(false, MSG_NOT_MATCH_LOGIN)));
            }
        }
    
    • Service

    @Transactional
        public TokenInfo login(LoginRequest request) {
            Member member = memberRepository.findByEmailAndDeletedFalse(request.email()).orElseThrow(() -> new ServiceErrorException(ErrorEnum.ERR_NOT_MATCH_LOGIN));
    
            // JWT 토큰 생성
            String accessToken = jwtTokenProvider.createAccessToken(request.email(), member.getMemberUid(), member.getRole());
            String refreshToken = jwtTokenProvider.createRefreshToken(request.email(), member.getMemberUid(), member.getRole());
    
            refreshTokenRepository.save(RefreshToken.register(member, refreshToken, jwtTokenProvider.getExpireTime(refreshToken)));
    
            return new TokenInfo(accessToken, refreshToken);
        }
    
    public record TokenInfo (
            String accessToken
            , String refreshToken
    ) {
    
    }
    

    Refresh Token 과 로그아웃

    Refresh Token 사용하기

    AccessToken 만료됨

    ↓

    [클라이언트]

    ↓ API 요청 (만료된 AccessToken)

    [서버: JwtAuthenticationFilter]

    ↓ 토큰 검증 실패 → 401 Unauthorized 응답

    [클라이언트]

    ↓ 401 감지 → RefreshToken으로 갱신 요청

    [서버: POST /api/auth/refresh]

    ↓

    1. RefreshToken 유효성 검증

    2. DB 또는 Redis 등 에서 해당 RefreshToken 존재 여부 확인

    3. (선택) RefreshToken 의 사용여부 확인

    4. 만료 시간 확인

    ↓ 모두 통과 시

    1. 새로운 AccessToken 발급

    2. 새로운 RefreshToken 발급

    ↓

    [클라이언트]

    새 AccessToken으로 재요청

    Refresh Token 을 통한 재발급

    재발급 과정

    [클라이언트]

    ↓ POST /api/auth/refresh / Request : { refreshToken: "구 토큰" }

    [서버]

    ↓ 1-3. 검증

    ↓ 4. 새 AccessToken 발급

    ↓ 5. 새 RefreshToken 발급

    ↓ 6. 기존 RefreshToken 삭제

    ↓ 7. 새 RefreshToken DB 저장

    ↓ Response : { accessToken: "새 토큰", refreshToken: "새 토큰" }

    [클라이언트]

    ↓ 둘 다 새로 저장

    • Controller

    // Refresh Token 을 통한 재발급
    @PostMapping("/refresh")
    public ResponseEntity<BaseResponse<RefreshResponse>> refresh(@RequestBody RefreshRequest request) {
        RefreshResponse response = authService.refreshToken(request.refreshToken());
        return ResponseEntity.status(HttpStatus.OK)
                .header("Authorization", "Bearer " + response.accessToken())
                .body(BaseResponse.success(HttpStatus.OK.name(), null, response));
    }
    
    • Service

    @Transactional
    public RefreshResponse refreshToken(String refreshToken) {
        if(jwtTokenProvider.validateToken(refreshToken)) {
            RefreshToken existRefreshToken = refreshTokenRepository.findByRefreshToken(refreshToken).orElseThrow(() -> new ServiceErrorException(ErrorEnum.ERR_TOKEN_EMPTY));
    
            // 리프레쉬 토큰이 만료 되었는지 확인
            if(existRefreshToken.getExpirationAt().isBefore(LocalDateTime.now())) {
                refreshTokenRepository.delete(existRefreshToken);
                throw new ServiceErrorException(ERR_TOKEN_EXPIRE);
            }
    
            // 유저 정보 가져오기
            Member member = existRefreshToken.getMember();
            if(!memberRepository.existsByEmailAndDeletedFalse(member.getEmail())) {
                refreshTokenRepository.delete(existRefreshToken);
                throw new ServiceErrorException(ERR_NOT_FOUND_MEMBER);
            }
    
            // 기존 토큰 삭제
            refreshTokenRepository.delete(existRefreshToken);
    
            // 새 토큰 발급
            String newAccessToken = jwtTokenProvider.createAccessToken(member.getEmail(), member.getMemberUid(), member.getRole());
            String newRefreshToken = jwtTokenProvider.createRefreshToken(member.getEmail(), member.getMemberUid(), member.getRole());
    
            // 새 리프레쉬 토큰 저장
            refreshTokenRepository.save(RefreshToken.register(member, newRefreshToken, jwtTokenProvider.getExpireTime(newRefreshToken)));
    
            return new RefreshResponse(newAccessToken, newRefreshToken);
        } else {
            log.error("Refresh Tokens Error : {}", "리프레쉬 토큰 올바르지 않음");
            throw new ServiceErrorException(ErrorEnum.ERR_TOKEN_INVALID);
        }
    }
    

    JWT 사용 했을 때의 로그아웃

    기본적으로 Stateless 라서 로그아웃 시 처리 방안에 대해 고민해야함

    (토큰 무효화라는 것이 서버에서 할 수가 없으므로)

    블랙 리스트 등록 방식

    로그아웃된 AccessToken 을 블랙리스트 DB/Redis에 저장하여,
    매 요청마다 확인하는 것

    • 구현 순서

    1. 전용 엔티티 셋업

    // AccessToken 블랙리스트 전용 Entity
    
    @Getter
    @Entity
    @Table(name = "black_access_tokens")
    @NoArgsConstructor(access = AccessLevel.PROTECTED)
    public class BlackAccessToken {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long tokenId;
    
        private String accessToken;
        
        private LocalDateTime expirationAt;
        
        private LocalDateTime listedAt;
        
        private BlackAccessToken(String accessToken, LocalDateTime expirationAt) {
            this.accessToken = accessToken;
            this.expirationAt = expirationAt;
            this.listedAt = LocalDateTime.now();
        }
        
        public static BlackAccessToken register(String accessToken, LocalDateTime expirationAt) {
            return new BlackAccessToken(accessToken, expirationAt);
        }
    }
    
    1. 로그아웃 시 RefreshToken 삭제 처리,
      AccessToken 을 블랙리스트 엔티티에 등록

    @PostMapping("/logout")
        public ResponseEntity<BaseResponse<Void>> logout(
                @AuthenticationPrincipal UserDetails loginMemberInfo
                , @RequestHeader(HttpHeaders.AUTHORIZATION) String accessTokenWithBearer
        ) {
            String email = loginMemberInfo.getUsername();
            String accessToken = accessTokenWithBearer.substring("Bearer ".length());
    
            authService.logout(accessToken, email);
    
            return ResponseEntity.status(HttpStatus.OK).body(BaseResponse.success(HttpStatus.OK.name(), MSG_LOGOUT, null));
    
        }
    
    1. JWT 인증 필터에서 블랙리스트 엔티티에 등록되었으면 거부하도록 처리

    @Slf4j
    @Component
    @RequiredArgsConstructor
    public class JwtAuthenticationFilter extends OncePerRequestFilter {
    
        //.....
    
                // AccessToken 블랙리스트 조회
                if(blackAccessTokenRepository.existsByAccessToken(token)) {
                    log.error("블랙 리스트 등록 토큰 사용 감지");
                    BaseResponse<Void> baseResponse = BaseResponse.fail(HttpStatus.UNAUTHORIZED.name(), MSG_AUTH_WRONG, null);
                    response.setContentType("application/json; charset=UTF-8");
                    response.setStatus(HttpStatus.UNAUTHORIZED.value());
                    response.getWriter().write(objectMapper.writeValueAsString(baseResponse));
                    return;
                }
    
                // 토큰 유효성 검증 및 후속 처리 계속
         //.....       
    }
    
    • JWT 인증 필터 풀 코드 (다른 토이프로젝트에서 적용한 코드)

    // BaseResponse 는 커스텀 된 공통 응답 코드
    
    @Slf4j
    @Component
    @RequiredArgsConstructor
    public class JwtAuthenticationFilter extends OncePerRequestFilter {
    
        private final JwtTokenProvider jwtTokenProvider;
        private final ObjectMapper objectMapper;
        private final UserDetailsServiceImpl userDetailsServiceImpl;
        private final BlackAccessTokenRepository blackAccessTokenRepository;
    
        // PathPatternParser 사용하여 인증 제외 URI 검별
        private static final PathPatternParser patternParser = new PathPatternParser();
    
        // 인증 제외 패턴 선언
        private static final List<PathPattern> EXCLUDE_PATTERNS = List.of(
                patternParser.parse(toStaticResources().atCommonLocations().toString()) // 정적 리소스
                , patternParser.parse("/**") // 리소스 통과
                , patternParser.parse("/pages/**") // 페이지 구성 통과
                , patternParser.parse("/api/signup") // 회원 가입
                , patternParser.parse("/api/auth/login") // 로그인
        );
    
        @Override
        protected void doFilterInternal(
                @NonNull HttpServletRequest request,
                @NonNull HttpServletResponse response,
                @NonNull FilterChain filterChain
        ) throws ServletException, IOException {
            log.info("===JWT FILTER IN===");
            try {
                // Request Header에서 JWT 토큰 추출
                String token = getJwtFromRequest(request);
    
                if(token == null) {
                    log.error("액세스 토큰 없음");
                    BaseResponse<Void> baseResponse = BaseResponse.fail(HttpStatus.UNAUTHORIZED.name(), MSG_TOKEN_EMPTY, null);
                    response.setContentType("application/json; charset=UTF-8");
                    response.setStatus(HttpStatus.UNAUTHORIZED.value());
                    response.getWriter().write(objectMapper.writeValueAsString(baseResponse));
                    return;
                }
    
                // AccessToken 블랙리스트 조회
                if(blackAccessTokenRepository.existsByAccessToken(token)) {
                    log.error("블랙 리스트 등록 토큰 사용 감지");
                    BaseResponse<Void> baseResponse = BaseResponse.fail(HttpStatus.UNAUTHORIZED.name(), MSG_AUTH_WRONG, null);
                    response.setContentType("application/json; charset=UTF-8");
                    response.setStatus(HttpStatus.UNAUTHORIZED.value());
                    response.getWriter().write(objectMapper.writeValueAsString(baseResponse));
                    return;
                }
    
                // 토큰 유효성 검증
                if (jwtTokenProvider.validateToken(token)) {
                    // 토큰에서 사용자 정보 추출
                    String email = jwtTokenProvider.getEmail(token);
    
                    // 인증 객체 생성
                    UserDetails userDetails = userDetailsServiceImpl.loadUserByUsername(email);
    
                    UsernamePasswordAuthenticationToken authentication =
                        new UsernamePasswordAuthenticationToken(
                               userDetails, null, userDetails.getAuthorities()
                        );
    
                    // SecurityContext에 인증 정보 설정
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                }
            } catch (Exception e) {
                logger.error("JWT 인증 실패", e);
    
                BaseResponse<Void> baseResponse = BaseResponse.fail(HttpStatus.UNAUTHORIZED.name(), MSG_AUTH_WRONG, null);
                response.setContentType("application/json; charset=UTF-8");
                response.setStatus(HttpStatus.UNAUTHORIZED.value());
                response.getWriter().write(objectMapper.writeValueAsString(baseResponse));
                return;
            }
    
            filterChain.doFilter(request, response);
    
            log.info("===JWT FILTER OUT===");
        }
    
        /**
         * Request Header에서 JWT 토큰 추출
         * Authorization: Bearer {token}
         */
        private String getJwtFromRequest(HttpServletRequest request) {
            String bearerToken = request.getHeader("Authorization");
    
            if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
                return bearerToken.substring(7);
            }
    
            return null;
        }
    
        @Override
        protected boolean shouldNotFilter(@NonNull HttpServletRequest request) {
            PathContainer path = PathContainer.parsePath(request.getRequestURI());
            return EXCLUDE_PATTERNS.stream().anyMatch(pattern -> pattern.matches(path));
        }
    }
    


    RefreshToken 의 로그아웃 시 Redis 사용이 성능이 더 좋다
    추후에 이 부분은 다시 정리하려한다

    Share article
    Contents
    Spring Security주요 필터들작동 흐름주요 인터페이스받은 인증 정보 활용하기JWT구성 요소JWT 토큰 생성 단계JWT 인증 Filter 만들기로그인 시 직접검증? 스프링 인증 객체 검증?Refresh Token 의 사용Refresh Token 과 로그아웃Refresh Token 사용하기Refresh Token 을 통한 재발급재발급 과정JWT 사용 했을 때의 로그아웃블랙 리스트 등록 방식

    LifeLog, DevLog - https://github.com/KYJTHEYJ

    RSS·Powered by Inblog