BJSNuruhee / levelup

0 stars 0 forks source link

[SpringBoot + Vue3] JWT를 이용한 로그인 구현 #42

Closed yejun95 closed 6 months ago

yejun95 commented 8 months ago

SpringBoot + Vue3 JWT를 이용한 로그인 구현

기본 흐름

✔ frontend 구조 구조

image



✔ backend 구조

image



✔ 회원 가입시 토큰 생성

-frontend / useAuth.js

// 회원 가입
const postSignUp = (id, pw, email, phone) => {
  axios.post(`${baseUrl}/post/user/signUp`, {
    userId: id,
    userPw: pw,
    userEmail: email,
    userPhone: phone,
    userToken: ``
  }).then(res => {
    console.log("전송 성공")
    if(res.data === 'fail') {
      window.alert("존재하는 아이디 입니다.")
    }
    console.log(res.data)
  }).catch(err => {
    error.value = err
    console.log(error.value)
  })
}



// 회원가입
@Override
public String postSignUp(UserDto vo) {
  List<UserDto> list = userRepository.findAll();
  try {
      if(list.stream().noneMatch(d -> 
          d.getUserSeq().equals(vo.getUserId()) ||
          d.getUserId().equals(vo.getUserId()) ||
          d.getUserToken().equals(vo.getUserToken()))) {
          String token = jwtService.getToken("id", vo.getUserId());
          vo.setUserToken(token);
          userRepository.postSignUp(vo);
          System.out.println("회원가입 성공");
      } else {
          throw new Exception();
      }
  } catch (Exception e) {
      System.out.println("회원가입 실패");
      e.printStackTrace();
      return "fail";
  } 
  return "success";
}



// JWT 생성 메서드 @Override public String getToken(String key, Object value) {

Date expTime = new Date(); expTime.setTime(expTime.getTime() + 1000 60 5); byte[] secretBytekey = DatatypeConverter.parseBase64Binary(secretKey); Key signKey = new SecretKeySpec(secretBytekey, SignatureAlgorithm.HS256.getJcaName());

Map<String, Object> headerMap = new HashMap<>(); headerMap.put("typ", "JWT"); headerMap.put("alg", "HS256");

Map<String, Object> map = new HashMap<>(); map.put(key, value);

JwtBuilder builder = Jwts.builder().setHeader(headerMap).setClaims(map).setExpiration(expTime).signWith(signKey, SignatureAlgorithm.HS256);

return builder.compact(); }

<br>
<hr>
<br>

### ✔ 서버에서 id, pw를 조회하여 검증
- 클라이언트에서 보낸 id, pw 데이터를 DTO에 담는다.

- 담은 데이터를 DB로 보내 id가 있는지 확인한다.

- frontend / useAuth.js
  - 로그인이 성공하면 store에 설정되어 있는 `account`에 리턴 데이터를 담는다.

```javascript
// JWT 로그인
const login = async (id, pw) => {
  await axios.post(`${baseUrl}/post/user/jwtLogin`, {
    userId: id,
    userPw: pw
  })
  .then(res => {
    account.value.id = res.data
    console.log("토큰 id : ", res.data)
    window.alert("로그인 하였습니다.")
  }).catch(err => {
    error.value = err
    console.log(error.value)
    window.alert("로그인 실패입니다.")
  })
}
// JWT 로그인
@Override
  public ResponseEntity memJWTLogin(UserDto dto, HttpServletResponse res) {
  UserDto mvo = userRepository.memLogin(dto);
  if (mvo != null) { // 로그인 성공
      String id = mvo.getUserId();
      Cookie cookie = new Cookie("token", mvo.getUserToken());
      cookie.setHttpOnly(true);
      cookie.setMaxAge(60 * 60);
      res.addCookie(cookie);

      return new ResponseEntity<>(id, HttpStatus.OK);
  } else {
      throw new ResponseStatusException(HttpStatus.NOT_FOUND);
  }
}




✔ 새로고침으로 인한 쿠키 풀림

// JWT 체크
@PostMapping("post/user/checkJwt")
  public ResponseEntity checkJwt(@CookieValue(value = "token", required = false) String token) {
  return jwtService.checkJwt(token);
}



// JWT 로그인 체크
@Override
public ResponseEntity checkJwt(String token) {
  Claims claims = getClaims(token);
        if (claims == null) {
            return new ResponseEntity<>("Invalid token", HttpStatus.BAD_REQUEST);
        }

   // token 생성시 key를 "id"로 하였기 때문에, key를 통해 value를 얻는다.
  // Stirng id = 여기에 token 생성 시의 value가 담기는 것 (= 로그인 ID)
  String id = claims.get("id").toString();

  // token의 value값을 이용해 db에서 해당 userId의 정보를 찾는다.
  UserDto dto = userRepository.findUserDetail(id);

  // 브라우저 쿠키 token 값과 DB의 token 값을 비교해 유효성 검증
  if (dto.getUserToken().equals(token)) {
      return new ResponseEntity<>(id, HttpStatus.OK);
  }
  return new ResponseEntity<>(null, HttpStatus.OK);
}

// JWT 로그인 체크 메서드
public Claims getClaims(String token) {
  if (token != null && !"".equals(token)) {
      try {
          byte[] secretBytekey = DatatypeConverter.parseBase64Binary(secretKey);
          Key signKey = new SecretKeySpec(secretBytekey, SignatureAlgorithm.HS256.getJcaName());
          return Jwts.parserBuilder().setSigningKey(signKey).build().parseClaimsJws(token).getBody();
      } catch (ExpiredJwtException e) {
          // 토큰이 만료됨
      } catch (JwtException e) {
          // 유효하지 않을 때
      }
  }
  return null;
}




💡 JWT를 이용한 로그인 과정에서의 회고

yejun95 commented 8 months ago

2024-01-04 내용 추가