From 1fdfec5a2af7e1c5ca5c7097af5d9e9e317431d8 Mon Sep 17 00:00:00 2001 From: swa07016 Date: Mon, 16 Oct 2023 02:37:33 +0900 Subject: [PATCH] =?UTF-8?q?:ambulance:=20Hotfix:=20Security=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 인증유무만 판단 --- .../java/briefing/exception/ErrorCode.java | 2 +- .../java/briefing/member/api/MemberApi.java | 3 +- .../security/config/JwtSecurityConfig.java | 23 --------- .../security/config/SecurityConfig.java | 51 +++++++++++++++---- .../handler/JwtAccessDeniedHandler.java | 3 +- .../handler/JwtAuthenticationEntryPoint.java | 1 - .../JwtAuthenticationExceptionHandler.java | 2 +- .../security/provider/TokenProvider.java | 1 + 8 files changed, 48 insertions(+), 38 deletions(-) delete mode 100644 src/main/java/briefing/security/config/JwtSecurityConfig.java diff --git a/src/main/java/briefing/exception/ErrorCode.java b/src/main/java/briefing/exception/ErrorCode.java index d419d00..8c2cd05 100644 --- a/src/main/java/briefing/exception/ErrorCode.java +++ b/src/main/java/briefing/exception/ErrorCode.java @@ -16,7 +16,7 @@ public enum ErrorCode { _INTERNAL_SERVER_ERROR(INTERNAL_SERVER_ERROR, "COMMON000", "서버 에러, 관리자에게 문의 바랍니다."), _BAD_REQUEST(BAD_REQUEST,"COMMON001","잘못된 요청입니다."), - _UNAUTHORIZED(UNAUTHORIZED,"COMMON002","권한이 잘못되었습니다"), + _UNAUTHORIZED(UNAUTHORIZED,"COMMON002","로그인이 필요합니다."), _METHOD_NOT_ALLOWED(METHOD_NOT_ALLOWED, "COMMON003", "지원하지 않는 Http Method 입니다."), _FORBIDDEN(FORBIDDEN, "COMMON004", "금지된 요청입니다."), diff --git a/src/main/java/briefing/member/api/MemberApi.java b/src/main/java/briefing/member/api/MemberApi.java index 5f928d4..3809280 100644 --- a/src/main/java/briefing/member/api/MemberApi.java +++ b/src/main/java/briefing/member/api/MemberApi.java @@ -6,6 +6,7 @@ import briefing.member.application.dto.MemberRequest; import briefing.member.application.dto.MemberResponse; import briefing.member.domain.Member; +import briefing.member.domain.MemberRole; import briefing.member.domain.SocialType; import briefing.redis.domain.RefreshToken; import briefing.redis.service.RedisService; @@ -62,7 +63,7 @@ public CommonResponse login( ) { Member member = memberCommandService.login(socialType, request); // TODO - TokenProvider에서 발급해주도록 변경 - String accessToken = tokenProvider.createAccessToken(member.getId(),member.getSocialType().toString() ,member.getSocialId(), Arrays.asList(new SimpleGrantedAuthority("USER"))); + String accessToken = tokenProvider.createAccessToken(member.getId(),member.getSocialType().toString() ,member.getSocialId(), List.of(new SimpleGrantedAuthority(MemberRole.ROLE_USER.name()))); String refreshToken = redisService.generateRefreshToken(member.getSocialId(),member.getSocialType()).getToken(); return CommonResponse.onSuccess(MemberConverter.toLoginDTO(member, accessToken, refreshToken)); } diff --git a/src/main/java/briefing/security/config/JwtSecurityConfig.java b/src/main/java/briefing/security/config/JwtSecurityConfig.java deleted file mode 100644 index 0b3a191..0000000 --- a/src/main/java/briefing/security/config/JwtSecurityConfig.java +++ /dev/null @@ -1,23 +0,0 @@ -package briefing.security.config; - -import briefing.security.filter.JwtRequestFilter; -import briefing.security.handler.JwtAuthenticationExceptionHandler; -import briefing.security.provider.TokenProvider; -import lombok.RequiredArgsConstructor; -import org.springframework.security.config.annotation.SecurityConfigurerAdapter; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.web.DefaultSecurityFilterChain; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; - -@RequiredArgsConstructor -public class JwtSecurityConfig extends SecurityConfigurerAdapter { - private final TokenProvider tokenProvider; - - @Override - public void configure(HttpSecurity http)throws Exception{ - JwtRequestFilter jwtFilter = new JwtRequestFilter(tokenProvider); - JwtAuthenticationExceptionHandler jwtAuthenticationExceptionHandler = new JwtAuthenticationExceptionHandler(); - http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class); - http.addFilterBefore(jwtAuthenticationExceptionHandler, JwtRequestFilter.class); - } -} diff --git a/src/main/java/briefing/security/config/SecurityConfig.java b/src/main/java/briefing/security/config/SecurityConfig.java index e4bc6d1..3cb43b2 100644 --- a/src/main/java/briefing/security/config/SecurityConfig.java +++ b/src/main/java/briefing/security/config/SecurityConfig.java @@ -9,6 +9,10 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +import org.springframework.security.access.hierarchicalroles.NullRoleHierarchy; +import org.springframework.security.access.hierarchicalroles.RoleHierarchy; +import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; @@ -20,21 +24,26 @@ import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import java.util.Collections; + +import static org.springframework.security.config.Customizer.withDefaults; + @Slf4j @EnableWebSecurity @RequiredArgsConstructor @Configuration public class SecurityConfig { - private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; + private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint = new JwtAuthenticationEntryPoint(); - private final JwtAccessDeniedHandler jwtAccessDeniedHandler; + private final JwtAccessDeniedHandler jwtAccessDeniedHandler = new JwtAccessDeniedHandler(); private final TokenProvider tokenProvider; - JwtAuthenticationExceptionHandler jwtAuthenticationExceptionHandler = new JwtAuthenticationExceptionHandler(); + private final JwtAuthenticationExceptionHandler jwtAuthenticationExceptionHandler = new JwtAuthenticationExceptionHandler(); private static final String[] WHITE_LIST = { @@ -45,6 +54,11 @@ public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } + @Bean + public RoleHierarchy roleHierarchy() { + return new NullRoleHierarchy(); + } + @Bean public WebSecurityCustomizer webSecurityCustomizer() { return (web) -> web.ignoring().requestMatchers( @@ -58,20 +72,24 @@ public WebSecurityCustomizer webSecurityCustomizer() { "/swagger-ui/**", "/docs/**", "/members/auth/**", - "/scraps/**","/briefings/**","/chattings/**"); // NOTE - 토큰 발급 MERGE 전 테스트를 위해 허용 + "/briefings/**", + "/chattings/**"); } @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { - return http.httpBasic(HttpBasicConfigurer::disable) + return http + .cors(corsConfigurer -> corsConfigurer.configurationSource(corsConfiguration())) + .httpBasic(withDefaults()) .csrf(AbstractHttpConfigurer::disable) // 비활성화 - .cors(AbstractHttpConfigurer::disable) .sessionManagement(manage -> manage.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // Session 사용 안함 .formLogin(AbstractHttpConfigurer::disable) // form login 사용 안함 - .httpBasic(AbstractHttpConfigurer::disable) // http basic 방식 사용 안함 - .authorizeHttpRequests(authorize -> authorize - .requestMatchers("/briefings/**").permitAll() // 모두 접근 가능합니다. - ) + .authorizeHttpRequests(authorize -> { + authorize.requestMatchers("/briefings/**").permitAll(); // 모두 접근 가능합니다. + authorize.requestMatchers(HttpMethod.DELETE, "/members/{memberId}").authenticated(); + authorize.requestMatchers("/scraps/**").authenticated(); + authorize.anyRequest().authenticated(); + }) .exceptionHandling(exceptionHandling -> exceptionHandling .authenticationEntryPoint(jwtAuthenticationEntryPoint) .accessDeniedHandler(jwtAccessDeniedHandler) @@ -80,4 +98,17 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .addFilterBefore(jwtAuthenticationExceptionHandler,JwtRequestFilter.class) .build(); } + + @Bean + public CorsConfigurationSource corsConfiguration() { + return request -> { + org.springframework.web.cors.CorsConfiguration config = + new org.springframework.web.cors.CorsConfiguration(); + config.setAllowedHeaders(Collections.singletonList("*")); + config.setAllowedMethods(Collections.singletonList("*")); + config.setAllowedOriginPatterns(Collections.singletonList("*")); + config.setAllowCredentials(true); + return config; + }; + } } diff --git a/src/main/java/briefing/security/handler/JwtAccessDeniedHandler.java b/src/main/java/briefing/security/handler/JwtAccessDeniedHandler.java index 1c1ba9b..a027eff 100644 --- a/src/main/java/briefing/security/handler/JwtAccessDeniedHandler.java +++ b/src/main/java/briefing/security/handler/JwtAccessDeniedHandler.java @@ -8,13 +8,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.stereotype.Component; import java.io.IOException; import java.io.PrintWriter; -@Component public class JwtAccessDeniedHandler implements AccessDeniedHandler { private final Logger LOGGER = LoggerFactory.getLogger(JwtAccessDeniedHandler.class); @@ -22,6 +22,7 @@ public class JwtAccessDeniedHandler implements AccessDeniedHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { + response.setContentType("application/json; charset=UTF-8"); response.setStatus(403); PrintWriter writer = response.getWriter(); diff --git a/src/main/java/briefing/security/handler/JwtAuthenticationEntryPoint.java b/src/main/java/briefing/security/handler/JwtAuthenticationEntryPoint.java index 859e2be..4ecf1ea 100644 --- a/src/main/java/briefing/security/handler/JwtAuthenticationEntryPoint.java +++ b/src/main/java/briefing/security/handler/JwtAuthenticationEntryPoint.java @@ -14,7 +14,6 @@ import java.io.IOException; import java.io.PrintWriter; -@Component public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint { private final Logger LOGGER = LoggerFactory.getLogger(JwtAuthenticationEntryPoint.class); diff --git a/src/main/java/briefing/security/handler/JwtAuthenticationExceptionHandler.java b/src/main/java/briefing/security/handler/JwtAuthenticationExceptionHandler.java index 38bb114..5390818 100644 --- a/src/main/java/briefing/security/handler/JwtAuthenticationExceptionHandler.java +++ b/src/main/java/briefing/security/handler/JwtAuthenticationExceptionHandler.java @@ -14,7 +14,7 @@ import java.io.IOException; import java.io.PrintWriter; -@Component +//@Component public class JwtAuthenticationExceptionHandler extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { diff --git a/src/main/java/briefing/security/provider/TokenProvider.java b/src/main/java/briefing/security/provider/TokenProvider.java index 4612db2..b0c5000 100644 --- a/src/main/java/briefing/security/provider/TokenProvider.java +++ b/src/main/java/briefing/security/provider/TokenProvider.java @@ -94,6 +94,7 @@ public Authentication getAuthentication(String token){ .build() .parseClaimsJws(token) .getBody(); + Collection authorities = Arrays.stream(claims.get(AUTHORITIES_KEY).toString().split(",")) .map(SimpleGrantedAuthority::new)