Spring Boot 2.7부터 H2가 업데이트 되어 USER가 예악어로 등록.
@Entity(name = "users")로 테이블 이름을 USER와 안겹치게 설정해주어야 한다.
userRole은 Enum으로 생성해서 Entity에 넣음
package com.sparta.myselectshop.entity;
public enum UserRoleEnum {
USER, // 사용자 권한
ADMIN // 관리자 권한
}
@Column(nullable = false)
@Enumerated(value = EnumType.STRING)
private UserRoleEnum role;
compileOnly group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.11.2'
runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.11.2'
runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.2'
jwt.secret.key=7ZWt7ZW0OTntmZTsnbTtjIXtlZzqta3snYTrhIjrqLjshLjqs4TroZzrgpjslYTqsIDsnpDtm4zrpa3tlZzqsJzrsJzsnpDrpbzrp4zrk6TslrTqsIDsnpA=
// Header KEY 값
public static final String AUTHORIZATION_HEADER = "Authorization";
// 사용자 권한 값의 KEY
public static final String AUTHORIZATION_KEY = "auth";
// Token 식별자
private static final String BEARER_PREFIX = "Bearer ";
// 토큰 만료시간 밀리세컨드 기준이라 60 * 60 * 1000L면 한시간
private static final long TOKEN_TIME = 60 * 60 * 1000L;
@Value("${jwt.secret.key}") //application.properties의 key값을 @value로 가져옴
private String secretKey;
private Key key;
private final SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
// enum 알고리즘안에 여러 암호화 알고리즘이 있어 골라서 쓰면 된다.
@PostConstruct // 처음 이 객체가 생성될 때 초기화하는 함수
public void init() {
byte[] bytes = Base64.getDecoder().decode(secretKey); // byte[]로 디코딩
key = Keys.hmacShaKeyFor(bytes); // 디코딩한 값을 키객체에 넣어줌
}
@PostConstruct : 의존성 주입이 이루어진 후 초기화를 수행하는 메서드
@PostConstruct를 사용하면, bean이 초기화 됨과 동시에 의존성을 확인할 수 있다.
Header에서 Token 가져오기
// header 토큰을 가져오기
public String resolveToken(HttpServletRequest request) {
String bearerToken = request.getHeader(AUTHORIZATION_HEADER);
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(BEARER_PREFIX)) {
return bearerToken.substring(7); //"BEARER "까지 7글자 떼냄
}
return null;
}
// 토큰 생성
public String createToken(String username, UserRoleEnum role) {
Date date = new Date();
return BEARER_PREFIX + //위의 토큰에서 "BEARER " 7글자 떼내서 다시 붙임
Jwts.builder()
.setSubject(username) // 특정 공간에 유저이름 넣고
.claim(AUTHORIZATION_KEY, role) // claim공간엔 사용자 권한넣고
.setExpiration(new Date(date.getTime() + TOKEN_TIME)) // 유효기간 지정(지금기준에서+유효기간)
.setIssuedAt(date) // 토큰 생성 시간 넣어줌. 필수는 아님
.signWith(key, signatureAlgorithm) // 위에서 만든 key객체와 어떤 알고리즘으로 암호화할건지 지정
.compact();
// String 형식의 JWT토큰으로 반환.
}
// 토큰 검증
public boolean validateToken(String token) {
try { // parserBuilder()로 검증. set~에 토큰 만들때 쓴 key 넣어주고, parse~에 검증할 토큰 넣는다.
Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token);
return true;
} catch (SecurityException | MalformedJwtException e) {
log.info("Invalid JWT signature, 유효하지 않는 JWT 서명 입니다.");
} catch (ExpiredJwtException e) {
log.info("Expired JWT token, 만료된 JWT token 입니다.");
} catch (UnsupportedJwtException e) {
log.info("Unsupported JWT token, 지원되지 않는 JWT 토큰 입니다.");
} catch (IllegalArgumentException e) {
log.info("JWT claims is empty, 잘못된 JWT 토큰 입니다.");
}
return false;
}
// 위의 검증과 거의 비슷한 키지만 마지막에 .getBody()로 안의 정보를 가져옴
// 위의 검증에서 이미 유효성 검사를 했다는 가정하라서 try-catch가 없다.
public Claims getUserInfoFromToken(String token) {
return Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token).getBody();
}
@PostMapping("/products")
public ProductResponseDto createProduct(@RequestBody ProductRequestDto requestDto, HttpServletRequest request) {
@Transactional(readOnly = true)
public List<ProductResponseDto> getProducts(HttpServletRequest request) {
// Request에서 Token 가져오기
String token = jwtUtil.resolveToken(request); // 토큰을 가져온다
Claims claims; //JWT안에 들어있는 정보들을 담을 수 있는 객체
// 토큰이 있는 경우에만 관심상품 조회 가능
if(token != null) {
// Token 검증 .validateToken()은 검증이 잘되면 true
if (jwtUtil.validateToken(token)) {
// 토큰에서 사용자 정보 가져와서 claims에 넣음. 문제가 생기면 예외처리
claims = jwtUtil.getUserInfoFromToken(token);
} else {
throw new IllegalArgumentException("Token Error");
}
// 토큰에서 가져온 사용자 정보를 사용하여 DB 조회 / claims.getSubject()를 하면 username 가져온다
User user = userRepository.findByUsername(claims.getSubject()).orElseThrow(
() -> new IllegalArgumentException("사용자가 존재하지 않습니다.")
);
// 사용자 권한 가져와서 ADMIN 이면 전체 조회, USER 면 본인이 추가한 부분 조회
UserRoleEnum userRoleEnum = user.getRole();
System.out.println("role = " + userRoleEnum);
List<ProductResponseDto> list = new ArrayList<>();
List<Product> productList;
if (userRoleEnum == UserRoleEnum.USER) {
// 사용자 권한이 USER일 경우 USER것만 가져오고 관리자면 다 가져옴
productList = productRepository.findAllByUserId(user.getId());
} else {
productList = productRepository.findAll();
}
// 그렇게 가져온 productlist를 DTO에 담아서 반환
for (Product product : productList) {
list.add(new ProductResponseDto(product));
}
return list;
}else {
return null;
}
}