From c1e09900aba0369f92ee8727a4c3ccaa08113eaa Mon Sep 17 00:00:00 2001 From: sunseo18 Date: Thu, 7 Dec 2023 16:47:03 +0900 Subject: [PATCH] =?UTF-8?q?[6=EC=A3=BC=EC=B0=A8]=20jwt=20=EC=9D=B8?= =?UTF-8?q?=EC=A6=9D=20filter=20=EA=B5=AC=ED=98=84=20=EB=B0=8F=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../security/CustomAccessDeniedHandler.java | 22 ++++++++ .../security/JwtAuthenticationFilter.java | 53 +++++++++++++++++++ .../config/security/SecurityConfig.java | 27 ++++++++-- .../member/application/AuthMemberService.java | 3 +- .../post/presentation/PostController.java | 15 +++--- 5 files changed, 110 insertions(+), 10 deletions(-) create mode 100644 Second Seminar/SecondSeminar/src/main/java/com/example/SecondSeminar/common/config/security/CustomAccessDeniedHandler.java create mode 100644 Second Seminar/SecondSeminar/src/main/java/com/example/SecondSeminar/common/config/security/JwtAuthenticationFilter.java diff --git a/Second Seminar/SecondSeminar/src/main/java/com/example/SecondSeminar/common/config/security/CustomAccessDeniedHandler.java b/Second Seminar/SecondSeminar/src/main/java/com/example/SecondSeminar/common/config/security/CustomAccessDeniedHandler.java new file mode 100644 index 0000000..2995a88 --- /dev/null +++ b/Second Seminar/SecondSeminar/src/main/java/com/example/SecondSeminar/common/config/security/CustomAccessDeniedHandler.java @@ -0,0 +1,22 @@ +package com.example.SecondSeminar.common.config.security; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.web.access.AccessDeniedHandler; +import org.springframework.stereotype.Component; + +@Component +public class CustomAccessDeniedHandler implements AccessDeniedHandler { + @Override + public void handle(HttpServletRequest request, HttpServletResponse response, + AccessDeniedException accessDeniedException) throws IOException, ServletException { + setResponse(response); + } + + private void setResponse(HttpServletResponse response) { + response.setStatus(HttpServletResponse.SC_FORBIDDEN); + } +} \ No newline at end of file diff --git a/Second Seminar/SecondSeminar/src/main/java/com/example/SecondSeminar/common/config/security/JwtAuthenticationFilter.java b/Second Seminar/SecondSeminar/src/main/java/com/example/SecondSeminar/common/config/security/JwtAuthenticationFilter.java new file mode 100644 index 0000000..7dffecb --- /dev/null +++ b/Second Seminar/SecondSeminar/src/main/java/com/example/SecondSeminar/common/config/security/JwtAuthenticationFilter.java @@ -0,0 +1,53 @@ +package com.example.SecondSeminar.common.config.security; + + +import static com.example.SecondSeminar.common.auth.JwtValidationType.VALID_JWT; + +import com.example.SecondSeminar.common.auth.JwtProvider; +import com.example.SecondSeminar.common.auth.UserAuthentication; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; +import org.springframework.web.filter.OncePerRequestFilter; + +@Component +@RequiredArgsConstructor +public class JwtAuthenticationFilter extends OncePerRequestFilter { + + private final JwtProvider jwtProvider; + + @Override + protected void doFilterInternal(@NonNull HttpServletRequest request, + @NonNull HttpServletResponse response, + @NonNull FilterChain filterChain) throws ServletException, IOException { + try { + final String token = getJwtFromRequest(request); + if (jwtProvider.validateToken(token) == VALID_JWT) { + Long memberId = jwtProvider.getUserFromJwt(token); + // authentication 객체 생성 -> principal에 유저정보를 담는다. + UserAuthentication authentication = new UserAuthentication(memberId.toString(), null, null); + authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + } catch (Exception exception) { + } + // 다음 필터로 요청 전달 + filterChain.doFilter(request, response); + } + + private String getJwtFromRequest(HttpServletRequest request) { + String bearerToken = request.getHeader("Authorization"); + if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { + return bearerToken.substring("Bearer ".length()); + } + return null; + } +} diff --git a/Second Seminar/SecondSeminar/src/main/java/com/example/SecondSeminar/common/config/security/SecurityConfig.java b/Second Seminar/SecondSeminar/src/main/java/com/example/SecondSeminar/common/config/security/SecurityConfig.java index eb79f60..f4d6f50 100644 --- a/Second Seminar/SecondSeminar/src/main/java/com/example/SecondSeminar/common/config/security/SecurityConfig.java +++ b/Second Seminar/SecondSeminar/src/main/java/com/example/SecondSeminar/common/config/security/SecurityConfig.java @@ -1,24 +1,45 @@ package com.example.SecondSeminar.common.config.security; +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.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +@RequiredArgsConstructor @Configuration @EnableWebSecurity public class SecurityConfig { + private final JwtAuthenticationFilter jwtAuthenticationFilter; + private final CustomJwtAuthenticationEntryPoint customJwtAuthenticationEntryPoint; + private final CustomAccessDeniedHandler customAccessDeniedHandler; + + private static final String[] AUTH_WHITELIST = { + "api/users/sign-up", + "api/users/sign-in" + }; @Bean SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - return http.csrf().disable() + return http + .csrf().disable() + .formLogin().disable() + .httpBasic().disable() + .exceptionHandling() + .authenticationEntryPoint(customJwtAuthenticationEntryPoint) + .accessDeniedHandler(customAccessDeniedHandler) + .and() .authorizeHttpRequests() - .anyRequest().permitAll() - .and().build(); + .requestMatchers(AUTH_WHITELIST).permitAll() + .anyRequest().authenticated() + .and() + .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) + .build(); } @Bean diff --git a/Second Seminar/SecondSeminar/src/main/java/com/example/SecondSeminar/member/application/AuthMemberService.java b/Second Seminar/SecondSeminar/src/main/java/com/example/SecondSeminar/member/application/AuthMemberService.java index 408f5f1..8ccbc40 100644 --- a/Second Seminar/SecondSeminar/src/main/java/com/example/SecondSeminar/member/application/AuthMemberService.java +++ b/Second Seminar/SecondSeminar/src/main/java/com/example/SecondSeminar/member/application/AuthMemberService.java @@ -43,7 +43,8 @@ public MemberSignInResponse signIn(AuthMemberRequest request) { Authentication authentication = new UserAuthentication(authMember.getId(), null, null); - String jwtToken = jwtProvider.generateToken(authentication, 1000L); + // 이거도 application.yaml로 해야됨 + String jwtToken = jwtProvider.generateToken(authentication, 1000L * 24); return MemberSignInResponse.of(jwtToken); } diff --git a/Second Seminar/SecondSeminar/src/main/java/com/example/SecondSeminar/post/presentation/PostController.java b/Second Seminar/SecondSeminar/src/main/java/com/example/SecondSeminar/post/presentation/PostController.java index 72a5a2c..acbdcb5 100644 --- a/Second Seminar/SecondSeminar/src/main/java/com/example/SecondSeminar/post/presentation/PostController.java +++ b/Second Seminar/SecondSeminar/src/main/java/com/example/SecondSeminar/post/presentation/PostController.java @@ -1,10 +1,11 @@ package com.example.SecondSeminar.post.presentation; +import com.example.SecondSeminar.post.application.PostService; import com.example.SecondSeminar.post.dto.request.PostCreateRequest; -import com.example.SecondSeminar.post.dto.response.PostGetResponse; import com.example.SecondSeminar.post.dto.request.PostUpdateRequest; -import com.example.SecondSeminar.post.application.PostService; +import com.example.SecondSeminar.post.dto.response.PostGetResponse; import java.net.URI; +import java.security.Principal; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; @@ -14,7 +15,6 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -27,14 +27,17 @@ public class PostController { private final PostService postService; @PostMapping - public ResponseEntity createPost(@RequestHeader(CUSTOM_AUTH_ID) Long memberId, - @RequestBody PostCreateRequest request) { + public ResponseEntity createPost(@RequestBody PostCreateRequest request, Principal principal) { + Long memberId = Long.valueOf(principal.getName()); + URI location = URI.create("/api/post/" + postService.create(request, memberId)); return ResponseEntity.created(location).build(); } @GetMapping - public ResponseEntity> getPostsByMemberId(@RequestHeader(CUSTOM_AUTH_ID) Long memberId) { + public ResponseEntity> getPostsByMemberId(Principal principal) { + Long memberId = Long.valueOf(principal.getName()); + return ResponseEntity.ok(postService.getPostsByMemberId(memberId)); }