Skip to content

Commit

Permalink
Merge pull request #64 from WE-ARE-RACCOONS/RAC-223
Browse files Browse the repository at this point in the history
RAC-223 : ๏ฟฝ๋กœ๊ทธ์•„์›ƒ ์ถ”๊ฐ€
  • Loading branch information
ywj9811 authored Dec 11, 2023
2 parents a562733 + f6e19e1 commit c09fe86
Show file tree
Hide file tree
Showing 17 changed files with 103 additions and 74 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import com.postgraduate.domain.auth.application.dto.res.JwtTokenResponse;
import com.postgraduate.domain.user.domain.entity.User;
import com.postgraduate.global.config.security.jwt.JwtProvider;
import com.postgraduate.global.config.security.jwt.util.JwtUtils;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
Expand All @@ -11,7 +11,7 @@
@RequiredArgsConstructor
@Service
public class JwtUseCase {
private final JwtProvider jwtProvider;
private final JwtUtils jwtUtils;
@Value("${jwt.refreshExpiration}")
private int refreshExpiration;
@Value("${jwt.accessExpiration}")
Expand All @@ -20,15 +20,19 @@ public class JwtUseCase {
public JwtTokenResponse signIn(User user) {
return generateToken(user);
}

public void logout(User user) {
jwtUtils.makeExpired(user.getUserId());
}

public JwtTokenResponse regenerateToken(User user, HttpServletRequest request) {
jwtProvider.checkRedis(user.getUserId(), request);
jwtUtils.checkRedis(user.getUserId(), request);
return generateToken(user);
}

private JwtTokenResponse generateToken(User user) {
String accessToken = jwtProvider.generateAccessToken(user.getUserId(), user.getRole());
String refreshToken = jwtProvider.generateRefreshToken(user.getUserId(), user.getRole());
String accessToken = jwtUtils.generateAccessToken(user.getUserId(), user.getRole());
String refreshToken = jwtUtils.generateRefreshToken(user.getUserId(), user.getRole());
return new JwtTokenResponse(accessToken, accessExpiration, refreshToken, refreshExpiration, user.getRole());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,20 @@
import com.postgraduate.domain.auth.application.usecase.SignInUseCase;
import com.postgraduate.domain.auth.application.usecase.oauth.kakao.KakaoSignInUseCase;
import com.postgraduate.domain.auth.exception.OauthException;
import com.postgraduate.domain.auth.presentation.constant.Provider;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

import static com.postgraduate.domain.auth.presentation.constant.Provider.KAKAO;

@RequiredArgsConstructor
@Component
public class SelectOauth {
private final KakaoSignInUseCase kakaoSignInUseCase;

public SignInUseCase selectStrategy(String provider) {
switch (provider) {
case "KAKAO" : return kakaoSignInUseCase;
default: throw new OauthException();
}
public SignInUseCase selectStrategy(Provider provider) {
if (provider.equals(KAKAO))
return kakaoSignInUseCase;
throw new OauthException();
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.postgraduate.domain.auth.exception;

import static com.postgraduate.domain.auth.presentation.contant.AuthResponseCode.AUTH_KAKAO_CODE;
import static com.postgraduate.domain.auth.presentation.contant.AuthResponseMessage.KAKAO_CODE;
import static com.postgraduate.domain.auth.presentation.constant.AuthResponseCode.AUTH_KAKAO_CODE;
import static com.postgraduate.domain.auth.presentation.constant.AuthResponseMessage.KAKAO_CODE;

public class KakaoCodeException extends AuthException{
public KakaoCodeException() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.postgraduate.domain.auth.exception;

import static com.postgraduate.domain.auth.presentation.contant.AuthResponseCode.AUTH_INVALID_KAKAO;
import static com.postgraduate.domain.auth.presentation.contant.AuthResponseMessage.KAKAO_INVALID;
import static com.postgraduate.domain.auth.presentation.constant.AuthResponseCode.AUTH_INVALID_KAKAO;
import static com.postgraduate.domain.auth.presentation.constant.AuthResponseMessage.KAKAO_INVALID;

public class KakaoException extends AuthException{
public KakaoException() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.postgraduate.domain.auth.exception;

import static com.postgraduate.domain.auth.presentation.contant.AuthResponseCode.NONE_PROVIDER;
import static com.postgraduate.domain.auth.presentation.contant.AuthResponseMessage.PROVIDER_NONE;
import static com.postgraduate.domain.auth.presentation.constant.AuthResponseCode.NONE_PROVIDER;
import static com.postgraduate.domain.auth.presentation.constant.AuthResponseMessage.PROVIDER_NONE;

public class OauthException extends AuthException{
public OauthException() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.postgraduate.domain.auth.exception;

import static com.postgraduate.domain.auth.presentation.contant.AuthResponseCode.AUTH_DENIED;
import static com.postgraduate.domain.auth.presentation.contant.AuthResponseMessage.PERMISSION_DENIED;
import static com.postgraduate.domain.auth.presentation.constant.AuthResponseCode.AUTH_DENIED;
import static com.postgraduate.domain.auth.presentation.constant.AuthResponseMessage.PERMISSION_DENIED;


public class PermissionDeniedException extends AuthException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.postgraduate.domain.auth.application.usecase.SignUpUseCase;
import com.postgraduate.domain.auth.application.usecase.jwt.JwtUseCase;
import com.postgraduate.domain.auth.application.usecase.SignInUseCase;
import com.postgraduate.domain.auth.presentation.constant.Provider;
import com.postgraduate.domain.user.domain.entity.User;
import com.postgraduate.global.dto.ResponseDto;
import io.swagger.v3.oas.annotations.Operation;
Expand All @@ -19,8 +20,8 @@
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;

import static com.postgraduate.domain.auth.presentation.contant.AuthResponseCode.*;
import static com.postgraduate.domain.auth.presentation.contant.AuthResponseMessage.*;
import static com.postgraduate.domain.auth.presentation.constant.AuthResponseCode.*;
import static com.postgraduate.domain.auth.presentation.constant.AuthResponseMessage.*;
import static com.postgraduate.domain.senior.presentation.constant.SeniorResponseCode.SENIOR_CREATE;
import static com.postgraduate.domain.senior.presentation.constant.SeniorResponseMessage.CREATE_SENIOR;

Expand All @@ -35,7 +36,7 @@ public class AuthController {

@PostMapping("/login/{provider}")
@Operation(summary = "์†Œ์…œ ๋กœ๊ทธ์ธ", description = "ํšŒ์›์ธ ๊ฒฝ์šฐ JWT๋ฅผ, ํšŒ์›์ด ์•„๋‹Œ ๊ฒฝ์šฐ socialId๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค(ํšŒ์›๊ฐ€์ž…์€ ์ง„ํ–‰ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค).")
public ResponseDto<?> authLogin(@RequestBody CodeRequest request, @PathVariable String provider) {
public ResponseDto<?> authLogin(@RequestBody CodeRequest request, @PathVariable Provider provider) {
SignInUseCase signInUseCase = selectOauth.selectStrategy(provider);
AuthUserResponse authUser = signInUseCase.getUser(request);
if (authUser.getUser().isEmpty())
Expand All @@ -44,11 +45,12 @@ public ResponseDto<?> authLogin(@RequestBody CodeRequest request, @PathVariable
return ResponseDto.create(AUTH_ALREADY.getCode(), SUCCESS_AUTH.getMessage(), jwtToken);
}

// @PostMapping("/logout")
// @Operation(summary = "๋กœ๊ทธ์•„์›ƒ", description = "ํ† ํฐ ๊ฐ™์ด ๋ณด๋‚ด์ฃผ์„ธ์š”")
// public ResponseDto logout() {
// return ResponseDto.create(AUTH_CREATE.getCode(), SUCCESS_AUTH.getMessage());
// }
@PostMapping("/logout")
@Operation(summary = "๋กœ๊ทธ์•„์›ƒ", description = "ํ† ํฐ ๊ฐ™์ด ๋ณด๋‚ด์ฃผ์„ธ์š”")
public ResponseDto logout(@AuthenticationPrincipal User user) {
jwtUseCase.logout(user);
return ResponseDto.create(AUTH_DELETE.getCode(), LOGOUT_USER.getMessage());
}

@PostMapping("/user/signup")
@Operation(summary = "๋Œ€ํ•™์ƒ ํšŒ์›๊ฐ€์ž…", description = "๋กœ๊ทธ์ธ API์—์„œ ๋ฐ˜ํ™˜ํ•œ socialId, ๋‹‰๋„ค์ž„, ๋ฒˆํ˜ธ, ๋งˆ์ผ€ํŒ… ์ˆ˜์‹ ์—ฌ๋ถ€, ํฌ๋ง ํ•™๊ณผ, ํฌ๋ง ๋ถ„์•ผ, ๋งค์นญ ํฌ๋ง ์—ฌ๋ถ€")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.postgraduate.domain.auth.presentation.contant;
package com.postgraduate.domain.auth.presentation.constant;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.postgraduate.domain.auth.presentation.contant;
package com.postgraduate.domain.auth.presentation.constant;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
Expand All @@ -9,6 +9,7 @@ public enum AuthResponseMessage {
SUCCESS_AUTH("์‚ฌ์šฉ์ž ์ธ์ฆ์— ์„ฑ๊ณตํ•˜์˜€์Šต๋‹ˆ๋‹ค."),
NOT_REGISTERED_USER("๊ฐ€์ž…ํ•˜์ง€ ์•Š์€ ์œ ์ €์ž…๋‹ˆ๋‹ค."),
SUCCESS_REGENERATE_TOKEN("ํ† ํฐ ์žฌ๋ฐœ๊ธ‰์— ์„ฑ๊ณตํ•˜์˜€์Šต๋‹ˆ๋‹ค."),
LOGOUT_USER("๋กœ๊ทธ์•„์›ƒ์— ์„ฑ๊ณตํ•˜์˜€์Šต๋‹ˆ๋‹ค."),

PERMISSION_DENIED("๊ถŒํ•œ์ด ์—†์Šต๋‹ˆ๋‹ค."),
KAKAO_INVALID("์นด์นด์˜ค ์ •๋ณด๊ฐ€ ์œ ํšจํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค."),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.postgraduate.domain.auth.presentation.constant;

public enum Provider {
KAKAO;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import com.postgraduate.global.config.security.jwt.filter.CustomAccessDeniedHandler;
import com.postgraduate.global.config.security.jwt.filter.CustomAuthenticationEntryPoint;
import com.postgraduate.global.config.security.jwt.filter.JwtFilter;
import com.postgraduate.global.config.security.jwt.JwtProvider;
import com.postgraduate.global.config.security.jwt.util.JwtUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
Expand All @@ -25,7 +25,7 @@
@RequiredArgsConstructor
public class SecurityConfig {
private static final String[] PASS = {"/resource/**", "/css/**", "/js/**", "/img/**", "/lib/**"};
private final JwtProvider jwtProvider;
private final JwtUtils jwtProvider;
private final CustomAuthenticationEntryPoint customAuthenticationEntryPoint;
private final CustomAccessDeniedHandler customAccessDeniedHandler;
@Value("${aesBytesEncryptor.secret}")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.postgraduate.global.config.security.jwt.constant;

public enum Type {
ACCESS, REFRESH
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@

import java.io.IOException;

import static com.postgraduate.domain.auth.presentation.contant.AuthResponseCode.AUTH_DENIED;
import static com.postgraduate.domain.auth.presentation.contant.AuthResponseMessage.PERMISSION_DENIED;
import static com.postgraduate.domain.auth.presentation.constant.AuthResponseCode.AUTH_DENIED;
import static com.postgraduate.domain.auth.presentation.constant.AuthResponseMessage.PERMISSION_DENIED;

@Component
@RequiredArgsConstructor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@

import java.io.IOException;

import static com.postgraduate.domain.auth.presentation.contant.AuthResponseCode.AUTH_FAILED;
import static com.postgraduate.domain.auth.presentation.contant.AuthResponseMessage.FAILED_AUTH;
import static com.postgraduate.domain.auth.presentation.constant.AuthResponseCode.AUTH_FAILED;
import static com.postgraduate.domain.auth.presentation.constant.AuthResponseMessage.FAILED_AUTH;

@Component
@RequiredArgsConstructor
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.postgraduate.global.config.security.jwt.filter;

import com.postgraduate.global.config.security.jwt.JwtProvider;
import com.postgraduate.global.config.security.jwt.util.JwtUtils;
import com.postgraduate.global.exception.ApplicationException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
Expand All @@ -15,10 +15,13 @@

import java.io.IOException;

import static com.postgraduate.global.config.security.jwt.constant.Type.ACCESS;
import static com.postgraduate.global.config.security.jwt.constant.Type.REFRESH;

@RequiredArgsConstructor
@Slf4j
public class JwtFilter extends OncePerRequestFilter {
private final JwtProvider jwtProvider;
private final JwtUtils jwtProvider;
private final String AUTHORIZATION = "Authorization";
private final String BEARER = "Bearer";

Expand All @@ -27,9 +30,16 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
String token = resolveToken(request);
if (token != null) {
log.info("ํ† ํฐ ํ•จ๊ป˜ ์š”์ฒญ : {}", token);
if (!jwtProvider.validateToken(response, token))
return;
try {
if (request.getRequestURI().contains("/refresh")) {
log.info("์žฌ๋ฐœ๊ธ‰ ์ง„ํ–‰");
if (!jwtProvider.validateToken(response, token, REFRESH))
return;
} else {
log.info("์ผ๋ฐ˜ ์ ‘๊ทผ");
if (!jwtProvider.validateToken(response, token, ACCESS))
return;
}
Authentication authentication = jwtProvider.getAuthentication(response, token);
SecurityContextHolder.getContext().setAuthentication(authentication);
log.info("context ์ธ์ฆ ์ •๋ณด ์ €์žฅ : {}", authentication.getName());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package com.postgraduate.global.config.security.jwt;
package com.postgraduate.global.config.security.jwt.util;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.postgraduate.domain.user.exception.UserNotFoundException;
import com.postgraduate.domain.user.domain.entity.User;
import com.postgraduate.domain.user.domain.entity.constant.Role;
import com.postgraduate.global.auth.AuthDetails;
import com.postgraduate.global.auth.AuthDetailsService;
import com.postgraduate.global.config.security.jwt.constant.Type;
import com.postgraduate.global.config.security.jwt.exception.InvalidRefreshTokenException;
import com.postgraduate.global.config.security.jwt.exception.InvalidTokenException;
import com.postgraduate.global.config.redis.RedisRepository;
Expand Down Expand Up @@ -35,10 +36,13 @@
import java.util.Date;
import java.util.List;

import static com.postgraduate.global.config.security.jwt.constant.Type.ACCESS;
import static com.postgraduate.global.config.security.jwt.constant.Type.REFRESH;

@Component
@RequiredArgsConstructor
@Slf4j
public class JwtProvider {
public class JwtUtils {
private final AuthDetailsService authDetailsService;
private final RedisRepository redisRepository;
private final LogService logService;
Expand All @@ -49,14 +53,18 @@ public class JwtProvider {
private int refreshExpiration;
@Value("${jwt.accessExpiration}")
private int accessExpiration;
private final String ROLE = "role";
private final String REFRESH = "refresh";
private final String AUTHORIZATION = "Authorization";
private static final String ROLE = "role";
private static final String TYPE = "type";
private static final String AUTHORIZATION = "Authorization";
private static final int STATUS = 500;
private static final String CONTENT_TYPE = "application/json";
private static final String CHARACTER_ENCODING = "UTF-8";

public String generateAccessToken(Long id, Role role) {
Instant accessDate = LocalDateTime.now().plusSeconds(accessExpiration).atZone(ZoneId.systemDefault()).toInstant();
return Jwts.builder()
.claim(ROLE, role)
.claim(TYPE, ACCESS)
.setSubject(String.valueOf(id))
.setExpiration(Date.from(accessDate))
.signWith(SignatureAlgorithm.HS256, secret)
Expand All @@ -67,14 +75,27 @@ public String generateRefreshToken(Long id, Role role) {
Instant refreshDate = LocalDateTime.now().plusSeconds(refreshExpiration).atZone(ZoneId.systemDefault()).toInstant();
String refreshToken = Jwts.builder()
.claim(ROLE, role)
.claim(TYPE, REFRESH)
.setSubject(String.valueOf(id))
.setExpiration(Date.from(refreshDate))
.signWith(SignatureAlgorithm.HS256, secret)
.compact();
redisRepository.setValues(REFRESH + id, refreshToken, Duration.ofSeconds(refreshExpiration));
redisRepository.setValues(REFRESH.toString() + id, refreshToken, Duration.ofSeconds(refreshExpiration));
return refreshToken;
}

public void checkRedis(Long id, HttpServletRequest request) {
String refreshToken = request.getHeader(AUTHORIZATION).split(" ")[1];
String redisToken = redisRepository.getValues(REFRESH.toString() + id)
.orElseThrow(NoneRefreshTokenException::new);
if (!redisToken.equals(refreshToken))
throw new InvalidRefreshTokenException();
}

public void makeExpired(Long id) {
redisRepository.deleteValues(REFRESH.toString() + id);
}

public Authentication getAuthentication(HttpServletResponse response, String token) throws UserNotFoundException {
Claims claims = parseClaims(token);
List<GrantedAuthority> authorities = Collections.singletonList(new SimpleGrantedAuthority(claims.get(ROLE).toString()));
Expand All @@ -92,11 +113,13 @@ private AuthDetails getDetails(HttpServletResponse response, Claims claims) {
}
}

public boolean validateToken(HttpServletResponse response, String token) {
public boolean validateToken(HttpServletResponse response, String token, Type type) {
try {
parseClaims(token);
Claims claims = parseClaims(token);
if (!claims.get(TYPE).equals(type.name()))
throw new InvalidTokenException();
return true;
} catch (SignatureException | UnsupportedJwtException | IllegalArgumentException | MalformedJwtException e) {
} catch (ApplicationException | SignatureException | UnsupportedJwtException | IllegalArgumentException | MalformedJwtException e) {
jwtExceptionHandler(response, new InvalidTokenException());
return false;
} catch (ExpiredJwtException e) {
Expand All @@ -105,23 +128,15 @@ public boolean validateToken(HttpServletResponse response, String token) {
}
}

public void checkRedis(Long id, HttpServletRequest request) {
String refreshToken = request.getHeader(AUTHORIZATION).split(" ")[1];
String redisToken = redisRepository.getValues(REFRESH + id)
.orElseThrow(NoneRefreshTokenException::new);
if (!redisToken.equals(refreshToken))
throw new InvalidRefreshTokenException();
}

public Claims parseClaims(String token) {
private Claims parseClaims(String token) {
JwtParser parser = Jwts.parser().setSigningKey(secret);
return parser.parseClaimsJws(token).getBody();
}

private void jwtExceptionHandler(HttpServletResponse response, ApplicationException ex) {
response.setStatus(500);
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.setStatus(STATUS);
response.setContentType(CONTENT_TYPE);
response.setCharacterEncoding(CHARACTER_ENCODING);
try {
logService.save(new LogRequest(ex.getMessage()));
String json = new ObjectMapper().writeValueAsString(ResponseDto.create(ex.getErrorCode(), ex.getMessage()));
Expand Down
Loading

0 comments on commit c09fe86

Please sign in to comment.