generated from f-lab-edu/f-lab-springboot-jdbc-project-template
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4 from f-lab-edu/feature/jwt_refresh
JWT refresh 로직 추가
- Loading branch information
Showing
11 changed files
with
235 additions
and
83 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
25 changes: 25 additions & 0 deletions
25
src/main/java/com/pjw/retry_view/controller/TokenController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 JWToken renewAccessToken(@RequestBody JWToken token){ | ||
return jwtService.renewAccessToken(token.getRefreshToken()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package com.pjw.retry_view.dto; | ||
|
||
import lombok.Getter; | ||
import lombok.Setter; | ||
|
||
@Getter | ||
@Setter | ||
public class UserInfo { | ||
private String name; | ||
private String loginId; | ||
|
||
public UserInfo(){} | ||
public UserInfo(String name, String loginId){ | ||
this.name = name; | ||
this.loginId = loginId; | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
src/main/java/com/pjw/retry_view/exception/InvalidTokenException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package com.pjw.retry_view.response; | ||
|
||
import io.micrometer.common.util.StringUtils; | ||
import lombok.Getter; | ||
import lombok.Setter; | ||
|
||
@Getter | ||
public class JWToken { | ||
private String accessToken; | ||
private String refreshToken; | ||
|
||
private JWToken(){} | ||
private JWToken(String accessToken, String refreshToken){ | ||
this.accessToken = accessToken; | ||
this.refreshToken = refreshToken; | ||
} | ||
|
||
public static JWToken getJWT(String accessToken, String refreshToken){ | ||
return new JWToken(accessToken, refreshToken); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,82 +1,45 @@ | ||
package com.pjw.retry_view.service; | ||
|
||
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 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 com.pjw.retry_view.util.JWTUtil; | ||
import jakarta.transaction.Transactional; | ||
import org.springframework.stereotype.Service; | ||
|
||
import javax.crypto.SecretKey; | ||
import java.time.Instant; | ||
import java.util.Date; | ||
import java.util.Map; | ||
|
||
@Service | ||
public class JWTService { | ||
private final UserRepository userRepository; | ||
|
||
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 REFRESH_TOKEN_EXPIRED = 1000 * 60 * 60 * 24 * 7; // 7day | ||
|
||
public JWTService(@Value("${jwt.key}")String key){ | ||
this.secretKey = Keys.hmacShaKeyFor(Decoders.BASE64.decode(key)); | ||
} | ||
|
||
public String createAccessToken(Map<String, Object> claims){ | ||
long currentTimeMillis = System.currentTimeMillis(); | ||
return Jwts.builder() | ||
.claims(claims) | ||
.issuer("issuer") | ||
.issuedAt(new Date(currentTimeMillis)) | ||
.expiration(new Date(currentTimeMillis+ACCESS_TOKEN_EXPIRED)) | ||
.signWith(secretKey, Jwts.SIG.HS512) | ||
.compact(); | ||
public JWTService(UserRepository userRepository){ | ||
this.userRepository = userRepository; | ||
} | ||
|
||
public String createRefreshToken(){ | ||
long currentTimeMillis = System.currentTimeMillis(); | ||
return Jwts.builder() | ||
.issuedAt(new Date(currentTimeMillis)) | ||
.expiration(new Date(currentTimeMillis+REFRESH_TOKEN_EXPIRED)) | ||
.signWith(secretKey, Jwts.SIG.HS512) | ||
.compact(); | ||
} | ||
|
||
public Claims getClaims(String token){ | ||
token = tokenSplit(token); | ||
return Jwts.parser() | ||
.verifyWith(secretKey) | ||
.build() | ||
.parseSignedClaims(token) | ||
.getPayload(); | ||
} | ||
|
||
public boolean validateToken(String token){ | ||
if(token == null) return false; | ||
token = tokenSplit(token); | ||
try { | ||
Jwts.parser().verifyWith(secretKey).build().parseSignedClaims(token); | ||
return true; | ||
} catch (SecurityException | MalformedJwtException e) { | ||
System.out.println("잘못된 JWT 서명입니다."); | ||
} catch (ExpiredJwtException e) { | ||
System.out.println("만료된 JWT 토큰입니다."); | ||
} catch (UnsupportedJwtException e) { | ||
System.out.println("지원되지 않는 JWT 토큰입니다."); | ||
} catch (IllegalArgumentException e) { | ||
System.out.println("JWT 토큰이 잘못되었습니다."); | ||
@Transactional | ||
public JWToken renewAccessToken(String refreshToken) throws InvalidTokenException { | ||
if (JWTUtil.isValidateToken(refreshToken)) { | ||
throw new InvalidTokenException(); | ||
} | ||
return false; | ||
} | ||
|
||
private String tokenSplit(String token){ | ||
if(token.startsWith(BEARER_TYPE)){ | ||
return token.split(" ")[1]; | ||
} else{ | ||
return token; | ||
UserDTO user = userRepository.findByRefreshToken(refreshToken).map(User::toDTO).orElseThrow(UserNotFoundException::new); | ||
UserInfo userInfo = new UserInfo(user.getName(), user.getLoginId()); | ||
|
||
boolean isExpired = JWTUtil.isTokenExpired(refreshToken); | ||
if (isExpired) { | ||
refreshToken = JWTUtil.createRefreshToken(); | ||
user.setRefreshToken(refreshToken); | ||
userRepository.save(user.toEntity()); | ||
return JWToken.getJWT(JWTUtil.createAccessToken(userInfo), refreshToken); | ||
} | ||
|
||
return JWToken.getJWT(JWTUtil.createAccessToken(userInfo), null); | ||
} | ||
|
||
|
||
} |
Oops, something went wrong.