From 14524f196b07131025a1827f0347a102a6ba27c8 Mon Sep 17 00:00:00 2001 From: ymkim97 Date: Wed, 6 Sep 2023 17:21:43 +0900 Subject: [PATCH] =?UTF-8?q?[JT-29]=20feat:=20ThreadLocal,=20Interceptor=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- module-application/build.gradle | 1 + .../member/presentation/MemberController.java | 20 ++++++++-------- .../jtoon/security/config/WebConfig.java | 17 +++++++++++++ .../filter/JwtAuthenticationFilter.java | 5 ++++ .../interceptor/MemberInterceptor.java | 22 +++++++++++++++++ .../jtoon/security/jwt/JwtProvider.java | 8 +++---- .../application/CustomUserDetailsService.java | 3 +-- .../jwt/domain/CustomUserDetails.java | 4 ++++ .../jwt/domain/MemberThreadLocal.java | 24 +++++++++++++++++++ .../member/repository/MemberRepository.java | 2 ++ 10 files changed, 90 insertions(+), 16 deletions(-) create mode 100644 module-application/src/main/java/com/devtoon/jtoon/security/config/WebConfig.java create mode 100644 module-application/src/main/java/com/devtoon/jtoon/security/interceptor/MemberInterceptor.java create mode 100644 module-application/src/main/java/com/devtoon/jtoon/security/jwt/domain/MemberThreadLocal.java diff --git a/module-application/build.gradle b/module-application/build.gradle index bafd6d1f..810abcd3 100644 --- a/module-application/build.gradle +++ b/module-application/build.gradle @@ -24,4 +24,5 @@ dependencies { // JWT implementation 'io.jsonwebtoken:jjwt-api:0.11.5' runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' + runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5' } diff --git a/module-application/src/main/java/com/devtoon/jtoon/member/presentation/MemberController.java b/module-application/src/main/java/com/devtoon/jtoon/member/presentation/MemberController.java index 610b7167..66219629 100644 --- a/module-application/src/main/java/com/devtoon/jtoon/member/presentation/MemberController.java +++ b/module-application/src/main/java/com/devtoon/jtoon/member/presentation/MemberController.java @@ -19,16 +19,16 @@ @RequestMapping("/members") public class MemberController { - private final MemberService memberService; + private final MemberService memberService; - @PostMapping - @ResponseStatus(HttpStatus.CREATED) - public void signUp(@RequestBody @Valid SignUpReq signUpReq) { - memberService.createMember(signUpReq); - } + @PostMapping + @ResponseStatus(HttpStatus.CREATED) + public void signUp(@RequestBody @Valid SignUpReq signUpReq) { + memberService.createMember(signUpReq); + } - @GetMapping("/email-authorization") - public String authenticateEmail(@RequestParam(value = "email") String email) { - return memberService.sendEmailAuthentication(email); - } + @GetMapping("/email-authorization") + public String authenticateEmail(@RequestParam(value = "email") String email) { + return memberService.sendEmailAuthentication(email); + } } diff --git a/module-application/src/main/java/com/devtoon/jtoon/security/config/WebConfig.java b/module-application/src/main/java/com/devtoon/jtoon/security/config/WebConfig.java new file mode 100644 index 00000000..f656cd44 --- /dev/null +++ b/module-application/src/main/java/com/devtoon/jtoon/security/config/WebConfig.java @@ -0,0 +1,17 @@ +package com.devtoon.jtoon.security.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import com.devtoon.jtoon.security.interceptor.MemberInterceptor; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(new MemberInterceptor()) + .addPathPatterns("/**"); + } +} diff --git a/module-application/src/main/java/com/devtoon/jtoon/security/filter/JwtAuthenticationFilter.java b/module-application/src/main/java/com/devtoon/jtoon/security/filter/JwtAuthenticationFilter.java index 196fc379..5a5d4891 100644 --- a/module-application/src/main/java/com/devtoon/jtoon/security/filter/JwtAuthenticationFilter.java +++ b/module-application/src/main/java/com/devtoon/jtoon/security/filter/JwtAuthenticationFilter.java @@ -8,6 +8,8 @@ import org.springframework.web.servlet.HandlerExceptionResolver; import com.devtoon.jtoon.security.jwt.JwtProvider; +import com.devtoon.jtoon.security.jwt.domain.CustomUserDetails; +import com.devtoon.jtoon.security.jwt.domain.MemberThreadLocal; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; @@ -36,7 +38,10 @@ protected void doFilterInternal( String token = header.split(" ")[1]; jwtProvider.validateToken(token); Authentication auth = jwtProvider.getAuthentication(token); + CustomUserDetails customUserDetails = (CustomUserDetails)auth.getPrincipal(); + SecurityContextHolder.getContext().setAuthentication(auth); + MemberThreadLocal.setMember(customUserDetails.getMember()); } catch (RuntimeException e) { log.error("Invalid Token", e); handlerExceptionResolver.resolveException(request, response, null, e); diff --git a/module-application/src/main/java/com/devtoon/jtoon/security/interceptor/MemberInterceptor.java b/module-application/src/main/java/com/devtoon/jtoon/security/interceptor/MemberInterceptor.java new file mode 100644 index 00000000..2d80ea35 --- /dev/null +++ b/module-application/src/main/java/com/devtoon/jtoon/security/interceptor/MemberInterceptor.java @@ -0,0 +1,22 @@ +package com.devtoon.jtoon.security.interceptor; + +import org.jetbrains.annotations.NotNull; +import org.springframework.web.servlet.HandlerInterceptor; + +import com.devtoon.jtoon.security.jwt.domain.MemberThreadLocal; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +public class MemberInterceptor implements HandlerInterceptor { + + @Override + public void afterCompletion( + @NotNull HttpServletRequest request, + @NotNull HttpServletResponse response, + @NotNull Object handler, + Exception ex) throws Exception { + if (MemberThreadLocal.getMember() != null) { + MemberThreadLocal.clear(); + } + } +} diff --git a/module-application/src/main/java/com/devtoon/jtoon/security/jwt/JwtProvider.java b/module-application/src/main/java/com/devtoon/jtoon/security/jwt/JwtProvider.java index 3db46c33..ef66034a 100644 --- a/module-application/src/main/java/com/devtoon/jtoon/security/jwt/JwtProvider.java +++ b/module-application/src/main/java/com/devtoon/jtoon/security/jwt/JwtProvider.java @@ -3,10 +3,10 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.stereotype.Component; +import com.devtoon.jtoon.security.jwt.application.CustomUserDetailsService; +import com.devtoon.jtoon.security.jwt.domain.CustomUserDetails; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; @@ -36,7 +36,7 @@ public class JwtProvider { private Key secretKey; - private final UserDetailsService userDetailsService; + private final CustomUserDetailsService userDetailsService; @PostConstruct private void init() { @@ -71,7 +71,7 @@ public void validateToken(String token) { public Authentication getAuthentication(String token) { String ClaimsEmail = parseClaimsBody(token).getSubject(); - UserDetails userDetails = userDetailsService.loadUserByUsername(ClaimsEmail); + CustomUserDetails userDetails = userDetailsService.loadUserByUsername(ClaimsEmail); return new UsernamePasswordAuthenticationToken(userDetails, " ", userDetails.getAuthorities()); } diff --git a/module-application/src/main/java/com/devtoon/jtoon/security/jwt/application/CustomUserDetailsService.java b/module-application/src/main/java/com/devtoon/jtoon/security/jwt/application/CustomUserDetailsService.java index 15738d49..235d645d 100644 --- a/module-application/src/main/java/com/devtoon/jtoon/security/jwt/application/CustomUserDetailsService.java +++ b/module-application/src/main/java/com/devtoon/jtoon/security/jwt/application/CustomUserDetailsService.java @@ -1,6 +1,5 @@ package com.devtoon.jtoon.security.jwt.application; -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; @@ -17,7 +16,7 @@ public class CustomUserDetailsService implements UserDetailsService { private final MemberRepository memberRepository; @Override - public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException { + public CustomUserDetails loadUserByUsername(String email) throws UsernameNotFoundException { Member member = memberRepository.findByEmail(email) .orElseThrow(() -> new UsernameNotFoundException("Invalid Email")); diff --git a/module-application/src/main/java/com/devtoon/jtoon/security/jwt/domain/CustomUserDetails.java b/module-application/src/main/java/com/devtoon/jtoon/security/jwt/domain/CustomUserDetails.java index f5b66116..8924d7c0 100644 --- a/module-application/src/main/java/com/devtoon/jtoon/security/jwt/domain/CustomUserDetails.java +++ b/module-application/src/main/java/com/devtoon/jtoon/security/jwt/domain/CustomUserDetails.java @@ -48,4 +48,8 @@ public boolean isCredentialsNonExpired() { public boolean isEnabled() { return true; } + + public Member getMember() { + return member; + } } diff --git a/module-application/src/main/java/com/devtoon/jtoon/security/jwt/domain/MemberThreadLocal.java b/module-application/src/main/java/com/devtoon/jtoon/security/jwt/domain/MemberThreadLocal.java new file mode 100644 index 00000000..4e9d6dd6 --- /dev/null +++ b/module-application/src/main/java/com/devtoon/jtoon/security/jwt/domain/MemberThreadLocal.java @@ -0,0 +1,24 @@ +package com.devtoon.jtoon.security.jwt.domain; + +import com.devtoon.jtoon.member.entity.Member; + +public final class MemberThreadLocal { + + private static final ThreadLocal memberThreadLocal; + + static { + memberThreadLocal = new ThreadLocal<>(); + } + + public static Member getMember() { + return memberThreadLocal.get(); + } + + public static void setMember(Member member) { + memberThreadLocal.set(member); + } + + public static void clear() { + memberThreadLocal.remove(); + } +} diff --git a/module-domain/src/main/java/com/devtoon/jtoon/member/repository/MemberRepository.java b/module-domain/src/main/java/com/devtoon/jtoon/member/repository/MemberRepository.java index ec40db4d..044d2114 100644 --- a/module-domain/src/main/java/com/devtoon/jtoon/member/repository/MemberRepository.java +++ b/module-domain/src/main/java/com/devtoon/jtoon/member/repository/MemberRepository.java @@ -7,6 +7,8 @@ public interface MemberRepository extends JpaRepository { boolean existsByEmail(String email); + Optional findByPhone(String phone); + Optional findByEmail(String email); }