๐Ÿ’ฅ [Error] JWT Token ์ธ์ฆ ๋ฌธ์ œ

๋ฐ•์ƒ๋ฏผยท2024๋…„ 6์›” 29์ผ

ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ…

๋ชฉ๋ก ๋ณด๊ธฐ
10/11

๋Œ€ํ•™๊ต ์ปค๋ฎค๋‹ˆํ‹ฐ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋˜ ๋„์ค‘ JWT ํ•„ํ„ฐ ๊ด€๋ จ ํ…Œ์ŠคํŠธ์—์„œ ๋ฌด์ˆ˜ํ•œ ์—๋Ÿฌ๋ฅผ ๋งˆ์ฃผํ–ˆ๋‹ค..

์ „์ฒด ํ…Œ์ŠคํŠธ ์ฝ”๋“œ

package com.Sucat.global.jwt.service;

import com.Sucat.domain.user.model.User;
import com.Sucat.domain.user.repository.UserRepository;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.persistence.EntityManager;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.Map;

import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest
@AutoConfigureMockMvc
@Transactional
public class JwtFilterAuthenticationTest {

    @Autowired
    MockMvc mockMvc;

    @Autowired
    UserRepository userRepository;

    @Autowired
    EntityManager em;

    @Autowired
    JwtService jwtService;

    PasswordEncoder delegatingPasswordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();


    @Value("${jwt.secret}")
    private String secret;
    @Value("${jwt.access.header}")
    private String accessHeader;
    @Value("${jwt.refresh.header}")
    private String refreshHeader;

    private static String KEY_USERNAME = "email";
    private static String KEY_PASSWORD = "password";
    private static String USERNAME = "test@naver.com";
    private static String PASSWORD = "123456789";
    private static String LOGIN_URL = "/login";


    private static final String ACCESS_TOKEN_SUBJECT = "AccessToken";
    private static final String BEARER = "Bearer";


    private ObjectMapper objectMapper = new ObjectMapper();



    private void clear(){
        em.flush();
        em.clear();
    }


    @BeforeEach
    private void init(){
        userRepository.save(User.builder()
                .email(USERNAME)
                .password(delegatingPasswordEncoder.encode(PASSWORD))
                .name("Member1")
                .nickname("NickName1")
                .build());
        clear();
    }



    private Map getUsernamePasswordMap(String username, String password){
        Map<String, String> map = new HashMap<>();
        map.put(KEY_USERNAME, username);
        map.put(KEY_PASSWORD, password);
        return map;
    }


    private Map getAccessAndRefreshToken() throws Exception {

        Map<String, String> map = getUsernamePasswordMap(USERNAME, PASSWORD);

        MvcResult result = mockMvc.perform(
                        post(LOGIN_URL)
                                .contentType(MediaType.APPLICATION_JSON)
                                .content(objectMapper.writeValueAsString(map)))
                .andReturn();

        String accessToken = result.getResponse().getHeader(accessHeader);
        String refreshToken = result.getResponse().getHeader(refreshHeader);

        Map<String, String> tokenMap = new HashMap<>();
        tokenMap.put(accessHeader,accessToken);
        tokenMap.put(refreshHeader,refreshToken);

        return tokenMap;
    }



    /**
     * AccessToken : ์กด์žฌํ•˜์ง€ ์•Š์Œ,
     * RefreshToken : ์กด์žฌํ•˜์ง€ ์•Š์Œ
     */
    @Test
    public void Access_Refresh_๋ชจ๋‘_์กด์žฌ_X() throws Exception {
        //when, then
        mockMvc.perform(get(LOGIN_URL+"123"))//login์ด ์•„๋‹Œ ๋‹ค๋ฅธ ์ž„์˜์˜ ์ฃผ์†Œ
                .andExpect(status().isForbidden());
    }

    /**
     * AccessToken : ์œ ํšจ,
     * RefreshToken : ์กด์žฌํ•˜์ง€ ์•Š์Œ
     */
    @Test
    public void AccessToken๋งŒ_๋ณด๋‚ด์„œ_์ธ์ฆ() throws Exception {
        //given
        Map accessAndRefreshToken = getAccessAndRefreshToken();
        String accessToken= (String) accessAndRefreshToken.get(accessHeader);

        //when, then
        mockMvc.perform(get(LOGIN_URL+"123").header(accessHeader,BEARER+ accessToken))//login์ด ์•„๋‹Œ ๋‹ค๋ฅธ ์ž„์˜์˜ ์ฃผ์†Œ
                .andExpectAll(status().isNotFound());

    }


    /**
     * AccessToken : ์œ ํšจํ•˜์ง€ ์•Š์Œ,
     * RefreshToken : ์กด์žฌํ•˜์ง€ ์•Š์Œ
     */
    @Test
    public void ์•ˆ์œ ํšจํ•œAccessToken๋งŒ_๋ณด๋‚ด์„œ_์ธ์ฆX_์ƒํƒœ์ฝ”๋“œ๋Š”_403() throws Exception {
        //given
        Map accessAndRefreshToken = getAccessAndRefreshToken();
        String accessToken= (String) accessAndRefreshToken.get(accessHeader);

        //when, then
        mockMvc.perform(get(LOGIN_URL+"123").header(accessHeader,accessToken+"1"))//login์ด ์•„๋‹Œ ๋‹ค๋ฅธ ์ž„์˜์˜ ์ฃผ์†Œ
                .andExpectAll(status().isForbidden()); // ์—†๋Š” ์ฃผ์†Œ๋กœ ๋ณด๋ƒˆ์œผ๋ฏ€๋กœ NotFound
    }


    /**
     * AccessToken : ์กด์žฌํ•˜์ง€ ์•Š์Œ
     * RefreshToken : ์œ ํšจ
     */
    @Test
    public void ์œ ํšจํ•œRefreshToken๋งŒ_๋ณด๋‚ด์„œ_AccessToken_์žฌ๋ฐœ๊ธ‰_200() throws Exception {
        //given
        Map accessAndRefreshToken = getAccessAndRefreshToken();
        String refreshToken= (String) accessAndRefreshToken.get(refreshHeader);

        // refreshToken์ด null์ธ์ง€ ํ™•์ธ
        assertThat(refreshToken).isNotNull();

        //when, then
        MvcResult result = mockMvc.perform(get("/login123").header(refreshHeader, BEARER + refreshToken))
                .andExpect(status().isOk())
                .andReturn();

        String accessToken = result.getResponse().getHeader(accessHeader);

        // accessToken์ด null์ธ์ง€ ํ™•์ธ
        assertThat(accessToken).isNotNull();

        String subject = JWT.require(Algorithm.HMAC512(secret)).build().verify(accessToken).getSubject();

        // subject๊ฐ€ ์˜ˆ์ƒํ•œ ๊ฐ’๊ณผ ์ผ์น˜ํ•˜๋Š”์ง€ ํ™•์ธ
        assertThat(subject).isEqualTo(ACCESS_TOKEN_SUBJECT);
    }



    /**
     * AccessToken : ์กด์žฌํ•˜์ง€ ์•Š์Œ
     * RefreshToken : ์œ ํšจํ•˜์ง€ ์•Š์Œ
     */
    @Test
    public void ์•ˆ์œ ํšจํ•œRefreshToken๋งŒ_๋ณด๋‚ด๋ฉด_403() throws Exception {
        //given
        Map accessAndRefreshToken = getAccessAndRefreshToken();
        String refreshToken= (String) accessAndRefreshToken.get(refreshHeader);

        //when, then
        mockMvc.perform(get(LOGIN_URL + "123").header(refreshHeader, refreshToken))//Bearer์„ ๋ถ™์ด์ง€ ์•Š์Œ
                .andExpect(status().isForbidden());

        mockMvc.perform(get(LOGIN_URL + "123").header(refreshHeader, BEARER+refreshToken+"1"))//์œ ํšจํ•˜์ง€ ์•Š์€ ํ† ํฐ
                .andExpect(status().isForbidden());
    }



    /**
     * AccessToken : ์œ ํšจ
     * RefreshToken : ์œ ํšจ
     */
    @Test
    public void ์œ ํšจํ•œRefreshToken์ด๋ž‘_์œ ํšจํ•œAccessToken_๊ฐ™์ด๋ณด๋ƒˆ์„๋•Œ_AccessToken_์žฌ๋ฐœ๊ธ‰_200() throws Exception {
        //given
        Map accessAndRefreshToken = getAccessAndRefreshToken();
        String accessToken= (String) accessAndRefreshToken.get(accessHeader);
        String refreshToken= (String) accessAndRefreshToken.get(refreshHeader);

        //when, then
        MvcResult result = mockMvc.perform(get(LOGIN_URL+"123")
                        .header(refreshHeader, BEARER + refreshToken)
                        .header(accessHeader, BEARER + accessToken))
                .andExpect(status().isOk())
                .andReturn();

        String responseAccessToken = result.getResponse().getHeader(accessHeader);
        String responseRefreshToken = result.getResponse().getHeader(refreshHeader);

        String subject = JWT.require(Algorithm.HMAC512(secret)).build().verify(responseAccessToken).getSubject();

        assertThat(subject).isEqualTo(ACCESS_TOKEN_SUBJECT);
        assertThat(responseRefreshToken).isNull();//refreshToken์€ ์žฌ๋ฐœ๊ธ‰๋˜์ง€ ์•Š์Œ
    }





    /**
     * AccessToken : ์œ ํšจํ•˜์ง€ ์•Š์Œ
     * RefreshToken : ์œ ํšจ
     */
    @Test
    public void ์œ ํšจํ•œRefreshToken์ด๋ž‘_์•ˆ์œ ํšจํ•œAccessToken_๊ฐ™์ด๋ณด๋ƒˆ์„๋•Œ_AccessToken_์žฌ๋ฐœ๊ธ‰_200() throws Exception {
        //given
        Map accessAndRefreshToken = getAccessAndRefreshToken();
        String accessToken= (String) accessAndRefreshToken.get(accessHeader);
        String refreshToken= (String) accessAndRefreshToken.get(refreshHeader);

        //when, then
        MvcResult result = mockMvc.perform(get(LOGIN_URL + "123")
                        .header(refreshHeader, BEARER + refreshToken)
                        .header(accessHeader, BEARER + accessToken + 1))
                .andExpect(status().isOk())
                .andReturn();

        String responseAccessToken = result.getResponse().getHeader(accessHeader);
        String responseRefreshToken = result.getResponse().getHeader(refreshHeader);

        String subject = JWT.require(Algorithm.HMAC512(secret)).build().verify(responseAccessToken).getSubject();

        assertThat(subject).isEqualTo(ACCESS_TOKEN_SUBJECT);
        assertThat(responseRefreshToken).isNull();//refreshToken์€ ์žฌ๋ฐœ๊ธ‰๋˜์ง€ ์•Š์Œ
    }


    /**
     * AccessToken : ์œ ํšจ
     * RefreshToken : ์œ ํšจํ•˜์ง€ ์•Š์Œ
     */
    @Test
    public void ์•ˆ์œ ํšจํ•œRefreshToken์ด๋ž‘_์œ ํšจํ•œAccessToken_๊ฐ™์ด๋ณด๋ƒˆ์„๋•Œ_์ƒํƒœ์ฝ”๋“œ200_ํ˜น์€404_RefreshToken์€_AccessToken๋ชจ๋‘_์žฌ๋ฐœ๊ธ‰๋˜์ง€์•Š์Œ() throws Exception {
        //given
        Map accessAndRefreshToken = getAccessAndRefreshToken();
        String accessToken= (String) accessAndRefreshToken.get(accessHeader);
        String refreshToken= (String) accessAndRefreshToken.get(refreshHeader);

        //when, then
        MvcResult result = mockMvc.perform(get(LOGIN_URL + "123")
                        .header(refreshHeader, BEARER + refreshToken+1)
                        .header(accessHeader, BEARER + accessToken ))
                .andExpect(status().isNotFound())//์—†๋Š” ์ฃผ์†Œ๋กœ ๋ณด๋ƒˆ์œผ๋ฏ€๋กœ NotFound
                .andReturn();

        String responseAccessToken = result.getResponse().getHeader(accessHeader);
        String responseRefreshToken = result.getResponse().getHeader(refreshHeader);


        assertThat(responseAccessToken).isNull();//accessToken์€ ์žฌ๋ฐœ๊ธ‰๋˜์ง€ ์•Š์Œ
        assertThat(responseRefreshToken).isNull();//refreshToken์€ ์žฌ๋ฐœ๊ธ‰๋˜์ง€ ์•Š์Œ
    }



    /**
     * AccessToken : ์œ ํšจํ•˜์ง€ ์•Š์Œ
     * RefreshToken : ์œ ํšจํ•˜์ง€ ์•Š์Œ
     */
    @Test
    public void ์•ˆ์œ ํšจํ•œRefreshToken์ด๋ž‘_์•ˆ์œ ํšจํ•œAccessToken_๊ฐ™์ด๋ณด๋ƒˆ์„๋•Œ_403() throws Exception {
        //given
        Map accessAndRefreshToken = getAccessAndRefreshToken();
        String accessToken= (String) accessAndRefreshToken.get(accessHeader);
        String refreshToken= (String) accessAndRefreshToken.get(refreshHeader);

        //when, then
        MvcResult result = mockMvc.perform(get(LOGIN_URL + "123")
                        .header(refreshHeader, BEARER + refreshToken+1)
                        .header(accessHeader, BEARER + accessToken+1 ))
                .andExpect(status().isForbidden())//์—†๋Š” ์ฃผ์†Œ๋กœ ๋ณด๋ƒˆ์œผ๋ฏ€๋กœ NotFound
                .andReturn();

        String responseAccessToken = result.getResponse().getHeader(accessHeader);
        String responseRefreshToken = result.getResponse().getHeader(refreshHeader);


        assertThat(responseAccessToken).isNull();//accessToken์€ ์žฌ๋ฐœ๊ธ‰๋˜์ง€ ์•Š์Œ
        assertThat(responseRefreshToken).isNull();//refreshToken์€ ์žฌ๋ฐœ๊ธ‰๋˜์ง€ ์•Š์Œ

    }

    @Test
    public void ๋กœ๊ทธ์ธ_์ฃผ์†Œ๋กœ_๋ณด๋‚ด๋ฉด_ํ•„ํ„ฐ์ž‘๋™_X() throws Exception {
        //given
        Map accessAndRefreshToken = getAccessAndRefreshToken();
        String accessToken= (String) accessAndRefreshToken.get(accessHeader);
        String refreshToken= (String) accessAndRefreshToken.get(refreshHeader);

        //when, then
        MvcResult result = mockMvc.perform(post(LOGIN_URL)  //get์ธ ๊ฒฝ์šฐ config์—์„œ permitAll์„ ํ–ˆ๊ธฐ์— notFound
                        .header(refreshHeader, BEARER + refreshToken)
                        .header(accessHeader, BEARER + accessToken))
                .andExpect(status().isBadRequest())
                .andReturn();

    }

}

์ž์„ธํ•œ ์ฝ”๋“œ๋Š” ์•„๋ž˜ ๊นƒํ—ˆ๋ธŒ ๋งํฌ ์ฐธ๊ณ !
https://github.com/pp8817/Sucat

์šฐ์„  ํ•˜๋‚˜ํ•˜๋‚˜ ํ•ด๊ฒฐํ•ด๋ณด๊ธฐ๋กœ ํ–ˆ๋‹ค.
์œ ํšจํ•œ RefreshToken๊ณผ ์œ ํšจํ•œ AccessToken์„ ๊ฐ™์ด ๋ณด๋‚ธ ๊ฒฝ์šฐ

	@Test
    public void ์œ ํšจํ•œRefreshToken์ด๋ž‘_์œ ํšจํ•œAccessToken_๊ฐ™์ด๋ณด๋ƒˆ์„๋•Œ_AccessToken_์žฌ๋ฐœ๊ธ‰_200() throws Exception {
        //given
        Map accessAndRefreshToken = getAccessAndRefreshToken();
        String accessToken= (String) accessAndRefreshToken.get(accessHeader);
        String refreshToken= (String) accessAndRefreshToken.get(refreshHeader);

        //when, then
        MvcResult result = mockMvc.perform(get(LOGIN_URL+"123")
                        .header(refreshHeader, BEARER + refreshToken)
                        .header(accessHeader, BEARER + accessToken))
                .andExpect(status().isOk())
                .andReturn();

        String responseAccessToken = result.getResponse().getHeader(accessHeader);
        String responseRefreshToken = result.getResponse().getHeader(refreshHeader);

        String subject = JWT.require(Algorithm.HMAC512(secret)).build().verify(responseAccessToken).getSubject();

        assertThat(subject).isEqualTo(ACCESS_TOKEN_SUBJECT);
        assertThat(responseRefreshToken).isNull();//refreshToken์€ ์žฌ๋ฐœ๊ธ‰๋˜์ง€ ์•Š์Œ
    }

๋ณธ๋ž˜ ์˜๋„ํ•œ ๋ชฉ์ ์€ ์œ ํšจํ•œ RefreshToken, ์œ ํšจํ•œ AccessToken์ด ๊ฐ™์ด ๋„˜์–ด๊ฐ„ ๊ฒฝ์šฐ RefreshToken์˜ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ํ›„ AccessToken์„ ์žฌ๋ฐœ๊ธ‰ํ•˜๋„๋ก ๊ฐœ๋ฐœํ–ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ๊ฒฐ๊ณผ ๋กœ๊ทธ๋ฅผ ๋ณด๋ฉด

์ด์ฒ˜๋Ÿผ 403 ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

403(Forbidden) ์—๋Ÿฌ?
์ž‘๋™์ค‘์ธ ์„œ๋ฒ„์— ํด๋ผ์ด์–ธํŠธ์˜ ์š”์ฒญ์ด ๋„๋‹ฌํ–ˆ์ง€๋งŒ, ์„œ๋ฒ„๊ฐ€ ํด๋ผ์ด์–ธํŠธ์˜ ์ ‘๊ทผ์„ ๊ฑฐ๋ถ€ํ•  ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ์—๋Ÿฌ ์ฝ”๋“œ์ด๋‹ค. ์ฆ‰, ์‚ฌ์šฉ์ž์˜ ๊ถŒํ•œ ๊ฒ€์ฆ ๋‹จ๊ณ„์—์„œ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธด๊ฒƒ์ด๋‹ค.

์ฝ”๋“œ์˜ ํ๋ฆ„์„ ๋ณด๋ฉด '/login123'์œผ๋กœ ์š”์ฒญ์ด ๊ฐ€์„œ ์šฐ์„  JWTAuthenticationProcessingFilter๋กœ ๋„˜์–ด๊ฐ€์„œ JWT ํ† ํฐ์— ๋Œ€ํ•œ ๊ฒ€์ฆ์„ ํ•œ๋‹ค.

JWTAuthenticationProcessingFilter

/**
 * OncePerRequestFilter: ๋ชจ๋“  ์„œ๋ธ”๋ฆฟ ์ปจํ…Œ์ด๋„ˆ์—์„œ ์š”์ฒญ ๋””์ŠคํŒจ์น˜๋‹น ๋‹จ์ผ ์‹คํ–‰์„ ๋ณด์žฅํ•˜๋Š” ๊ฒƒ์„ ๋ชฉํ‘œ๋กœ ํ•˜๋Š” ํ•„ํ„ฐ ๊ธฐ๋ณธ ํด๋ž˜์Šค
 */
@RequiredArgsConstructor
public class JwtAuthenticationProcessingFilter extends OncePerRequestFilter {
    private final JwtService jwtService;
    private final UserRepository userRepository;

    private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();

    private static final String NO_CHECK_URL = "/login";

    /**
     * 1. RefreshToken์ด ์˜ค๋Š” ๊ฒฝ์šฐ -> ์œ ํšจํ•˜๋ฉด AccessToken ์žฌ๋ฐœ๊ธ‰ํ›„, ํ•„ํ„ฐ ์ง„ํ–‰ X
     * 2. RefreshToken์€ ์—†๊ณ  AccessToken๋งŒ ์žˆ๋Š” ๊ฒฝ์šฐ -> ์œ ์ €์ •๋ณด ์ €์žฅ ํ›„ ํ•„ํ„ฐ ์ง„ํ–‰, RefreshToken ์žฌ๋ฐœ๊ธ‰X
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        System.out.println("Received request URI: " + request.getRequestURI());
        if (request.getRequestURI().equals(NO_CHECK_URL)) {
            filterChain.doFilter(request, response);
            return;
        }

        String refreshToken = jwtService
                .extractRefreshToken(request)
                .filter(jwtService::isTokenValid)
                .orElse(null);

        if (refreshToken != null) { // Request ์š”์ฒญ์œผ๋กœ ์˜จ ์‚ฌ์šฉ์ž๊ฐ€ refreshToken์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๋ฉด AccessToken์„ ์žฌ๋ฐœ๊ธ‰
            checkRefreshTokenAndReIssueAccessToken(response, refreshToken);
            return;
        }

        checkAccessTokenAndAuthentication(request, response, filterChain);
    }

    private void checkAccessTokenAndAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        jwtService.extractAccessToken(request).filter(jwtService::isTokenValid).ifPresent(

                accessToken -> jwtService.extractEmail(accessToken).ifPresent(

                        email -> userRepository.findByEmail(email).ifPresent(

                                user -> saveAuthentication(user)
                        )
                )
        );

        filterChain.doFilter(request,response);
    }



    private void saveAuthentication(User users) {
        UserDetails user = org.springframework.security.core.userdetails.User.builder()
                .username(users.getEmail())
                .password(users.getPassword())
                .roles(users.getRole().name())
                .build();

        Authentication authentication = new UsernamePasswordAuthenticationToken(user, null, authoritiesMapper.mapAuthorities(user.getAuthorities()));


        SecurityContext context = SecurityContextHolder.createEmptyContext();//5
        context.setAuthentication(authentication);
        SecurityContextHolder.setContext(context);
    }

    private void checkRefreshTokenAndReIssueAccessToken(HttpServletResponse response, String refreshToken) {
        userRepository.findByRefreshToken(refreshToken).ifPresent(
                user -> jwtService.sendAccessToken(response, jwtService.createAccessToken(user.getEmail()))
        );


    }
}

์šฐ์„  ๋ฌด์‹ํ•˜๊ฒŒ ๋””๋ฒ„๊น…์„ ์ฐ์–ด๋ดค๋‹ค.

๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ ์ง€์ ์„ ์ฐพ์•˜๋‹ค. ๋ถ„๋ช… ํ…Œ์ŠคํŠธ์ฝ”๋“œ์—์„œ ์˜๋„ํ•œ ๋ฐ”์— ๋”ฐ๋ฅด๋ฉด RefreshToken์ด ์œ ํšจํ•œ ์ƒํƒœ๋กœ ๋„˜์–ด๊ฐ€๊ฒŒ ๋˜๋Š”๋ฐ RefreshToken์„ ์ถ”์ถœํ•˜๋Š” ๊ณผ์ •์—์„œ null์ด ๋‚˜์™”๋‹ค.

RefreshToken ์ถ”์ถœ

	@Override
    public Optional<String> extractRefreshToken(HttpServletRequest request) {
        return Optional.ofNullable(request.getHeader(refreshHeader)).filter(
                refreshToken -> refreshToken.startsWith(BEARER)
        ).map(refreshToken -> refreshToken.replace(BEARER, ""));
    }

์ด์ œ ์ด ์ฝ”๋“œ๋ฅผ ๋””๋ฒ„๊น… ํ•ด๋ณด๋ฉด ์˜ค๋ฅ˜์˜ ์›์ธ์„ ์ฐพ์„ ์ˆ˜ ์žˆ์„๊ฑฐ ๊ฐ™๋‹ค.


์Œ... ์ด์ œ ๋‹ต์€ 2๊ฐœ ์ค‘ ํ•˜๋‚˜์ด๋‹ค.
1. refreshToken -> refreshToken.startsWith(BEARER) ๊ณผ์ •์— ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค.
2. request.getHeader(refreshHeader)์—์„œ ๊ฐ’์„ ๊ฐ€์ ธ์˜ค์ง€ ๋ชปํ•œ๋‹ค.

1๋ฒˆ ๋จผ์ € ํ™•์ธํ•ด๋ณด์ž.

์•„.. ๋ณด์ด๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ์ƒ์ˆ˜๋Š” Constants๋ผ๋Š” ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด ๋”ฐ๋กœ ๋ณด๊ด€ํ•˜๋Š”๋ฐ BEARER์—์„œ ์˜ค๋ฅ˜๊ฐ€ ์žˆ๋Š” ๊ฒƒ์„ ํ™•์ผํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ฝค๋‚˜ ํ—ˆ๋ฌดํ•˜์ง€๋งŒ ์‚ฌ์†Œํ•œ ์‹ค์ˆ˜ ๋•Œ๋ฌธ์— ํฐ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธฐ๋Š” ๊ฒƒ์„ ์ž˜ ์•Œ๊ณ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ผ์ฐ ๋ฐœ๊ฒฌํ•ด์„œ ๋‹คํ–‰์ด๋ผ๊ณ  ์ƒ๊ฐํ•œ๋‹ค.

์˜ค๋ฅ˜๋ฅผ ์ˆ˜์ •ํ•˜๊ณ  ๋‹ค์‹œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•ด๋ณด์ž.


๋ฌธ์ œ ํ•ด๊ฒฐ


์†”์งํžˆ ๋‹จ์ˆœํžˆ ๋„์–ด์“ฐ๊ธฐ๋ฅผ ์ž˜๋ชปํ•œ ์‚ฌ์†Œํ•œ ์‹ค์ˆ˜์ธ๋ฐ ๊ตณ์ด ๊ธ€๋กœ ์ •๋ฆฌํ•ด์•ผ ๋˜๋‚˜? ๋ผ๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ๋‹ค.

ํ•˜์ง€๋งŒ ๋ชจ๋“  ๋ฌธ์ œ๋Š” ์‚ฌ์†Œํ•œ ๋ฌธ์ œ์—์„œ ์‹œ์ž‘ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ฒฝ๊ฐ์‹ฌ์„ ๊ฐ€์ง€์ž๋Š” ์˜๋ฏธ์™€ ์ด๋˜ํ•œ ์†Œ์ค‘ํ•œ ๊ฒฝํ—˜์ด๋ผ๊ณ  ์ƒ๊ฐํ•ด ๊ธ€๋กœ ์ •๋ฆฌํ•ด๋ดค๋‹ค.

0๊ฐœ์˜ ๋Œ“๊ธ€