Skip to content

Commit

Permalink
[feat][#6] Implement refreshToken in UserController
Browse files Browse the repository at this point in the history
- Access Token이 만료되면, 클라이언트는 refresh를 요청하고 새로운 토큰을 받는다
- JwtAuthFilter : 만료되지 않은 업데이트 이전의 토큰을 가진 요청은 400 Status를 반환
  • Loading branch information
Sinyoung3016 committed May 8, 2022
1 parent 82d04a3 commit 439b778
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 11 deletions.
4 changes: 3 additions & 1 deletion src/main/java/gp/cnusambe/config/WebSecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import gp.cnusambe.security.jwt.JwtAuthFilter;
import gp.cnusambe.security.jwt.JwtTokenProvider;
import gp.cnusambe.service.user.UserDetailsServiceImpl;
import gp.cnusambe.util.RedisUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
Expand All @@ -26,6 +27,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final UserDetailsServiceImpl userDetailsServiceImpl;
private final JwtAuthEntryPoint unauthorizedHandler;
private final JwtAccessDeniedHandler accessDeniedHandler;
private final RedisUtil redisUtil;

@Bean
public PasswordEncoder passwordEncoder() {
Expand All @@ -40,7 +42,7 @@ public AuthenticationManager authenticationManagerBean() throws Exception {

@Bean
public JwtAuthFilter authTokenFilter() {
return new JwtAuthFilter(jwtTokenProvider, userDetailsServiceImpl);
return new JwtAuthFilter(jwtTokenProvider, userDetailsServiceImpl, redisUtil);
}

@Override
Expand Down
30 changes: 30 additions & 0 deletions src/main/java/gp/cnusambe/controller/user/UserController.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import java.net.URI;
import java.util.UUID;

@RequiredArgsConstructor
@RestController
Expand Down Expand Up @@ -65,6 +66,28 @@ public ResponseEntity<LoginResponse> login(@RequestBody LoginRequest loginReques
return new ResponseEntity<>(jwtResponse, HttpStatus.OK);
}

@PostMapping("/refresh")
public ResponseEntity<LoginResponse> refreshToken(@RequestBody LogoutOrRefreshRequest request) {
String uuid = request.getUuid();
String userId = request.getUserId();
String oldAccessToken = request.getAccessToken();
String oldRefreshToken = redisUtil.getData(uuid).orElseThrow(RefreshTokenException::new);

if (!jwtTokenProvider.validateJwtToken(oldAccessToken)) {
throw new RefreshTokenException();
}

if(!userId.equals(jwtTokenProvider.getUserIdFromJwtToken(oldRefreshToken)) && !userId.equals(jwtTokenProvider.getUserIdFromJwtToken(oldAccessToken))) {
throw new RefreshTokenException();
}

UserDetailsImpl userDetailsImpl = (UserDetailsImpl) userDetailsServiceImp.loadUserByUsername(userId);
LoginResponse jwtResponse = generateAndSaveToken(userDetailsImpl);
deleteToken(uuid, oldAccessToken);

return new ResponseEntity<>(jwtResponse, HttpStatus.OK);
}

private LoginResponse generateAndSaveToken(UserDetailsImpl userDetailsImpl) {
String userId = userDetailsImpl.getUserId();
String uuid = UUID.randomUUID().toString();
Expand All @@ -75,4 +98,11 @@ private LoginResponse generateAndSaveToken(UserDetailsImpl userDetailsImpl) {

return new LoginResponse(userId, accessToken, uuid);
}

private void deleteToken(String uuid, String oldAccessToken) {
if (redisUtil.getData(uuid).isPresent()) {
redisUtil.deleteData(uuid);
}
redisUtil.setDataExpire(oldAccessToken, oldAccessToken, (int)JwtTokenProvider.TOKEN_EXPIRATION_SECONDS);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@
import lombok.Getter;

@Getter
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public class LoginResponse {
@JsonProperty("token_type")
private final String TOKEN_TYPE = "Bearer";
private final String tokenType = "Bearer";
private String userId;
private String accessToken;
private String uuid;
Expand Down
26 changes: 19 additions & 7 deletions src/main/java/gp/cnusambe/security/jwt/JwtAuthFilter.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package gp.cnusambe.security.jwt;

import gp.cnusambe.service.user.UserDetailsServiceImpl;
import gp.cnusambe.util.RedisUtil;
import io.jsonwebtoken.ExpiredJwtException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -25,29 +26,40 @@ public class JwtAuthFilter extends OncePerRequestFilter {

private final JwtTokenProvider jwtTokenProvider;
private final UserDetailsServiceImpl userDetailsServiceImpl;
private final RedisUtil redisUtil;

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
try {
Optional<String> jwt = parseJwt(request);

if (jwt.isPresent() && jwtTokenProvider.validateJwtToken(jwt.get())) {
String username = jwtTokenProvider.getUserIdFromJwtToken(jwt.get());
UserDetails userDetails = userDetailsServiceImpl.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
Optional<String> isLockedAccessToken = redisUtil.getData(jwt.get());

if (isLockedAccessToken.isEmpty()) {
String username = jwtTokenProvider.getUserIdFromJwtToken(jwt.get());
UserDetails userDetails = userDetailsServiceImpl.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
} else {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
}
} else {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
}
} catch (ExpiredJwtException e) {
log.error("ExpiredJwtException : {}", e.getMessage());
} catch (Exception e) {
log.error("Cannot set user authentication : {}", e.getMessage());
}

filterChain.doFilter(request, response);
}

private Optional<String> parseJwt(HttpServletRequest request) {
String headerAuth = request.getHeader("Authorization");
if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) {
String headerAuth = request.getHeader(AUTHORIZATION_HEADER);
if (StringUtils.hasText(headerAuth) && headerAuth.startsWith(BEARER_PREFIX)) {
return Optional.of(headerAuth.substring(7));
}
return Optional.empty();
Expand Down

0 comments on commit 439b778

Please sign in to comment.