Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JWT refresh 로직 추가 #3

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 14 additions & 8 deletions src/main/java/com/pjw/retry_view/controller/LoginController.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
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;
import com.pjw.retry_view.response.LoginResponse;
import com.pjw.retry_view.service.JWTService;
Expand All @@ -13,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 {
Expand All @@ -31,13 +30,20 @@ public LoginController(UserService userService, JWTService jwtService){
public ResponseEntity<LoginResponse> userLogin(@RequestBody @Valid LoginRequest loginReq){
UserDTO user = userService.userLogin(loginReq);

Map<String, Object> 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));
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<LoginResponse>(response, HttpStatus.OK);
}
}
25 changes: 25 additions & 0 deletions src/main/java/com/pjw/retry_view/controller/TokenController.java
Original file line number Diff line number Diff line change
@@ -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<JWToken> renweAccessToken(@RequestBody JWToken token){
return new ResponseEntity<JWToken>(jwtService.renewAccessToken(token.getRefreshToken()), HttpStatus.OK);
}
}
11 changes: 11 additions & 0 deletions src/main/java/com/pjw/retry_view/dto/UserInfo.java
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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(), "권한이 없습니다.");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -13,5 +12,6 @@ public interface UserRepository extends JpaRepository<User, Long> {
public List<User> findAll();
public Optional<User> findByLoginId(String loginId);
public Optional<User> findByLoginIdAndPassword(String loginId, String password);
public Optional<User> findByRefreshToken(String refreshToken);
public User save(User user);
}
11 changes: 11 additions & 0 deletions src/main/java/com/pjw/retry_view/response/JWToken.java
Original file line number Diff line number Diff line change
@@ -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;
}
4 changes: 2 additions & 2 deletions src/main/java/com/pjw/retry_view/response/LoginResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
@Getter
@Setter
public class LoginResponse {
private String accessToken;
private String refreshToken;
private String msg;
private JWToken token;
}
56 changes: 52 additions & 4 deletions src/main/java/com/pjw/retry_view/service/JWTService.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
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.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;

@Service
Expand All @@ -18,15 +29,24 @@ 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

public JWTService(@Value("${jwt.key}")String key){
private static final String USER_INFO_NAME = "name";
private static final String USER_INFO_LOGIN_ID = "loginId";

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(Map<String, Object> claims){
public String createAccessToken(UserInfo userInfo){
long currentTimeMillis = System.currentTimeMillis();
Map<String, Object> 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")
Expand All @@ -36,6 +56,34 @@ public String createAccessToken(Map<String, Object> claims){
.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()
Expand Down
Loading