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

[REFACTOR] JWT 처리 방식 변경 및 리팩토링 #223

Merged
merged 21 commits into from
Jul 25, 2024
Merged
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
1d20d97
feat: JwtService에 인터페이스 적용
SSung023 Jul 10, 2024
dccd526
refactor: JWT 요청 API의 Request DTO 변경
SSung023 Jul 10, 2024
48bc68d
refactor: 필요없는 구문 삭제
SSung023 Jul 10, 2024
b9997a7
refactor: JwtService의 의존 관계 변경
SSung023 Jul 10, 2024
64a4be1
refactor: tokenService를 활용하는 코드로 리팩토링
SSung023 Jul 10, 2024
31bed44
refactor: Facade 패턴 적용
SSung023 Jul 10, 2024
e008d4d
feat: Access token 처리 방식 변경
SSung023 Jul 12, 2024
9e43778
refactor: enum 이름 변경
SSung023 Jul 12, 2024
f9bd69f
test: JWT 처리 방식 변경에 의한 테스트 코드 변경
SSung023 Jul 13, 2024
df83264
fix: JWT 에러 메세지 변경 및 버그 픽스
SSung023 Jul 13, 2024
4b0ffae
test: JwtFacade 테스트 코드에 DCI 패턴 적용
SSung023 Jul 13, 2024
b057291
test: TokenService 테스트 코드에 DCI 패턴 적용
SSung023 Jul 15, 2024
a63536b
feat: JWT 처리 도중 Exception 발생 시, logout하도록 처리
SSung023 Jul 15, 2024
630ba8c
Merge branch 'main' into refactor/217-refactor-jwt
SSung023 Jul 15, 2024
1625373
fix: logout 처리 위치 변경
SSung023 Jul 15, 2024
d3c115b
fix: custom header가 전달되지 않는 버그 픽스
SSung023 Jul 21, 2024
3cc8913
fix: 브라우저에서 Authorization 헤더 접근이 안되는 버그 픽스
SSung023 Jul 21, 2024
e8e2879
feat: Access token 재발급 여부 관련 헤더 추가
SSung023 Jul 21, 2024
8a987b5
fix: 브라우저에서 token-reissued 헤더 접근이 안되는 버그 픽스
SSung023 Jul 21, 2024
c079067
refactor: 불필요한 로직 제거
SSung023 Jul 24, 2024
36feda0
refactor: Facade 구현체 이름 통일
SSung023 Jul 24, 2024
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
Original file line number Diff line number Diff line change
@@ -116,6 +116,10 @@ public long updatePoints(Long amount) {
return this.point;
}

public boolean isRegistered() {
return this.role != Role.NOT_REGISTERED;
}

@Override
public Optional<Files> getFiles() {
return Optional.ofNullable(this.files);
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.genius.gitget.global.security.config;

import static com.genius.gitget.global.security.constants.JwtRule.ACCESS_HEADER;
import static com.genius.gitget.global.security.constants.JwtRule.ACCESS_REISSUED_HEADER;

import jakarta.servlet.http.HttpServletRequest;
import java.util.Collections;
import java.util.List;
@@ -24,6 +27,10 @@ public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
config.setAllowedMethods(ALLOWED_METHODS);
config.setAllowCredentials(true);
config.setAllowedHeaders(Collections.singletonList("*"));

config.setExposedHeaders(Collections.singletonList(ACCESS_HEADER.getValue()));
config.addExposedHeader(ACCESS_REISSUED_HEADER.getValue());

config.setMaxAge(3600L);
return config;
}
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@
import com.genius.gitget.global.security.handler.OAuth2FailureHandler;
import com.genius.gitget.global.security.handler.OAuth2SuccessHandler;
import com.genius.gitget.global.security.service.CustomOAuth2UserService;
import com.genius.gitget.global.security.service.JwtService;
import com.genius.gitget.global.security.service.JwtFacade;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -32,7 +32,7 @@ public class SecurityConfig {
private static final String PERMITTED_ROLES[] = {"USER", "ADMIN"};
private final CustomCorsConfigurationSource customCorsConfigurationSource;
private final CustomOAuth2UserService customOAuthService;
private final JwtService jwtService;
private final JwtFacade jwtFacade;
private final UserService userService;
private final OAuth2SuccessHandler successHandler;
private final OAuth2FailureHandler failureHandler;
@@ -57,7 +57,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.sessionCreationPolicy(SessionCreationPolicy.STATELESS))

// JWT 검증 필터 추가
.addFilterBefore(new JwtAuthenticationFilter(jwtService, userService),
.addFilterBefore(new JwtAuthenticationFilter(jwtFacade, userService),
UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(new ExceptionHandlerFilter(), JwtAuthenticationFilter.class)

Original file line number Diff line number Diff line change
@@ -6,10 +6,15 @@
@RequiredArgsConstructor
@Getter
public enum JwtRule {
JWT_ISSUE_HEADER("Set-Cookie"),
JWT_RESOLVE_HEADER("Cookie"),
ACCESS_PREFIX("access"),
REFRESH_PREFIX("refresh");

ACCESS_HEADER("Authorization"),
ACCESS_PREFIX("Bearer "),
ACCESS_REISSUED_HEADER("token-reissued"),

REFRESH_PREFIX("refresh"),

REFRESH_ISSUE("Set-Cookie"),
REFRESH_RESOLVE("Cookie");

private final String value;
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package com.genius.gitget.global.security.controller;

import static com.genius.gitget.global.util.exception.ErrorCode.NOT_AUTHENTICATED_USER;
import static com.genius.gitget.global.util.exception.SuccessCode.SUCCESS;

import com.genius.gitget.challenge.user.domain.User;
import com.genius.gitget.challenge.user.service.UserService;
import com.genius.gitget.global.security.domain.UserPrincipal;
import com.genius.gitget.global.security.dto.AuthResponse;
import com.genius.gitget.global.security.dto.SignupResponse;
import com.genius.gitget.global.security.service.JwtService;
import com.genius.gitget.global.security.dto.TokenRequest;
import com.genius.gitget.global.security.service.JwtFacade;
import com.genius.gitget.global.util.exception.BusinessException;
import com.genius.gitget.global.util.response.dto.CommonResponse;
import com.genius.gitget.global.util.response.dto.SingleResponse;
import jakarta.servlet.http.HttpServletResponse;
@@ -27,18 +29,20 @@
@RequestMapping("/api")
public class AuthController {
private final UserService userService;
private final JwtService jwtService;
private final JwtFacade jwtFacade;

@PostMapping("/auth")
public ResponseEntity<SingleResponse<AuthResponse>> generateToken(HttpServletResponse response,
@RequestBody SignupResponse tokenRequest) {
User requestUser = userService.findUserByIdentifier(tokenRequest.identifier());
jwtService.validateUser(requestUser);
@RequestBody TokenRequest tokenRequest) {
User user = userService.findUserByIdentifier(tokenRequest.identifier());
if (!user.isRegistered()) {
throw new BusinessException(NOT_AUTHENTICATED_USER);
}

jwtService.generateAccessToken(response, requestUser);
jwtService.generateRefreshToken(response, requestUser);
jwtFacade.generateAccessToken(response, user);
jwtFacade.generateRefreshToken(response, user);

AuthResponse authResponse = userService.getUserAuthInfo(requestUser.getIdentifier());
AuthResponse authResponse = userService.getUserAuthInfo(user.getIdentifier());

return ResponseEntity.ok().body(
new SingleResponse<>(SUCCESS.getStatus(), SUCCESS.getMessage(), authResponse)
@@ -49,7 +53,7 @@ public ResponseEntity<SingleResponse<AuthResponse>> generateToken(HttpServletRes
public ResponseEntity<CommonResponse> logout(
@AuthenticationPrincipal UserPrincipal userPrincipal,
HttpServletResponse response) {
jwtService.logout(userPrincipal.getUser(), response);
jwtFacade.logout(response, userPrincipal.getUser().getIdentifier());

return ResponseEntity.ok().body(
new CommonResponse(SUCCESS.getStatus(), SUCCESS.getMessage())
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.genius.gitget.global.security.dto;

public record TokenRequest(
String identifier
) {
}
Original file line number Diff line number Diff line change
@@ -8,18 +8,20 @@
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.filter.OncePerRequestFilter;

@Slf4j
@RequiredArgsConstructor
public class ExceptionHandlerFilter extends OncePerRequestFilter {

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
try {
filterChain.doFilter(request, response);
} catch (BusinessException e) {
log.error("[ERROR]" + e.getMessage(), e);
setErrorResponse(response, e);
}
}
Original file line number Diff line number Diff line change
@@ -2,10 +2,9 @@

import static com.genius.gitget.global.security.config.SecurityConfig.PERMITTED_URI;

import com.genius.gitget.global.security.constants.JwtRule;
import com.genius.gitget.global.security.service.JwtService;
import com.genius.gitget.challenge.user.domain.User;
import com.genius.gitget.challenge.user.service.UserService;
import com.genius.gitget.global.security.service.JwtFacade;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
@@ -19,7 +18,7 @@

@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtService jwtService;
private final JwtFacade jwtFacade;
private final UserService userService;

@Override
@@ -32,26 +31,27 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
return;
}

String accessToken = jwtService.resolveTokenFromCookie(request, JwtRule.ACCESS_PREFIX);
if (jwtService.validateAccessToken(accessToken)) {
String accessToken = jwtFacade.resolveAccessToken(request);
if (jwtFacade.validateAccessToken(accessToken)) {
setAuthenticationToContext(accessToken);
filterChain.doFilter(request, response);
return;
}

String refreshToken = jwtService.resolveTokenFromCookie(request, JwtRule.REFRESH_PREFIX);
String refreshToken = jwtFacade.resolveRefreshToken(request);
User user = findUserByRefreshToken(refreshToken);

if (jwtService.validateRefreshToken(refreshToken, user.getIdentifier())) {
String reissuedAccessToken = jwtService.generateAccessToken(response, user);
jwtService.generateRefreshToken(response, user);
if (jwtFacade.validateRefreshToken(refreshToken, user.getIdentifier())) {
String reissuedAccessToken = jwtFacade.generateAccessToken(response, user);
jwtFacade.generateRefreshToken(response, user);
jwtFacade.setReissuedHeader(response);

setAuthenticationToContext(reissuedAccessToken);
filterChain.doFilter(request, response);
return;
}

jwtService.logout(user, response);
jwtFacade.logout(response, user.getIdentifier());
}

private boolean isPermittedURI(String requestURI) {
@@ -63,12 +63,12 @@ private boolean isPermittedURI(String requestURI) {
}

private User findUserByRefreshToken(String refreshToken) {
String identifier = jwtService.getIdentifierFromRefresh(refreshToken);
String identifier = jwtFacade.getIdentifierFromRefresh(refreshToken);
return userService.findUserByIdentifier(identifier);
}

private void setAuthenticationToContext(String accessToken) {
Authentication authentication = jwtService.getAuthentication(accessToken);
Authentication authentication = jwtFacade.getAuthentication(accessToken);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
Original file line number Diff line number Diff line change
@@ -2,14 +2,13 @@

import static com.genius.gitget.global.util.exception.ErrorCode.MEMBER_NOT_FOUND;

import com.genius.gitget.global.security.domain.UserPrincipal;
import com.genius.gitget.challenge.user.domain.User;
import com.genius.gitget.challenge.user.repository.UserRepository;
import com.genius.gitget.global.security.domain.UserPrincipal;
import com.genius.gitget.global.util.exception.BusinessException;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

@Service
@@ -18,7 +17,7 @@ public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
public UserDetails loadUserByUsername(String username) {
User user = userRepository.findById(Long.valueOf(username))
.orElseThrow(() -> new BusinessException(MEMBER_NOT_FOUND));

Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.genius.gitget.global.security.service;

import com.genius.gitget.challenge.user.domain.User;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;

public interface JwtFacade {
String generateAccessToken(HttpServletResponse response, User user);

String generateRefreshToken(HttpServletResponse response, User user);

String resolveAccessToken(HttpServletRequest request);

String resolveRefreshToken(HttpServletRequest request);

String getIdentifierFromRefresh(String refreshToken);

boolean validateAccessToken(String accessToken);

boolean validateRefreshToken(String refreshToken, String identifier);

void setReissuedHeader(HttpServletResponse response);

void logout(HttpServletResponse response, String identifier);

Authentication getAuthentication(String accessToken);
}
Loading