-
Spring boot 쿠키에 JWT 토큰값 세팅졸업과제/BackEnd 2023. 2. 27. 13:09
Swagger 테스트할 때는 Swagger에서 Header에 특정 값을 세팅해 주는 기능을 이용해 발급된 jwt 토큰을 헤더에 세팅해서
로그인이 유지된 상태를 재현할수 있는데 막상 실제로 웹페이지에서 구현하고자 하니까 게시물 작성이나 홈페이지에 들어갈 때 요청 헤더에 토큰을 설정하는 방법을 알 수가 없었다..
그래서 수많은 구글링을 통해 Cookie에 token값을 세팅해서 구현하는 방식으로 도전해보았다!
Settings
public class WebConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .exposedHeaders("X-AUTH-TOKEN") .allowCredentials(true) .allowedOrigins("http://localhost:8081"); } }
우선 CORS 설정을 해줘야 한다고 해서 CORS가 무엇인지 궁금해 공부를 좀 해보았다.
https://hannut91.github.io/blogs/infra/cors
CORS란 무엇인가? – Yunseok's Dev Blog
hannut91.github.io
브라우저는 보안적인 이유로 cross-origin http 요청들을 제한한다고 한다.
cross-origin 요청이란 URL 구조에서 protocol, Host, Post 중 하나라도 다르게 된다면 상호 간에 데이터를 서로 요청할 수
없도록 하는 브라우저의 정책이라고 볼 수 있다.
cross-origin 요청을 제한하면 CSRF, XSS 등의 공격을 방어할 수는 있지만 현실적으로 외부 리소스를 참고하는 것은 필요하기 때문에 외부 리소스를 사용하기 위한 예외 조항이 CORS 설정인 것이다.
즉 로그인을 하고 jwt 토큰을 발행하고 난 뒤 발행한 jwt 토큰이 로그인 응답헤더에 실려서 전송이 되지 않았던 이유가
브라우저의 CORS 정책 때문에 토큰 값이 헤더에 실려서 전송이 되지 않았던 것이다.
. addMapping 메서드를 통해 CORS 정책을 적용할 URL 패턴을 설정하고. exposedHeaders를 이용해 반환하고 싶은
헤더값을 추가시켜 준다.. allowCredentials(true)을 이용해 응답 헤더로 전달된 token값을 브라우저가 사용할 수 있도록 해주고. allowedOrigins을 통해 CORS 정책을 허용할 출처를 적어준다.
Controller
@PostMapping(value = "/login") public String signUp(@Valid @ModelAttribute SignInRequestDto signInRequestDto, HttpServletResponse response) { logger.info("[signIn] 로그인을 시도하고 있습니다. id : {}, pw : ****", signInRequestDto.getId()); SignInResultDto signInResultDto = signService.signIn(signInRequestDto); String token = signInResultDto.getToken(); response.setHeader("X-AUTH-TOKEN",token); Cookie cookie = new Cookie("X-AUTH-TOKEN", token); cookie.setPath("/"); cookie.setSecure(true); cookie.setHttpOnly(true); response.addCookie(cookie); if(signInResultDto.getCode()==0){ logger.info("[signIn] 정상적으로 로그인되었습니다. id : {}, token : {}", signInRequestDto.getId(), signInResultDto.getToken()); } return "redirect:/"; } }
POST 매핑으로 사용자의 로그인 정보를 담은 form이 날아오면 동작하는 Controller이다.
SignService의 signIn 메서드를 호출해 아이디와 비밀번호를 DB에서 비교하고 일치한다면 DTO에 TOKEN값을 담하서 리턴하게 된다.
DTO에 담겨 있는 token값을 HttpServletResponse header에 세팅해 주고. 새로운 쿠키를 발행해 응답 헤더에 추가해 준다.
token 인증 및 추출
@Override protected void doFilterInternal(HttpServletRequest servletRequest, HttpServletResponse servletResponse, FilterChain filterChain) throws ServletException, IOException { String token = jwtTokenProvider.resolveToken(servletRequest); LOGGER.info("[doFilterInternal] token 값 추출 완료. token : {}", token); LOGGER.info("[doFilterInternal] token 값 유효성 체크 시작"); if(token !=null && jwtTokenProvider.validateToken(token)){ Authentication authentication = jwtTokenProvider.getAuthentication(token); SecurityContextHolder.getContext().setAuthentication(authentication); LOGGER.info("[doFilterInternal] token 값 유효성 체크 완료"); } filterChain.doFilter(servletRequest, servletResponse); }
public String resolveToken(HttpServletRequest request) { logger.info("[resolveToken] HTTP 헤더 cookie 에서 Token 값 추출"); Cookie[] cookies = request.getCookies(); String token = new String(); for (Cookie cookie : cookies) { if(cookie.getName().equals("X-AUTH-TOKEN")) { token = cookie.getValue(); logger.info("[resolveToken] cookie 에서 Token 값 추출 완료:{}",token); } } return token; }
첫 번째 코드는
요청이 오면 요청을 중간에 가로채서 jwt 토큰을 인증해 주는 서블릿 필터인데 OncePerRequest를 오버라이딩한 코드이다.
ServletRequest에서 token값을 추출해 토큰의 유효성을 확인하고 유효하다면 SecurityContextHolder에 등록해서
사용자 인증을 할 수 있도록 해준다.
ServletRequest에 쿠키에 토큰값이 담겨있도록 Controller에서 설정해 주었으므로 Request의 Cookie에서 토큰값을
추출할 수 있도록 해준다. 반복문을 돌려준 이유는 cookie에 설정되어 있는 값들이 여러 개 일수 있고 토큰 값이 아직 설정 안 되어있기 때문에(로그인 전) cookie를 반복문으로 돌면서 cookie의 이름이 X-AUTH-TOKEN과 같다면 토큰값이므로
해당 토큰갑을 받아서 리턴할 수 있도록 해주었다.
TEST
로그인이 완료되고 token이 발행됨. 로그인 이후 요청헤더의 모습 로그인이 성공적으로 수행되고 난 뒤 요청헤더의 모습을 확인하면 맨 아래에 cookie값에 X-AUTH-TOKEN이름으로 token 저장되어 있는 것을 확인할 수 있다.
ServletFilter가 동작하는 모습 서블릿 필터가 동작하면서 요청헤더의 cookie에서 토큰값을 추출해 온 뒤 현재 로그인한 유저의 정보를
추출한 토큰값을 기반으로 알아내는 모습을 확인할 수 있다.
4일 동안 억지로 억지로 로그인 post이후 응답헤더에 추가도 해보려 하고 서블릿 필터에 추가도 해볼려하고
난리를 쳐보았지만 전혀 되지 않다가 cookie에 한번 설정해야 되나 라는 생각을 하고 난 뒤
문제가 1시간 만에 성공하였다. 사실 처음에 도전할 때 cookie에 설정해야 하나라는 생각을 하긴 했었는대
괜한 고집을 부리며 하지 않았다가 고집을 꺾고 cookie에 설정을 했는 데 성공하였다.
이럴 때 보면 고집이 없는 거 같다가도 황소고집 기질이 있는 나 자신을 발견한다!
나의 생각이 틀렸을 수도 있겠다는 생각을 가지고 항상 배워나가는 내가 되야겠다(자아 성찰!)
'졸업과제 > BackEnd' 카테고리의 다른 글
Thymeleaf를 이용한 게시물 댓글 출력 (0) 2023.03.10 Spring data JPA, Thymeleaf를 이용한 댓글 등록 (0) 2023.03.08 Spring Data Jpa의 Pageable을 이용한 게시물 페이징 처리 (0) 2023.02.20 Spring Data Jpa를 이용한 게시물 댓글 삭제 (0) 2023.02.19 Spring Data JPA를 이용한 게시물 댓글 작성 기능 구현 (0) 2023.02.18