반응형
잊지 않기위해 저장저장
jwt 토큰 발급해 세션 저장하는 로직 구현
세션은 redis에 저장한다.
세션 하루 만료 기준!
세션이 발급되면 jwt토큰값은 모든 API를 헤더값에 넣어서 보내준다.
만료되면 다시 발급요청! 하기
AuthInterceptor.java
@Component
@Slf4j
public class AuthInterceptor implements HandlerInterceptor {
@Autowired
private JwtUtil jwtUtil;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String jwtToken = request.getHeader(HttpHeaders.AUTHORIZATION);
String uri = request.getRequestURI();
if(jwtToken != null){
try {
if(jwtUtil.isUsable(jwtToken)) { // JWT 토큰이 유효하면
return true;
}
throw new InterceptorException(InterceptorExceptionEnum.UNAUTHORIZED);
} catch (MalformedJwtException e) { // 위조 시도
log.error("Malformed Jwt Token: {}", e.getMessage());
throw new InterceptorException(InterceptorExceptionEnum.COUNTERFEIT);
} catch (ExpiredJwtException e) { // 만료된 토큰
log.error("ExpiredJ Jwt Token: {}", e.getMessage());
throw new InterceptorException(InterceptorExceptionEnum.EXPIREDTOKEN);
}
} else { // 토큰이 없음
if(uri.contains("/token")) { // 토큰 발급
log.info("Generate Token, Request URI: {}", uri);
return true;
}
throw new InterceptorException(InterceptorExceptionEnum.UNAUTHORIZED);
}
// return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.debug("Interceptor > postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object object, Exception arg3) throws Exception {
log.debug("Interceptor > afterCompletion" );
}
}
jwtUtil.java
package com.kt.cleanpass.loginservice.util;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.util.Date;
import com.kt.cleanpass.loginservice.domain.user.UserInfo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
@Component
public class JwtUtil{
@Value("${jwt.secret}")
private String SECRET_KEY;
private String DATA_KEY = "userId";
public final static long TOKEN_VALIDATION_SECOND = 1000L * 3600 * 24 * 1;
public final static long REFRESH_TOKEN_VALIDATION_SECOND = 1000L * 3600 * 24 * 1;
//토큰 생성
public String createToken(String userId) {
return doCreateToken(userId, TOKEN_VALIDATION_SECOND);
}
public String createRefreshToken(String userId) {
return doCreateToken(userId, REFRESH_TOKEN_VALIDATION_SECOND);
}
public String extendExpireDate(String token){
long curTime = System.currentTimeMillis();
Jws<Claims> claims = Jwts.parser()
.setSigningKey(getSigningKey(SECRET_KEY))
.parseClaimsJws(token);
return claims.getBody().setExpiration(new Date(curTime + REFRESH_TOKEN_VALIDATION_SECOND)).getSubject();
}
private String doCreateToken(String userId, long expireTime){
long curTime = System.currentTimeMillis();
String sessionToken = Jwts.builder()
.setHeaderParam("typ", "JWT") // JWT Header가 지닐 정보(default : HS256)
.setExpiration(new Date(curTime + expireTime)) // 만료 시각 (+1일)
.setIssuedAt(new Date(curTime)) // 발급 시각
.claim(DATA_KEY, userId) //Payload에 Private Claims를 담기 위해 claim method를 이용
.signWith(getSigningKey(SECRET_KEY), SignatureAlgorithm.HS256) // 복호화할때 서버만이 알고 있는 비밀키로 해쉬를 하여 생성
.compact();
//세션토큰 발급 완료
return sessionToken;
}
private Key getSigningKey(String secretKey) {
byte[] keyBytes = secretKey.getBytes(StandardCharsets.UTF_8);
return Keys.hmacShaKeyFor(keyBytes);
}
//session 에 있는지 확인
public boolean isUsable(String jwtToken) {
try{
Jws<Claims> claims = Jwts.parser()
.setSigningKey(getSigningKey(SECRET_KEY))
.parseClaimsJws(jwtToken);
System.out.println(claims.getBody());
return true;
}catch (Exception e) {
return false;
}
}
// 토큰이 유효한 토큰인지 검사한 후, 토큰에 담긴 Payload 값을 가져온다.
public Claims extractAllClaims(String jwtToken) throws ExpiredJwtException {
return Jwts.parser()
.setSigningKey(getSigningKey(SECRET_KEY))
.parseClaimsJws(jwtToken)
.getBody();
}
//만료된 토큰인지 체크
public Boolean isTokenExpired(String token) {
final Date expiration = extractAllClaims(token).getExpiration();
return expiration.before(new Date());
}
public String getUserId(String token) {
return extractAllClaims(token).get("userId", String.class);
}
public Boolean validateToken(String token, UserInfo user) {
final String userId = getUserId(token);
return (userId.equals(user.getUserId()) && !isTokenExpired(token));
}
}
세션 관리 (redis)
sessionUtil.java
@Slf4j
@Component
public class SessionUtil {
@Autowired
UserSessionRepository userSessionRepository;
public Optional<UserSession> getSession(String jwtToken) {
return userSessionRepository.findById(jwtToken);
}
public void saveSession(UserSession userSession) {
userSessionRepository.save(userSession);
return;
}
}
loginService.java
@Service
@Slf4j
@Component
public class LoginService {
@Autowired
UserSessionRepository userSessionRepository;
@Autowired
UserInfoRepository userInfoRepository;
@Autowired
JwtUtil jwtUtil;
@Autowired
SessionUtil sessionUtil;
public void saveSession(UserInfo user, String jwtToken) {
// 세션 저장
Map<String, Object> sessionData = new HashMap<>();
sessionData.put("userId", user.getUserId());
sessionData.put("email", user.getEmail());
sessionData.put("name", user.getName());
sessionData.put("deviceId", user.getDeviceId());
UserSession userSession = UserSession.builder()
.userId(user.getUserId())
.createdTime(LocalDateTime.now())
.expiredTime(LocalDateTime.now().plusDays(1))
.sessionData(sessionData)
.build();
log.debug("##### {} #####", userSession.toString());
try{
sessionUtil.saveSession(userSession);
} catch(RedisException e) {
log.debug(e.toString());
}
}
public String extendExpireDate(String userId){
String returnString;
UserSession userSession = userSessionRepository.findById(userId).get();
userSession.update(LocalDateTime.now().plusDays(1));
try{
userSessionRepository.save(userSession);
log.debug("레디스 만료일자 업데이트 성공");
returnString = "만료일자 업데이트 성공";
} catch(RedisException e) {
log.debug(e.toString());
returnString = "만료일자 업데이트 실패";
}
return returnString;
}
public Optional<UserSession> getSession(UserInfo user) {
Optional<UserSession> userSession = userSessionRepository.findById(user.getUserId());
return userSession;
}
public void saveUserInfo(UserInfo user) {
userInfoRepository.save(user);
}
@Transactional
public void procLogin(UserInfo user, String jwtToken) { // 로그인
Optional<UserInfo> userinfo = userInfoRepository.findById(user.getUserId());
if(!userinfo.isPresent()) { // 디비에 회원 정보가 없으면 없으면 저장
user.setCretrId("SYSTEM");
saveUserInfo(user);
}
saveSession(user, jwtToken); // 세션에 데이터 저장
}
}
loginController.java
@RestController
@RequestMapping("login")
public class LoginController {
@Autowired
LoginService loginService;
@Autowired
JwtUtil jwtUtil;
@PostMapping("")
@ResultConvert
public Object procLogin(@RequestBody UserInfo user, HttpServletRequest request) {
String jwtToken = request.getHeader(HttpHeaders.AUTHORIZATION);
loginService.procLogin(user, jwtToken);
return null;
}
@PostMapping("/token")
@ResultConvert
public Object generateToken(@RequestBody String userId) {
String jwtToken = jwtUtil.createToken(userId);
Map<String, Object> result = new HashMap<>();
result.put("accessToken", jwtToken);
return result;
}
@PostMapping("/session")
@ResultConvert
public Object getUserSession(@RequestBody UserInfo user, HttpServletRequest request) {
Optional<UserSession> findSession = loginService.getSession(user);
return findSession;
}
}
userInfo.java , UserInfoRepository.java
///////////////////// UserInfoRepository.java
public interface UserInfoRepository extends JpaRepository<UserInfo, String>{
}
///////////////////// userInfo.java
@Entity
@Data
@ToString
@NoArgsConstructor
@JsonIgnoreProperties({"hibernateLazyInitializer"})
public class UserInfo implements Serializable {
@Id
private String userId;
@Column(length=15)
private String name;
@Column(length=40)
private String email;
@Column(length=13)
private String phone;
@Transient
private String deviceId;
@Column(length=20, nullable=false, columnDefinition="SYSTEM")
private String cretrId;
@Column(nullable=true)
// @JsonFormat(pattern="yyyy-MM-dd kk:mm:ss")
// private LocalDateTime cretDt;
String cretDt;
}
반응형
'개발(라이브러리,프레임워크) > Spring boot' 카테고리의 다른 글
CORS 설정 (0) | 2021.07.02 |
---|---|
백엔드 Oauth SNS로그인 구현하기 (0) | 2021.06.28 |
vsCode 에서 JUnit Test (0) | 2021.05.26 |
JPA (0) | 2021.04.04 |
Bean 생성 관리 (0) | 2021.03.29 |