티스토리 뷰

이번 포스팅에서는 토큰을 발급하고, 가져오는 방법을 다르게 해보겠다. 모든 코드는 깃허브에 있으니 참고해주세요.

 

 

프론트가 좋아하는 방식

프론트가 좋아한다는 방식이 무슨 말일까 ? 기존 토큰 주고받기 방식이라고 한다면, 

 

  • 클라이언트가 로그인을 한다.
  • 서버가 토큰을 발급해준다.
  • 클라이언트는 이제 요청을 보낼 때 마다 토큰을 헤더에 넣어서 보낸다.
  • 서버는 요청에 담겨있는 헤더를 찾아서 검사한다.
  • 만족하는 응답을 한다. 

 

정도로 되겠다. 여기서 중요하게 볼 점은 로그인 이후 요청을 보낼 때 프론트에서 직접 토큰을 헤더에 넣어주는 작업을 해야 한다. 하지만 만약 직접 헤더에 넣어주는 작업을 해주지 않아도 요청할 때 자동으로 토큰을 넣어서 보내준다면 어떨까 ? 프론트가 해야할 일이 줄어드니 좋아할 것이다. 

 

 

토큰을 쿠키에 담는 방식

자동으로 토큰을 넣어준다고 했는데, 누가 어떻게 넣어줄까 ? 바로 브라우저가 해준다. 백엔드에서는 원래 방식인 토큰에다가 쿠키를 감싸서 보내면, 쿠키를 받은 브라우저는 자동으로 요청에다가 쿠키를 포함시킨다. 즉, 토큰을 쿠키에 담기만 하면 된다. 아래는 기존 로그인 서비스 로직이다. 

 

@Transactional
public String login(MemberLoginRequestDto requestDto) {
    Member member = memberRepository.findByEmail(requestDto.getEmail())
            .orElseThrow(() -> new IllegalArgumentException("가입되지 않은 이메일입니다."));

    if(!passwordEncoder.matches(requestDto.getPassword(), member.getPassword())) {
        throw new IllegalArgumentException("비밀번호가 일치하지 않습니다.");
    }
    return jwtTokenProvider.createAccessToken(member.getEmail(), member.getRole().name());
}

 

토큰을 발급하고 반환하는 것은 똑같다. 그럼 이 메서드를 사용하는 컨트롤러를 보자. 아래 컨트롤러 역시 기존 방식이다. 

여기서 우리는 발급한 토큰을 클라이언트한테 그대로 주지말고 쿠키에 감싸서 보내야 한다.

 

@PostMapping("/login")
    public String login(@RequestBody MemberLoginRequestDto requestDto) {
        return memberService.login(requestDto);
    }

 

 

쿠키 구현

아래와 같이 쿠키를 생성한다. 쿠키는 key와 value로 이루어져 있고, 나중에 요청에서 쿠키를 찾을 때 key를 이용해서 찾는다. key는 마음대로 정하고 value에 발급한 쿠키를 넣는다. 

 

@PostMapping("/login")
    public void login(@RequestBody MemberLoginRequestDto requestDto) {
        String token = memberService.login(requestDto);
        Cookie cookie = new Cookie("ACCESS_TOKEN", response.getAccessToken());
    }

 

그리고 파라미터에 HttpServletResponse 인터페이스를 추가한다. 추가하는 이유는 아래에 나와있다. 

 

@PostMapping("/login")
    public void login(@RequestBody MemberLoginRequestDto requestDto, HttpServletResponse res) {
        String token = memberService.login(requestDto);
        Cookie cookie = new Cookie("ACCESS_TOKEN", response.getAccessToken());
    }

 

그리고 다음과 같이 속성들을 설정한다. 

 

cookie.setHttpOnly(true);
cookie.setPath("/");
res.addCookie(cookie);

 

setHttpOnly는 브라우저에서 쿠키에 접근 여부다. true라면 브라우저에서 쿠키에 접근하지 못하게 되고, false라면 접근이 가능하다. 사용자들이 브라우저에서 쿠키로 접근하지 못하게 true로 설정한다.

setPath는 쿠키를 특정 url로 전송할지 선택할 때 사용된다. /는 모든 url에 전송한다는 뜻이다. 

addCookie는 브라우저에 우리가 만든 쿠키를 적용시킨다. res.addCookie를 함으로써 브라우저에 적용된 쿠키가 자동으로 요청에 포함될 수 있는 것이다. 

 

최종코드는 다음과 같다. 

 

@PostMapping("/login")
    public void login(@RequestBody MemberLoginRequestDto requestDto, HttpServletResponse res) {
        String token = memberService.login(requestDto);
        Cookie cookie = new Cookie("ACCESS_TOKEN", response.getAccessToken());
        cookie.setHttpOnly(false);
        cookie.setPath("/");
        res.addCookie(cookie);
    }

 

 

가져오기

가져오는 방법은 간단하다. 요청에서 헤더를 찾아서 값을 반환한 것처럼 해당쿠키를 찾아서 값을 반환해주면 된다. 코드는 다음과 같다. 

 

public String resolveAccessToken(HttpServletRequest request) {
    Cookie[] cookies = request.getCookies();

    if(cookies != null) {
        for(Cookie c : cookies) {
            if(c.getName().equals(HEADER_ACCESS_TOKEN)) {
                return c.getValue();
            }
        }
    }
    else {
        throw new IllegalArgumentException("쿠키가 존재하지 않습니다.");
    }
    return null;
}

 

 

표준 CORS 요청

표준 CORS 요청에 따르면 기본적으로 쿠키를 설정하거나 주고받을 수 없다. 그래서 수동으로 쿠키를 CORS 요청에 넣어주어야 하는데, 

이 역할을 하는 것이 바로 withCredentials 옵션이다. 참고로 프론트 백엔드 둘 다 해줘야 하는 옵션이다. 

 

백엔드

스프링 시큐리티를 쓰고 있다면 아래와 같이 설정할 수 있다. 

 

@Bean
public CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration configuration = new CorsConfiguration();

    configuration.setAllowCredentials(true);

    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", configuration);
    return source;
}

 

만약 스프링 시큐리티를 쓰지 않는다면 아래와 같이 설정할 수 있다. 

 

public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowCredentials(true);
    }
}

 

프론트

프론트에서는 axios에서 아래와 같이 설정할 수 있다. 

 

axios
      .post(www.test.com, {
        test: test
      },
      {
      withCredentials : true
      })
      .then()

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday