STUDY/TIL
[TIL] 241111
7alswn
2024. 11. 11. 20:54
Do - List
- 이론 복습
- 전수면담 사전 질문 작성
- 회원가입 기능 구현 (https://github.com/sparta-rambo/delivery_project/tree/feature/user)
- 로그인 기능 구현(JWT 토큰 발급)
회원가입 기능 에러
1. ERROR: operator does not exist: bigint = uuid
// public interface UserRepository extends JpaRepository<User, Long> {
public interface UserRepository extends JpaRepository<User, UUID> {
Optional<User> findByUsername(String username);
}
(1) UserRepository에서 타입명이 Long으로 설정되어 있었습니다.
User 테이블의 pk(primary key)인 id의 타입인 UUID로 변환해야 합니다.
// User user = new User(username, password, address, role);
User user = User.builder()
.id(UUID.randomUUID())
.username(username)
.password(password)
.address(address)
.role(role)
.build();
(2) PostgreSQL에서 UUID 타입을 사용하는 경우, 자동으로 UUID를 생성할 수 있어 @GeneratedValue 어노테이션이 꼭 필요하지는 않습니다. 따라서, User 엔티티에서 Id 부분에 해당 어노테이션을 삭제하고, UserService에서 객체를 생성하는 방식을 바꿨습니다.
2. ERROR: value too long for type character varying(50)
@Entity
@Table(name = "p_users")
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Builder
public class User extends Timestamped{
@Id
private UUID id;
@Column(nullable = false, unique = true, length = 50)
private String username;
//@Column(nullable = false, length = 50)
@Column(nullable = false, length = 255)
private String password;
@Column(nullable = false, length = 20)
@Enumerated(value = EnumType.STRING)
private UserRoleEnum role;
@Column(nullable = false, length = 255)
private String address;
}
User 엔티티에서 password의 길이가 짧아서 나타나는 오류였으므로, length를 255로 바꿔주거나 아예 삭제합니다.
JWT 토큰 생성 및 테스트
1. JWT Util
* Util Class: 특정한 매개변수나 파라미터에 대한 작업을 수행하는 메서드들이 존재하는 Class
즉, 다른 객체에 의존하지 않고 하나의 모듈로서 동작하는 Class
@Component
public class JwtUtil {
// Header KEY 값
public static final String AUTHORIZATION_HEADER = "Authorization";
// 사용자 권한 값의 KEY
public static final String AUTHORIZATION_KEY = "auth";
// Token 식별자
public static final String BEARER_PREFIX = "Bearer ";
// 토큰 만료시간
private final long TOKEN_TIME = 60 * 60 * 1000L; // 60분
@Value("${jwt.secret.key}") // Base64 Encode 한 SecretKey
private String secretKey;
private Key key;
private final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
// 로그 설정
public static final Logger logger = LoggerFactory.getLogger("JWT 관련 로그");
@PostConstruct
public void init() {
byte[] bytes = Base64.getDecoder().decode(secretKey);
key = Keys.hmacShaKeyFor(bytes);
}
// JWT 토큰 생성
public String createToken(String username, UserRoleEnum role) {
Date date = new Date();
return BEARER_PREFIX +
Jwts.builder()
.setSubject(username) // 사용자 식별자값(ID)
.claim(AUTHORIZATION_KEY, role) // 사용자 권한
.setExpiration(new Date(date.getTime() + TOKEN_TIME)) // 만료 시간
.setIssuedAt(date) // 발급일
.signWith(key, signatureAlgorithm) // 암호화 알고리즘
.compact();
}
// JWT Cookie 에 저장
public void addJwtToCookie(String token, HttpServletResponse res) {
try {
token = URLEncoder.encode(token, "utf-8").replaceAll("\\+", "%20"); // Cookie Value 에는 공백이 불가능해서 encoding 진행
Cookie cookie = new Cookie(AUTHORIZATION_HEADER, token); // Name-Value
cookie.setPath("/");
// Response 객체에 Cookie 추가
res.addCookie(cookie);
} catch (UnsupportedEncodingException e) {
logger.error(e.getMessage());
}
}
// JWT 토큰 substring
public String substringToken(String tokenValue) {
if (StringUtils.hasText(tokenValue) && tokenValue.startsWith(BEARER_PREFIX)) {
return tokenValue.substring(7);
}
logger.error("Not Found Token");
throw new NullPointerException("Not Found Token");
}
// JWT 토큰 검증
public boolean validateToken(String token) {
try {
Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token);
return true;
} catch (SecurityException | MalformedJwtException | SignatureException e) {
logger.error("Invalid JWT signature, 유효하지 않는 JWT 서명 입니다.");
} catch (ExpiredJwtException e) {
logger.error("Expired JWT token, 만료된 JWT token 입니다.");
} catch (UnsupportedJwtException e) {
logger.error("Unsupported JWT token, 지원되지 않는 JWT 토큰 입니다.");
} catch (IllegalArgumentException e) {
logger.error("JWT claims is empty, 잘못된 JWT 토큰 입니다.");
}
return false;
}
// JWT 토큰에서 사용자 정보 가져오기
public Claims getUserInfoFromToken(String token) {
return Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();
}
}
2. JWT 테스트
@GetMapping("/create-jwt")
public String createJwt(HttpServletResponse res) {
// Jwt 생성
String token = jwtUtil.createToken("grace", UserRoleEnum.CUSTOMER);
// Jwt 쿠키 저장
jwtUtil.addJwtToCookie(token, res);
return "createJwt : " + token;
}
@GetMapping("/get-jwt")
public String getJwt(@CookieValue(JwtUtil.AUTHORIZATION_HEADER) String tokenValue) {
// JWT 토큰 substring
String token = jwtUtil.substringToken(tokenValue);
// 토큰 검증
if(!jwtUtil.validateToken(token)){
throw new IllegalArgumentException("Token Error");
}
// 토큰에서 사용자 정보 가져오기
Claims info = jwtUtil.getUserInfoFromToken(token);
// 사용자 username
String username = info.getSubject();
System.out.println("username = " + username);
// 사용자 권한
String authority = (String) info.get(JwtUtil.AUTHORIZATION_KEY);
System.out.println("authority = " + authority);
return "getJwt : " + username + ", " + authority;
}