[ STUDY ]/Spring Boot

[ JWT를 활용한 Spring Security ] Access Token, Refresh Token 발급 및 저장

김강니 2024. 9. 25. 03:15

1.  JWT 토큰 발급 메소드 수정

public Map<String, String> getToken(String email) {
        User user = userRepository.findByEmail(email)
                .orElseThrow(() -> new IllegalArgumentException("User not found"));

        String role = user.getUserType().toString();
        Long userId = user.getUserId();

        String refreshToken = Jwts.builder()
                        .setSubject(email)
                        .claim("role", role)
                        .claim("userId", userId)
                        .setExpiration(new Date(System.currentTimeMillis() + REFRESH_TOKEN_EXPIRE_TIME))
                        .signWith(key)
                        .compact();

        String accessToken = Jwts.builder()
                        .setSubject(email)
                        .claim("role", role)
                        .claim("userId", userId)
                        .setExpiration(new Date(System.currentTimeMillis() + ACCESS_TOKEN_EXPIRE_TIME))
                        .signWith(key)
                        .compact();

        Map<String, String> tokens = new HashMap<>();
        tokens.put("access_token", accessToken);
        tokens.put("refresh_token", refreshToken);

        return tokens;
    }

 

accessToken과 refreshToken 발급!

 

2.  refreshToken DB에 저장(추후에 redis에 저장할 계획)

// RefreshToken DB에 저장
public void saveRefreshToken(String email, String refreshToken) {
    User user = userRepository.findByEmail(email)
            .orElseThrow(() -> new IllegalArgumentException("User not found"));

    User updateUser = user.toBuilder().refreshToken(refreshToken).build();
    userRepository.save(updateUser);
}

 

toBuilder를 사용하여 객체의 불변성을 유지하며 refreshToken 값을 업데이트!

이 방식은 setter를 사용하지 않고 객체를 업데이트할 수 있는 방법이다.

 

⬇️⬇️ setter를 사용하지 않고 tobuilder()를 사용한 이유 ⬇️⬇️

더보기

객체의 불변성을 유지하기 위해서 setter를 사용하지 않고 toBuilder를 사용했다.

 

객체에 setter를 사용하면 필드 값을 언제든지 변경할 수 있기 때문에, 객체의 상태가 예기치 않게 변할 수 있다.

특히 동시성 문제버그를 유발할 가능성이 높아짐.

불변 객체는 한 번 생성된 후 그 상태가 변하지 않기 때문에, 더 안전하고 안정적인 코드 작성이 가능하다.

 

toBuilder를 사용하면 기존 객체의 상태는 그대로 유지하면서, 일부 값만 변경된 새로운 객체를 생성할 수 있다.

이렇게 하면 객체의 불변성을 보장하면서도 필요한 값을 유연하게 변경할 수 있는 장점이 있다.

 

3.  클라이언트에 JWT 응답(access, refresh)

return ResponseEntity.ok(jwts);

 

 

4. 클라이언트에서 토큰 처리(cookie, localStorage 사용)

//로그인 요청
const Login = async () => {
try {
  const response = await api.post("/api/login", {
    email,
    password,
  });

  if (response.status===200) {
    const {access_token, refresh_token} = response.data;

    if(access_token&&refresh_token){
      const user = saveToken(access_token); // accessToken은 localStorage에 저장
      document.cookie = `refresh_token=${refresh_token}; HttpOnly`;

      if (user) {
        // 구독 및 초기화 작업 수행
        dispatch(setUser(user));
        dispatch(subscribeToSSE(user.userId, access_token));
        dispatch(initAlarm(user.userId, access_token));
      }
    }
    ···

 

access_token은 기존에 저장한 것처럼 localStorage에 저장하고, refreshToken은 보안문제가 있을 수 있으니 쿠키에 저장한다.

 

-> 이거 완전 틀렸음요....고친 부분은 다시 정리해서 올릴 예정...

 

document.cookie = `refresh_token=${refresh_token}; HttpOnly`;

 document.cookie
  document.cookie는 브라우저에서 쿠키를 설정할 수 있는 API.
  이 속성을 사용하여 쿠키를 추가하거나 수정한다.

* 쿠키는 사용자의 브라우저에 저장되는 작은 데이터로, 주로 세션 유지나 인증 정보를 관리하는 데 사용

 refresh_token=${refresh_token}
  저장하려는 쿠키의 이름과 값을 설정

• HttpOnly
  HttpOnly 플래그는 쿠키가 클라이언트 측 JavaScript에서 접근하지 못하게 제한하는 속성
  - HttpOnly가 설정된 쿠키는 오직 서버로의 HTTP(S) 요청을 통해서만 접근가능
  - JavaScript 코드로는 읽거나 수정할 수 없다.

* 이 속성은 XSS(크로스 사이트 스크립팅) 공격을 방지하는 데 중요한 역할을 한다.
   공격자가 악성 스크립트를 주입하더라도 JavaScript에서 refresh_token을 탈취할 수 없게 만든다.

쿠키의 보안 플래그
HttpOnly: JavaScript에서 쿠키에 접근하지 못하게 함으로써 보안 수준을 높임.
   * HttpOnly 속성을 사용하면 개발자모드에서 쿠키를 확인하지 못함
Secure: 쿠키가 HTTPS 연결을 통해서만 전송되도록 제한 (HTTPS 사용 시 추가 추천).
SameSite: 쿠키가 사이트 간 요청에 전송되지 않도록 제한해 CSRF 공격을 방지하는 데 도움.

 

 

5. AccessToken이 만료되었을때 재발급!

다음 포스팅에서 계속,,,,,,,to be continue