치치ㅤ 2024. 11. 5. 15:04

의존성 추가

implementation 'com.auth0:java-jwt:4.4.0'

dependencies {
	// Java jwt lib 가져오기
	implementation 'com.auth0:java-jwt:4.4.0'


	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-mustache'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	compileOnly 'org.projectlombok:lombok'
	developmentOnly 'org.springframework.boot:spring-boot-devtools'
	runtimeOnly 'com.h2database:h2'
	runtimeOnly 'com.mysql:mysql-connector-j'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

 

 

Jwt lib 추가 (java-jwt)

https://github.com/auth0/java-jwt?tab=readme-ov-file

 

GitHub - auth0/java-jwt: Java implementation of JSON Web Token (JWT)

Java implementation of JSON Web Token (JWT). Contribute to auth0/java-jwt development by creating an account on GitHub.

github.com

 

implementation group: 'com.auth0', name: 'java-jwt', version: '4.3.0'

 

💡 학습 목표

1. JWT를 사용하여 사용자 정보를 안전하게 전달하고 검증하는 방법을 배운다.

 

💡 Gradle의 캐시 파일이 문제가 되어 라이브러리를 인식하지 못할 수 있습니다. 이 경우, 터미널에서 다음 명령어를 실행하여 Gradle 캐시를 정리할 수 있습니다.

./gradlew clean --refresh-dependencies

 

 

JwtUtil 클래스 만들기

package com.tenco.blog_v3.common.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.tenco.blog_v3.user.User;

import java.util.Date;

/**
 * JWT 토큰의 생성 및 검증을 위한 유틸 클래스 이다.
 * 여기서는 알고리즘 HMAC512 알고리즘을 사용한다.
 */
public class JwtUtil {

    /**
     *
     * 주어진 사용자 정보(USER)로 JWT 토큰을 생성한다.
     *
     * return 생성된 JWT String
     */
    public static String create(User user) {
        return JWT.create()
                // 헤더
                .withSubject("blog")
                .withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 60))
                // 페이로드 - 데이터 조각 클레임(사용자 id, 사용자 이름)
                .withClaim("id", user.getId())
                .withClaim("username", user.getUsername())
                // 서명
                .sign(Algorithm.HMAC512("tencoding"));
    }

    public static User verify(String token) {

        // JWT 디코딩
        DecodedJWT decodedJWT = JWT.require(Algorithm.HMAC512("tencoding"))
                .build().verify(token);

        // 검증된 JWT 에서 사용자 ID 와 이름 추출 해보자
        int id = decodedJWT.getClaim("id").asInt();
        String username = decodedJWT.getClaim("username").asString();

        return User.builder().id(id).username(username).build();
    }


}

 

 

UserService

 /**
     *  로그인 서비스
     *
     */
    public String signIn(UserRequest.LoginDTO reqDTO) {
        User seessionUser = userJPARepository
                .findByUsernameAndPassword(reqDTO.getUsername(), reqDTO.getPassword())
                .orElseThrow( () -> new Exception401("인증되지 않았습니다"));

        return JwtUtil.create(seessionUser);
    }

 

 

UserController

    @PostMapping("/login")
    public ResponseEntity<ApiUtil<UserResponse.DTO>> login(@RequestBody UserRequest.LoginDTO reqDto) {
        String jwt = userService.signIn(reqDto);
        return ResponseEntity.ok()
                .header(Define.AUTHORIZATION, Define.BEARER + jwt)
                .body(new ApiUtil<>(null));
    }

 

 

 

회원 정보 수정

  • UserController
   /**
     * 회원 정보 수정
     */
    @PutMapping("/api/users/{id}")
    public ResponseEntity<?> update(@PathVariable(name = "id") Integer id, @RequestBody UserRequest.UpdateDTO reqDTO, HttpServletRequest request) {

        // 헤더에 있는 JWT 토큰을 가져 오기
        // 토큰에서 사용자 정보 추출
        // 사용자 정보 수정 로직일 그대로 사용
        String authorizationHeader = request.getHeader(Define.AUTHORIZATION);
        if (authorizationHeader == null || !authorizationHeader.startsWith(Define.BEARER)) {
            throw new Exception401("인증 정보가 유효하지 않습니다");
        }
        // BEARER 문자열과 공백 한칸 제거 처리
        String token = authorizationHeader.replace(Define.BEARER, "");
        User sessionUser = JwtUtil.verify(token);

        if(sessionUser == null) {
            throw new Exception401("인증 토큰이 유효하지 않습니다");
        }
        if(sessionUser.getId() != id) {
            throw  new Exception403("해당 사용자를 수정할 권한이 없습니다.");
        }
        // 서비스에 사용자 정보 수정 요청
        UserResponse.DTO resDTO = userService.updateUser(id, reqDTO);
        return ResponseEntity.ok(new ApiUtil<>(resDTO));
    }

 

 

UserService

  @Transactional // 트랜잭션 관리
  public UserResponse.DTO updateUser(int id, UserRequest.UpdateDTO reqDTO){
      // 1. 사용자 조회 및 예외 처리
      User user = userJPARepository.findById(id)
              .orElseThrow(() -> new Exception404("회원정보를 찾을 수 없습니다"));

      // 2. 사용자 정보 수정
      user.setPassword(reqDTO.getPassword());
      user.setEmail(reqDTO.getEmail());

      // 더티 체킹을 통해 변경 사항이 자동으로 반영됩니다.
      return new UserResponse.DTO(user);
  }

 

 

포스트맨 활용