From 762c2f1c711787fdb0ae5d85c04b8d8461a32e32 Mon Sep 17 00:00:00 2001 From: emost22 Date: Sat, 13 Jul 2024 21:53:58 +0900 Subject: [PATCH 1/9] Feat: add jwt utils --- build.gradle | 5 + .../com/sirius/spurt/common/jwt/JwtUtils.java | 119 ++++++++++++++++++ .../spurt/common/jwt/token/AccessToken.java | 15 +++ .../spurt/common/jwt/token/RefreshToken.java | 15 +++ 4 files changed, 154 insertions(+) create mode 100644 src/main/java/com/sirius/spurt/common/jwt/JwtUtils.java create mode 100644 src/main/java/com/sirius/spurt/common/jwt/token/AccessToken.java create mode 100644 src/main/java/com/sirius/spurt/common/jwt/token/RefreshToken.java diff --git a/build.gradle b/build.gradle index dcb6f66..77b800b 100644 --- a/build.gradle +++ b/build.gradle @@ -57,6 +57,11 @@ dependencies { testImplementation 'com.h2database:h2:2.1.214' compileOnly('com.h2database:h2:2.1.214') + implementation group: 'com.auth0', name: 'java-jwt', version: '4.2.1' + implementation 'io.jsonwebtoken:jjwt-api:0.11.5' + runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' + runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5' + implementation 'org.springframework.boot:spring-boot-starter-data-redis' } diff --git a/src/main/java/com/sirius/spurt/common/jwt/JwtUtils.java b/src/main/java/com/sirius/spurt/common/jwt/JwtUtils.java new file mode 100644 index 0000000..5ded878 --- /dev/null +++ b/src/main/java/com/sirius/spurt/common/jwt/JwtUtils.java @@ -0,0 +1,119 @@ +package com.sirius.spurt.common.jwt; + +import static com.sirius.spurt.common.meta.ResultCode.AUTHENTICATION_FAILED; + +import com.sirius.spurt.common.exception.GlobalException; +import com.sirius.spurt.common.jwt.token.AccessToken; +import com.sirius.spurt.common.jwt.token.RefreshToken; +import com.sirius.spurt.store.repository.redis.token.TokenRepository; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.io.Decoders; +import io.jsonwebtoken.security.Keys; +import jakarta.annotation.PostConstruct; +import jakarta.servlet.http.HttpServletResponse; +import java.security.Key; +import java.util.Date; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.ResponseCookie; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +@RequiredArgsConstructor +public class JwtUtils { + private final TokenRepository tokenRepository; + + @Value("${jwt.secret}") + private String jwtSecret; + + public static final String ACCESS_TOKEN_NAME = "Authorization"; + public static final String REFRESH_TOKEN_NAME = "RefreshToken"; + public static final String TOKEN_TYPE = "Bearer%20"; + public static final String KEY_PREFIX = "jwt:"; + private final long ACCESS_TOKEN_EXPIRE_TIME = 10 * 60 * 1000L; + private final long REFRESH_TOKEN_EXPIRE_TIME = 7 * 24 * 60 * 60 * 1000L; + private Key key; + + @PostConstruct + public void init() { + byte[] keyBytes = Decoders.BASE64.decode(jwtSecret); + this.key = Keys.hmacShaKeyFor(keyBytes); + } + + private AccessToken getAccessToken(String userId) { + String accessToken = + Jwts.builder() + .setSubject("accessToken") + .claim("userId", userId) + .setExpiration(new Date(getCurrentTimestamp() + ACCESS_TOKEN_EXPIRE_TIME)) + .signWith(key, SignatureAlgorithm.HS256) + .compact(); + + return AccessToken.builder().token(accessToken).expireTime(ACCESS_TOKEN_EXPIRE_TIME).build(); + } + + private RefreshToken getRefreshToken(String userId) { + String refreshToken = + Jwts.builder() + .setSubject("refreshToken") + .claim("userId", userId) + .setExpiration(new Date(getCurrentTimestamp() + REFRESH_TOKEN_EXPIRE_TIME)) + .signWith(key, SignatureAlgorithm.HS256) + .compact(); + + return RefreshToken.builder().token(refreshToken).expireTime(REFRESH_TOKEN_EXPIRE_TIME).build(); + } + + public void setAccessToken(HttpServletResponse response, String userId) { + AccessToken accessToken = getAccessToken(userId); + setCookie(response, ACCESS_TOKEN_NAME, accessToken.getToken(), accessToken.getExpireTime()); + } + + public void setRefreshToken(HttpServletResponse response, String userId) { + RefreshToken refreshToken = getRefreshToken(userId); + setCookie(response, REFRESH_TOKEN_NAME, refreshToken.getToken(), refreshToken.getExpireTime()); + } + + private void setCookie(HttpServletResponse response, String key, String token, Long expireTime) { + ResponseCookie responseCookie = + ResponseCookie.from(key, token) + .path("/") + .sameSite("None") + .httpOnly(false) + .secure(true) + .maxAge(Math.toIntExact(expireTime)) + .build(); + response.addHeader("Set-Cookie", responseCookie.toString()); + } + + private long getCurrentTimestamp() { + return System.currentTimeMillis(); + } + + public String getUserId(String token) { + try { + return Jwts.parserBuilder() + .setSigningKey(key) + .build() + .parseClaimsJws(token) + .getBody() + .get("userId") + .toString(); + } catch (Exception e) { + log.error(e.getMessage()); + return null; + } + } + + public void updateTokens(HttpServletResponse response, String userId) { + if (!tokenRepository.hasRefreshToken(KEY_PREFIX + userId)) { + throw new GlobalException(AUTHENTICATION_FAILED); + } + + setAccessToken(response, userId); + setRefreshToken(response, userId); + } +} diff --git a/src/main/java/com/sirius/spurt/common/jwt/token/AccessToken.java b/src/main/java/com/sirius/spurt/common/jwt/token/AccessToken.java new file mode 100644 index 0000000..fc1060d --- /dev/null +++ b/src/main/java/com/sirius/spurt/common/jwt/token/AccessToken.java @@ -0,0 +1,15 @@ +package com.sirius.spurt.common.jwt.token; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AccessToken { + private String token; + private Long expireTime; +} diff --git a/src/main/java/com/sirius/spurt/common/jwt/token/RefreshToken.java b/src/main/java/com/sirius/spurt/common/jwt/token/RefreshToken.java new file mode 100644 index 0000000..0bc0d2f --- /dev/null +++ b/src/main/java/com/sirius/spurt/common/jwt/token/RefreshToken.java @@ -0,0 +1,15 @@ +package com.sirius.spurt.common.jwt.token; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class RefreshToken { + private String token; + private Long expireTime; +} From 6d013db6b6a0f05dc746358c190365bc89113aad Mon Sep 17 00:00:00 2001 From: emost22 Date: Sat, 13 Jul 2024 21:54:14 +0900 Subject: [PATCH 2/9] Feat: add principal details and service --- .../spurt/common/auth/PrincipalDetails.java | 61 +++++++++++++++++++ .../common/auth/PrincipalDetailsService.java | 23 +++++++ 2 files changed, 84 insertions(+) create mode 100644 src/main/java/com/sirius/spurt/common/auth/PrincipalDetails.java create mode 100644 src/main/java/com/sirius/spurt/common/auth/PrincipalDetailsService.java diff --git a/src/main/java/com/sirius/spurt/common/auth/PrincipalDetails.java b/src/main/java/com/sirius/spurt/common/auth/PrincipalDetails.java new file mode 100644 index 0000000..5bd1adc --- /dev/null +++ b/src/main/java/com/sirius/spurt/common/auth/PrincipalDetails.java @@ -0,0 +1,61 @@ +package com.sirius.spurt.common.auth; + +import com.sirius.spurt.store.repository.database.entity.UserEntity; +import java.util.Collection; +import java.util.Map; +import lombok.Builder; +import lombok.Getter; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.oauth2.core.user.OAuth2User; + +@Getter +@Builder +public class PrincipalDetails implements UserDetails, OAuth2User { + private final UserEntity userEntity; + + @Override + public String getName() { + return null; + } + + @Override + public Map getAttributes() { + return null; + } + + @Override + public Collection getAuthorities() { + return null; + } + + @Override + public String getPassword() { + return null; + } + + @Override + public String getUsername() { + return null; + } + + @Override + public boolean isAccountNonExpired() { + return false; + } + + @Override + public boolean isAccountNonLocked() { + return false; + } + + @Override + public boolean isCredentialsNonExpired() { + return false; + } + + @Override + public boolean isEnabled() { + return false; + } +} diff --git a/src/main/java/com/sirius/spurt/common/auth/PrincipalDetailsService.java b/src/main/java/com/sirius/spurt/common/auth/PrincipalDetailsService.java new file mode 100644 index 0000000..481a279 --- /dev/null +++ b/src/main/java/com/sirius/spurt/common/auth/PrincipalDetailsService.java @@ -0,0 +1,23 @@ +package com.sirius.spurt.common.auth; + +import com.sirius.spurt.common.validator.UserValidator; +import com.sirius.spurt.store.repository.database.entity.UserEntity; +import com.sirius.spurt.store.repository.database.repository.UserRepository; +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 +@RequiredArgsConstructor +public class PrincipalDetailsService implements UserDetailsService { + private final UserRepository userRepository; + + @Override + public UserDetails loadUserByUsername(String userId) throws UsernameNotFoundException { + UserEntity userEntity = userRepository.findByUserId(userId); + UserValidator.validator(userEntity); + return PrincipalDetails.builder().userEntity(userEntity).build(); + } +} From 18968bda306d40b7c3439a625101b22ec06a220b Mon Sep 17 00:00:00 2001 From: emost22 Date: Sat, 13 Jul 2024 21:55:01 +0900 Subject: [PATCH 3/9] Feat: add authorization filter and token validator --- build.gradle | 2 + .../spurt/common/jwt/AuthorizationFilter.java | 107 ++++++++++++++++++ .../sirius/spurt/common/meta/ResultCode.java | 1 + .../common/validator/TokenValidator.java | 27 +++++ 4 files changed, 137 insertions(+) create mode 100644 src/main/java/com/sirius/spurt/common/jwt/AuthorizationFilter.java create mode 100644 src/main/java/com/sirius/spurt/common/validator/TokenValidator.java diff --git a/build.gradle b/build.gradle index 77b800b..fbf9665 100644 --- a/build.gradle +++ b/build.gradle @@ -62,6 +62,8 @@ dependencies { runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5' + implementation 'org.apache.commons:commons-lang3:3.4' + implementation 'org.springframework.boot:spring-boot-starter-data-redis' } diff --git a/src/main/java/com/sirius/spurt/common/jwt/AuthorizationFilter.java b/src/main/java/com/sirius/spurt/common/jwt/AuthorizationFilter.java new file mode 100644 index 0000000..a2804e9 --- /dev/null +++ b/src/main/java/com/sirius/spurt/common/jwt/AuthorizationFilter.java @@ -0,0 +1,107 @@ +package com.sirius.spurt.common.jwt; + +import static com.sirius.spurt.common.jwt.JwtUtils.ACCESS_TOKEN_NAME; +import static com.sirius.spurt.common.jwt.JwtUtils.REFRESH_TOKEN_NAME; +import static com.sirius.spurt.common.jwt.JwtUtils.TOKEN_TYPE; +import static org.springframework.http.HttpMethod.GET; + +import com.sirius.spurt.common.auth.PrincipalDetails; +import com.sirius.spurt.common.validator.TokenValidator; +import com.sirius.spurt.common.validator.UserValidator; +import com.sirius.spurt.store.repository.database.entity.UserEntity; +import com.sirius.spurt.store.repository.database.repository.UserRepository; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ArrayUtils; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.security.web.util.matcher.RequestMatcher; +import org.springframework.util.StringUtils; +import org.springframework.web.filter.OncePerRequestFilter; + +@Slf4j +@RequiredArgsConstructor +public class AuthorizationFilter extends OncePerRequestFilter { + private final UserRepository userRepository; + private final JwtUtils jwtUtils; + + private static final List whiteList = + List.of(new AntPathRequestMatcher("/v1/question/random", GET.name())); + + @Override + protected void doFilterInternal( + HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + if (isTestHeader(request)) { + setAuthentication("111705357761926793028"); + filterChain.doFilter(request, response); + return; + } + + String accessCookie = getCookie(request, ACCESS_TOKEN_NAME); + String refreshCookie = getCookie(request, REFRESH_TOKEN_NAME); + + log.info("accessCookie: " + accessCookie); + log.info("refreshCookie: " + refreshCookie); + TokenValidator.validateCookie(accessCookie); + + String accessToken = accessCookie.replace(TOKEN_TYPE, ""); + String userId = jwtUtils.getUserId(accessToken); + if (userId == null) { + TokenValidator.validateCookie(refreshCookie); + String refreshToken = refreshCookie.replace(TOKEN_TYPE, ""); + userId = jwtUtils.getUserId(refreshToken); + TokenValidator.validateUserId(userId); + jwtUtils.updateTokens(response, userId); + } + + setAuthentication(userId); + filterChain.doFilter(request, response); + } + + private boolean isTestHeader(HttpServletRequest request) { + return StringUtils.hasText(request.getHeader("test")); + } + + private String getCookie(HttpServletRequest request, String key) { + if (ArrayUtils.isEmpty(request.getCookies())) { + return null; + } + + Cookie cookie = + Arrays.stream(request.getCookies()) + .filter(c -> key.equals(c.getName())) + .findFirst() + .orElse(null); + if (cookie == null) { + return null; + } + + return cookie.getValue(); + } + + private void setAuthentication(String userId) { + UserEntity userEntity = userRepository.findByUserId(userId); + UserValidator.validator(userEntity); + PrincipalDetails principalDetails = PrincipalDetails.builder().userEntity(userEntity).build(); + Authentication authentication = + new UsernamePasswordAuthenticationToken( + principalDetails, null, principalDetails.getAuthorities()); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + + @Override + protected boolean shouldNotFilter(HttpServletRequest request) { + return whiteList.stream().anyMatch(url -> url.matches(request)); + } +} diff --git a/src/main/java/com/sirius/spurt/common/meta/ResultCode.java b/src/main/java/com/sirius/spurt/common/meta/ResultCode.java index 9599693..3db4ba2 100644 --- a/src/main/java/com/sirius/spurt/common/meta/ResultCode.java +++ b/src/main/java/com/sirius/spurt/common/meta/ResultCode.java @@ -4,6 +4,7 @@ public enum ResultCode { SUCCESS(0, "정상 처리 되었습니다"), SYSTEM_ERROR(1000, "알 수 없는 에러가 발생했습니다."), AUTHENTICATION_FAILED(2000, "인증에 실패했습니다."), + UNKNOWN_SOCIAL(2001, "알 수 없는 소셜입니다."), NOT_QUESTION_OWNER(3000, "질문이 존재하지 않거나 작성자가 아닙니다."), NOT_EXIST_USER(3001, "존재하지 않는 유저입니다."), NOT_EXPERIENCE_OWNER(3002, "경험이 존재하지 않거나 작성자가 아닙니다."), diff --git a/src/main/java/com/sirius/spurt/common/validator/TokenValidator.java b/src/main/java/com/sirius/spurt/common/validator/TokenValidator.java new file mode 100644 index 0000000..ef7dd9f --- /dev/null +++ b/src/main/java/com/sirius/spurt/common/validator/TokenValidator.java @@ -0,0 +1,27 @@ +package com.sirius.spurt.common.validator; + +import static com.sirius.spurt.common.meta.ResultCode.AUTHENTICATION_FAILED; + +import com.sirius.spurt.common.exception.GlobalException; + +public class TokenValidator { + public static void validateCookie(String cookie) { + if (!isExistCookie(cookie)) { + throw new GlobalException(AUTHENTICATION_FAILED); + } + } + + public static void validateUserId(String userId) { + if (!isExistUserId(userId)) { + throw new GlobalException(AUTHENTICATION_FAILED); + } + } + + private static boolean isExistCookie(String cookie) { + return cookie != null; + } + + private static boolean isExistUserId(String userId) { + return userId != null; + } +} From 3d845785ac288a001ba502e4f44e53c16eb08567 Mon Sep 17 00:00:00 2001 From: emost22 Date: Sat, 13 Jul 2024 21:55:17 +0900 Subject: [PATCH 4/9] Feat: add exception handler filter --- .../common/filter/ExceptionHandlerFilter.java | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 src/main/java/com/sirius/spurt/common/filter/ExceptionHandlerFilter.java diff --git a/src/main/java/com/sirius/spurt/common/filter/ExceptionHandlerFilter.java b/src/main/java/com/sirius/spurt/common/filter/ExceptionHandlerFilter.java new file mode 100644 index 0000000..a2aaf6c --- /dev/null +++ b/src/main/java/com/sirius/spurt/common/filter/ExceptionHandlerFilter.java @@ -0,0 +1,50 @@ +package com.sirius.spurt.common.filter; + +import static jakarta.servlet.http.HttpServletResponse.SC_OK; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.sirius.spurt.common.exception.GlobalException; +import com.sirius.spurt.common.meta.ResultCode; +import com.sirius.spurt.service.controller.RestResponse; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +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 { + private final ObjectMapper objectMapper; + + @Override + protected void doFilterInternal( + HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + try { + filterChain.doFilter(request, response); + } catch (GlobalException e) { + setErrorResponse(response, e.getResultCode()); + } + } + + private void setErrorResponse(HttpServletResponse response, ResultCode resultCode) + throws IOException { + response.setCharacterEncoding(UTF_8.name()); + response.setContentType(APPLICATION_JSON_VALUE); + response.setStatus(SC_OK); + + try { + response.getWriter().write(objectMapper.writeValueAsString(RestResponse.error(resultCode))); + } catch (Exception e) { + log.error(e.getMessage()); + } finally { + response.getWriter().close(); + } + } +} From 1d57430fd94d1201bfc661e1f121f4c894041ba8 Mon Sep 17 00:00:00 2001 From: emost22 Date: Sat, 13 Jul 2024 21:56:11 +0900 Subject: [PATCH 5/9] Feat: add security config --- build.gradle | 2 + .../spurt/common/config/SecurityConfig.java | 60 +++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 src/main/java/com/sirius/spurt/common/config/SecurityConfig.java diff --git a/build.gradle b/build.gradle index fbf9665..9c092c4 100644 --- a/build.gradle +++ b/build.gradle @@ -57,6 +57,8 @@ dependencies { testImplementation 'com.h2database:h2:2.1.214' compileOnly('com.h2database:h2:2.1.214') + implementation 'org.springframework.boot:spring-boot-starter-security' + implementation group: 'com.auth0', name: 'java-jwt', version: '4.2.1' implementation 'io.jsonwebtoken:jjwt-api:0.11.5' runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' diff --git a/src/main/java/com/sirius/spurt/common/config/SecurityConfig.java b/src/main/java/com/sirius/spurt/common/config/SecurityConfig.java new file mode 100644 index 0000000..af04dd8 --- /dev/null +++ b/src/main/java/com/sirius/spurt/common/config/SecurityConfig.java @@ -0,0 +1,60 @@ +package com.sirius.spurt.common.config; + +import static org.springframework.security.config.http.SessionCreationPolicy.STATELESS; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.sirius.spurt.common.filter.ExceptionHandlerFilter; +import com.sirius.spurt.common.jwt.AuthorizationFilter; +import com.sirius.spurt.common.jwt.JwtUtils; +import com.sirius.spurt.store.repository.database.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.security.web.authentication.logout.LogoutFilter; + +@Configuration +@EnableWebSecurity +@RequiredArgsConstructor +public class SecurityConfig { + private final UserRepository userRepository; + private final JwtUtils jwtUtils; + private final ObjectMapper objectMapper; + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http.csrf(AbstractHttpConfigurer::disable) + .formLogin(AbstractHttpConfigurer::disable) + .logout(AbstractHttpConfigurer::disable) + .httpBasic(AbstractHttpConfigurer::disable) + .sessionManagement( + (sessionManagement) -> sessionManagement.sessionCreationPolicy(STATELESS)) + .authorizeHttpRequests( + (requests) -> + requests + .requestMatchers("/v1/question/random") + .permitAll() + .requestMatchers("/error") + .permitAll() + .anyRequest() + .authenticated()) + .addFilterBefore(authorizationFilter(), UsernamePasswordAuthenticationFilter.class) + .addFilterBefore(exceptionHandlerFilter(), LogoutFilter.class); + + return http.build(); + } + + @Bean + public AuthorizationFilter authorizationFilter() { + return new AuthorizationFilter(userRepository, jwtUtils); + } + + @Bean + public ExceptionHandlerFilter exceptionHandlerFilter() { + return new ExceptionHandlerFilter(objectMapper); + } +} From a7db2a2e6b7dd7be0c0d186270e88af3534e2707 Mon Sep 17 00:00:00 2001 From: emost22 Date: Sun, 14 Jul 2024 00:42:01 +0900 Subject: [PATCH 6/9] Fix: auth repository --- .../sirius/spurt/common/config/WebConfig.java | 12 +-- .../com/sirius/spurt/common/jwt/JwtUtils.java | 8 +- .../common/resolver/NonLoginUserResolver.java | 72 +++++++++++++---- .../common/resolver/user/NonLoginUser.java | 1 - .../store/provider/auth/AuthProvider.java | 8 +- .../provider/auth/impl/AuthProviderImpl.java | 25 +++--- .../spurt/store/provider/auth/vo/AuthVo.java | 1 - .../AuthRepository.java} | 6 +- .../impl/AuthRepositoryImpl.java} | 10 +-- .../resttemplate/auth/AuthRepository.java | 7 -- .../auth/impl/AuthRepositoryImpl.java | 46 ----------- .../auth/playload/UserInfoPayload.java | 34 -------- .../repository/resttemplate/package-info.java | 1 - .../java/com/sirius/spurt/BaseMvcTest.java | 33 ++++---- .../auth/impl/AuthProviderImplTest.java | 30 +------ .../impl/AuthRepositoryImplTest.java} | 8 +- .../auth/impl/AuthRepositoryImplTest.java | 78 ------------------- 17 files changed, 123 insertions(+), 257 deletions(-) rename src/main/java/com/sirius/spurt/store/repository/redis/{token/TokenRepository.java => auth/AuthRepository.java} (57%) rename src/main/java/com/sirius/spurt/store/repository/redis/{token/impl/TokenRepositoryImpl.java => auth/impl/AuthRepositoryImpl.java} (74%) delete mode 100644 src/main/java/com/sirius/spurt/store/repository/resttemplate/auth/AuthRepository.java delete mode 100644 src/main/java/com/sirius/spurt/store/repository/resttemplate/auth/impl/AuthRepositoryImpl.java delete mode 100644 src/main/java/com/sirius/spurt/store/repository/resttemplate/auth/playload/UserInfoPayload.java delete mode 100644 src/main/java/com/sirius/spurt/store/repository/resttemplate/package-info.java rename src/test/java/com/sirius/spurt/store/repository/redis/{token/impl/TokenRepositoryImplTest.java => auth/impl/AuthRepositoryImplTest.java} (91%) delete mode 100644 src/test/java/com/sirius/spurt/store/repository/resttemplate/auth/impl/AuthRepositoryImplTest.java diff --git a/src/main/java/com/sirius/spurt/common/config/WebConfig.java b/src/main/java/com/sirius/spurt/common/config/WebConfig.java index b57e5ec..aaa1433 100644 --- a/src/main/java/com/sirius/spurt/common/config/WebConfig.java +++ b/src/main/java/com/sirius/spurt/common/config/WebConfig.java @@ -1,10 +1,7 @@ package com.sirius.spurt.common.config; -import com.sirius.spurt.common.resolver.LoginUserResolver; +import com.sirius.spurt.common.jwt.JwtUtils; import com.sirius.spurt.common.resolver.NonLoginUserResolver; -import com.sirius.spurt.store.provider.auth.AuthProvider; -import com.sirius.spurt.store.provider.jobgroup.JobGroupProvider; -import com.sirius.spurt.store.provider.user.UserProvider; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Configuration; @@ -14,13 +11,10 @@ @Configuration @RequiredArgsConstructor public class WebConfig implements WebMvcConfigurer { - private final AuthProvider authProvider; - private final UserProvider userProvider; - private final JobGroupProvider jobGroupProvider; + private final JwtUtils jwtUtils; @Override public void addArgumentResolvers(List resolvers) { - resolvers.add(new LoginUserResolver(authProvider, userProvider, jobGroupProvider)); - resolvers.add(new NonLoginUserResolver(authProvider)); + resolvers.add(new NonLoginUserResolver(jwtUtils)); } } diff --git a/src/main/java/com/sirius/spurt/common/jwt/JwtUtils.java b/src/main/java/com/sirius/spurt/common/jwt/JwtUtils.java index 5ded878..82ecab0 100644 --- a/src/main/java/com/sirius/spurt/common/jwt/JwtUtils.java +++ b/src/main/java/com/sirius/spurt/common/jwt/JwtUtils.java @@ -5,7 +5,7 @@ import com.sirius.spurt.common.exception.GlobalException; import com.sirius.spurt.common.jwt.token.AccessToken; import com.sirius.spurt.common.jwt.token.RefreshToken; -import com.sirius.spurt.store.repository.redis.token.TokenRepository; +import com.sirius.spurt.store.provider.auth.AuthProvider; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.io.Decoders; @@ -24,7 +24,7 @@ @Component @RequiredArgsConstructor public class JwtUtils { - private final TokenRepository tokenRepository; + private final AuthProvider authProvider; @Value("${jwt.secret}") private String jwtSecret; @@ -74,6 +74,8 @@ public void setAccessToken(HttpServletResponse response, String userId) { public void setRefreshToken(HttpServletResponse response, String userId) { RefreshToken refreshToken = getRefreshToken(userId); + authProvider.setRefreshToken( + KEY_PREFIX + userId, refreshToken.getToken(), refreshToken.getExpireTime()); setCookie(response, REFRESH_TOKEN_NAME, refreshToken.getToken(), refreshToken.getExpireTime()); } @@ -109,7 +111,7 @@ public String getUserId(String token) { } public void updateTokens(HttpServletResponse response, String userId) { - if (!tokenRepository.hasRefreshToken(KEY_PREFIX + userId)) { + if (!authProvider.hasRefreshToken(KEY_PREFIX + userId)) { throw new GlobalException(AUTHENTICATION_FAILED); } diff --git a/src/main/java/com/sirius/spurt/common/resolver/NonLoginUserResolver.java b/src/main/java/com/sirius/spurt/common/resolver/NonLoginUserResolver.java index feb372f..cba1eec 100644 --- a/src/main/java/com/sirius/spurt/common/resolver/NonLoginUserResolver.java +++ b/src/main/java/com/sirius/spurt/common/resolver/NonLoginUserResolver.java @@ -1,10 +1,17 @@ package com.sirius.spurt.common.resolver; +import static com.sirius.spurt.common.jwt.JwtUtils.ACCESS_TOKEN_NAME; +import static com.sirius.spurt.common.jwt.JwtUtils.REFRESH_TOKEN_NAME; + +import com.sirius.spurt.common.jwt.JwtUtils; import com.sirius.spurt.common.resolver.user.NonLoginUser; -import com.sirius.spurt.store.provider.auth.AuthProvider; -import com.sirius.spurt.store.provider.auth.vo.AuthVo; +import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.util.Arrays; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ArrayUtils; import org.springframework.core.MethodParameter; import org.springframework.util.StringUtils; import org.springframework.web.bind.support.WebDataBinderFactory; @@ -12,9 +19,10 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; +@Slf4j @RequiredArgsConstructor public class NonLoginUserResolver implements HandlerMethodArgumentResolver { - private final AuthProvider authProvider; + private final JwtUtils jwtUtils; private final String TOKEN_TYPE = "Bearer "; @Override @@ -30,23 +38,59 @@ public Object resolveArgument( WebDataBinderFactory binderFactory) throws Exception { HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest(); + HttpServletResponse response = (HttpServletResponse) webRequest.getNativeResponse(); - String testHeader = request.getHeader("test"); - if (StringUtils.hasLength(testHeader)) { - return new NonLoginUser("admin", "email"); + if (isTestHeader(request)) { + return new NonLoginUser("admin"); } - String accessHeader = request.getHeader("Authorization"); - if (!StringUtils.hasLength(accessHeader) || !accessHeader.startsWith(TOKEN_TYPE)) { - return new NonLoginUser(null, null); + String accessCookie = getCookie(request, ACCESS_TOKEN_NAME); + String refreshCookie = getCookie(request, REFRESH_TOKEN_NAME); + if (isInValidCookie(accessCookie)) { + return new NonLoginUser(null); } - AuthVo userInfo = new AuthVo(); - if (StringUtils.hasLength(accessHeader) && accessHeader.startsWith(TOKEN_TYPE)) { - String accessToken = accessHeader.replace(TOKEN_TYPE, ""); - userInfo = authProvider.getUserId(accessToken); + String accessToken = accessCookie.replace(TOKEN_TYPE, ""); + String userId = jwtUtils.getUserId(accessToken); + if (userId == null) { + if (isInValidCookie(refreshCookie)) { + return new NonLoginUser(null); + } + + String refreshToken = refreshCookie.replace(TOKEN_TYPE, ""); + userId = jwtUtils.getUserId(refreshToken); + if (userId == null) { + return new NonLoginUser(null); + } + + jwtUtils.updateTokens(response, userId); } - return new NonLoginUser(userInfo.getUserId(), userInfo.getEmail()); + return new NonLoginUser(userId); + } + + private boolean isTestHeader(HttpServletRequest request) { + return StringUtils.hasText(request.getHeader("test")); + } + + private String getCookie(HttpServletRequest request, String key) { + if (ArrayUtils.isEmpty(request.getCookies())) { + return null; + } + + Cookie cookie = + Arrays.stream(request.getCookies()) + .filter(c -> key.equals(c.getName())) + .findFirst() + .orElse(null); + if (cookie == null) { + return null; + } + + return cookie.getValue(); + } + + private boolean isInValidCookie(String cookie) { + return !StringUtils.hasText(cookie) || !cookie.startsWith(TOKEN_TYPE); } } diff --git a/src/main/java/com/sirius/spurt/common/resolver/user/NonLoginUser.java b/src/main/java/com/sirius/spurt/common/resolver/user/NonLoginUser.java index 8d0c637..cbd4e1e 100644 --- a/src/main/java/com/sirius/spurt/common/resolver/user/NonLoginUser.java +++ b/src/main/java/com/sirius/spurt/common/resolver/user/NonLoginUser.java @@ -11,5 +11,4 @@ @AllArgsConstructor public class NonLoginUser { private String userId; - private String email; } diff --git a/src/main/java/com/sirius/spurt/store/provider/auth/AuthProvider.java b/src/main/java/com/sirius/spurt/store/provider/auth/AuthProvider.java index 60af9c6..a3c0c94 100644 --- a/src/main/java/com/sirius/spurt/store/provider/auth/AuthProvider.java +++ b/src/main/java/com/sirius/spurt/store/provider/auth/AuthProvider.java @@ -3,5 +3,11 @@ import com.sirius.spurt.store.provider.auth.vo.AuthVo; public interface AuthProvider { - AuthVo getUserId(String accessToken); + void setRefreshToken(final String key, final String value, final long expireTime); + + AuthVo getRefreshToken(final String key); + + Boolean hasRefreshToken(final String key); + + void deleteRefreshToken(final String key); } diff --git a/src/main/java/com/sirius/spurt/store/provider/auth/impl/AuthProviderImpl.java b/src/main/java/com/sirius/spurt/store/provider/auth/impl/AuthProviderImpl.java index c11a528..de3c393 100644 --- a/src/main/java/com/sirius/spurt/store/provider/auth/impl/AuthProviderImpl.java +++ b/src/main/java/com/sirius/spurt/store/provider/auth/impl/AuthProviderImpl.java @@ -2,11 +2,8 @@ import com.sirius.spurt.store.provider.auth.AuthProvider; import com.sirius.spurt.store.provider.auth.vo.AuthVo; -import com.sirius.spurt.store.repository.resttemplate.auth.AuthRepository; -import com.sirius.spurt.store.repository.resttemplate.auth.playload.UserInfoPayload; +import com.sirius.spurt.store.repository.redis.auth.AuthRepository; import lombok.RequiredArgsConstructor; -import org.mapstruct.Mapper; -import org.mapstruct.factory.Mappers; import org.springframework.stereotype.Service; @Service @@ -15,14 +12,22 @@ public class AuthProviderImpl implements AuthProvider { private final AuthRepository authRepository; @Override - public AuthVo getUserId(String accessToken) { - return AuthProviderImplMapper.INSTANCE.toAuthVo(authRepository.getUserInfo(accessToken)); + public void setRefreshToken(String key, String value, long expireTime) { + authRepository.setRefreshToken(key, value, expireTime); } - @Mapper - public interface AuthProviderImplMapper { - AuthProviderImplMapper INSTANCE = Mappers.getMapper(AuthProviderImplMapper.class); + @Override + public AuthVo getRefreshToken(final String key) { + return AuthVo.builder().userId(authRepository.getRefreshToken(key)).build(); + } + + @Override + public Boolean hasRefreshToken(String key) { + return authRepository.hasRefreshToken(key); + } - AuthVo toAuthVo(UserInfoPayload userInfoPayload); + @Override + public void deleteRefreshToken(String key) { + authRepository.deleteRefreshToken(key); } } diff --git a/src/main/java/com/sirius/spurt/store/provider/auth/vo/AuthVo.java b/src/main/java/com/sirius/spurt/store/provider/auth/vo/AuthVo.java index 8afb161..1d46503 100644 --- a/src/main/java/com/sirius/spurt/store/provider/auth/vo/AuthVo.java +++ b/src/main/java/com/sirius/spurt/store/provider/auth/vo/AuthVo.java @@ -11,5 +11,4 @@ @AllArgsConstructor public class AuthVo { private String userId; - private String email; } diff --git a/src/main/java/com/sirius/spurt/store/repository/redis/token/TokenRepository.java b/src/main/java/com/sirius/spurt/store/repository/redis/auth/AuthRepository.java similarity index 57% rename from src/main/java/com/sirius/spurt/store/repository/redis/token/TokenRepository.java rename to src/main/java/com/sirius/spurt/store/repository/redis/auth/AuthRepository.java index a53f72f..4c558e7 100644 --- a/src/main/java/com/sirius/spurt/store/repository/redis/token/TokenRepository.java +++ b/src/main/java/com/sirius/spurt/store/repository/redis/auth/AuthRepository.java @@ -1,11 +1,11 @@ -package com.sirius.spurt.store.repository.redis.token; +package com.sirius.spurt.store.repository.redis.auth; -public interface TokenRepository { +public interface AuthRepository { void setRefreshToken(final String key, final String value, final long expireTime); String getRefreshToken(final String key); Boolean hasRefreshToken(final String key); - void deleteRedisToken(final String key); + void deleteRefreshToken(final String key); } diff --git a/src/main/java/com/sirius/spurt/store/repository/redis/token/impl/TokenRepositoryImpl.java b/src/main/java/com/sirius/spurt/store/repository/redis/auth/impl/AuthRepositoryImpl.java similarity index 74% rename from src/main/java/com/sirius/spurt/store/repository/redis/token/impl/TokenRepositoryImpl.java rename to src/main/java/com/sirius/spurt/store/repository/redis/auth/impl/AuthRepositoryImpl.java index 680b9cb..e58865d 100644 --- a/src/main/java/com/sirius/spurt/store/repository/redis/token/impl/TokenRepositoryImpl.java +++ b/src/main/java/com/sirius/spurt/store/repository/redis/auth/impl/AuthRepositoryImpl.java @@ -1,6 +1,6 @@ -package com.sirius.spurt.store.repository.redis.token.impl; +package com.sirius.spurt.store.repository.redis.auth.impl; -import com.sirius.spurt.store.repository.redis.token.TokenRepository; +import com.sirius.spurt.store.repository.redis.auth.AuthRepository; import java.time.Duration; import lombok.RequiredArgsConstructor; import org.springframework.data.redis.core.RedisTemplate; @@ -8,13 +8,13 @@ @Component @RequiredArgsConstructor -public class TokenRepositoryImpl implements TokenRepository { +public class AuthRepositoryImpl implements AuthRepository { private final RedisTemplate redisTemplate; @Override public void setRefreshToken(final String key, final String value, final long expireTime) { if (hasRefreshToken(key)) { - deleteRedisToken(key); + deleteRefreshToken(key); } redisTemplate.opsForValue().set(key, value, Duration.ofMillis(expireTime)); @@ -31,7 +31,7 @@ public Boolean hasRefreshToken(final String key) { } @Override - public void deleteRedisToken(final String key) { + public void deleteRefreshToken(final String key) { redisTemplate.delete(key); } } diff --git a/src/main/java/com/sirius/spurt/store/repository/resttemplate/auth/AuthRepository.java b/src/main/java/com/sirius/spurt/store/repository/resttemplate/auth/AuthRepository.java deleted file mode 100644 index 192f268..0000000 --- a/src/main/java/com/sirius/spurt/store/repository/resttemplate/auth/AuthRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.sirius.spurt.store.repository.resttemplate.auth; - -import com.sirius.spurt.store.repository.resttemplate.auth.playload.UserInfoPayload; - -public interface AuthRepository { - UserInfoPayload getUserInfo(String accessToken); -} diff --git a/src/main/java/com/sirius/spurt/store/repository/resttemplate/auth/impl/AuthRepositoryImpl.java b/src/main/java/com/sirius/spurt/store/repository/resttemplate/auth/impl/AuthRepositoryImpl.java deleted file mode 100644 index caeaa0b..0000000 --- a/src/main/java/com/sirius/spurt/store/repository/resttemplate/auth/impl/AuthRepositoryImpl.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.sirius.spurt.store.repository.resttemplate.auth.impl; - -import com.sirius.spurt.common.exception.GlobalException; -import com.sirius.spurt.common.meta.ResultCode; -import com.sirius.spurt.store.repository.resttemplate.auth.AuthRepository; -import com.sirius.spurt.store.repository.resttemplate.auth.playload.UserInfoPayload; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpMethod; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Service; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestTemplate; - -@Service -@Slf4j -@RequiredArgsConstructor -public class AuthRepositoryImpl implements AuthRepository { - private final RestTemplate restTemplate; - - @Value("${user-info-endpoint}") - private String userInfoEndpoint; - - @Override - public UserInfoPayload getUserInfo(String accessToken) { - try { - MultiValueMap httpBody = new LinkedMultiValueMap<>(); - HttpEntity> req = new HttpEntity<>(httpBody); - - ResponseEntity res = - restTemplate.exchange( - userInfoEndpoint + "?access_token=" + accessToken, - HttpMethod.GET, - req, - UserInfoPayload.class); - - return res.getBody(); - } catch (Exception e) { - log.error(e.getMessage()); - throw new GlobalException(ResultCode.AUTHENTICATION_FAILED); - } - } -} diff --git a/src/main/java/com/sirius/spurt/store/repository/resttemplate/auth/playload/UserInfoPayload.java b/src/main/java/com/sirius/spurt/store/repository/resttemplate/auth/playload/UserInfoPayload.java deleted file mode 100644 index c57fee1..0000000 --- a/src/main/java/com/sirius/spurt/store/repository/resttemplate/auth/playload/UserInfoPayload.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.sirius.spurt.store.repository.resttemplate.auth.playload; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -@Setter -@Getter -@JsonIgnoreProperties -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class UserInfoPayload { - @JsonProperty("id") - private String userId; - - private String email; - - @JsonProperty("verified_email") - private Boolean verifiedEmail; - - private String name; - - @JsonProperty("given_name") - private String givenName; - - private String picture; - private String locale; - private String hd; -} diff --git a/src/main/java/com/sirius/spurt/store/repository/resttemplate/package-info.java b/src/main/java/com/sirius/spurt/store/repository/resttemplate/package-info.java deleted file mode 100644 index 9913052..0000000 --- a/src/main/java/com/sirius/spurt/store/repository/resttemplate/package-info.java +++ /dev/null @@ -1 +0,0 @@ -package com.sirius.spurt.store.repository.resttemplate; diff --git a/src/test/java/com/sirius/spurt/BaseMvcTest.java b/src/test/java/com/sirius/spurt/BaseMvcTest.java index b20d46d..52ef36c 100644 --- a/src/test/java/com/sirius/spurt/BaseMvcTest.java +++ b/src/test/java/com/sirius/spurt/BaseMvcTest.java @@ -1,20 +1,20 @@ package com.sirius.spurt; -import static org.mockito.ArgumentMatchers.anyString; +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; import capital.scalable.restdocs.AutoDocumentation; import capital.scalable.restdocs.jackson.JacksonResultHandlers; import capital.scalable.restdocs.response.ResponseModifyingPreprocessors; import com.fasterxml.jackson.databind.ObjectMapper; +import com.sirius.spurt.common.auth.PrincipalDetails; +import com.sirius.spurt.common.jwt.JwtUtils; +import com.sirius.spurt.common.jwt.MockSpringSecurityFilter; import com.sirius.spurt.custom.CustomRequestFieldSnippet; import com.sirius.spurt.custom.CustomResponseFieldSnippet; -import com.sirius.spurt.store.provider.auth.AuthProvider; -import com.sirius.spurt.store.provider.auth.vo.AuthVo; -import com.sirius.spurt.store.provider.jobgroup.JobGroupProvider; -import com.sirius.spurt.store.provider.user.UserProvider; +import com.sirius.spurt.test.UserTest; +import java.security.Principal; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.mock.mockito.MockBean; @@ -24,6 +24,7 @@ import org.springframework.restdocs.http.HttpDocumentation; import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation; import org.springframework.restdocs.operation.preprocess.Preprocessors; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; @@ -31,20 +32,18 @@ import org.springframework.web.context.WebApplicationContext; @ExtendWith({RestDocumentationExtension.class, MockitoExtension.class, SpringExtension.class}) -public class BaseMvcTest { +public class BaseMvcTest implements UserTest { @Autowired private WebApplicationContext context; @Autowired protected ObjectMapper objectMapper; - @MockBean private AuthProvider authProvider; - @MockBean private UserProvider userProvider; - @MockBean private JobGroupProvider jobGroupProvider; + @MockBean private JwtUtils jwtUtils; + protected MockMvc mockMvc; + protected Principal mockPrincipal; @BeforeEach public void setUp(RestDocumentationContextProvider restDocumentation) throws Exception { - AuthVo authVo = AuthVo.builder().userId("test").build(); - - Mockito.when(authProvider.getUserId(anyString())).thenReturn(authVo); + setUpMockUser(); var mockMvcRequestBuilders = MockMvcRequestBuilders.get("https://api.spurtapp.com") .header("Authorization", "Bearer <<전달받은토큰값>>"); @@ -60,6 +59,7 @@ public void setUp(RestDocumentationContextProvider restDocumentation) throws Exc ResponseModifyingPreprocessors.replaceBinaryContent(), // ResponseModifyingPreprocessors.limitJsonArrayLength(objectMapper), Preprocessors.prettyPrint()))) + .apply(springSecurity(new MockSpringSecurityFilter())) .apply( MockMvcRestDocumentation.documentationConfiguration(restDocumentation) .uris() @@ -83,4 +83,11 @@ public void setUp(RestDocumentationContextProvider restDocumentation) throws Exc AutoDocumentation.section())) .build(); } + + private void setUpMockUser() { + PrincipalDetails principalDetails = PrincipalDetails.builder().userEntity(TEST_USER).build(); + this.mockPrincipal = + new UsernamePasswordAuthenticationToken( + principalDetails, null, principalDetails.getAuthorities()); + } } diff --git a/src/test/java/com/sirius/spurt/store/provider/auth/impl/AuthProviderImplTest.java b/src/test/java/com/sirius/spurt/store/provider/auth/impl/AuthProviderImplTest.java index 06b130d..302e59e 100644 --- a/src/test/java/com/sirius/spurt/store/provider/auth/impl/AuthProviderImplTest.java +++ b/src/test/java/com/sirius/spurt/store/provider/auth/impl/AuthProviderImplTest.java @@ -1,39 +1,15 @@ package com.sirius.spurt.store.provider.auth.impl; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import com.sirius.spurt.store.provider.auth.vo.AuthVo; -import com.sirius.spurt.store.repository.resttemplate.auth.AuthRepository; -import com.sirius.spurt.store.repository.resttemplate.auth.playload.UserInfoPayload; -import com.sirius.spurt.test.UserTest; -import org.junit.jupiter.api.Test; +import com.sirius.spurt.store.repository.redis.auth.AuthRepository; +import com.sirius.spurt.test.RefreshTokenTest; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @ExtendWith(MockitoExtension.class) -class AuthProviderImplTest implements UserTest { +class AuthProviderImplTest implements RefreshTokenTest { @InjectMocks private AuthProviderImpl authProvider; @Mock private AuthRepository authRepository; - - @Test - void 유저_정보_조회_테스트() { - // given - UserInfoPayload userInfo = - UserInfoPayload.builder().userId(TEST_USER_ID).email(TEST_EMAIL).build(); - when(authRepository.getUserInfo(any())).thenReturn(userInfo); - - // when - AuthVo authVo = authProvider.getUserId("accessToken"); - - // then - verify(authRepository).getUserInfo(any()); - assertThat(authVo.getUserId()).isEqualTo(userInfo.getUserId()); - assertThat(authVo.getEmail()).isEqualTo(userInfo.getEmail()); - } } diff --git a/src/test/java/com/sirius/spurt/store/repository/redis/token/impl/TokenRepositoryImplTest.java b/src/test/java/com/sirius/spurt/store/repository/redis/auth/impl/AuthRepositoryImplTest.java similarity index 91% rename from src/test/java/com/sirius/spurt/store/repository/redis/token/impl/TokenRepositoryImplTest.java rename to src/test/java/com/sirius/spurt/store/repository/redis/auth/impl/AuthRepositoryImplTest.java index c4a2281..6024b59 100644 --- a/src/test/java/com/sirius/spurt/store/repository/redis/token/impl/TokenRepositoryImplTest.java +++ b/src/test/java/com/sirius/spurt/store/repository/redis/auth/impl/AuthRepositoryImplTest.java @@ -1,4 +1,4 @@ -package com.sirius.spurt.store.repository.redis.token.impl; +package com.sirius.spurt.store.repository.redis.auth.impl; import static java.lang.Boolean.FALSE; import static java.lang.Boolean.TRUE; @@ -19,8 +19,8 @@ import org.springframework.data.redis.core.ValueOperations; @ExtendWith(MockitoExtension.class) -class TokenRepositoryImplTest implements RefreshTokenTest { - @InjectMocks private TokenRepositoryImpl tokenRepository; +class AuthRepositoryImplTest implements RefreshTokenTest { + @InjectMocks private AuthRepositoryImpl tokenRepository; @Mock private RedisTemplate redisTemplate; @Mock private ValueOperations valueOperations; @@ -74,7 +74,7 @@ class TokenRepositoryImplTest implements RefreshTokenTest { // given // when - tokenRepository.deleteRedisToken(TEST_TOKEN_KEY); + tokenRepository.deleteRefreshToken(TEST_TOKEN_KEY); // then verify(redisTemplate).delete(anyString()); diff --git a/src/test/java/com/sirius/spurt/store/repository/resttemplate/auth/impl/AuthRepositoryImplTest.java b/src/test/java/com/sirius/spurt/store/repository/resttemplate/auth/impl/AuthRepositoryImplTest.java deleted file mode 100644 index 2e8b033..0000000 --- a/src/test/java/com/sirius/spurt/store/repository/resttemplate/auth/impl/AuthRepositoryImplTest.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.sirius.spurt.store.repository.resttemplate.auth.impl; - -import static com.sirius.spurt.common.meta.ResultCode.AUTHENTICATION_FAILED; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import com.sirius.spurt.common.exception.GlobalException; -import com.sirius.spurt.store.repository.resttemplate.auth.playload.UserInfoPayload; -import com.sirius.spurt.test.UserTest; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.client.HttpServerErrorException; -import org.springframework.web.client.RestTemplate; - -@ExtendWith(MockitoExtension.class) -class AuthRepositoryImplTest implements UserTest { - @InjectMocks private AuthRepositoryImpl authRepository; - - @Mock private RestTemplate restTemplate; - - @Nested - class 유저_정보_조회 { - @Test - void 유저_정보_조회_테스트() { - // given - UserInfoPayload userInfo = - UserInfoPayload.builder().userId(TEST_USER_ID).email(TEST_EMAIL).build(); - when(restTemplate.exchange( - anyString(), any(HttpMethod.class), any(HttpEntity.class), eq(UserInfoPayload.class))) - .thenReturn(ResponseEntity.ok(userInfo)); - - // when - UserInfoPayload res = authRepository.getUserInfo("accessToken"); - - // then - verify(restTemplate) - .exchange( - anyString(), any(HttpMethod.class), any(HttpEntity.class), eq(UserInfoPayload.class)); - assertThat(res.getUserId()).isEqualTo(userInfo.getUserId()); - assertThat(res.getEmail()).isEqualTo(userInfo.getEmail()); - } - - @Test - void 유저_정보_조회_실패_테스트() { - // given - when(restTemplate.exchange( - anyString(), any(HttpMethod.class), any(HttpEntity.class), eq(UserInfoPayload.class))) - .thenThrow(new HttpServerErrorException(HttpStatus.INTERNAL_SERVER_ERROR)); - - // when - GlobalException exception = - assertThrows( - GlobalException.class, - () -> { - authRepository.getUserInfo("accessToken"); - }); - - // then - verify(restTemplate) - .exchange( - anyString(), any(HttpMethod.class), any(HttpEntity.class), eq(UserInfoPayload.class)); - assertThat(exception.getResultCode()).isEqualTo(AUTHENTICATION_FAILED); - } - } -} From 03a9c9b205e7bece567139f3414473bdfcd60e16 Mon Sep 17 00:00:00 2001 From: emost22 Date: Sun, 14 Jul 2024 00:42:16 +0900 Subject: [PATCH 7/9] Feat: add oauth2 dependency --- build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle b/build.gradle index 9c092c4..ff4d148 100644 --- a/build.gradle +++ b/build.gradle @@ -58,6 +58,7 @@ dependencies { compileOnly('com.h2database:h2:2.1.214') implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' implementation group: 'com.auth0', name: 'java-jwt', version: '4.2.1' implementation 'io.jsonwebtoken:jjwt-api:0.11.5' From b8e5d25c91418c0f004cbe9452c58a37bc10410b Mon Sep 17 00:00:00 2001 From: emost22 Date: Sun, 14 Jul 2024 00:46:16 +0900 Subject: [PATCH 8/9] Fix: delete loginuser --- .../common/resolver/LoginUserResolver.java | 63 ------------------- .../spurt/common/resolver/user/LoginUser.java | 15 ----- .../jobgroup/SaveJobGroupBusiness.java | 10 +-- .../jobgroup/UpdateJobGroupBusiness.java | 10 +-- .../experience/ExperienceController.java | 30 +++++---- .../jobgroup/JobGroupController.java | 15 +++-- .../question/QuestionController.java | 34 +++++----- .../controller/user/UserController.java | 38 +++++++---- .../spurt/ExperienceControllerTest.java | 43 ++----------- .../sirius/spurt/JobGroupControllerTest.java | 19 ++---- .../sirius/spurt/QuestionControllerTest.java | 20 ++++-- .../com/sirius/spurt/UserControllerTest.java | 25 ++++++-- .../jobgroup/SaveJobGroupBusinessTest.java | 7 +-- .../jobgroup/UpdateJobGroupBusinessTest.java | 8 +-- 14 files changed, 133 insertions(+), 204 deletions(-) delete mode 100644 src/main/java/com/sirius/spurt/common/resolver/LoginUserResolver.java delete mode 100644 src/main/java/com/sirius/spurt/common/resolver/user/LoginUser.java diff --git a/src/main/java/com/sirius/spurt/common/resolver/LoginUserResolver.java b/src/main/java/com/sirius/spurt/common/resolver/LoginUserResolver.java deleted file mode 100644 index 3f8f0de..0000000 --- a/src/main/java/com/sirius/spurt/common/resolver/LoginUserResolver.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.sirius.spurt.common.resolver; - -import com.sirius.spurt.common.exception.GlobalException; -import com.sirius.spurt.common.meta.ResultCode; -import com.sirius.spurt.common.resolver.user.LoginUser; -import com.sirius.spurt.store.provider.auth.AuthProvider; -import com.sirius.spurt.store.provider.auth.vo.AuthVo; -import com.sirius.spurt.store.provider.jobgroup.JobGroupProvider; -import com.sirius.spurt.store.provider.user.UserProvider; -import jakarta.servlet.http.HttpServletRequest; -import lombok.RequiredArgsConstructor; -import org.springframework.core.MethodParameter; -import org.springframework.util.StringUtils; -import org.springframework.web.bind.support.WebDataBinderFactory; -import org.springframework.web.context.request.NativeWebRequest; -import org.springframework.web.method.support.HandlerMethodArgumentResolver; -import org.springframework.web.method.support.ModelAndViewContainer; - -@RequiredArgsConstructor -public class LoginUserResolver implements HandlerMethodArgumentResolver { - private final AuthProvider authProvider; - private final UserProvider userProvider; - private final JobGroupProvider jobGroupProvider; - private final String TOKEN_TYPE = "Bearer "; - - @Override - public boolean supportsParameter(MethodParameter parameter) { - return parameter.getParameterType().equals(LoginUser.class); - } - - @Override - public Object resolveArgument( - MethodParameter parameter, - ModelAndViewContainer mavContainer, - NativeWebRequest webRequest, - WebDataBinderFactory binderFactory) - throws Exception { - HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest(); - - String testHeader = request.getHeader("test"); - if (StringUtils.hasLength(testHeader)) { - return new LoginUser("admin", "email"); - } - - String accessHeader = request.getHeader("Authorization"); - if (!StringUtils.hasLength(accessHeader) || !accessHeader.startsWith(TOKEN_TYPE)) { - throw new GlobalException(ResultCode.AUTHENTICATION_FAILED); - } - - AuthVo userInfo = new AuthVo(); - if (StringUtils.hasLength(accessHeader) && accessHeader.startsWith(TOKEN_TYPE)) { - String accessToken = accessHeader.replace(TOKEN_TYPE, ""); - userInfo = authProvider.getUserId(accessToken); - // 로그인 이후 직군 미선택 유저 추가 체크 - // UserVo userVo = userProvider.getUserInfo(userInfo.getUserId()); - // if (userVo == null) { - // jobGroupProvider.saveJobGroup( - // userInfo.getUserId(), userInfo.getEmail(), JobGroup.DEVELOPER); - // } - } - return new LoginUser(userInfo.getUserId(), userInfo.getEmail()); - } -} diff --git a/src/main/java/com/sirius/spurt/common/resolver/user/LoginUser.java b/src/main/java/com/sirius/spurt/common/resolver/user/LoginUser.java deleted file mode 100644 index 40d8cc1..0000000 --- a/src/main/java/com/sirius/spurt/common/resolver/user/LoginUser.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.sirius.spurt.common.resolver.user; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Getter -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class LoginUser { - private String userId; - private String email; -} diff --git a/src/main/java/com/sirius/spurt/service/business/jobgroup/SaveJobGroupBusiness.java b/src/main/java/com/sirius/spurt/service/business/jobgroup/SaveJobGroupBusiness.java index c39c363..0fd5b40 100644 --- a/src/main/java/com/sirius/spurt/service/business/jobgroup/SaveJobGroupBusiness.java +++ b/src/main/java/com/sirius/spurt/service/business/jobgroup/SaveJobGroupBusiness.java @@ -2,7 +2,6 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.sirius.spurt.common.meta.JobGroup; -import com.sirius.spurt.common.resolver.user.LoginUser; import com.sirius.spurt.common.template.Business; import com.sirius.spurt.service.business.jobgroup.SaveJobGroupBusiness.Dto; import com.sirius.spurt.service.business.jobgroup.SaveJobGroupBusiness.Result; @@ -23,8 +22,7 @@ public class SaveJobGroupBusiness implements Business { @Override public Result execute(Dto input) { - jobGroupProvider.saveJobGroup( - input.getLoginUser().getUserId(), input.getLoginUser().getEmail(), input.getJobGroup()); + jobGroupProvider.saveJobGroup(input.getUserId(), input.getEmail(), input.getJobGroup()); return new SaveJobGroupBusiness.Result(); } @@ -36,8 +34,10 @@ public Result execute(Dto input) { @NoArgsConstructor @AllArgsConstructor public static class Dto implements Business.Dto, Serializable { - /** loginUser 프론트 전달 x */ - private LoginUser loginUser; + /** userId 프론트 전달 x */ + private String userId; + /** 이메일 프론트 전달 x */ + private String email; /** 직군 */ private JobGroup jobGroup; } diff --git a/src/main/java/com/sirius/spurt/service/business/jobgroup/UpdateJobGroupBusiness.java b/src/main/java/com/sirius/spurt/service/business/jobgroup/UpdateJobGroupBusiness.java index 22d4386..b22c781 100644 --- a/src/main/java/com/sirius/spurt/service/business/jobgroup/UpdateJobGroupBusiness.java +++ b/src/main/java/com/sirius/spurt/service/business/jobgroup/UpdateJobGroupBusiness.java @@ -2,7 +2,6 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.sirius.spurt.common.meta.JobGroup; -import com.sirius.spurt.common.resolver.user.LoginUser; import com.sirius.spurt.common.template.Business; import com.sirius.spurt.service.business.jobgroup.UpdateJobGroupBusiness.Dto; import com.sirius.spurt.service.business.jobgroup.UpdateJobGroupBusiness.Result; @@ -23,8 +22,7 @@ public class UpdateJobGroupBusiness implements Business { @Override public Result execute(Dto input) { - jobGroupProvider.updateJobGroup( - input.getLoginUser().getUserId(), input.getLoginUser().getEmail(), input.getJobGroup()); + jobGroupProvider.updateJobGroup(input.getUserId(), input.getEmail(), input.getJobGroup()); return new UpdateJobGroupBusiness.Result(); } @@ -36,8 +34,10 @@ public Result execute(Dto input) { @NoArgsConstructor @AllArgsConstructor public static class Dto implements Business.Dto, Serializable { - /** loginUser 프론트 전달 x */ - private LoginUser loginUser; + /** userId 프론트 전달 x */ + private String userId; + /** 이메일 프론트 전달 x */ + private String email; /** 직군 */ private JobGroup jobGroup; } diff --git a/src/main/java/com/sirius/spurt/service/controller/experience/ExperienceController.java b/src/main/java/com/sirius/spurt/service/controller/experience/ExperienceController.java index 3851892..9ddda7f 100644 --- a/src/main/java/com/sirius/spurt/service/controller/experience/ExperienceController.java +++ b/src/main/java/com/sirius/spurt/service/controller/experience/ExperienceController.java @@ -1,6 +1,6 @@ package com.sirius.spurt.service.controller.experience; -import com.sirius.spurt.common.resolver.user.LoginUser; +import com.sirius.spurt.common.auth.PrincipalDetails; import com.sirius.spurt.service.business.experience.DeleteExperienceBusiness; import com.sirius.spurt.service.business.experience.GetAllExperienceBusiness; import com.sirius.spurt.service.business.experience.GetExperienceBusiness; @@ -8,6 +8,7 @@ import com.sirius.spurt.service.business.experience.UpdateExperienceBusiness; import com.sirius.spurt.service.controller.RestResponse; import lombok.RequiredArgsConstructor; +import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -34,8 +35,9 @@ public class ExperienceController { */ @PostMapping public RestResponse saveExperience( - LoginUser loginUser, @RequestBody SaveExperienceBusiness.Dto dto) { - dto.setUserId(loginUser.getUserId()); + @AuthenticationPrincipal PrincipalDetails principalDetails, + @RequestBody SaveExperienceBusiness.Dto dto) { + dto.setUserId(principalDetails.getUserEntity().getUserId()); return RestResponse.success(saveExperienceBusiness.execute(dto)); } @@ -46,8 +48,9 @@ public RestResponse saveExperience( */ @PutMapping public RestResponse updateExperience( - LoginUser loginUser, @RequestBody UpdateExperienceBusiness.Dto dto) { - dto.setUserId(loginUser.getUserId()); + @AuthenticationPrincipal PrincipalDetails principalDetails, + @RequestBody UpdateExperienceBusiness.Dto dto) { + dto.setUserId(principalDetails.getUserEntity().getUserId()); return RestResponse.success(updateExperienceBusiness.execute(dto)); } @@ -58,8 +61,9 @@ public RestResponse updateExperience( */ @DeleteMapping public RestResponse deleteExperience( - LoginUser loginUser, @RequestBody DeleteExperienceBusiness.Dto dto) { - dto.setUserId(loginUser.getUserId()); + @AuthenticationPrincipal PrincipalDetails principalDetails, + @RequestBody DeleteExperienceBusiness.Dto dto) { + dto.setUserId(principalDetails.getUserEntity().getUserId()); return RestResponse.success(deleteExperienceBusiness.execute(dto)); } @@ -69,9 +73,12 @@ public RestResponse deleteExperience( * @title 나의 모든 경험 조회 api */ @GetMapping - public RestResponse getAllExperience(LoginUser loginUser) { + public RestResponse getAllExperience( + @AuthenticationPrincipal PrincipalDetails principalDetails) { GetAllExperienceBusiness.Dto dto = - GetAllExperienceBusiness.Dto.builder().userId(loginUser.getUserId()).build(); + GetAllExperienceBusiness.Dto.builder() + .userId(principalDetails.getUserEntity().getUserId()) + .build(); return RestResponse.success(getAllExperienceBusiness.execute(dto)); } @@ -82,10 +89,11 @@ public RestResponse getAllExperience(LoginUser */ @GetMapping("/{experienceId}") public RestResponse getExperience( - LoginUser loginUser, @PathVariable("experienceId") Long experienceId) { + @AuthenticationPrincipal PrincipalDetails principalDetails, + @PathVariable("experienceId") Long experienceId) { GetExperienceBusiness.Dto dto = GetExperienceBusiness.Dto.builder() - .userId(loginUser.getUserId()) + .userId(principalDetails.getUserEntity().getUserId()) .experienceId(experienceId) .build(); return RestResponse.success(getExperienceBusiness.execute(dto)); diff --git a/src/main/java/com/sirius/spurt/service/controller/jobgroup/JobGroupController.java b/src/main/java/com/sirius/spurt/service/controller/jobgroup/JobGroupController.java index aa33165..c4e27a3 100644 --- a/src/main/java/com/sirius/spurt/service/controller/jobgroup/JobGroupController.java +++ b/src/main/java/com/sirius/spurt/service/controller/jobgroup/JobGroupController.java @@ -1,11 +1,12 @@ package com.sirius.spurt.service.controller.jobgroup; -import com.sirius.spurt.common.resolver.user.LoginUser; +import com.sirius.spurt.common.auth.PrincipalDetails; import com.sirius.spurt.service.business.jobgroup.SaveJobGroupBusiness; import com.sirius.spurt.service.business.jobgroup.UpdateJobGroupBusiness; import com.sirius.spurt.service.controller.RestResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -27,8 +28,10 @@ public class JobGroupController { */ @PostMapping public RestResponse saveJobGroup( - LoginUser loginUser, @RequestBody SaveJobGroupBusiness.Dto dto) { - dto.setLoginUser(loginUser); + @AuthenticationPrincipal PrincipalDetails principalDetails, + @RequestBody SaveJobGroupBusiness.Dto dto) { + dto.setUserId(principalDetails.getUserEntity().getUserId()); + dto.setEmail(principalDetails.getUserEntity().getEmail()); return RestResponse.success(saveJobGroupBusiness.execute(dto)); } @@ -39,8 +42,10 @@ public RestResponse saveJobGroup( */ @PutMapping public RestResponse updateJobGroup( - LoginUser loginUser, @RequestBody UpdateJobGroupBusiness.Dto dto) { - dto.setLoginUser(loginUser); + @AuthenticationPrincipal PrincipalDetails principalDetails, + @RequestBody UpdateJobGroupBusiness.Dto dto) { + dto.setUserId(principalDetails.getUserEntity().getUserId()); + dto.setEmail(principalDetails.getUserEntity().getEmail()); return RestResponse.success(updateJobGroupBusiness.execute(dto)); } } diff --git a/src/main/java/com/sirius/spurt/service/controller/question/QuestionController.java b/src/main/java/com/sirius/spurt/service/controller/question/QuestionController.java index 31576b6..503b616 100644 --- a/src/main/java/com/sirius/spurt/service/controller/question/QuestionController.java +++ b/src/main/java/com/sirius/spurt/service/controller/question/QuestionController.java @@ -1,8 +1,8 @@ package com.sirius.spurt.service.controller.question; +import com.sirius.spurt.common.auth.PrincipalDetails; import com.sirius.spurt.common.meta.Category; import com.sirius.spurt.common.meta.JobGroup; -import com.sirius.spurt.common.resolver.user.LoginUser; import com.sirius.spurt.common.resolver.user.NonLoginUser; import com.sirius.spurt.service.business.question.DeleteQuestionBusiness; import com.sirius.spurt.service.business.question.GetQuestionBusiness; @@ -14,6 +14,7 @@ import com.sirius.spurt.service.controller.RestResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -39,15 +40,16 @@ public class QuestionController { private final PutPinQuestionBusiness putPinQuestionBusiness; /** - * @param loginUser + * @param principalDetails * @param dto * @return * @title pin 고정/비고정 */ @PutMapping("/question/pin") public RestResponse questionPin( - LoginUser loginUser, @RequestBody PutPinQuestionBusiness.Dto dto) { - dto.setUserId(loginUser.getUserId()); + @AuthenticationPrincipal PrincipalDetails principalDetails, + @RequestBody PutPinQuestionBusiness.Dto dto) { + dto.setUserId(principalDetails.getUserEntity().getUserId()); return RestResponse.success(putPinQuestionBusiness.execute(dto)); } @@ -81,8 +83,9 @@ public RestResponse questionRandom( */ @DeleteMapping("/question") public RestResponse questionDelete( - LoginUser loginUser, @RequestBody DeleteQuestionBusiness.Dto dto) { - dto.setUserId(loginUser.getUserId()); + @AuthenticationPrincipal PrincipalDetails principalDetails, + @RequestBody DeleteQuestionBusiness.Dto dto) { + dto.setUserId(principalDetails.getUserEntity().getUserId()); return RestResponse.success(deleteQuestionBusiness.execute(dto)); } @@ -93,8 +96,9 @@ public RestResponse questionDelete( */ @PutMapping("/question") public RestResponse questionPut( - LoginUser loginUser, @RequestBody PutQuestionBusiness.Dto dto) { - dto.setUserId(loginUser.getUserId()); + @AuthenticationPrincipal PrincipalDetails principalDetails, + @RequestBody PutQuestionBusiness.Dto dto) { + dto.setUserId(principalDetails.getUserEntity().getUserId()); return RestResponse.success(putQuestionBusiness.execute(dto)); } @@ -105,11 +109,12 @@ public RestResponse questionPut( */ @GetMapping("/question/{questionId}") public RestResponse questionGet( - LoginUser loginUser, @PathVariable("questionId") String questionId) { + @AuthenticationPrincipal PrincipalDetails principalDetails, + @PathVariable("questionId") String questionId) { GetQuestionBusiness.Dto dto = GetQuestionBusiness.Dto.builder() .questionId(Long.parseLong(questionId)) - .userId(loginUser.getUserId()) + .userId(principalDetails.getUserEntity().getUserId()) .build(); return RestResponse.success(getQuestionBusiness.execute(dto)); } @@ -120,8 +125,9 @@ public RestResponse questionGet( */ @PostMapping("/question") public RestResponse questionSave( - LoginUser loginUser, @RequestBody SaveQuestionBusiness.Dto dto) { - dto.setUserId(loginUser.getUserId()); + @AuthenticationPrincipal PrincipalDetails principalDetails, + @RequestBody SaveQuestionBusiness.Dto dto) { + dto.setUserId(principalDetails.getUserEntity().getUserId()); return RestResponse.success(saveQuestionBusiness.execute(dto)); } @@ -138,7 +144,7 @@ public RestResponse questionSave( */ @GetMapping("/question") public RestResponse questionRetrieve( - LoginUser loginUser, + @AuthenticationPrincipal PrincipalDetails principalDetails, @RequestParam(name = "subject", required = false) String subject, @RequestParam(name = "jobGroup", required = false) JobGroup jobGroup, @RequestParam(name = "category", required = false) Category category, @@ -148,7 +154,7 @@ public RestResponse questionRetrieve( @RequestParam(name = "size", defaultValue = "10") String size) { RetrieveQuestionBusiness.Dto dto = RetrieveQuestionBusiness.Dto.builder() - .userId(loginUser.getUserId()) + .userId(principalDetails.getUserEntity().getUserId()) .subject(subject) .jobGroup(jobGroup) .category(category) diff --git a/src/main/java/com/sirius/spurt/service/controller/user/UserController.java b/src/main/java/com/sirius/spurt/service/controller/user/UserController.java index bbcd828..878a0cd 100644 --- a/src/main/java/com/sirius/spurt/service/controller/user/UserController.java +++ b/src/main/java/com/sirius/spurt/service/controller/user/UserController.java @@ -1,6 +1,6 @@ package com.sirius.spurt.service.controller.user; -import com.sirius.spurt.common.resolver.user.LoginUser; +import com.sirius.spurt.common.auth.PrincipalDetails; import com.sirius.spurt.service.business.user.CheckUserExistsBusiness; import com.sirius.spurt.service.business.user.CheckUserHasPinedBusiness; import com.sirius.spurt.service.business.user.CheckUserHasPostedBusiness; @@ -9,6 +9,7 @@ import com.sirius.spurt.service.controller.RestResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -30,10 +31,13 @@ public class UserController { * @title 유저 존재 여부 확인 api */ @GetMapping("/exist") - public RestResponse checkUserExists(LoginUser loginUser) { + public RestResponse checkUserExists( + @AuthenticationPrincipal PrincipalDetails principalDetails) { return RestResponse.success( checkUserExistsBusiness.execute( - CheckUserExistsBusiness.Dto.builder().userId(loginUser.getUserId()).build())); + CheckUserExistsBusiness.Dto.builder() + .userId(principalDetails.getUserEntity().getUserId()) + .build())); } /** @@ -41,10 +45,13 @@ public RestResponse checkUserExists(LoginUser lo * @title 유저 최초 핀 고정 확인 api */ @GetMapping("/pin") - public RestResponse checkUserHasPined(LoginUser loginUser) { + public RestResponse checkUserHasPined( + @AuthenticationPrincipal PrincipalDetails principalDetails) { return RestResponse.success( checkUserHasPinedBusiness.execute( - CheckUserHasPinedBusiness.Dto.builder().userId(loginUser.getUserId()).build())); + CheckUserHasPinedBusiness.Dto.builder() + .userId(principalDetails.getUserEntity().getUserId()) + .build())); } /** @@ -52,10 +59,13 @@ public RestResponse checkUserHasPined(LoginUse * @title 유저 질문&답변 작성 이력 확인 api */ @GetMapping("/posting") - public RestResponse checkUserHasPosted(LoginUser loginUser) { + public RestResponse checkUserHasPosted( + @AuthenticationPrincipal PrincipalDetails principalDetails) { return RestResponse.success( checkUserHasPostedBusiness.execute( - CheckUserHasPostedBusiness.Dto.builder().userId(loginUser.getUserId()).build())); + CheckUserHasPostedBusiness.Dto.builder() + .userId(principalDetails.getUserEntity().getUserId()) + .build())); } /** @@ -63,10 +73,13 @@ public RestResponse checkUserHasPosted(LoginU * @title 유저 정보 조회 */ @GetMapping("/info") - public RestResponse getUserInfo(LoginUser loginUser) { + public RestResponse getUserInfo( + @AuthenticationPrincipal PrincipalDetails principalDetails) { return RestResponse.success( userInfoBusiness.execute( - UserInfoBusiness.Dto.builder().userId(loginUser.getUserId()).build())); + UserInfoBusiness.Dto.builder() + .userId(principalDetails.getUserEntity().getUserId()) + .build())); } /** @@ -74,9 +87,12 @@ public RestResponse getUserInfo(LoginUser loginUser) { * @title 유저 삭제 */ @DeleteMapping - public RestResponse deleteUser(LoginUser loginUser) { + public RestResponse deleteUser( + @AuthenticationPrincipal PrincipalDetails principalDetails) { return RestResponse.success( deleteUserBusiness.execute( - DeleteUserBusiness.Dto.builder().userId(loginUser.getUserId()).build())); + DeleteUserBusiness.Dto.builder() + .userId(principalDetails.getUserEntity().getUserId()) + .build())); } } diff --git a/src/test/java/com/sirius/spurt/ExperienceControllerTest.java b/src/test/java/com/sirius/spurt/ExperienceControllerTest.java index 2d60673..a10d5f4 100644 --- a/src/test/java/com/sirius/spurt/ExperienceControllerTest.java +++ b/src/test/java/com/sirius/spurt/ExperienceControllerTest.java @@ -10,7 +10,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import com.sirius.spurt.common.meta.Category; -import com.sirius.spurt.common.resolver.user.LoginUser; import com.sirius.spurt.service.business.experience.DeleteExperienceBusiness; import com.sirius.spurt.service.business.experience.GetAllExperienceBusiness; import com.sirius.spurt.service.business.experience.GetExperienceBusiness; @@ -19,7 +18,6 @@ import com.sirius.spurt.service.controller.experience.ExperienceController; import com.sirius.spurt.test.ExperienceTest; import java.util.List; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; @@ -33,13 +31,6 @@ public class ExperienceControllerTest extends BaseMvcTest implements ExperienceT @MockBean private GetAllExperienceBusiness getAllExperienceBusiness; @MockBean private GetExperienceBusiness getExperienceBusiness; - private LoginUser loginUser; - - @BeforeEach - void setUp() { - loginUser = LoginUser.builder().userId(TEST_USER_ID).email(TEST_EMAIL).build(); - } - @Test void 본인_경험_저장() throws Exception { SaveExperienceBusiness.Dto dto = @@ -58,11 +49,7 @@ void setUp() { post("/v1/experience") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(dto)) - .with( - request -> { - request.setAttribute("loginUser", loginUser); - return request; - })) + .principal(this.mockPrincipal)) .andExpect(status().isOk()) .andDo(print()); } @@ -84,11 +71,7 @@ void setUp() { put("/v1/experience") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(dto)) - .with( - request -> { - request.setAttribute("loginUser", loginUser); - return request; - })) + .principal(this.mockPrincipal)) .andExpect(status().isOk()) .andDo(print()); } @@ -103,11 +86,7 @@ void setUp() { delete("/v1/experience") .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(dto)) - .with( - request -> { - request.setAttribute("loginUser", loginUser); - return request; - })) + .principal(this.mockPrincipal)) .andExpect(status().isOk()) .andDo(print()); } @@ -145,13 +124,7 @@ void setUp() { .build(); when(getAllExperienceBusiness.execute(any())).thenReturn(result); this.mockMvc - .perform( - get("/v1/experience") - .with( - request -> { - request.setAttribute("loginUser", loginUser); - return request; - })) + .perform(get("/v1/experience").principal(this.mockPrincipal)) .andExpect(status().isOk()) .andDo(print()); } @@ -184,13 +157,7 @@ void setUp() { .build(); when(getExperienceBusiness.execute(any())).thenReturn(result); this.mockMvc - .perform( - get("/v1/experience/1") - .with( - request -> { - request.setAttribute("loginUser", loginUser); - return request; - })) + .perform(get("/v1/experience/1").principal(this.mockPrincipal)) .andExpect(status().isOk()) .andDo(print()); } diff --git a/src/test/java/com/sirius/spurt/JobGroupControllerTest.java b/src/test/java/com/sirius/spurt/JobGroupControllerTest.java index e05b569..47006eb 100644 --- a/src/test/java/com/sirius/spurt/JobGroupControllerTest.java +++ b/src/test/java/com/sirius/spurt/JobGroupControllerTest.java @@ -8,7 +8,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import com.sirius.spurt.common.meta.JobGroup; -import com.sirius.spurt.common.resolver.user.LoginUser; import com.sirius.spurt.service.business.jobgroup.SaveJobGroupBusiness; import com.sirius.spurt.service.business.jobgroup.UpdateJobGroupBusiness; import com.sirius.spurt.service.controller.jobgroup.JobGroupController; @@ -24,38 +23,32 @@ public class JobGroupControllerTest extends BaseMvcTest { @Test void 유저_직군_저장() throws Exception { - LoginUser loginUser = new LoginUser("admin", "email"); SaveJobGroupBusiness.Dto dto = - SaveJobGroupBusiness.Dto.builder() - .loginUser(loginUser) - .jobGroup(JobGroup.DEVELOPER) - .build(); + SaveJobGroupBusiness.Dto.builder().jobGroup(JobGroup.DEVELOPER).build(); when(saveJobGroupBusiness.execute(any())).thenReturn(new SaveJobGroupBusiness.Result()); this.mockMvc .perform( post("/v1/jobgroup") .requestAttr("userId", "admin") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(dto))) + .content(objectMapper.writeValueAsString(dto)) + .principal(this.mockPrincipal)) .andExpect(status().isOk()) .andDo(print()); } @Test void 유저_직군_수정() throws Exception { - LoginUser loginUser = new LoginUser("admin", "email"); UpdateJobGroupBusiness.Dto dto = - UpdateJobGroupBusiness.Dto.builder() - .loginUser(loginUser) - .jobGroup(JobGroup.DEVELOPER) - .build(); + UpdateJobGroupBusiness.Dto.builder().jobGroup(JobGroup.DEVELOPER).build(); when(updateJobGroupBusiness.execute(any())).thenReturn(new UpdateJobGroupBusiness.Result()); this.mockMvc .perform( put("/v1/jobgroup") .requestAttr("userId", "admin") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(dto))) + .content(objectMapper.writeValueAsString(dto)) + .principal(this.mockPrincipal)) .andExpect(status().isOk()) .andDo(print()); } diff --git a/src/test/java/com/sirius/spurt/QuestionControllerTest.java b/src/test/java/com/sirius/spurt/QuestionControllerTest.java index d21c32d..508b147 100644 --- a/src/test/java/com/sirius/spurt/QuestionControllerTest.java +++ b/src/test/java/com/sirius/spurt/QuestionControllerTest.java @@ -48,7 +48,8 @@ public class QuestionControllerTest extends BaseMvcTest .perform( put("/v1/question/pin") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(dto))) + .content(objectMapper.writeValueAsString(dto)) + .principal(this.mockPrincipal)) .andExpect(status().isOk()) .andDo(print()); } @@ -82,7 +83,8 @@ public class QuestionControllerTest extends BaseMvcTest .perform( delete("/v1/question") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(dto))) + .content(objectMapper.writeValueAsString(dto)) + .principal(this.mockPrincipal)) .andExpect(status().isOk()) .andDo(print()); } @@ -104,7 +106,10 @@ public class QuestionControllerTest extends BaseMvcTest .keyWordList(List.of("키워드")) .build(); when(getQuestionBusiness.execute(any())).thenReturn(result); - this.mockMvc.perform(get("/v1/question/1")).andExpect(status().isOk()).andDo(print()); + this.mockMvc + .perform(get("/v1/question/1").principal(this.mockPrincipal)) + .andExpect(status().isOk()) + .andDo(print()); } @Test @@ -122,7 +127,8 @@ public class QuestionControllerTest extends BaseMvcTest .perform( put("/v1/question") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(dto))) + .content(objectMapper.writeValueAsString(dto)) + .principal(this.mockPrincipal)) .andExpect(status().isOk()) .andDo(print()); } @@ -143,7 +149,8 @@ public class QuestionControllerTest extends BaseMvcTest .perform( post("/v1/question") .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(dto))) + .content(objectMapper.writeValueAsString(dto)) + .principal(this.mockPrincipal)) .andExpect(status().isOk()) .andDo(print()); } @@ -177,7 +184,8 @@ public class QuestionControllerTest extends BaseMvcTest .perform( get("/v1/question") .param("jobGroup", TEST_JOB_GROUP.name()) - .param("category", TEST_CATEGORY.name())) + .param("category", TEST_CATEGORY.name()) + .principal(this.mockPrincipal)) .andExpect(status().isOk()) .andDo(print()); } diff --git a/src/test/java/com/sirius/spurt/UserControllerTest.java b/src/test/java/com/sirius/spurt/UserControllerTest.java index c31f886..a9f7bbf 100644 --- a/src/test/java/com/sirius/spurt/UserControllerTest.java +++ b/src/test/java/com/sirius/spurt/UserControllerTest.java @@ -31,7 +31,10 @@ public class UserControllerTest extends BaseMvcTest { UserInfoBusiness.Result result = UserInfoBusiness.Result.builder().userId("wieribwer").jobGroup(JobGroup.DEVELOPER).build(); when(userInfoBusiness.execute(any())).thenReturn(result); - this.mockMvc.perform(get("/v1/user/info")).andExpect(status().isOk()).andDo(print()); + this.mockMvc + .perform(get("/v1/user/info").principal(this.mockPrincipal)) + .andExpect(status().isOk()) + .andDo(print()); } @Test @@ -41,7 +44,10 @@ public class UserControllerTest extends BaseMvcTest { CheckUserExistsBusiness.Result result = CheckUserExistsBusiness.Result.builder().isUserExists(false).build(); when(checkUserExistsBusiness.execute(any())).thenReturn(result); - this.mockMvc.perform(get("/v1/user/exist")).andExpect(status().isOk()).andDo(print()); + this.mockMvc + .perform(get("/v1/user/exist").principal(this.mockPrincipal)) + .andExpect(status().isOk()) + .andDo(print()); } @Test @@ -51,7 +57,10 @@ public class UserControllerTest extends BaseMvcTest { CheckUserHasPinedBusiness.Result result = CheckUserHasPinedBusiness.Result.builder().hasPined(false).build(); when(checkUserHasPinedBusiness.execute(any())).thenReturn(result); - this.mockMvc.perform(get("/v1/user/pin")).andExpect(status().isOk()).andDo(print()); + this.mockMvc + .perform(get("/v1/user/pin").principal(this.mockPrincipal)) + .andExpect(status().isOk()) + .andDo(print()); } @Test @@ -61,7 +70,10 @@ public class UserControllerTest extends BaseMvcTest { CheckUserHasPostedBusiness.Result result = CheckUserHasPostedBusiness.Result.builder().hasPosted(false).build(); when(checkUserHasPostedBusiness.execute(any())).thenReturn(result); - this.mockMvc.perform(get("/v1/user/posting")).andExpect(status().isOk()).andDo(print()); + this.mockMvc + .perform(get("/v1/user/posting").principal(this.mockPrincipal)) + .andExpect(status().isOk()) + .andDo(print()); } @Test @@ -69,6 +81,9 @@ public class UserControllerTest extends BaseMvcTest { DeleteUserBusiness.Dto dto = DeleteUserBusiness.Dto.builder().userId("userId").build(); DeleteUserBusiness.Result result = new DeleteUserBusiness.Result(); when(deleteUserBusiness.execute(any())).thenReturn(result); - this.mockMvc.perform(delete("/v1/user")).andExpect(status().isOk()).andDo(print()); + this.mockMvc + .perform(delete("/v1/user").principal(this.mockPrincipal)) + .andExpect(status().isOk()) + .andDo(print()); } } diff --git a/src/test/java/com/sirius/spurt/service/business/jobgroup/SaveJobGroupBusinessTest.java b/src/test/java/com/sirius/spurt/service/business/jobgroup/SaveJobGroupBusinessTest.java index d07f90f..46c3ff0 100644 --- a/src/test/java/com/sirius/spurt/service/business/jobgroup/SaveJobGroupBusinessTest.java +++ b/src/test/java/com/sirius/spurt/service/business/jobgroup/SaveJobGroupBusinessTest.java @@ -3,7 +3,6 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.verify; -import com.sirius.spurt.common.resolver.user.LoginUser; import com.sirius.spurt.service.business.jobgroup.SaveJobGroupBusiness.Dto; import com.sirius.spurt.store.provider.jobgroup.JobGroupProvider; import com.sirius.spurt.test.UserTest; @@ -22,11 +21,7 @@ class SaveJobGroupBusinessTest implements UserTest { @Test void 직군_저장_테스트() { // given - Dto dto = - Dto.builder() - .loginUser(LoginUser.builder().userId(TEST_USER_ID).email(TEST_EMAIL).build()) - .jobGroup(TEST_JOB_GROUP) - .build(); + Dto dto = Dto.builder().userId(TEST_USER_ID).email(TEST_EMAIL).jobGroup(TEST_JOB_GROUP).build(); // when saveJobGroupBusiness.execute(dto); diff --git a/src/test/java/com/sirius/spurt/service/business/jobgroup/UpdateJobGroupBusinessTest.java b/src/test/java/com/sirius/spurt/service/business/jobgroup/UpdateJobGroupBusinessTest.java index f4bcd2e..1020ac7 100644 --- a/src/test/java/com/sirius/spurt/service/business/jobgroup/UpdateJobGroupBusinessTest.java +++ b/src/test/java/com/sirius/spurt/service/business/jobgroup/UpdateJobGroupBusinessTest.java @@ -1,10 +1,8 @@ package com.sirius.spurt.service.business.jobgroup; -import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.verify; -import com.sirius.spurt.common.resolver.user.LoginUser; import com.sirius.spurt.service.business.jobgroup.UpdateJobGroupBusiness.Dto; import com.sirius.spurt.store.provider.jobgroup.JobGroupProvider; import com.sirius.spurt.test.UserTest; @@ -23,11 +21,7 @@ class UpdateJobGroupBusinessTest implements UserTest { @Test void 직군_수정_테스트() { // given - Dto dto = - Dto.builder() - .loginUser(LoginUser.builder().userId(TEST_USER_ID).email(TEST_EMAIL).build()) - .jobGroup(TEST_JOB_GROUP) - .build(); + Dto dto = Dto.builder().userId(TEST_USER_ID).email(TEST_EMAIL).jobGroup(TEST_JOB_GROUP).build(); // when updateJobGroupBusiness.execute(dto); From ec59b4122a8f659d48a6ae6ff3af490c0d3d0c04 Mon Sep 17 00:00:00 2001 From: emost22 Date: Sun, 14 Jul 2024 00:46:42 +0900 Subject: [PATCH 9/9] Feat: add MockSpringSecurityFilter --- build.gradle | 1 + .../common/jwt/MockSpringSecurityFilter.java | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 src/test/java/com/sirius/spurt/common/jwt/MockSpringSecurityFilter.java diff --git a/build.gradle b/build.gradle index ff4d148..bcaa996 100644 --- a/build.gradle +++ b/build.gradle @@ -58,6 +58,7 @@ dependencies { compileOnly('com.h2database:h2:2.1.214') implementation 'org.springframework.boot:spring-boot-starter-security' + testImplementation 'org.springframework.security:spring-security-test' implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' implementation group: 'com.auth0', name: 'java-jwt', version: '4.2.1' diff --git a/src/test/java/com/sirius/spurt/common/jwt/MockSpringSecurityFilter.java b/src/test/java/com/sirius/spurt/common/jwt/MockSpringSecurityFilter.java new file mode 100644 index 0000000..f75ac49 --- /dev/null +++ b/src/test/java/com/sirius/spurt/common/jwt/MockSpringSecurityFilter.java @@ -0,0 +1,30 @@ +package com.sirius.spurt.common.jwt; + +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.FilterConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import java.io.IOException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; + +public class MockSpringSecurityFilter implements Filter { + @Override + public void init(FilterConfig filterConfig) {} + + @Override + public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) + throws IOException, ServletException, ServletException { + SecurityContextHolder.getContext() + .setAuthentication((Authentication) ((HttpServletRequest) req).getUserPrincipal()); + chain.doFilter(req, res); + } + + @Override + public void destroy() { + SecurityContextHolder.clearContext(); + } +}