From 78c404c9f3a006863c15b950eaa89cf276e7eb43 Mon Sep 17 00:00:00 2001 From: 5upportPark <5upportpark7@gmail.com> Date: Sat, 21 Sep 2024 17:34:01 +0900 Subject: [PATCH 1/2] =?UTF-8?q?jwt=20claims=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EB=B0=A9=EC=8B=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pjw/retry_view/controller/LoginController.java | 7 ++++--- src/main/java/com/pjw/retry_view/dto/UserInfo.java | 11 +++++++++++ .../java/com/pjw/retry_view/service/JWTService.java | 13 +++++++++++-- 3 files changed, 26 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/pjw/retry_view/dto/UserInfo.java diff --git a/src/main/java/com/pjw/retry_view/controller/LoginController.java b/src/main/java/com/pjw/retry_view/controller/LoginController.java index aa2429a..ef0f3ad 100644 --- a/src/main/java/com/pjw/retry_view/controller/LoginController.java +++ b/src/main/java/com/pjw/retry_view/controller/LoginController.java @@ -1,6 +1,7 @@ package com.pjw.retry_view.controller; import com.pjw.retry_view.dto.UserDTO; +import com.pjw.retry_view.dto.UserInfo; import com.pjw.retry_view.request.LoginRequest; import com.pjw.retry_view.response.LoginResponse; import com.pjw.retry_view.service.JWTService; @@ -31,9 +32,9 @@ public LoginController(UserService userService, JWTService jwtService){ public ResponseEntity userLogin(@RequestBody @Valid LoginRequest loginReq){ UserDTO user = userService.userLogin(loginReq); - Map userInfo = new HashMap<>(); - userInfo.put("name", user.getName()); - userInfo.put("loginId", user.getLoginId()); + UserInfo userInfo = new UserInfo(); + userInfo.setName(user.getName()); + userInfo.setLoginId(user.getLoginId()); LoginResponse response = new LoginResponse(); response.setAccessToken(jwtService.createAccessToken(userInfo)); diff --git a/src/main/java/com/pjw/retry_view/dto/UserInfo.java b/src/main/java/com/pjw/retry_view/dto/UserInfo.java new file mode 100644 index 0000000..1317a5c --- /dev/null +++ b/src/main/java/com/pjw/retry_view/dto/UserInfo.java @@ -0,0 +1,11 @@ +package com.pjw.retry_view.dto; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class UserInfo { + private String name; + private String loginId; +} diff --git a/src/main/java/com/pjw/retry_view/service/JWTService.java b/src/main/java/com/pjw/retry_view/service/JWTService.java index 8e5c965..00c03fe 100644 --- a/src/main/java/com/pjw/retry_view/service/JWTService.java +++ b/src/main/java/com/pjw/retry_view/service/JWTService.java @@ -1,15 +1,18 @@ package com.pjw.retry_view.service; +import com.pjw.retry_view.dto.UserInfo; import io.jsonwebtoken.*; import io.jsonwebtoken.io.Decoders; import io.jsonwebtoken.security.Keys; import io.jsonwebtoken.security.SecurityException; import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Service; import javax.crypto.SecretKey; import java.time.Instant; import java.util.Date; +import java.util.HashMap; import java.util.Map; @Service @@ -18,15 +21,21 @@ public class JWTService { private final SecretKey secretKey; //private static final String AUTH_KEY = "auth"; private static final String BEARER_TYPE = "Bearer"; - private static final long ACCESS_TOKEN_EXPIRED = 1000 * 60 * 60 * 24; // 1day + private static final long ACCESS_TOKEN_EXPIRED = 1000 * 60 * 60; // 1h private static final long REFRESH_TOKEN_EXPIRED = 1000 * 60 * 60 * 24 * 7; // 7day + private static final String USER_INFO_NAME = "name"; + private static final String USER_INFO_LOGIN_ID = "loginId"; + public JWTService(@Value("${jwt.key}")String key){ this.secretKey = Keys.hmacShaKeyFor(Decoders.BASE64.decode(key)); } - public String createAccessToken(Map claims){ + public String createAccessToken(UserInfo userInfo){ long currentTimeMillis = System.currentTimeMillis(); + Map claims = new HashMap<>(); + claims.put(USER_INFO_NAME, userInfo.getName()); + claims.put(USER_INFO_LOGIN_ID, userInfo.getLoginId()); return Jwts.builder() .claims(claims) .issuer("issuer") From 95b52be93ba81323e4e520a202d87bc0e49b9141 Mon Sep 17 00:00:00 2001 From: 5upportPark <5upportpark7@gmail.com> Date: Sun, 22 Sep 2024 11:06:40 +0900 Subject: [PATCH 2/2] =?UTF-8?q?jwt=20refresh=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/LoginController.java | 15 ++++--- .../controller/TokenController.java | 25 +++++++++++ .../exception/InvalidTokenException.java | 18 ++++++++ .../retry_view/filter/JWTVerifyFilter.java | 1 + .../retry_view/repository/UserRepository.java | 2 +- .../com/pjw/retry_view/response/JWToken.java | 11 +++++ .../retry_view/response/LoginResponse.java | 4 +- .../pjw/retry_view/service/JWTService.java | 45 +++++++++++++++++-- 8 files changed, 110 insertions(+), 11 deletions(-) create mode 100644 src/main/java/com/pjw/retry_view/controller/TokenController.java create mode 100644 src/main/java/com/pjw/retry_view/exception/InvalidTokenException.java create mode 100644 src/main/java/com/pjw/retry_view/response/JWToken.java diff --git a/src/main/java/com/pjw/retry_view/controller/LoginController.java b/src/main/java/com/pjw/retry_view/controller/LoginController.java index ef0f3ad..63cae94 100644 --- a/src/main/java/com/pjw/retry_view/controller/LoginController.java +++ b/src/main/java/com/pjw/retry_view/controller/LoginController.java @@ -1,5 +1,6 @@ package com.pjw.retry_view.controller; +import com.pjw.retry_view.response.JWToken; import com.pjw.retry_view.dto.UserDTO; import com.pjw.retry_view.dto.UserInfo; import com.pjw.retry_view.request.LoginRequest; @@ -14,9 +15,6 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import java.util.HashMap; -import java.util.Map; - @RestController @RequestMapping("/login") public class LoginController { @@ -37,8 +35,15 @@ public ResponseEntity userLogin(@RequestBody @Valid LoginRequest userInfo.setLoginId(user.getLoginId()); LoginResponse response = new LoginResponse(); - response.setAccessToken(jwtService.createAccessToken(userInfo)); - response.setRefreshToken(jwtService.createRefreshToken()); + JWToken token = new JWToken(); + String refreshToken = jwtService.createRefreshToken(); + token.setAccessToken(jwtService.createAccessToken(userInfo)); + token.setRefreshToken(refreshToken); + response.setToken(token); + + user.setRefreshToken(refreshToken); + userService.saveUser(user); + return new ResponseEntity(response, HttpStatus.OK); } } diff --git a/src/main/java/com/pjw/retry_view/controller/TokenController.java b/src/main/java/com/pjw/retry_view/controller/TokenController.java new file mode 100644 index 0000000..ba7febf --- /dev/null +++ b/src/main/java/com/pjw/retry_view/controller/TokenController.java @@ -0,0 +1,25 @@ +package com.pjw.retry_view.controller; + +import com.pjw.retry_view.response.JWToken; +import com.pjw.retry_view.service.JWTService; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; + +@Controller +@RequestMapping("/token") +public class TokenController { + private final JWTService jwtService; + + public TokenController(JWTService jwtService) { + this.jwtService = jwtService; + } + + @PostMapping + public ResponseEntity renweAccessToken(@RequestBody JWToken token){ + return new ResponseEntity(jwtService.renewAccessToken(token.getRefreshToken()), HttpStatus.OK); + } +} diff --git a/src/main/java/com/pjw/retry_view/exception/InvalidTokenException.java b/src/main/java/com/pjw/retry_view/exception/InvalidTokenException.java new file mode 100644 index 0000000..f84756d --- /dev/null +++ b/src/main/java/com/pjw/retry_view/exception/InvalidTokenException.java @@ -0,0 +1,18 @@ +package com.pjw.retry_view.exception; + +import org.springframework.http.HttpStatus; + +public class InvalidTokenException extends BusinessException{ + @Override + public HttpStatus getHttpStatus() { + return HttpStatus.UNAUTHORIZED; + } + + public InvalidTokenException(){} + public InvalidTokenException(String msg){ + super(msg); + } + public InvalidTokenException(Exception e){ + super(e); + } +} diff --git a/src/main/java/com/pjw/retry_view/filter/JWTVerifyFilter.java b/src/main/java/com/pjw/retry_view/filter/JWTVerifyFilter.java index fadace7..3d69e00 100644 --- a/src/main/java/com/pjw/retry_view/filter/JWTVerifyFilter.java +++ b/src/main/java/com/pjw/retry_view/filter/JWTVerifyFilter.java @@ -29,6 +29,7 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo servletRequest.setAttribute("loginId", loginId); filterChain.doFilter(servletRequest,servletResponse); }else{ + // accessToken 재발급받게 하기 HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse; httpServletResponse.sendError(HttpStatus.UNAUTHORIZED.value(), "권한이 없습니다."); } diff --git a/src/main/java/com/pjw/retry_view/repository/UserRepository.java b/src/main/java/com/pjw/retry_view/repository/UserRepository.java index 202498b..08c0cc1 100644 --- a/src/main/java/com/pjw/retry_view/repository/UserRepository.java +++ b/src/main/java/com/pjw/retry_view/repository/UserRepository.java @@ -1,7 +1,6 @@ package com.pjw.retry_view.repository; import com.pjw.retry_view.entity.User; -import com.pjw.retry_view.request.LoginRequest; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @@ -13,5 +12,6 @@ public interface UserRepository extends JpaRepository { public List findAll(); public Optional findByLoginId(String loginId); public Optional findByLoginIdAndPassword(String loginId, String password); + public Optional findByRefreshToken(String refreshToken); public User save(User user); } diff --git a/src/main/java/com/pjw/retry_view/response/JWToken.java b/src/main/java/com/pjw/retry_view/response/JWToken.java new file mode 100644 index 0000000..0797219 --- /dev/null +++ b/src/main/java/com/pjw/retry_view/response/JWToken.java @@ -0,0 +1,11 @@ +package com.pjw.retry_view.response; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class JWToken { + private String accessToken; + private String refreshToken; +} diff --git a/src/main/java/com/pjw/retry_view/response/LoginResponse.java b/src/main/java/com/pjw/retry_view/response/LoginResponse.java index 2d5d6b8..e8b39e0 100644 --- a/src/main/java/com/pjw/retry_view/response/LoginResponse.java +++ b/src/main/java/com/pjw/retry_view/response/LoginResponse.java @@ -6,6 +6,6 @@ @Getter @Setter public class LoginResponse { - private String accessToken; - private String refreshToken; + private String msg; + private JWToken token; } diff --git a/src/main/java/com/pjw/retry_view/service/JWTService.java b/src/main/java/com/pjw/retry_view/service/JWTService.java index 00c03fe..52160a4 100644 --- a/src/main/java/com/pjw/retry_view/service/JWTService.java +++ b/src/main/java/com/pjw/retry_view/service/JWTService.java @@ -1,16 +1,24 @@ package com.pjw.retry_view.service; +import com.pjw.retry_view.response.JWToken; +import com.pjw.retry_view.dto.UserDTO; import com.pjw.retry_view.dto.UserInfo; +import com.pjw.retry_view.entity.User; +import com.pjw.retry_view.exception.InvalidTokenException; +import com.pjw.retry_view.exception.UserNotFoundException; +import com.pjw.retry_view.repository.UserRepository; import io.jsonwebtoken.*; import io.jsonwebtoken.io.Decoders; import io.jsonwebtoken.security.Keys; import io.jsonwebtoken.security.SecurityException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.transaction.Transactional; import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Service; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; import javax.crypto.SecretKey; -import java.time.Instant; import java.util.Date; import java.util.HashMap; import java.util.Map; @@ -27,8 +35,11 @@ public class JWTService { private static final String USER_INFO_NAME = "name"; private static final String USER_INFO_LOGIN_ID = "loginId"; - public JWTService(@Value("${jwt.key}")String key){ + private final UserRepository userRepository; + + public JWTService(@Value("${jwt.key}")String key, UserRepository userRepository){ this.secretKey = Keys.hmacShaKeyFor(Decoders.BASE64.decode(key)); + this.userRepository = userRepository; } public String createAccessToken(UserInfo userInfo){ @@ -45,6 +56,34 @@ public String createAccessToken(UserInfo userInfo){ .compact(); } + @Transactional + public JWToken renewAccessToken(String refreshToken) throws InvalidTokenException { + //HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + + if(this.validateToken(refreshToken)){ // refreshToken이 유효한 경우 + Claims jwtClaims = getClaims(refreshToken); + JWToken token = new JWToken(); + UserDTO user = userRepository.findByRefreshToken(refreshToken).map(User::toDTO).orElseThrow(UserNotFoundException::new); + UserInfo userInfo = new UserInfo(); + userInfo.setName(user.getName()); + userInfo.setLoginId(user.getLoginId()); + + if(jwtClaims.getExpiration().before(new Date())){ // refreshToken이 만료되지 않은 경우 accessToken만 생성 + token.setAccessToken(createAccessToken(userInfo)); + return token; + }else{ // refreshToken이 만료된 경우 access,refreshToken 둘 다 생성 + refreshToken = createRefreshToken(); + token.setAccessToken(createAccessToken(userInfo)); + token.setRefreshToken(refreshToken); + user.setRefreshToken(refreshToken); + userRepository.save(user.toEntity()); + return token; + } + }else{ + throw new InvalidTokenException(); + } + } + public String createRefreshToken(){ long currentTimeMillis = System.currentTimeMillis(); return Jwts.builder()