From e4c422c21b213b0f654070972764261f72ed5ee9 Mon Sep 17 00:00:00 2001 From: eastmeet Date: Fri, 27 Jan 2023 01:59:04 +0900 Subject: [PATCH] test: refresh token test complete(#94) --- .../member/repository/MemberRepository.java | 1 - .../main36/pikcha/global/aop/LoginAspect.java | 18 ++++++-- .../global/exception/ExceptionCode.java | 5 ++- .../security/SecurityConfiguration.java | 4 +- .../security/controller/TokenController.java | 45 ++++++++++++------- .../filter/JwtVerificationFilter.java | 4 +- .../global/security/jwt/JwtGenerator.java | 8 ++-- .../pikcha/global/security/jwt/JwtParser.java | 39 ++++++++++++++-- .../jwt/exception/RefreshExpired.java | 12 +++++ .../security/jwt/exception/TokenExpired.java | 3 +- .../oauth/OAuth2MemberSuccessHandler.java | 4 +- .../src/main/resources/application-local.yml | 2 +- .../src/main/resources/application-server.yml | 4 +- .../src/main/resources/application-test.yml | 4 +- server/src/main/resources/application.yml | 2 +- 15 files changed, 112 insertions(+), 43 deletions(-) create mode 100644 server/src/main/java/com/main36/pikcha/global/security/jwt/exception/RefreshExpired.java diff --git a/server/src/main/java/com/main36/pikcha/domain/member/repository/MemberRepository.java b/server/src/main/java/com/main36/pikcha/domain/member/repository/MemberRepository.java index c2c77f92..6bc32cbd 100644 --- a/server/src/main/java/com/main36/pikcha/domain/member/repository/MemberRepository.java +++ b/server/src/main/java/com/main36/pikcha/domain/member/repository/MemberRepository.java @@ -2,7 +2,6 @@ import com.main36.pikcha.domain.member.entity.Member; -import io.lettuce.core.Value; import org.springframework.data.jpa.repository.JpaRepository; import java.util.Optional; diff --git a/server/src/main/java/com/main36/pikcha/global/aop/LoginAspect.java b/server/src/main/java/com/main36/pikcha/global/aop/LoginAspect.java index c745c888..846d18f0 100644 --- a/server/src/main/java/com/main36/pikcha/global/aop/LoginAspect.java +++ b/server/src/main/java/com/main36/pikcha/global/aop/LoginAspect.java @@ -2,6 +2,8 @@ import com.main36.pikcha.domain.member.entity.Member; import com.main36.pikcha.domain.member.service.MemberService; +import com.main36.pikcha.global.exception.BusinessLogicException; +import com.main36.pikcha.global.exception.ExceptionCode; import com.main36.pikcha.global.security.jwt.JwtParser; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -28,24 +30,32 @@ public class LoginAspect { private final MemberService memberService; @Around("@annotation(com.main36.pikcha.global.aop.LoginUserEmail)") - public Object getUserEmail(ProceedingJoinPoint joinPoint) throws Throwable { + public Object getUserEmail(ProceedingJoinPoint joinPoint) { HttpServletRequest request = ((ServletRequestAttributes) requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest(); Object[] args = joinPoint.getArgs(); String email = jwtParser.getLoginUserEmail(request); args[0] = email; - return joinPoint.proceed(args); + try { + return joinPoint.proceed(args); + } catch (Throwable e) { + throw new BusinessLogicException(ExceptionCode.TOKEN_EXPIRED); + } } @Around("@annotation(com.main36.pikcha.global.aop.LoginUser)") - public Object getUser(ProceedingJoinPoint joinPoint) throws Throwable { + public Object getUser(ProceedingJoinPoint joinPoint) { HttpServletRequest request = ((ServletRequestAttributes) requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest(); Object[] args = joinPoint.getArgs(); Member loginUser = memberService.getLoginMember(request); args[0] = loginUser; - return joinPoint.proceed(args); + try { + return joinPoint.proceed(args); + } catch (Throwable e) { + throw new BusinessLogicException(ExceptionCode.TOKEN_EXPIRED); + } } } diff --git a/server/src/main/java/com/main36/pikcha/global/exception/ExceptionCode.java b/server/src/main/java/com/main36/pikcha/global/exception/ExceptionCode.java index f2e27761..ff99ada0 100644 --- a/server/src/main/java/com/main36/pikcha/global/exception/ExceptionCode.java +++ b/server/src/main/java/com/main36/pikcha/global/exception/ExceptionCode.java @@ -7,7 +7,6 @@ public enum ExceptionCode { MEMBER_NOT_ALLOW(405, "That Member doesn't have authority"), MEMBER_EXISTS(409, "Member exists"), USER_IS_NOT_EQUAL(400, "Client is not equal memberId"), - ATTRACTION_NOT_FOUND(404, "Attraction not found"), ATTRACTION_EXISTS(409, "Attraction exists"), @@ -27,9 +26,11 @@ public enum ExceptionCode { POST_IMAGE_NOT_FOUND(404, "Post Image not found"), /* JWT */ - ACCESS_TOKEN_NOT_FOUND(404,"액세스토큰을 찾을 수 없습니다."), + ACCESS_TOKEN_NOT_FOUND(404,"AccessToken can not be found"), TOKEN_EXPIRED(400, "Token Expired"), + REFRESH_TOKEN_EXPIRED(400, "RefreshToken Expired"), TOKEN_INVALID(400, "Token Invalid"), + TOKEN_EMPTY(404, "Token not found"), TOKEN_SIGNATURE_INVALID(400, "Token Signature Invalid"), TOKEN_MALFORMED(400, "Token Malformed"), TOKEN_UNSUPPORTED(400, "Token Unsupported"), diff --git a/server/src/main/java/com/main36/pikcha/global/security/SecurityConfiguration.java b/server/src/main/java/com/main36/pikcha/global/security/SecurityConfiguration.java index 5dcc1be9..15a52d85 100644 --- a/server/src/main/java/com/main36/pikcha/global/security/SecurityConfiguration.java +++ b/server/src/main/java/com/main36/pikcha/global/security/SecurityConfiguration.java @@ -64,8 +64,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .and() .authorizeHttpRequests(authorize -> authorize - .requestMatchers(toH2Console()).permitAll() - .antMatchers("attractions/upload", "attractions/edit/**", "attractions/delete", "admin").hasRole("ADMIN") +// .requestMatchers(toH2Console()).permitAll() + .antMatchers("/attractions/upload", "/attractions/edit/**", "/attractions/delete", "admin").hasRole("ADMIN") .anyRequest().permitAll()) .oauth2Login(oauth2 -> oauth2 diff --git a/server/src/main/java/com/main36/pikcha/global/security/controller/TokenController.java b/server/src/main/java/com/main36/pikcha/global/security/controller/TokenController.java index 98b2b007..0972fe8d 100644 --- a/server/src/main/java/com/main36/pikcha/global/security/controller/TokenController.java +++ b/server/src/main/java/com/main36/pikcha/global/security/controller/TokenController.java @@ -1,35 +1,24 @@ package com.main36.pikcha.global.security.controller; -import com.amazonaws.Response; import com.main36.pikcha.domain.member.entity.Member; import com.main36.pikcha.domain.member.service.MemberService; -import com.main36.pikcha.global.aop.LoginUser; -import com.main36.pikcha.global.aop.LoginUserEmail; +import com.main36.pikcha.global.exception.BusinessLogicException; +import com.main36.pikcha.global.exception.ExceptionCode; import com.main36.pikcha.global.response.DataResponseDto; import com.main36.pikcha.global.security.dto.RenewTokenDto; -import com.main36.pikcha.global.security.dto.TokenDto; import com.main36.pikcha.global.security.jwt.JwtGenerator; import com.main36.pikcha.global.security.jwt.JwtParser; -import com.main36.pikcha.global.security.userdetails.AuthMember; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.*; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import java.util.prefs.PreferenceChangeEvent; - -import static com.main36.pikcha.global.security.filter.JwtVerificationFilter.BEARER_PREFIX; - @Slf4j @RestController @Validated @RequiredArgsConstructor -@RequestMapping("/token") + public class TokenController { private final JwtGenerator jwtGenerator; @@ -50,11 +39,15 @@ public class TokenController { // return ResponseEntity.ok(new DataResponseDto<>(renewTokenDto)); // } - @GetMapping("/refresh/{member-id}") + @GetMapping("/token/refresh/{member-id}") public ResponseEntity findCookie(@PathVariable("member-id") Long memberId, @CookieValue(value = "refreshToken") String refresh) { + log.info("refresh= {}", refresh); + if (refresh.isEmpty()) { + throw new BusinessLogicException(ExceptionCode.TOKEN_EMPTY); + } - jwtParser.verifyToken(refresh); + jwtParser.verifyRefreshToken(refresh); Member member = memberService.findMemberByMemberId(memberId); RenewTokenDto.RenewTokenDtoBuilder builder = RenewTokenDto.builder(); RenewTokenDto renewTokenDto = @@ -65,4 +58,24 @@ public ResponseEntity findCookie(@PathVariable("member-id") Long memberId, return ResponseEntity.ok(new DataResponseDto<>(renewTokenDto)); } + +// @LoginUser +// @GetMapping("/token/refresh") +// public ResponseEntity findCookie(Member loginUser, +// @CookieValue(value = "refreshToken") String refresh) { +// log.info("refresh= {}", refresh); +// if (refresh.isEmpty()) { +// throw new BusinessLogicException(ExceptionCode.TOKEN_EMPTY); +// } +// +// jwtParser.verifyToken(refresh); +// RenewTokenDto.RenewTokenDtoBuilder builder = RenewTokenDto.builder(); +// RenewTokenDto renewTokenDto = +// builder.memberId(loginUser.getMemberId()) +// .email(loginUser.getEmail()) +// .accessToken("Bearer " + jwtGenerator.generateAccessToken(loginUser.getEmail(), loginUser.getRoles())) +// .build(); +// +// return ResponseEntity.ok(new DataResponseDto<>(renewTokenDto)); +// } } diff --git a/server/src/main/java/com/main36/pikcha/global/security/filter/JwtVerificationFilter.java b/server/src/main/java/com/main36/pikcha/global/security/filter/JwtVerificationFilter.java index 75eb85e1..ee64c6e8 100644 --- a/server/src/main/java/com/main36/pikcha/global/security/filter/JwtVerificationFilter.java +++ b/server/src/main/java/com/main36/pikcha/global/security/filter/JwtVerificationFilter.java @@ -15,7 +15,6 @@ import javax.servlet.FilterChain; import javax.servlet.ServletException; -import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @@ -41,6 +40,7 @@ public class JwtVerificationFilter extends OncePerRequestFilter { "/home", "/attractions", "/posts", + "/token/refresh", "/oauth2/authorization/google", "/oauth2/authorization/kakao" ); @@ -63,7 +63,7 @@ protected void doFilterInternal(HttpServletRequest request, try { String jwt = getAccessToken(request); log.info("jwt = ==== {}", jwt); - if (StringUtils.hasText(jwt) && jwtParser.verifyToken(jwt)) { + if (StringUtils.hasText(jwt) && jwtParser.verifyAccessToken(jwt)) { Authentication authentication = jwtGenerator.getAuthentication(jwt); log.info("# Token verification success !"); SecurityContextHolder.getContext().setAuthentication(authentication); diff --git a/server/src/main/java/com/main36/pikcha/global/security/jwt/JwtGenerator.java b/server/src/main/java/com/main36/pikcha/global/security/jwt/JwtGenerator.java index f9a8374c..1717f08d 100644 --- a/server/src/main/java/com/main36/pikcha/global/security/jwt/JwtGenerator.java +++ b/server/src/main/java/com/main36/pikcha/global/security/jwt/JwtGenerator.java @@ -64,7 +64,8 @@ public String generateAccessToken(String payload, List roles) { Claims claims = Jwts.claims().setSubject(payload); claims.put("roles", roles); Date now = new Date(); - Date validity = new Date(now.getTime() + 30000); // 30000이 30분을 의미함 + //TODO: 시간 변경할것 + Date validity = new Date(now.getTime() + 30 * 60 * 1000); // 단위 100ns 0.1ms -> 30분 return Jwts.builder() .setClaims(claims) @@ -76,7 +77,8 @@ public String generateAccessToken(String payload, List roles) { public String generateRefreshToken(String payload) { Date now = new Date(); - Date validity = new Date(now.getTime() + 420000); // 420000은 420분을 의미함 + //TODO: 시간 변경할것 + Date validity = new Date(now.getTime() + 420 * 60 * 1000); // 420분 return Jwts.builder() .setSubject(payload) .setIssuedAt(Calendar.getInstance().getTime()) @@ -104,7 +106,7 @@ public TokenDto generateTokenDto(AuthMember authMember) { // Refresh Token 생성 String refreshToken = Jwts.builder() - .setSubject(authMember.getEmail()) + .setSubject(authMember.getMemberId().toString()) .setIssuedAt(Calendar.getInstance().getTime()) .setExpiration(refreshTokenExpiresIn) .signWith(key, SignatureAlgorithm.HS512) diff --git a/server/src/main/java/com/main36/pikcha/global/security/jwt/JwtParser.java b/server/src/main/java/com/main36/pikcha/global/security/jwt/JwtParser.java index ec6ea4ed..ca107f1f 100644 --- a/server/src/main/java/com/main36/pikcha/global/security/jwt/JwtParser.java +++ b/server/src/main/java/com/main36/pikcha/global/security/jwt/JwtParser.java @@ -1,5 +1,7 @@ package com.main36.pikcha.global.security.jwt; +import com.main36.pikcha.global.exception.BusinessLogicException; +import com.main36.pikcha.global.exception.ExceptionCode; import com.main36.pikcha.global.security.jwt.exception.*; import io.jsonwebtoken.*; import io.jsonwebtoken.io.Decoders; @@ -63,13 +65,21 @@ public Date getTokenExpiration(int expirationMinutes) { } public String getLoginUserEmail(HttpServletRequest request) { + log.info("token= {}", request.getHeader("Authorization")); + if (request.getHeader("Authorization") == null) { + throw new BusinessLogicException(ExceptionCode.TOKEN_EMPTY); + } + String authorization = request.getHeader("Authorization"); - String accessToken = authorization.substring(7); + log.info("authorization= {}", authorization); + // try-catch + String accessToken = authorization.substring(7); // 500 error + verifyAccessToken(accessToken); return getBody(accessToken).get("sub").toString(); } - public boolean verifyToken(String token) { + public boolean verifyAccessToken(String token) { try { getBody(token); return true; @@ -91,14 +101,35 @@ public boolean verifyToken(String token) { } } + public boolean verifyRefreshToken(String token) { + try { + getBody(token); + return true; + } catch (SignatureException e) { + log.trace("Invalid JWT signature trace: {}", e); + throw new TokenSignatureInvalid(); + } catch (MalformedJwtException e) { + log.trace("Invalid JWT token trace: {}", e); + throw new TokenMalformed(); + } catch (ExpiredJwtException e) { + log.trace("trace Expired JWT token trace: {}", e); + throw new RefreshExpired(); + } catch (UnsupportedJwtException e) { + log.trace("Unsupported JWT token trace: {}", e); + throw new TokenUnsupported(); + } catch (IllegalArgumentException e) { + log.trace("JWT claims string is empty trace: {}", e); + throw new TokenEmpty(); + } + } - private Claims getBody(String accessToken) { + private Claims getBody(String token) { // TODO: 예외 처리 필요 키값이 이상할때! return Jwts. parserBuilder(). setSigningKey(key) .build() - .parseClaimsJws(accessToken) + .parseClaimsJws(token) .getBody(); } diff --git a/server/src/main/java/com/main36/pikcha/global/security/jwt/exception/RefreshExpired.java b/server/src/main/java/com/main36/pikcha/global/security/jwt/exception/RefreshExpired.java new file mode 100644 index 00000000..a41fa37b --- /dev/null +++ b/server/src/main/java/com/main36/pikcha/global/security/jwt/exception/RefreshExpired.java @@ -0,0 +1,12 @@ +package com.main36.pikcha.global.security.jwt.exception; + +import com.main36.pikcha.global.exception.BusinessLogicException; +import com.main36.pikcha.global.exception.ExceptionCode; +import net.minidev.json.writer.BeansMapper; + +public class RefreshExpired extends BusinessLogicException { + + public RefreshExpired() { + super(ExceptionCode.REFRESH_TOKEN_EXPIRED.getMessage(),ExceptionCode.REFRESH_TOKEN_EXPIRED); + } +} diff --git a/server/src/main/java/com/main36/pikcha/global/security/jwt/exception/TokenExpired.java b/server/src/main/java/com/main36/pikcha/global/security/jwt/exception/TokenExpired.java index 13d66de4..327497ec 100644 --- a/server/src/main/java/com/main36/pikcha/global/security/jwt/exception/TokenExpired.java +++ b/server/src/main/java/com/main36/pikcha/global/security/jwt/exception/TokenExpired.java @@ -6,6 +6,7 @@ public class TokenExpired extends BusinessLogicException { public TokenExpired() { - super(ExceptionCode.TOKEN_EXPIRED.getMessage(),ExceptionCode.TOKEN_EXPIRED); + super(ExceptionCode.TOKEN_EXPIRED.getMessage(), ExceptionCode.TOKEN_EXPIRED); } + } \ No newline at end of file diff --git a/server/src/main/java/com/main36/pikcha/global/security/oauth/OAuth2MemberSuccessHandler.java b/server/src/main/java/com/main36/pikcha/global/security/oauth/OAuth2MemberSuccessHandler.java index e31c28f0..365260c0 100644 --- a/server/src/main/java/com/main36/pikcha/global/security/oauth/OAuth2MemberSuccessHandler.java +++ b/server/src/main/java/com/main36/pikcha/global/security/oauth/OAuth2MemberSuccessHandler.java @@ -75,12 +75,12 @@ private URI createURI(HttpServletRequest request) { // queryParams.add("access_token", accessToken); // queryParams.add("refresh_token", refreshToken); String serverName = request.getServerName(); - + log.info("serverName = {}", serverName); return UriComponentsBuilder .newInstance() .scheme("http") .host(serverName) -// .port() + .port(3000) // .path("") // .path("/token/oauth2") // .queryParams(queryParams) diff --git a/server/src/main/resources/application-local.yml b/server/src/main/resources/application-local.yml index d8436405..108f08d7 100644 --- a/server/src/main/resources/application-local.yml +++ b/server/src/main/resources/application-local.yml @@ -75,7 +75,7 @@ mail: jwt: secret-key: ${JWT_SECRET_KEY} - access-token-expiration-minutes: 420 + access-token-expiration-minutes: 30 refresh-token-expiration-minutes: 420 cloud: diff --git a/server/src/main/resources/application-server.yml b/server/src/main/resources/application-server.yml index 71d71324..32adb251 100644 --- a/server/src/main/resources/application-server.yml +++ b/server/src/main/resources/application-server.yml @@ -74,8 +74,8 @@ mail: jwt: secret-key: ${JWT_SECRET_KEY} # 환경 변수로 설정했음 - access-token-expiration-minutes: 1 - refresh-token-expiration-minutes: 1 + access-token-expiration-minutes: 30 + refresh-token-expiration-minutes: 420 cloud: diff --git a/server/src/main/resources/application-test.yml b/server/src/main/resources/application-test.yml index fbb0fa12..42a03075 100644 --- a/server/src/main/resources/application-test.yml +++ b/server/src/main/resources/application-test.yml @@ -36,7 +36,7 @@ spring: kakao: client-id: ${KAKAO_CLIENT_ID} client-secret: ${KAKAO_CLIENT_SECRET} - redirect-uri: http://localhost:8080/login/oauth2/code/kakao + redirect-uri: http://pikcha36.o-r.kr/login/oauth2/code/kakao # redirect-uri: http://localhost:8080 authorization-grant-type: authorization_code client-authentication-method: POST @@ -71,7 +71,7 @@ mail: jwt: secret-key: ${JWT_SECRET_KEY} - access-token-expiration-minutes: 1 + access-token-expiration-minutes: 30 refresh-token-expiration-minutes: 420 cloud: diff --git a/server/src/main/resources/application.yml b/server/src/main/resources/application.yml index d74c444c..03c30d37 100644 --- a/server/src/main/resources/application.yml +++ b/server/src/main/resources/application.yml @@ -1,3 +1,3 @@ spring: profiles: - active: local + active: test