728x90
학습을 하던중 암호화 되어있는 jwt토큰을 헤더에 노출하게 배웠는데,
이건 아무래도 위험하다고 생각되어 알아보던 중, ( 어세스토큰 ? 리프레시 토큰 ? )
어세스 토큰을 빠르게 만료 시키고, 리프레쉬토큰으로 만료된 어세스토큰을 체크해서,
서버 디비에 저장되어있는 리프레쉬토큰과 비교 후 , 재발급 해주는 과정을 거치면 된다.
그러나 리프레쉬토큰 마저 탈취, 조작 당하게 되면 엄청나게 큰일 !!
그래서 리프레쉬 토큰의 보안은 철저하게 해주어야 한다고 판단되어 쿠키에 저장 하는 방법을 선택 했다.
(쿠키는 여러가지 보안 옵션을 할 수 있기 때문에)
우선 어세스 토큰과 리프레쉬 토큰을 만들어주자 !
만드는법은 jwt 토큰을 이용하여
기간이 짧은 토큰1(어세스토큰) - [사용자의 정보 저장]
기간이 긴~ 토큰2(리프레쉬토큰) - [간단한 사용자 식별정보 와 리프레쉬]
이 두가지를 만든다
public String createAccessToken(String userName, UserRoleEnum role) {
Date date = new Date();
return BEARER_PREFIX + Jwts.builder()
.setSubject(userName) //토큰 사용자의 이름
.claim(AUTHORIZATION_KEY, role) //토큰 사용자의 권한
.setExpiration(new Date(System.currentTimeMillis() + 30000)) // 토큰의 만료시간 설정
.setIssuedAt(date)
.signWith(key, signatureAlgorithm) //키와 알고리즘을 이용하여 암호화
.compact();
}
public String createReFreshToken(String userName) {
Date date = new Date();
return Jwts.builder()
.setSubject(userName)
.setExpiration(new Date(System.currentTimeMillis() + 60L * 60L * 1000L))
.setIssuedAt(date)
.signWith(key, signatureAlgorithm)
.compact();
}
어세스 토큰은 테스를 위해 굉장히 짧은 시간을 주었다 (30초)
여기서 만들어준 어세스 토큰을 헤드에 저장을 해준다.
response.addHeader(JwtUtil.AUTHORIZATION_HEADER, jwtUtil.createAccessToken(user.getUsername(), user.getRole()));
리프레쉬토큰을 담을 쿠키 를 만들어 준다.
public Cookie createCookie(String userName) {
String cookieName = "refreshtoken";
String cookieValue = createReFreshToken(userName); // 쿠키벨류엔 글자제한이 이써, 벨류로 만들어담아준다.
Cookie cookie = new Cookie(cookieName, cookieValue);
// 쿠키 속성 설정
cookie.setHttpOnly(true); //httponly 옵션 설정
cookie.setSecure(true); //https 옵션 설정
cookie.setPath("/"); // 모든 곳에서 쿠키열람이 가능하도록 설정
cookie.setMaxAge(60 * 60 * 24); //쿠키 만료시간 설정
return cookie;
RefreshToken refreshToken = RefreshToken.builder() //리프레쉬토큰 객체를 만든다.
.userName(username)
.refreshToken(cookie.getValue())
.build();
refreshTokenRepository.save(refreshToken); //리프레쉬토큰을 디비에 저장.
// 쿠키 전송
response.addCookie(cookie); //만들어진 쿠키를 쿠키에 저장해준다.
로그인시 쿠키에 담은 리프레쉬토큰과
헤드에 담은 어세스 토큰이 잘 뜨는걸 확인 할 수 있다. (post맨 활용)
그리고 만료된 토큰인지 검증 해주는 곳은 필터 쪽에서 설정 해 주도록 하였다.
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String token = jwtUtil.resolveToken(request);
if (token != null) {
if (!jwtUtil.validateToken(token)) { //쿠키가 만료되면 false가되어 밑에 코드가 실행됌
Cookie rc[] = request.getCookies(); //request에서 쿠키를 받아온다.
String refreshToken = "";
for (Cookie cookie : rc) { //벨류에 넣었던 리프레쉬토큰을 꺼내서 넣어준다.
refreshToken = cookie.getValue();
}
if (request.getCookies() != null && jwtUtil.getRefreshTokenEquals(refreshToken)) { //디비의 리프레시 토큰의 유효성 검사를 해준다.
Claims info = jwtUtil.getUserInfoFromToken(refreshToken); //리프레쉬토큰의 정보를 꺼내온다.
setAuthentication(info.getSubject()); //리프레쉬토큰의 정보를 인증객체로 바꾸어준다.
User authUser = jwtUtil.Authenticateduser(info.getSubject());
response.addHeader(JwtUtil.AUTHORIZATION_HEADER, jwtUtil.createAccessToken(info.getSubject(), authUser.getRole())); //다시 어세스토큰을 발급해준다.
}
} else {
Claims info = jwtUtil.getUserInfoFromToken(token);
setAuthentication(info.getSubject());
}
}
filterChain.doFilter(request, response);
}
이렇게 필터에서 토큰을 검증해서 어세스토큰을 재발급 해주는 기능을 구현 하였다.
728x90
'개-발 > Java + Spring + Kotlin' 카테고리의 다른 글
[Spring] 트랜잭션 @transational 어노테이션 (0) | 2023.03.03 |
---|---|
[Spring] Custom Query 사용 전 ! (0) | 2023.01.27 |
[Spring] Cookie 란 무엇 '인가' ? (0) | 2023.01.10 |
[Spring] Access Token 과 Refresh Token (0) | 2023.01.10 |
[Spring] @PathVariable 어노테이션 (0) | 2023.01.01 |