spring boot 에서 jwt 토큰을 발급 받은 뒤 어떤식으로 처리하면 좋을까에 대한 생각을 적은 글
주의 : 이론적 설명을 제외하고 어떻게 사용할까에 대한 담은 글입니다. 참고용도로만 적용해주시고 실무에서는 꼭 보안규칙을 따져가면서 꼼꼼히 작성하셔야합니다!
jwt 토큰은 user 정보가 들어있기 때문에 매우 신중히 고려해서 다뤄줘야됨
구글링으로 찾은 내용으로,, jwt 를 저장하는 방식으로는
1. 로컬스토리지 저장
2. 쿠키에 저장
+a. httpSession 활용
간략히 이렇게 나타나 있는데, 각자의 장단점 및 이론에 대해서는 추후 정리하고 일단 어떻게 사용하는지에 대해서 설명함.
보통 jwt 값이 백엔드에서는 위와 같이 리턴되게 될텐데 (jwt 를 string 방식으로 반환해준 것)
로컬 스토리지를 사용할 경우 프론트엔드에서 처리를 하게 됨
1. 로컬 스토리지 + JWT
loginTest.js
async function loginTest() {
let response = await fetch("/api/v1/users/login", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
userId: document.getElementById("username1").value,
password: document.getElementById("password1").value
})
})
// fetch 비동기 방식으로 해당 body값에 대해서 api 접근
if(response.ok){
let json = await response.json();
document.getElementById("area_login").innerHTML = json.resultCode;
localStorage.setItem("jwt",'Bearer ' + json.result.accessToken);
console.log(localStorage.getItem("jwt"));
}else{
let json = await response.text();
document.getElementById("area_login").innerHTML = json;
}
// 해당 리스폰스 값에 따른 출력
;
}
위 소스에서 중요하게 볼 점은 백엔드에서 나온 Response를 어떻게 처리하냐 이말이다.
localStorage.setItem("jwt",'Bearer ' + json.result.accessToken);
fetch 비동기 메소드를 통해 나온 Response 값(promise 타입)을 > Json 형식으로 전환시켜줘서 해당 데이터에 접근 할 수 있다.
결과 값으로 나온 데이터를 위 소스를 통해 setItem 으로 jwt 토큰을 localstorage에 저장 할 수 있다.
F12 개발자도구 - Application - Storage - Local Storage 에서 확인 가능!
사용은 어떻게?
로그인의 유무와 Security 설정 후 Authentication 의 헤더에 적용할 수 있는 방법
간단히 게시글 작성하는 비동기식 메소드 에서
async function crewMake() {
let response = await fetch("/api/v1/crews", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": localStorage.getItem('jwt'),
"Authentication": localStorage.getItem('jwt')
},
body: JSON.stringify({
content: document.getElementById("content").value,
title: document.getElementById("title").value
})
})
if(response.ok){
let json = await response.json()
console.log(json);
document.getElementById("area_crew").innerHTML = json.result.crewId + " 모임생성 " + json.result.message;
}else{
let json = await response.text();
document.getElementById("area_crew").innerHTML = json;
}
}
fetch 헤더부분을 참고해보면,
"Authorization": localStorage.getItem('jwt'),
"Authentication": localStorage.getItem('jwt')
getItem 으로 접근해서 데이터를 헤더에 담아 보낼 수 있다.
2. 쿠키 + jwt
백엔드단에서 쿠키의 방식을 설정해줘서 보낼 수 있다.
간단한 로그인 예제
로그인이 성공하면 cookie를 담아 view 를 반환해준다.
@PostMapping("/view/v1/signin")
public String login(UserLoginRequest userLoginRequest, HttpServletResponse response) throws UnsupportedEncodingException {
UserLoginResponse tokens = userService.login(userLoginRequest);
//cookie 설정은 스페이스가 안되기 때문에 Bearer 앞에 +를 붙인다. Security Filter에서 + -> " " 로 치환할 것이다.
Cookie cookie = new Cookie("jwt", "Bearer+"+tokens.getAccessToken());
cookie.setPath("/");
cookie.setSecure(true);
cookie.setHttpOnly(true);
cookie.setMaxAge(60 * 25); //초단위 25분설정
response.addCookie(cookie);
return "redirect:/view/v1/crews";
}
이슈1. cookie 설정에서 스페이스가 안되어 저장하는 방식을 다음과 같이 설정해줬음.
이슈2. setPath를 안해줘서 다른 api 호출간 쿠키가 넘어가지 않는 현상을 확인
Cookie cookie = new Cookie("jwt", "Bearer+"+tokens.getAccessToken());
//쿠키를 만듬
cookie.setPath("/");
//쿠키가 적용되는 범위 및 경로지정 (/ 모든경로)
cookie.setSecure(true);
//쿠키 ssl 통신시에만 쿠키를 넘겨주도록 설정
cookie.setHttpOnly(true);
//설정해주면 브라우저에서 쿠키에 접근하지 못하게됨 ㅠㅠ
cookie.setMaxAge(60 * 25); //초단위 25분설정
response.addCookie(cookie);
//response 에 쿠키를 담아서 보냄
view 화면에서 로그인을 성공하게 되면, 아래와 같이 쿠키 단에서 jwt 토큰값이 잘 저장되어 있는 것을 확인 할 수 있다.
사용은 어떻게?
다른 url 을 호출하게 되면 쿠키에 있는 데이터가 헤더에 담겨 자동적으로 넘어간다.
백엔드에서는 request의 header 부분에 jwt 토큰값에 접근해서 filter 처리를 해주면 됨.
httponly true 옵션 쿠키를 비동기 방식에서 처리
let response = await fetch(url,{
method: "GET",
headers: {
"Content-Type": "application/json"
},
credentials: "same-origin",
credentials: 'include'
})
header 부분에 credentials 옵션을 적용하면 쿠키값 전체가 넘어간다.
+ 쿠키는 원래 js 에서 접근 가능하다. 단) httponly 속성이 아닐 경우
3. a+ 세션에 저장
간단히 로그인 로직을 거친 뒤 jwt 결과값을 session에 저장 후 view 로 반환해주는 컨트롤러
@PostMapping("/login")
public String login(@Valid UserLoginRequest userLoginRequest , HttpServletRequest httpServletRequest, Model model) {
String jwtToken = "";
jwtToken = userService.login(userLoginRequest).getJwt();
httpServletRequest.getSession().invalidate();
//세션의 데이터를 삭제함
//초기화, 로그아웃할때 활용
HttpSession session = httpServletRequest.getSession(true);
//로그인 성공처리
//새로운 신규 세션을 생성
session.setAttribute("jwt", "Bearer " + jwtToken);
//세션에 jwt 관련 정보 저장
session.setMaxInactiveInterval(1800);
//유효시간 설정
return "redirect:/view/v1/posts/list";
}
서블릿을 통해 HttpSession을 생성하면 다음과 같은 쿠키를 생성
쿠키 이름이 JSESSIONID이고, 값은 추정 불가능한 랜덤 값이다.
사용은 어떻게?
세션에 저장한 데이터에 대해서는
protected void method(HttpServletRequest request){
header = request.getSession().getAttribute("jwt").toString();
}
getSession() 메소드를 활용해서 데이터의 유무를 찾음
간략하게 스프링부트에서 jwt 토큰 저장 방식과 활용 방법에 대해서 확인했는데,, 아직 더 많은 산이 남아 있는거 같다.
쿠키와 로컬스토리지 차이에 대해서는 서로 문제가 있다고 하니 이론적인 부분에 대해서는 추후 공부예정이며, redis 를 통한 refresh 토큰 방식을 채택한다면 두 단점을 조금이나마 커버할 수 있는 팁을 남기고 갑니다. 감사합니다.
'2023년 > 멋쟁이사자처럼 팀프로젝트' 카테고리의 다른 글
[팀프로젝트] datepicker / Timepicker 적용하기 (2) | 2023.02.07 |
---|---|
[팀프로젝트] STOMP 활용한 프로젝트 채팅방 구현 (0) | 2023.02.02 |
[팀프로젝트] 비동기 방식 통신 + 스프링부트 RestController 활용한 웹 페이지 (0) | 2023.01.26 |
[팀프로젝트] 깃허브 워크플로우 (2) | 2023.01.20 |
[팀프로젝트] Springboot 사용하면서 UI 화면은 어떻게 처리하면 좋을까? (1) | 2023.01.19 |