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

Feature/#28 #44

Merged
merged 13 commits into from
Aug 14, 2024
Merged
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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,4 @@ out/
/.nb-gradle/

### VS Code ###
.vscode/
.vscode/
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package com.server.bbo_gak.domain.auth.controller;

import com.server.bbo_gak.domain.auth.dto.request.LoginRequest;
import com.server.bbo_gak.domain.auth.dto.request.RefreshTokenRequest;
import com.server.bbo_gak.domain.auth.service.AuthService;
import com.server.bbo_gak.domain.user.entity.User;
import com.server.bbo_gak.global.annotation.AuthUser;
import com.server.bbo_gak.global.security.jwt.dto.TokenDto;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
Expand All @@ -24,7 +26,12 @@ public ResponseEntity<TokenDto> login(@RequestBody LoginRequest request) {
return ResponseEntity.ok(authService.login(request));
}

@PostMapping("/test/logout")
@PostMapping("/refreshToken")
public ResponseEntity<TokenDto> validateRefreshToken(@RequestBody RefreshTokenRequest request) {
return ResponseEntity.ok(authService.validateRefreshToken(request));
}

@GetMapping("/logout")
public ResponseEntity<Void> logout(@AuthUser User user) {
authService.logout(user);
return ResponseEntity.ok().body(null);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.server.bbo_gak.domain.auth.dto.request;

public record RefreshTokenRequest(
String refreshToken
) {

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.server.bbo_gak.domain.auth.service;

import com.server.bbo_gak.domain.auth.dto.request.LoginRequest;
import com.server.bbo_gak.domain.auth.dto.request.RefreshTokenRequest;
import com.server.bbo_gak.domain.user.entity.User;
import com.server.bbo_gak.global.security.jwt.dto.TokenDto;
import org.springframework.stereotype.Service;
Expand All @@ -10,6 +11,8 @@ public interface AuthService {

TokenDto login(LoginRequest request);

TokenDto validateRefreshToken(RefreshTokenRequest request);

void logout(User user);

}
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
package com.server.bbo_gak.domain.auth.service;

import com.server.bbo_gak.domain.auth.dto.request.LoginRequest;
import com.server.bbo_gak.domain.auth.dto.request.RefreshTokenRequest;
import com.server.bbo_gak.domain.auth.entity.AuthTestUser;
import com.server.bbo_gak.domain.auth.entity.AuthTestUserRepository;
import com.server.bbo_gak.domain.user.entity.User;
import com.server.bbo_gak.global.error.exception.BusinessException;
import com.server.bbo_gak.global.error.exception.ErrorCode;
import com.server.bbo_gak.global.error.exception.NotFoundException;
import com.server.bbo_gak.global.security.jwt.dto.AccessTokenDto;
import com.server.bbo_gak.global.security.jwt.dto.TokenDto;
import com.server.bbo_gak.global.security.jwt.entity.RefreshTokenRepository;
import com.server.bbo_gak.global.security.jwt.service.JwtTokenService;
import io.jsonwebtoken.JwtException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -20,7 +23,7 @@ public class AuthServiceImpl implements AuthService {

private final AuthTestUserRepository authTestUserRepository;
private final RefreshTokenRepository refreshTokenRepository;
private final JwtTokenService tokenService;
private final JwtTokenService jwtTokenService;

@Override
@Transactional
Expand All @@ -34,7 +37,27 @@ public TokenDto login(LoginRequest request) {
if (refreshTokenRepository.existsRefreshTokenByMemberId(authTestUser.getId())) {
refreshTokenRepository.deleteById(authTestUser.getId());
}
return tokenService.createTokenDto(authTestUser.getId(), authTestUser.getRole());
return jwtTokenService.createTokenDto(authTestUser.getId(), authTestUser.getRole());
}

@Override
public TokenDto validateRefreshToken(RefreshTokenRequest request) {
String refreshToken = request.refreshToken();
if (refreshToken == null) {
throw new JwtException(ErrorCode.RT_NOT_FOUND.getMessage()); //AT ๋งŒ๋ฃŒ RT null
}

TokenDto tokenDto = null;
if (jwtTokenService.validateRefreshToken(refreshToken)) {
// RT ์œ ํšจ
tokenDto = jwtTokenService.recreateTokenDtoAtValidate(refreshToken);

AccessTokenDto accessTokenDto = jwtTokenService.retrieveAccessToken(tokenDto.accessToken());
jwtTokenService.setAuthenticationToContext(accessTokenDto.memberId(), accessTokenDto.role());

}

return tokenDto;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,4 @@ public enum RecruitStatus {


private final String value;

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
public class SecurityConfig {

private final JwtTokenService jwtTokenService;
private String[] allowUrls = {"/", "/api/v1/users/test/login", "/docs/**", "/v3/**", "/favicon.ico"};
private String[] allowUrls = {"/", "/api/v1/users/test/login", "/api/v1/users/refreshToken", "/docs/**", "/v3/**",
"/favicon.ico"};

@Bean
public WebSecurityCustomizer configure() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.server.bbo_gak.global.error.exception.BusinessException;
import com.server.bbo_gak.global.error.exception.ErrorCode;
import com.server.bbo_gak.global.error.exception.NotFoundException;
import io.jsonwebtoken.JwtException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
Expand Down Expand Up @@ -37,4 +38,12 @@ protected ResponseEntity<ErrorResponse> handleException(final Exception exceptio
final ErrorResponse response = ErrorResponse.from(ErrorCode.INTERNAL_SERVER_ERROR);
return new ResponseEntity<>(response, response.getStatus());
}

@ExceptionHandler(JwtException.class)
protected ResponseEntity<ErrorResponse> handleJwtException(final JwtException exception) {
log.error("handleEntityNotFoundException", exception);
final ErrorCode errorCode = ErrorCode.RT_NOT_FOUND;
final ErrorResponse response = ErrorResponse.from(errorCode);
return new ResponseEntity<>(response, errorCode.getStatus());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public enum ErrorCode {
ACCESS_DENIED(HttpStatus.UNAUTHORIZED, "ํ† ํฐ์ด ์—†์Šต๋‹ˆ๋‹ค"),
TOKEN_SUBJECT_FORMAT_ERROR(HttpStatus.UNAUTHORIZED, "Subject ๊ฐ’์— Long ํƒ€์ž…์ด ์•„๋‹Œ ๋‹ค๋ฅธ ํƒ€์ž…์ด ๋“ค์–ด์žˆ์Šต๋‹ˆ๋‹ค."),
AT_EXPIRED_AND_RT_NOT_FOUND(HttpStatus.UNAUTHORIZED, "AT๋Š” ๋งŒ๋ฃŒ๋˜์—ˆ๊ณ  RT๋Š” ๋น„์–ด์žˆ์Šต๋‹ˆ๋‹ค."),
RT_NOT_FOUND(HttpStatus.UNAUTHORIZED, "RT๊ฐ€ ๋น„์–ด์žˆ์Šต๋‹ˆ๋‹ค"),

PASSWORD_NOT_MATCHES(HttpStatus.BAD_REQUEST, "๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž˜๋ชป ์ž…๋ ฅํ•˜์…จ์Šต๋‹ˆ๋‹ค."),

Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,14 @@
package com.server.bbo_gak.global.security.jwt.filter;

import com.server.bbo_gak.domain.user.entity.UserRole;
import com.server.bbo_gak.global.error.exception.ErrorCode;
import com.server.bbo_gak.global.security.PrincipalDetails;
import com.server.bbo_gak.global.security.jwt.dto.AccessTokenDto;
import com.server.bbo_gak.global.security.jwt.dto.TokenDto;
import com.server.bbo_gak.global.security.jwt.service.JwtTokenService;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtException;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.filter.OncePerRequestFilter;

@RequiredArgsConstructor
Expand All @@ -40,38 +30,15 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
filterChain.doFilter(request, response); //AT null
}

try {
if (jwtTokenService.validateAccessToken(accessTokenHeaderValue)) {
//AT ์œ ํšจ
accessTokenDto = jwtTokenService.retrieveAccessToken(accessTokenHeaderValue);
setAuthenticationToContext(accessTokenDto.memberId(), accessTokenDto.role());
}
} catch (ExpiredJwtException e) {
if (refreshTokenHeaderValue == null) {
throw new JwtException(ErrorCode.AT_EXPIRED_AND_RT_NOT_FOUND.getMessage()); //AT ๋งŒ๋ฃŒ RT null
}

if (jwtTokenService.validateRefreshToken(refreshTokenHeaderValue)) {
// AT ๋งŒ๋ฃŒ && RT ์œ ํšจ
TokenDto tokenDto = jwtTokenService.recreateTokenDtoAtValidate(refreshTokenHeaderValue);

//Refresh, Access Token์„ ํ—ค๋”์— ๋„ฃ์–ด ์ „์†กํ•ฉ๋‹ˆ๋‹ค (GET ์š”์ฒญ์‹œ์—๋Š” body๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์—)
jwtTokenService.setHeaderAccessToken(response, tokenDto.accessToken());
jwtTokenService.setHeaderRefreshToken(response, tokenDto.refreshToken());

accessTokenDto = jwtTokenService.retrieveAccessToken(tokenDto.accessToken());
setAuthenticationToContext(accessTokenDto.memberId(), accessTokenDto.role());
}
if (jwtTokenService.validateAccessToken(accessTokenHeaderValue)) {
//AT ์œ ํšจ
accessTokenDto = jwtTokenService.retrieveAccessToken(accessTokenHeaderValue);
jwtTokenService.setAuthenticationToContext(accessTokenDto.memberId(), accessTokenDto.role());
}

filterChain.doFilter(request, response);
}

private void setAuthenticationToContext(Long memberId, UserRole role) {
UserDetails userDetails = PrincipalDetails.ofJwt(memberId, role);
Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, null,
userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
}

private String extractAccessTokenFromHeader(HttpServletRequest request) {
return Optional.ofNullable(request.getHeader("Authorization"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.server.bbo_gak.domain.user.entity.User;
import com.server.bbo_gak.domain.user.entity.UserRole;
import com.server.bbo_gak.global.error.exception.ErrorCode;
import com.server.bbo_gak.global.security.PrincipalDetails;
import com.server.bbo_gak.global.security.jwt.dto.AccessTokenDto;
import com.server.bbo_gak.global.security.jwt.dto.TokenDto;
import com.server.bbo_gak.global.security.jwt.entity.RefreshToken;
Expand All @@ -18,6 +19,10 @@
import java.util.Date;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;

@Slf4j
Expand All @@ -29,6 +34,13 @@ public class JwtTokenService {
private final JwtUtil jwtUtil;
private final RefreshTokenRepository refreshTokenRepository;

public void setAuthenticationToContext(Long memberId, UserRole role) {
UserDetails userDetails = PrincipalDetails.ofJwt(memberId, role);
Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, null,
userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
}

/**
* ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋กœ๊ทธ์ธ ์‹œ ์ „๋‹ฌํ•  AT, RT๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public class JwtUtil {

private final JwtProperties jwtProperties;


/**
* AccessToken์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
*
Expand Down
Loading
Loading