0. 프로젝트 소개(Spring Security) 및 Security에 대하여
1. User Entity 생성(Spring Security)
2. User 회원가입 (Spring Secureity)
3.Jwt의 사용 이유 (Spring Security)
4. Authorization(인증)
- 4-0 Authorization(인증) - 프로세스 설명
- 4-1 Authentication(인증) - UserDetails, UserDetailsService 및 JwtService 생성
- 4-2 Authentication(인증) - UsernamePasswordAuthenticationfilter 생성
- 4-3 Authentication(인증) - CorsFilter(선택사항)
- 4-4 Authentication(인증) - SecurityConfig
5. Authorization(인가)
- 5-1 Authorization(인가) - Jwt 인가 관련 에러 처리
- 5-2 Authorization(인가) - Jwt 인가 서비스 로직 추가
- 5-3 Authorization(인가) - AuthorizationFilter 생성
- 5-4 Authorization(인가) - SecurityConfig에 인가 관련 method 추가
6. OAuth
JwtService에 인가 관련 method 추가
/**
* @package com.example.spring_security.config.security.jwt.Service
* @class JwtServiceImpl
* @brief jwt 비즈니스로직 구현 클래스
* @author 최원호
* @date 2023.06.22
* version 1.0
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class JwtServiceImpl implements JwtService {
@Value("${jwt.secret}")
private String SECRET_KEY;
private final UserRepository userRepository;
/**
* @brief 토큰 생성
* @param email
* @return accessToken
*/
@Override
public String createAccessToken(String email) {
return JWT.create()
.withSubject(JwtProperties.ACCESS_TOKEN)
.withClaim(JwtProperties.EMAIL, email)
.withExpiresAt(new Date(System.currentTimeMillis()+JwtProperties.EXPIRATION_TIME))
.sign(Algorithm.HMAC512(SECRET_KEY));
}
/**
* @brief refresh 토큰 생성
* @return refreshToken
*/
@Override
public String createRefreshToken() {
return JWT.create()
.withSubject(JwtProperties.REFRESH_TOKEN)
.withExpiresAt(new Date(System.currentTimeMillis()+JwtProperties.EXPIRATION_TIME))
.sign(Algorithm.HMAC512(SECRET_KEY));
}
/**
* @brief email을 통한 유저 entity 조회
* @param email
* @return LoginResponseDto
*/
@Override
public LoginResponseDto retrieveByEmail(String email) {
User user = userRepository.findByEmail(email).orElseThrow();
return LoginResponseDto.builder().userId(user.getUserId())
.email(user.getEmail())
.password(user.getPassword())
.nickName(user.getNickname())
.profileUrl(user.getProfileUrl())
.refreshToken(user.getRefreshToken())
.userRole(user.getUserRole())
.socialType(user.getSocialType())
.build();
}
/**
* @brief refreshToken 수정
* @param userId
* @param refreshToken
*/
@Override
@Transactional
public void setRefreshToken(long userId, String refreshToken) {
userRepository.findById(userId).orElseThrow().updateRefreshToken(refreshToken);
}
/**
* @brief accessToken Header에 설정
* @param response
* @param accessToken
*/
@Override
public void setAccessTokenToHeader(HttpServletResponse response, String accessToken) {
response.setHeader(JwtProperties.ACCESS_TOKEN_HEADER,JwtProperties.TOKEN_PREFIX + accessToken);
}
/**
* @brief refreshToken Header에 설정
* @param response
* @param refreshToken
*/
@Override
public void setRefreshTokenToHeader(HttpServletResponse response, String refreshToken) {
response.setHeader(JwtProperties.REFRESH_TOKEN_HEADER, JwtProperties.TOKEN_PREFIX + refreshToken);
}
/**
* @brief 결과 메시지 구현
* @param result
* @param response
* @param message
*/
@Override
public void setResponseMessage(boolean result, HttpServletResponse response, String message) throws IOException {
JSONObject jObject = new JSONObject();
response.setContentType("application/json;charset=UTF-8");
if(result){
response.setStatus(HttpServletResponse.SC_OK);
jObject.put("success", true);
jObject.put("code", 200);
jObject.put("message", message);
} else {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
jObject.put("success", false);
jObject.put("code", 999);
jObject.put("message", message);
}
response.getWriter().print(jObject);
}
/**
* @brief Header에 정상적인 토큰 여부 확인
* @param req
* @return true
*/
@Override
public boolean isValidHeaderOrThrow(HttpServletRequest req) {
log.info("** Check whether there is token in header **");
String accessToken = req.getHeader(JwtProperties.ACCESS_TOKEN_HEADER);
String refreshToken = req.getHeader(JwtProperties.REFRESH_TOKEN_HEADER);
if(accessToken != null
&& refreshToken != null
&& accessToken.startsWith(JwtProperties.TOKEN_PREFIX)
&& refreshToken.startsWith(JwtProperties.TOKEN_PREFIX)){
return true;
} else {
throw new JwtException(JwtErrorMessage.JWT_HEADER_IS_NOT_VALID);
}
}
/**
* @brief Access Token 추출
* @param req
* @return accessToken
*/
@Override
public String replaceAccessToken(HttpServletRequest req) {
return req.getHeader(JwtProperties.ACCESS_TOKEN_HEADER).replace(JwtProperties.TOKEN_PREFIX,"");
}
/**
* @brief Refresh Token 추출
* @param req
* @return refreshToken
*/
@Override
public String replaceRefreshToken(HttpServletRequest req) {
return req.getHeader(JwtProperties.REFRESH_TOKEN_HEADER).replace(JwtProperties.TOKEN_PREFIX,"");
}
/**
* @brief 토큰을 해석하여 정상 여부 확인
* @param refreshToken
* @return true
*/
@Override
public boolean isNotExpiredRefreshToken(String refreshToken) {
try {
log.info("** check the refresh token **");
JWT.require(Algorithm.HMAC512(SECRET_KEY))
.build()
.verify(refreshToken);
} catch (Exception e){
throw new JwtException(JwtErrorMessage.JWT_REFRESH_IS_NOT_VALID);
}
return true;
}
/**
* @param accessToken
* @brief accessToken 확인
* @details accessToken의 만료 여부를 확인한다
* @return true
*/
@Override
public boolean isNotExpiredAccessToken(String accessToken) {
try {
log.info("** check the access token **");
JWT.require(Algorithm.HMAC512(SECRET_KEY))
.build()
.verify(accessToken);
return true;
} catch (TokenExpiredException e) {
throw new JwtException(JwtErrorMessage.JWT_ACCESS_IS_EXPIRED);
} catch (Exception e2) {
throw new JwtException(JwtErrorMessage.JWT_ACCESS_IS_NOT_VALID);
}
}
/**
* @brief 토큰을 통한 User 조회
* @param refreshToken
* @return loginResponseDto
*/
@Override
@Transactional
public LoginResponseDto selectByRefreshToken(String refreshToken) {
User user = userRepository.findByRefreshToken(refreshToken).orElseThrow();
return LoginResponseDto.builder()
.userId(user.getUserId())
.email(user.getEmail())
.password(user.getPassword())
.userRole(user.getUserRole())
.build();
}
/**
* @brief 토큰의 10경과 여부 확인
* @param refreshToken
* @return true
*/
@Override
public boolean checkTokenIsMadeInTendays(String refreshToken) {
try {
Date expiresDate = JWT.require(Algorithm.HMAC512(SECRET_KEY)).build().verify(refreshToken).getExpiresAt();
Date currentDate = new Date();
return expiresDate.before(currentDate) ? true : false;
} catch (TokenExpiredException e) {
throw new JwtException(JwtErrorMessage.JWT_REFRESH_IS_EXPIRED);
} catch (Exception e2) {
throw new JwtException(JwtErrorMessage.JWT_REFRESH_IS_NOT_VALID);
}
}
/**
* @brief Refresh Token 재설정
* @param email
* @param refreshToken
* @return refreshToken
*/
@Override
public String updateRefreshToken(String email, String refreshToken) {
String newRefreshToken = createRefreshToken();
User user = userRepository.findByEmail(email).orElseThrow();
user.updateRefreshToken(newRefreshToken);
return newRefreshToken;
}
}
토큰 정상 여부 확인
@Override
public boolean isValidHeaderOrThrow(HttpServletRequest req) {
log.info("** Check whether there is token in header **");
String accessToken = req.getHeader(JwtProperties.ACCESS_TOKEN_HEADER);
String refreshToken = req.getHeader(JwtProperties.REFRESH_TOKEN_HEADER);
if(accessToken != null
&& refreshToken != null
&& accessToken.startsWith(JwtProperties.TOKEN_PREFIX)
&& refreshToken.startsWith(JwtProperties.TOKEN_PREFIX)){
return true;
} else {
throw new JwtException(JwtErrorMessage.JWT_HEADER_IS_NOT_VALID);
}
}
토큰의 유무와 토큰 인증 방식이 Bearer인지 확인한다.
토큰 추출
/**
* @brief Access Token 추출
* @param req
* @return accessToken
*/
@Override
public String replaceAccessToken(HttpServletRequest req) {
return req.getHeader(JwtProperties.ACCESS_TOKEN_HEADER).replace(JwtProperties.TOKEN_PREFIX,"");
}
/**
* @brief Refresh Token 추출
* @param req
* @return refreshToken
*/
@Override
public String replaceRefreshToken(HttpServletRequest req) {
return req.getHeader(JwtProperties.REFRESH_TOKEN_HEADER).replace(JwtProperties.TOKEN_PREFIX,"");
}
인증방식을 제외한 순수 토큰의 값을 추출하는 메소드이다.
토큰 유효 여부 확인
/**
* @brief 토큰을 해석하여 정상 여부 확인
* @param refreshToken
* @return true
*/
@Override
public boolean isNotExpiredToken(String refreshToken) {
try {
JWT.require(Algorithm.HMAC512(SECRET_KEY))
.build()
.verify(refreshToken);
} catch (Exception e){
throw new JwtException(JwtErrorMessage.JWT_REFRESH_IS_NOT_VALID);
}
return true;
}
/**
* @param accessToken
* @brief accessToken 확인
* @details accessToken의 만료 여부를 확인한다
* @return true
*/
@Override
public boolean checkValidToken(String accessToken) {
try {
JWT.require(Algorithm.HMAC512(SECRET_KEY))
.build()
.verify(accessToken);
return true;
} catch (TokenExpiredException e) {
throw new JwtException(JwtErrorMessage.JWT_ACCESS_IS_EXPIRED);
} catch (Exception e2) {
throw new JwtException(JwtErrorMessage.JWT_ACCESS_IS_NOT_VALID);
}
}
시크릿 키를 이용하여 정상적인 토큰인가를 확인하는 메소드이다.
'SpringBoot Security' 카테고리의 다른 글
5-4 Authorization(인가) - SecurityConfig에 인가 관련 method 추가 (0) | 2023.07.11 |
---|---|
5-3 Authorization(인가) - AuthorizationFilter 생성 (0) | 2023.07.11 |
5-1 Authorization(인가) - Jwt 인가 관련 에러 처리 (0) | 2023.07.11 |
4-4 Authentication(인증) - SecurityConfig (0) | 2023.07.09 |
4-3 Authentication(인증) - CorsFilter(선택사항) (0) | 2023.07.09 |