Search
Duplicate
🔫

Spring Security Cors - Swagger 너 외않되?

분야
BE
ISSUE
주제
BE
CORS
심각도
중간😵
제보자
sichoi, eunbikim
담당자
작성자
상태
처리 완료
이슈링크(optional)
작성일자
2023/12/10 08:22
공개여부
공개
글감

문제 상황

Main 클라우드 서버(EC2 혹은 oracle ) 에 배포하였을 때, Swagger GET 요청 외에 다른 요청이 안되는 현상
POST, PATCH, DELETE, GET 을 제외한 모든 요청이 403 ( Not Allowed) 응답
POSTMAN 이나 curl 은 되는 현상

원인

추정 원인
Spring Security CORS 설정 문제
실제 원인
Spring Security CORS 설정 문제

최종 해결

@Configuration @RequiredArgsConstructor @EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true) @EnableWebSecurity public class SecurityConfig { private final AuthenticationManager jwtAuthenticationManager; private final AccessDeniedHandler jwtAccessDeniedHandler; private final AuthenticationEntryPoint jwtAuthenticationEntryPoint; private final JwtAuthenticationConverter jwtAuthenticationConverter; private final JwtDecoder jwtDecoder; @Bean public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception { // @formatter:off return httpSecurity .cors() .configurationSource(corsConfigurationSource()) .and() .csrf().disable() @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); // TODO: 나중에 front주소로 바꿔야함 configuration.addAllowedOrigin("http://localhost:3000"); configuration.addAllowedHeader("*"); configuration.addAllowedMethod("*"); configuration.setAllowCredentials(true); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; } }
Java
복사
해결방법
addAllowedOrigin 에, Swagger 요청주소가 없었다.
@Configuration @RequiredArgsConstructor @EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true) @EnableWebSecurity public class SecurityConfig { ... @Value("${swagger.base-url}") private String baseUrl; @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); configuration.setAllowedOrigins(List.of("localhost:3000", baseUrl)); ... }
Java
복사
configuration.setAllowedOrigins(List.of("localhost:3000", baseUrl));
프론트요청과, baseUrl (Swagger url) 을 CORS Allow 에 추가하여 해결하였다.

참고자료

EC2 로그와 로컬 로그를 비교했다.
무시된 POST 요청 로그
[nio-8080-exec-9] o.s.security.web.FilterChainProxy : Securing POST /v1/images/presigned-url [nio-8080-exec-9] o.j.e.auth.MyCustomLoggingFilter : MyCustomLoggingFilter.doFilterInternal() is invoked. [nio-8080-exec-9] o.j.e.auth.MyCustomLoggingFilter : request: /v1/images/presigned-url [nio-8080-exec-9] s.s.w.c.SecurityContextPersistenceFilter : Set SecurityContextHolder to empty SecurityContext [nio-8080-exec-9] s.s.w.c.SecurityContextPersistenceFilter : Cleared SecurityContextHolder to complete request
Java
복사
정상적인 GET 요청 로그
[nio-8080-exec-7] o.s.security.web.FilterChainProxy : Securing GET /v1/bookmarks?page=0&size=10 [nio-8080-exec-7] o.j.e.auth.MyCustomLoggingFilter : MyCustomLoggingFilter.doFilterInternal() is invoked. [nio-8080-exec-7] o.j.e.auth.MyCustomLoggingFilter : request: /v1/bookmarks [nio-8080-exec-7] s.s.w.c.SecurityContextPersistenceFilter : Set SecurityContextHolder to empty SecurityContext [nio-8080-exec-7] .o.s.r.w.BearerTokenAuthenticationFilter : Set SecurityContextHolder to JwtAuthenticationToken [Principal=org.springframework.security.oauth2.jwt.Jwt@decdbdc6, Credentials=[PROTECTED], Authenticated=true, Details=null, Granted Authorities=[ROLE_USER]] [nio-8080-exec-7] o.j.e.a.UserSessionAuthenticationFilter : UserSessionAuthenticationFilter.doFilterInternal() is invoked. [nio-8080-exec-7] o.j.e.a.UserSessionAuthenticationFilter : request: org.springframework.security.web.header.HeaderWriterFilter$HeaderWriterRequest@7e233ed [nio-8080-exec-7] o.j.e.a.UserSessionAuthenticationFilter : request Referer: https://api.exchange-diary.com/swagger-ui/index.html [nio-8080-exec-7] o.j.e.a.UserSessionAuthenticationFilter : convert token: principalUserSessionDto(userId=1, roles=["USER"]), authorities [ROLE_USER] [nio-8080-exec-7] o.s.s.w.a.i.FilterSecurityInterceptor : Authorized filter invocation [GET /v1/bookmarks?page=0&size=10] with attributes [permitAll] [nio-8080-exec-7] o.s.security.web.FilterChainProxy : Secured GET /v1/bookmarks?page=0&size=10 [nio-8080-exec-7] o.j.e.MyCustomSecurityFilter : Not security filter [nio-8080-exec-7] o.s.s.a.i.a.MethodSecurityInterceptor : Authorized ReflectiveMethodInvocation: public org.johoeunsae.exchangediary.dto.NotePreviewPaginationDto org.johoeunsae.exchangediary.bookmark.controller.BookmarkController.getBookmarkList(org.johoeunsae.exchangediary.auth.UserSessionDto,int,int); target is of class [org.johoeunsae.exchangediary.bookmark.controller.BookmarkController] with attributes [[authorize: 'isAuthenticated()', filter: 'null', filterTarget: 'null']] [nio-8080-exec-7] o.j.e.b.controller.BookmarkController : [userId : 1] getBookmarkList [nio-8080-exec-7] s.s.w.c.SecurityContextPersistenceFilter : Cleared SecurityContextHolder to complete request
Java
복사
Spring Security 흐름
@Bean public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception { return httpSecurity .cors() .configurationSource(corsConfigurationSource()) .and() .csrf().disable() .authorizeRequests() .anyRequest().permitAll() .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .oauth2ResourceServer() .bearerTokenResolver(new DefaultBearerTokenResolver()) .jwt() .jwtAuthenticationConverter(jwtAuthenticationConverter) .decoder(jwtDecoder) .authenticationManager(jwtAuthenticationManager) .and() .accessDeniedHandler(jwtAccessDeniedHandler) .authenticationEntryPoint(jwtAuthenticationEntryPoint) .and() .formLogin().disable() .addFilterAfter( new UserSessionAuthenticationFilter(), BearerTokenAuthenticationFilter.class ) .addFilterBefore( new MyCustomLoggingFilter(), WebAsyncManagerIntegrationFilter.class ) .build(); }
Java
복사
.addFilterAfter() ⇒ SpringSecurity
.addFilterBefore() ⇒ SpringSecurity 로직 필터 이전에 실행된다.
@Log4j2 public class MyCustomLoggingFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { log.info("MyCustomLoggingFilter.doFilterInternal() is invoked."); log.info("request: " + request.getRequestURI()); doFilter(request, response, filterChain); } }
Java
복사