이 글은 아래의 글의 2편입니다
2022.11.28 - [프로그래밍/ㄴ 개인홈페이지] - :: Spring boot + JWT + OAuth2 + Vue + Redis 로그인을 만들어보자 1
이번 편에서는 util들을 설정해보자 간단한게 JWT와 Redis용 유틸들을 만들어보자
1. JWTUtil(manager)
@Log4j2
public class JwtManager {
@Value("${dev.hyns.secretkey}")
private String secretKey;
private final long DAY = 259200000 / 3;
public String AccessTokenGenerator(Long mid, String email) {
SecretKey key = Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8));
Date today = new Date();
String jwt = Jwts.builder()
.setIssuer("hyns.dev")
.setIssuedAt(today)
.setExpiration(new Date(today.getTime() + DAY/24/6))
.claim("email", email)
.claim("userNumber", mid)
.setSubject("bblog token")
.signWith(key, SignatureAlgorithm.HS256)
.compact();
return "Bearer "+jwt;
}
public String RefreshTokenGenerator(Long mid, String email) {
SecretKey key = Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8));
Date today = new Date();
String jwt = Jwts.builder()
.setIssuer("hyns.dev")
.setIssuedAt(today)
.setExpiration(new Date(today.getTime() + DAY*7))
.claim("email", email)
.claim("userNumber", mid)
.setSubject("bblog token")
.signWith(key, SignatureAlgorithm.HS256)
.compact();
return "Bearer "+jwt;
}
public Boolean tokenValidator(String token) {
try {
Jwts.parserBuilder()
.setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8))
.build()
.parseClaimsJws(token.split("Bearer ")[1]);
return true;
} catch (io.jsonwebtoken.security.SecurityException | MalformedJwtException e) {
log.info("잘못된 토큰", e);
return false;
} catch (ExpiredJwtException e) {
log.info("유효기간이 지난 토큰", e);
return false;
} catch (UnsupportedJwtException e) {
log.info("지원되지 않는 토큰", e);
return false;
} catch (IllegalArgumentException e) {
log.info("claims가 비어있음", e);
return false;
}
}
public Claims parseClaims(String token) {
try {
return Jwts.parserBuilder()
.setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8))
.build()
.parseClaimsJws(token.split("Bearer ")[1])
.getBody();
} catch (ExpiredJwtException e) {
return e.getClaims();
}
}
}
(리팩토링이 절실하다 액세스 토큰만 만들고 시작한 프로젝트인데 리프래시 토큰
따로 만들어서 여러 개 수정하기가 너무 귀찮아서 그냥 리프래시용을 하나 더 만들어버렸다 ㅂㄷㅂㄷ..... )
이렇게 생성용 하나, 증명용 하나, 그냥 말 그대로 payload용 하나 이렇게 만들어놨다
이제 아무 곳이나 가서 써먹기로 하고..
Redis도 설정해주자 제일 먼저 redis의 설정부터 해줬다
1. RedisConfig
@Configuration
@RequiredArgsConstructor
public class RedisConfig {
@Value("${dev.hyns.redishost}")
private String host;
@Value("${dev.hyns.redisport}")
private int port;
@Bean
public RedisConnectionFactory redisConnectionFactory() {
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
config.setHostName(host);
config.setPort(port);
return new LettuceConnectionFactory(config);
}
@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory());
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
return redisTemplate;
}
}
application.properties에 미리 설정해둔 host, port를 적어주고 (아까 도커에 적어둔 것과 똑같이 적으면 된다 )
나중에 쓰일 템플릿도 지정해준다
요즘에는 이거 안 써도 지 알아서 넣어준다는데.. 믿을 수가 없으니 그냥 넣어준다
그러고 나서 이 컨픽 파일을 이용해 사용할 유틸을 만들어준다
2. RedisUtil
@Component
@RequiredArgsConstructor
public class RedisUtil {
private final RedisTemplate<String, String> rt;
private final MembersRepository mrepo;
private final JwtManager manager;
@Transactional
public void setRefreshToken(String token, Long mid) {
ValueOperations<String, String> rToken = rt.opsForValue();
rToken.set(token, mid.toString(), Duration.ofDays(7L));
mrepo.loggedMember(mid, true);
}
@Transactional
public void removeRefreshToken(String rToken) {
Optional<String> mid = Optional.ofNullable(rt.opsForValue().get(rToken));
if (mid.isPresent()) {
mrepo.loggedMember(Long.parseLong(mid.get()), false);
rt.delete(rToken);
}
}
@Transactional
public TokenInfo tokenReIssueValidator(String aToken, String rToken) {
Optional<Members> targetMember = mrepo
.findById(Long.parseLong(manager.parseClaims(aToken)
.get("userNumber")
.toString()));
Long atkMid = targetMember
.orElse(
Members
.builder()
.mid(0L)
.oauth(false)
.build())
.getMid();
String mid = Optional
.ofNullable(rt.opsForValue().get(rToken))
.orElse("-1");
if (
Long.parseLong(mid) == atkMid
& manager.tokenValidator(rToken)
& isLogged(aToken)
) {
String rtkn = manager
.RefreshTokenGenerator(
targetMember.get().getMid(),
targetMember.get().getEmail()
);
String atkn = manager
.AccessTokenGenerator(
targetMember.get().getMid(),
targetMember.get().getEmail()
);
removeRefreshToken(rToken);
setRefreshToken(rtkn, targetMember.get().getMid());
return TokenInfo.builder()
.nickname(targetMember.get().getNickname())
.aToken(atkn)
.rToken(rtkn)
.build();
} else {
removeRefreshToken(rToken);
mrepo.loggedMember(Long.parseLong(mid), false);
return null;
}
}
public Boolean rTokenChecker(String rToken) {
return Optional
.ofNullable(rt.opsForValue()
.get(rToken))
.isPresent() ? true : false;
}
public Boolean adminChecker(String rToken) {
return mrepo.findById(Long.parseLong(manager.parseClaims(rToken)
.get("userNumber")
.toString())
)
.orElse(Members.builder().mid(0L).oauth(false).build()).getRoles().stream()
.anyMatch(v -> v.equals(Roles.ROLE_ADMIN));
}
public Boolean isLogged(String aToken) {
return mrepo.findById(Long.parseLong(manager.parseClaims(aToken)
.get("userNumber")
.toString())
)
.orElse(Members.builder().mid(0L).oauth(false).logged(false).build()).isLogged();
}
}
일단 간단하게 rtk를 설정해줄 set, 그다음 rm 두 개만 만들어주고 나머지는 validator, checker로 검사한다
원래 이렇게 길지 않았는데.. 뭐 하나 넣고 계속 넣고 수정하다 보니 이리 길어졌다
흑흑.. 너무 슬프다
이제 security의 설정으로 들어가보자
는.. 다음 편에서
3편
2022.11.28 - [프로그래밍/ㄴ 개인홈페이지] - :: Spring boot + JWT + OAuth2 + Redis로 Restful 로그인을 만들어보자 3
'프로그래밍 > 개인홈페이지' 카테고리의 다른 글
:: Spring boot + JWT + OAuth2 + Redis로 Restful 로그인을 만들어보자 完 (0) | 2022.11.28 |
---|---|
:: Spring boot + JWT + OAuth2 + Redis로 Restful 로그인을 만들어보자 3 (0) | 2022.11.28 |
:: Spring boot + JWT + OAuth2 + Redis로 Restful 로그인을 만들어보자 1 (0) | 2022.11.28 |
[Spring] @Autowired vs @RequiredArgsConstructor 뭘 쓸까 feat .IOC, DI, Autowiring (0) | 2022.11.26 |
:: 블로그 만들기 프로젝트 - 백앤드 작업 3 (0) | 2022.11.23 |