๐ŸŒ WEB/Spring

Spring boot + JWT + RefreshToken ๊ตฌํ˜„ํ•˜๊ธฐ

์• ์ •์“ฐ 2021. 1. 29. 17:07

 

์•„์ฃผ ๊ธฐ๋ณธ์ ์ธ ์ฝ”๋“œ๊นŒ์ง€ ๋ชจ๋‘ ์žˆ์Šต๋‹ˆ๋‹ค.. ์™œ๋ƒ๋ฉด... ์ €๋„ ๊ธฐ์ˆ  ๋ธ”๋กœ๊ทธ๋“ค์„ ๋ณด๋ฉด์„œ ๋น ์ ธ์žˆ๋Š” ๋ถ€๋ถ„์„ ๋ณด๋ฉด ๋‚˜๊ฐ™์€ ์ดˆ๋ณด๋Š” ์–ด์ฉŒ๋ผ๊ณ  ํ•˜๋ฉฐ ์Šฌํผํ–ˆ๊ธฐ ๋•Œ๋ฌธ์—,,,

 

์‚ฌ์‹ค Security์˜ ์—ฌ๋Ÿฌ๊ฐ€์ง€ Filter๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ JWT ๋ฐฉ์‹์„ ์ด์šฉํ•˜๋Š”๊ฒŒ ๋งž์ง€๋งŒ 

ํ”„๋กœ์ ํŠธ ํฌ๊ธฐ,, ๊ธฐ๊ฐ„์ƒ ๋ถˆํ•„์š”ํ•˜๋‹ค ํŒ๋‹จํ•˜๊ฒŒ ๋˜์–ด์„œ JWT ๋งŒ์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌํ˜„ํ•˜์˜€๋‹ค. ๋‚˜์ค‘์— Security๋„ ๊ฐ™์ด ๊ตฌํ˜„ํ•˜์—ฌ ์˜ฌ๋ฆฌ๋„๋ก ํ•˜๊ฒ ์Œ!

์ด๋ฒˆ์—๋Š” git์— ์˜ฌ๋ ค ์ฝ”๋“œ๊นŒ์ง€ ๊ณต์œ ํ•˜๊ธฐ๋กœ ํ–ˆ๋‹ค! ์ฒ˜์Œ๋ณด๋ฉด ์•ฝ๊ฐ„ ๋ณต์žกํ•˜๋‹ค๊ณ  ์ƒ๊ฐ๋˜๊ณ  ๋‚˜๋˜ํ•œ ๊ทธ๋žฌ๋‹ค ใ… ใ… ,, (๋ถ€๋„๋Ÿฌ์šด) ๊ตฌํ˜„์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด์„œ ์ดํ•ดํ•˜๊ณ  ํ˜น์‹œ ์ด์ƒํ•œ๊ณณ์€ PRํ•ด์ฃผ์‹œ๋ฉด ๊ฐ์‚ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค...(๊ฐ„์ ˆ,,) 

 

git ์ฃผ์†Œ (์‹ค๋ฌด์—์„œ ์‹ค์ œ๋กœ ์‚ฌ์šฉํ•˜๋ฉด์„œ ์ˆ˜์ •ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค ~!)

git clone https://github.com/aejeong-context/tokenTest

 

 

API ๋ฐฉ์‹ ์ด๊ธฐ ๋•Œ๋ฌธ์— Server๋งŒ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค!

์ œ๊ฐ€ ๊ตฌํ˜„ ํ•œ ๋ฐฉ์‹์€ ์ด๋ ‡์Šต๋‹ˆ๋‹ค!

1. ์œ ์ €๊ฐ€ ํšŒ์›๊ฐ€์ž…์„ ํ• ๋•Œ ํ•ด๋‹น ์œ ์ €์˜ ์•„์ด๋””๋กœ Token์„ ์ƒ์„ฑํ•ด์„œ AccessToken, RefreshToken์„ ์ƒ์„ฑํ•œ๋‹ค. 

2. ์ด ๋•Œ ์ƒ์„ฑํ•œ RefreshToken์„ DB์— ์ €์žฅํ•œ๋‹ค.

3. ์‚ฌ์šฉ์ž๊ฐ€ AccessToken์„ ์ด์šฉํ•˜์—ฌ ๋กœ๊ทธ์ธ์„ ํ•˜๊ณ  ๊ทธ token์œผ๋กœ ์—ฌ๋Ÿฌ ์š”์ฒญ์˜ ๋Œ€ํ•œ ๋ฆฌ์†Œ์Šค๋ฅผ ์ œ๊ณต๋ฐ›๋Š”๋‹ค.

4. AccessToken์€ 30๋ถ„์œผ๋กœ ์ •ํ–ˆ๋‹ค. 30๋ถ„ ํ›„์— ์–ด๋– ํ•œ ์š”์ฒญ์„ ํ•  ๊ฒฝ์šฐ ๋‹ค์‹œ ๋กœ๊ทธ์ธ์„ ํ•ด์„œ RefreshToken์˜ Token๊ณผ ์œ ํšจ์‹œ๊ฐ„๋„ ํ™•์ธํ•˜๊ณ  ์‹œ๊ฐ„์ด ์ž๋‚ฌ๋‹ค๋ฉด ์žฌ๋ฐœ๊ธ‰ ํ•ด์ฃผ๊ณ  AccessToken๋„ ์žฌ๋ฐœ๊ธ‰ ๋ฐ›๋Š”๋‹ค. (์ด๋•Œ ํ”„๋ก ํŠธ์—์„œ ๋กœ๊ทธ์ธ์š”์ฒญ์„ ๋‹ค์‹œ ํ•˜๊ฒ ์ฃ ?)

-> ์—ฌ๊ธฐ์„œ ๊ณ ๋ฏผ์ธ๊ฒŒ Interceptor์—์„œ ์š”์ฒญ๋งˆ๋‹ค ํ™•์ธ์„ ํ•ด์„œ ๋กœ๊ทธ์ธ์„ ์•ˆ์‹œํ‚ค๋„๋ก ํ•ด์•ผํ•  ์ง€, ์‚ฌ์šฉ์„ฑ์„ ์œ„ํ•ด ๋‹ค์‹œ ๋กœ๊ทธ์ธ ์‹œํ‚ค๋Š”๊ฒŒ ๋งž๋Š”์ง€ ๊ณ ๋ฏผ์ด๋‹ค.... 

5.์žฌ๋ฐœ๊ธ‰ ๋ฐ›์€ AccessToken์œผ๋กœ ๋ฆฌ์†Œ์Šค๋ฅผ ์–ป๋Š”๋‹ค.

 

์ด๋ ‡๊ฒŒ ๋˜๋ฉด session๊ด€๋ฆฌ๋ฅผ ํ•ด์ค„ ํ•„์š” ์—†์ด ์š”์ฒญ๋งˆ๋‹ค Token์„ ํ™•์ธ ํ•ด์ฃผ๋ฉด ๋œ๋‹ค!

 


0. ํŒจํ‚ค์ง€ ๊ตฌ์„ฑ

 

1. build.gradle ์˜์กด์„ฑ ์ถ”๊ฐ€ํ•˜๊ธฐ

implementation 'io.jsonwebtoken:jjwt:0.9.1'

2. UserEntitiy.class ์ •ํ•˜๊ธฐ

Test์šฉ์ด๋ผ์„œ userId, pw๋กœ ๋‚˜๋ˆ„์—ˆ์ง€๋งŒ ์ด๋ถ€๋ถ„์€ ๋ณ€๊ฒฝํ•˜์…”๋„ ๋ฉ๋‹ˆ๋‹ค email์ด๋ผ๋˜๊ฐ€... 

package com.token.domains.users.domain;

import lombok.Builder;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

import javax.persistence.*;

@Getter
@RequiredArgsConstructor
@Table(name = "users")
@Entity
public class UsersEntity {

  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Id
  private Long id;

  private String userId;

  private String pw;

  @Builder
  public UsersEntity(String userId, String pw) {
    this.userId = userId;
    this.pw = pw;
  }
}

 

3. UserRepository.intrerface

Optional์€ Java8๋ฌธ๋ฒ•์ธ๋ฐ null์ฒ˜๋ฆฌ์— ์•„์ฃผ ์œ ์šฉํ•˜๋‹ˆ ํ•œ๋ฒˆ์”ฉ ์‚ฌ์šฉํ•ด๋ณด์‹œ๊ธธ ์ถ”์ฒœ๋“œ๋ฆฝ๋‹ˆ๋‹ค!

์šฐ์„  ์ž‘์„ฑํ•˜๊ณ  ์‚ฌ์šฉํ•  ๋•Œ ์„ค๋ช…์„ ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค

public interface UsersRepository extends JpaRepository<UsersEntity,Long> {
    Optional<UsersEntity> findByUserIdAndPw(String userId,String pw);
    Optional<UsersEntity> findByUserId(String userId);
}

 

4.UserController.class

์‚ฌ์‹ค ์ด๋ฆ„์„ ์ด๋”ด์‹์œผ๋กœ ์ง€์œผ๋ฉด ํ˜ผ๋‚ฉ๋‹ˆ๋‹ค. ์ด๊ฑด Test์ด๊ธฐ ๋•Œ๋ฌธ์— ^^...

/signUp, /signIn์€ ํšŒ์›๊ฐ€์ž…, ๋กœ๊ทธ์ธ์ด๊ตฌ์š” Test๋กœ AccessToken์ด ์œ ํšจํ•ด์•ผ๋งŒ ๋ฆฌ์†Œ์Šค๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” /info๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ๋ชจ๋“  ์š”์ฒญ๋“ค์€ Interceptor๊ฐ€ ๋จผ์ € ์ฒ˜๋ฆฌ๋˜๊ณ  ๋“ค์–ด๊ฐ€๊ธฐ ๋•Œ๋ฌธ์— ์—ฌ๊ธฐ์„œ ๋”ฑํžˆ ํ•ด์•ผํ•˜๋Š”๊ฑด ์—†์Šต๋‹ˆ๋‹ค.

@RequiredArgsConstructor
@RestController
public class UserController {

  private final UserService userService;

  @PostMapping("/user/signUp")
  public ResponseEntity signUp(@RequestBody UserRequest userRequest) {
    return userService.findByUserId(userRequest.getUserId()).isPresent()
        ? ResponseEntity.badRequest().build()
        : ResponseEntity.ok(userService.signUp(userRequest));
  }

  @PostMapping("/user/signIn")
  public ResponseEntity<TokenResponse> signIn(@RequestBody UserRequest userRequest) {

    return ResponseEntity.ok().body(userService.signIn(userRequest));
  }

  @GetMapping("/info")
  public ResponseEntity<List<UsersEntity>> findUser() {
    return ResponseEntity.ok().body(userService.findUsers());
  }
}

 

5. AuthEntity.class

ํ† ํฐ์„ ๊ด€๋ฆฌํ•ด์ค„ ํ…Œ์ด๋ธ”์ž…๋‹ˆ๋‹ค. N:1๋กœ ๋‹จ๋ฐ˜ํ–ฅ ๋งตํ•‘ ํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์‹ค ์–‘๋ฐ˜ํ–ฅ์„ ํ•ด์ฃผ์–ด๋„ ์ƒ๊ด€์—†์ง€๋งŒ ์šฐ์„  User๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธ ํ›„ Token์„ ํ™•์ธํ•˜๋Š” ํ”Œ๋กœ์šฐ๊ฐ€ ๋งž์ง€ ์•Š๋‚˜ ์‹ถ์–ด์„œ ๋‹จ๋ฐ˜ํ–ฅ์œผ๋กœ ์„ค์ •ํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. 

 

- refreshUpdate ๋ฉ”์„œ๋“œ๋Š” DB์— ์ €์žฅํ•˜๊ณ , ์‚ฌ์šฉํ•˜๋Š” refreshToken์ด ์œ ํšจ์‹œ๊ฐ„์ด ๋งŒ๋ฃŒ๋˜์—ˆ์„ ๋•Œ DB์— ์—…๋ฐ์ดํŠธ ๋˜๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.

@Getter
@RequiredArgsConstructor
@Table(name = "auth")
@Entity
public class AuthEntity {

  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Id
  private Long id;

  private String refreshToken;

  @ManyToOne
  @JoinColumn(name = "user_id")
  private UsersEntity usersEntity;

  @Builder
  public AuthEntity(String refreshToken, UsersEntity usersEntity) {
    this.refreshToken = refreshToken;
    this.usersEntity = usersEntity;
  }
  public void refreshUpdate(String refreshToken) {
    this.refreshToken = refreshToken;
  }
}

 

6. AuthRepository.inrerface 

public interface AuthRepository extends JpaRepository<AuthEntity, Long> {

  Optional<AuthEntity> findByUsersEntityId(Long userId);
}

 

7. WebMvcConfig.class

TokenInterceptor๋ฅผ ๊ตฌํ˜„ํ•ด์ฃผ๊ธฐ ์œ„ํ•ด ์›ํ•˜๋Š” ๊ฒฝ๋กœ ๋ฅผ ์ถ”๊ฐ€ํ•ด์ค๋‹ˆ๋‹ค.

@Configuration
@RequiredArgsConstructor
public class WebMvcConfig implements WebMvcConfigurer {
  private final JwtTokenInterceptor jwtTokenInterceptor;

  public void addInterceptors(InterceptorRegistry registry) {
    System.out.println("์ธํ„ฐ์…‰ํ„ฐ ๋“ฑ๋ก");
    registry.addInterceptor(jwtTokenInterceptor).addPathPatterns("/info");
  }
}

 /info๋งŒ ํ•ด์„œ testํ•  ์˜ˆ์ •์ด๊ธฐ ๋•Œ๋ฌธ์— ์ €๋ ‡๊ฒŒ ํ•ด๋†จ์ง€๋งŒ.

๋ณดํ†ต ํšŒ์›๊ฐ€์ž…, ๋ฉ”์ธ ํŽ˜์ด์ง€ ์กฐํšŒ ๋“ฑ ์€ ํšŒ์›์ด ์•„๋‹ˆ์–ด๋„ ๋ณผ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์‹ค์ œ๋กœ ๊ตฌํ˜„ํ•˜์‹ค ๋•Œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์„ค์ •ํ•ด์ฃผ์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

registry.addInterceptor(jwtTokenInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/signUp")

 

8. JwtTokenInterceptor.class

ํ•˜..log๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•˜๋Š”๋ฐ ์•„์ง๋„ syso๊ฐ€ ์ต์ˆ™ํ•œ ๋ฐ”๋žŒ์— System ๋ฒ”๋ฒ…์ž…๋‹ˆ๋‹ค,, ์ดํ•ด๋ถ€ํƒ,,

์•„๊นŒ /info๋ฅผ ์ถ”๊ฐ€ ํ•ด์คฌ์œผ๋‹ˆ API๋กœ /info์— ๋Œ€ํ•œ ๋ฆฌ์†Œ์Šค๋ฅผ ์š”์ฒญํ• ๋•Œ ๋งˆ๋‹ค ์ด ํด๋ž˜์Šค๊ฐ€ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

 

request.getheader("ACCESS_TOKEN") ์€ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ACCESS_TOKEN์ด๋ผ๋Š” key๊ฐ’์œผ๋กœ ํšŒ์›๊ฐ€์ž… ๋•Œ ์ƒ์„ฑํ•˜์—ฌ ๋ณด๊ด€ํ•˜๋˜ token์„ ๋ณด๋‚ด์ฃผ๋ฉด ๊ทธ value๊ฐ’์„ ๊ฐ€์ ธ์™€์„œ null์ธ์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. null์ด ์•„๋‹๊ฒฝ์šฐ isValidToken์—์„œ ํ•ด๋‹น token์ด ์„œ๋ฒ„์—์„œ ์ƒ์„ฑํ•œ token์ธ์ง€, ์œ ํšจ๊ธฐ๊ฐ„์ด ์ง€๋‚ฌ๋Š”์ง€ ํ™•์ธํ• ํ…๋ฐ์š”! ๋ฐ‘์—์„œ ํ™•์ธํ•ด๋ด…์‹œ๋‹ค!

 

@Component
@RequiredArgsConstructor
public class JwtTokenInterceptor implements HandlerInterceptor {

  private final TokenUtils tokenUtils;

  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
      throws IOException {

    System.out.println("JwtToken ํ˜ธ์ถœ");
    String accessToken = request.getHeader("ACCESS_TOKEN");
    System.out.println("AccessToken:" + accessToken);
    String refreshToken = request.getHeader("REFRESH_TOKEN");
    System.out.println("RefreshToken:" + refreshToken);

    if (accessToken != null) {
      if (tokenUtils.isValidToken(accessToken)) {
        return true;
      }
    }
    response.setStatus(401);
    response.setHeader("ACCESS_TOKEN", accessToken);
    response.setHeader("REFRESH_TOKEN", refreshToken);
    response.setHeader("msg", "Check the tokens.");
    return false;
  }

 

9. TokenUtils.class

๋Œ€๋ง์˜ Token ์ƒ์„ฑ ํด๋ž˜์Šค์ž…๋‹ˆ๋‹ค,,์ „์ฒด ์†Œ์Šค์ž…๋‹ˆ๋‹ค.

@RequiredArgsConstructor
@Service
public class TokenUtils {

  private final String SECRET_KEY = "secretKey";
  private final String REFRESH_KEY = "refreshKey";
  private final String DATA_KEY = "userId";

  public String generateJwtToken(UsersEntity usersEntity) {
    return Jwts.builder()
        .setSubject(usersEntity.getUserId())
        .setHeader(createHeader())
        .setClaims(createClaims(usersEntity))
        .setExpiration(createExpireDate(1000 * 60 * 5))
        .signWith(SignatureAlgorithm.HS256, createSigningKey(SECRET_KEY))
        .compact();
  }

  public String saveRefreshToken(UsersEntity usersEntity) {
    return Jwts.builder()
        .setSubject(usersEntity.getUserId())
        .setHeader(createHeader())
        .setClaims(createClaims(usersEntity))
        .setExpiration(createExpireDate(1000 * 60 * 10))
        .signWith(SignatureAlgorithm.HS256, createSigningKey(REFRESH_KEY))
        .compact();
  }



  public boolean isValidToken(String token) {
    System.out.println("isValidToken is : " +token);
    try {
      Claims accessClaims = getClaimsFormToken(token);
      System.out.println("Access expireTime: " + accessClaims.getExpiration());
      System.out.println("Access userId: " + accessClaims.get("userId"));
      return true;
    } catch (ExpiredJwtException exception) {
      System.out.println("Token Expired UserID : " + exception.getClaims().getSubject());
      return false;
    } catch (JwtException exception) {
      System.out.println("Token Tampered");
      return false;
    } catch (NullPointerException exception) {
      System.out.println("Token is null");
      return false;
    }
  }
  public boolean isValidRefreshToken(String token) {
    try {
      Claims accessClaims = getClaimsToken(token);
      System.out.println("Access expireTime: " + accessClaims.getExpiration());
      System.out.println("Access userId: " + accessClaims.get("userId"));
      return true;
    } catch (ExpiredJwtException exception) {
      System.out.println("Token Expired UserID : " + exception.getClaims().getSubject());
      return false;
    } catch (JwtException exception) {
      System.out.println("Token Tampered");
      return false;
    } catch (NullPointerException exception) {
      System.out.println("Token is null");
      return false;
    }
  }


  private Date createExpireDate(long expireDate) {
    long curTime = System.currentTimeMillis();
    return new Date(curTime + expireDate);
  }

  private Map<String, Object> createHeader() {
    Map<String, Object> header = new HashMap<>();

    header.put("typ", "ACCESS_TOKEN");
    header.put("alg", "HS256");
    header.put("regDate", System.currentTimeMillis());

    return header;
  }

  private Map<String, Object> createClaims(UsersEntity usersEntity) {
    Map<String, Object> claims = new HashMap<>();
    claims.put(DATA_KEY, usersEntity.getUserId());
    return claims;
  }

  private Key createSigningKey(String key) {
    byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(key);
    return new SecretKeySpec(apiKeySecretBytes, SignatureAlgorithm.HS256.getJcaName());
  }

  private Claims getClaimsFormToken(String token) {
    return Jwts.parser()
        .setSigningKey(DatatypeConverter.parseBase64Binary(SECRET_KEY))
        .parseClaimsJws(token)
        .getBody();
  }
  private Claims getClaimsToken(String token) {
    return Jwts.parser()
            .setSigningKey(DatatypeConverter.parseBase64Binary(REFRESH_KEY))
            .parseClaimsJws(token)
            .getBody();
  }
  
 }

 

private final String SECRET_KEY = "secretKey";
private final String REFRESH_KEY = "refreshKey";
private final String DATA_KEY = "userId";

์œ„ ๋ณ€์ˆ˜๋Š” token์„ ๋งŒ๋“ค ๋•Œ ์‚ฌ์šฉํ•  ์•”ํ˜ธ์ž…๋‹ˆ๋‹ค. DATA_KEY๋Š” ์–ด๋–ค ๋‚ด์šฉ์œผ๋กœ token์„ ๋งŒ๋“ค์–ด์ง€ ์ •ํ•ด์ค๋‹ˆ๋‹ค.

์ด๋ถ€๋ถ„์€ JWT์ด๋ก ์„ ์ž์„ธํžˆ ๊ณต๋ถ€ํ•ด๋ด์•ผ ํ•˜๋Š”๋ฐ์š” ๊ฐ„๋‹จํžˆ ์„ค๋ช…ํ•˜์ž๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด Payload์— ๋“ค์–ด๊ฐˆ ๋‚ด์šฉ์ด DATA_KEY์ž…๋‹ˆ๋‹ค. ์ €๋Š” userId๋งŒ ์„ค์ •ํ•  ์˜ˆ์ •์ด๊ธฐ ๋•Œ๋ฌธ์— 6. ์„ ๋ณด์‹œ๋ฉด ์•„์‹œ๊ฒ ์ง€๋งŒ userEntity์—์„œ userId๋งŒ์„ ๊ฐ€์ง€๊ณ ์™€์„œ claims.put์„ ํ•ด์ค๋‹ˆ๋‹ค.

๋ฌผ๋ก  ์—ฌ๋Ÿฌ๊ฐœ๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ token์€ ์š”์ฒญ๋งˆ๋‹ค header์— ๋ถ™์–ด ๋‹ค๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋งŒ์•ฝ ํƒˆ์ทจ๋‹นํ•œ๋‹ค๋ฉด putํ•ด์ค€ ๋ชจ๋“  ์ •๋ณด๋“ค์ด ๋…ธ์ถœ๋ฉ๋‹ˆ๋‹ค,,

 

๊ทธ๋ฆฌ๊ณ  token๊ธธ์ด๋„ ๊ธธ์–ด์ง‘๋‹ˆ๋‹ค! (์ƒ๊ด€์—†๋‚˜..) 

 

https://research.securitum.com/jwt-json-web-token-security/

 

1.generateJwtToken

AceessToken ์ƒ์„ฑ

 

2.saveRefreshToken

ResfeshToken ์ƒ์„ฑ

 

3.isValidToken, isValidRefreshToken

ํ•ด๋‹น Token ์˜ ์œ ํšจ์„ฑ ํ™•์ธ

 

4. createExpireDate

์œ ํšจ์‹œ๊ฐ„ ์„ค์ •

 

5.  createHeader

Token ์ƒ์„ฑ์‹œ Header ๋ถ€๋ถ„์„ ์ €์ •ํ•ด์ค€๋‹ค

 

6. createClaims

Token ์ƒ์„ฑ์‹œ Payload ๋ถ€๋ถ„์„ ์ •ํ•ด์ค๋‹ˆ์ค€๋‹ค

 

7.createSigningKey

ํ•ด๋‹น key๋กœ ์•”ํ˜ธํ™”

 

8.getClaimsFormToken,getClaimsToken

์œ ํšจ์„ฑ ๊ฒ€์ƒ‰์„ ์œ„ํ•ด token ์ •๋ณด๋ฅผ ์ฝ์–ด์˜จ๋‹ค

 

Token์˜ ์œ ํšจ์„ฑ ํ™•์ธ๊ณผ Token ์ •๋ณด๋ฅผ ์ฝ์–ด์˜ค๋Š” 3,8๋ฒˆ์€ ์ค‘๋ณต์ฝ”๋“œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค... ์ด๊ฑด ๋‚˜์ค‘์— ๋‹ค์‹œ ๋ณด๊ณ  ์ˆ˜์ •ํ•ด์•ผ ํ•  ๊ฒƒ ๊ฐ™๋‹ค ใ…œใ…œ,,

 

 

10. UserService.class

@Service
@RequiredArgsConstructor
public class UserService {
  private final UsersRepository usersRepository;
  private final TokenUtils tokenUtils;
  private final AuthRepository authRepository;

  public Optional<UsersEntity> findByUserId(String userId) {

    return usersRepository.findByUserId(userId);
  }

  @Transactional
  public TokenResponse signUp(UserRequest userRequest) {
    UsersEntity usersEntity =
        usersRepository.save(
            UsersEntity.builder()
                .pw(userRequest.getUserPw())
                .userId(userRequest.getUserId())
                .build());

    String accessToken = tokenUtils.generateJwtToken(usersEntity);
    String refreshToken = tokenUtils.saveRefreshToken(usersEntity);

    authRepository.save(
        AuthEntity.builder().usersEntity(usersEntity).refreshToken(refreshToken).build());

    return TokenResponse.builder().ACCESS_TOKEN(accessToken).REFRESH_TOKEN(refreshToken).build();
  }

  @Transactional
  public TokenResponse signIn(UserRequest userRequest) {
    UsersEntity usersEntity =
        usersRepository
            .findByUserIdAndPw(userRequest.getUserId(), userRequest.getUserPw())
            .orElseThrow(() -> new IllegalArgumentException("์กด์žฌํ•˜์ง€ ์•Š๋Š” ํšŒ์›์ž…๋‹ˆ๋‹ค."));
    AuthEntity authEntity =
        authRepository
            .findByUsersEntityId(usersEntity.getId())
            .orElseThrow(() -> new IllegalArgumentException("Token ์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค."));
    String accessToken = "";
    String refreshToken= authEntity.getRefreshToken();

    if (tokenUtils.isValidRefreshToken(refreshToken)) {
      accessToken = tokenUtils.generateJwtToken(authEntity.getUsersEntity());
      return TokenResponse.builder()
          .ACCESS_TOKEN(accessToken)
          .REFRESH_TOKEN(authEntity.getRefreshToken())
          .build();
    } else {
      refreshToken = tokenUtils.saveRefreshToken(usersEntity);
      authEntity.refreshUpdate(refreshToken);
    }

    return TokenResponse.builder().ACCESS_TOKEN(accessToken).REFRESH_TOKEN(refreshToken).build();
  }

  public List<UsersEntity> findUsers() {
    return usersRepository.findAll();
  }
}

Service๋Š” ์ฒ˜์Œ์— ๋งํ–ˆ๋˜ ํ”Œ๋กœ์šฐ์™€ ๊ฐ™๋‹ค! ํšŒ์›๊ฐ€์ž…์‹œ ACCESS_TOKEN๊ณผ REFRESH_TOKEN์„ ๋ฐœ๊ธ‰ํ•˜๊ณ 

๋ฆฌ์†Œ์Šค ์š”์ฒญ์„ ํ•˜๋‹ค๊ฐ€ ์—ฐ๊ฒฐ์ด ๋Š์–ด์งˆ ๊ฒฝ์šฐ ๋กœ๊ทธ์ธ์„ ๋‹ค์‹œ ์‹œํ‚ค๋Š”๋ฐ ์ด๋•Œ refreshToken์˜ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ๋ฅผ ํ•ด์ค€ ๋‹ค์Œ์— ํ†ต๊ณผ๋˜๋ฉด AccessToken์„ ์žฌ๋ฐœ๊ธ‰ํ•ด์ค€๋‹ค! ๋งŒ์•ฝ refreshToken์ด ์œ ํšจํ•˜์ง€ ์•Š๋‹ค๋ฉด refreshToken๋„ ์—…๋ฐ์ดํŠธ ํ•ด์ค๋‹ˆ๋‹ค!

 

์†”์งํžˆ ์ด ๋กœ์ง์ด ๋งž๋Š”์ง€ ์ž˜ ๋ชจ๋ฅด๊ฒ ๋„ค์š”,, ์šฐ์„  POSTMAN์œผ๋กœ ์ž‘๋™์ด ์ž˜๋˜๋‹ˆ ๋„˜์–ด๊ฐ€๊ฒ ์ˆฉ๋‹ˆ๋‹ค!

 


ํšŒ์›๊ฐ€์ž… TEST

 

 

ํšŒ์›๊ฐ€์ž… ํ›„ ํ•ด๋‹น AccessToken์œผ๋กœ ์š”์ฒญํ•˜์—ฌ ๋ฆฌ์†Œ์Šค๋ฅผ ๋ฐ›๋Š” TEST

 

์žฌ ๋กœ๊ทธ์ธ ์‹œ AccessToken ์žฌ๋ฐœ๊ธ‰, RefreshToken ์œ ํšจ์„ฑ ํ™•์ธ 

 

 

๋ถ€์กฑํ•œ ์„ค๋ช…์ด๋‚˜ ํ‹€๋ฆฐ ๋ถ€๋ถ„ ๋Œ“๊ธ€ ๋‚จ๊ฒจ์ฃผ์‹œ๋ฉด ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค! 

๋ฐ˜์‘ํ˜•