Spring Security 请求的资源上不存在“Access-Control-Allow-Origin”标头, Spring 安全[副本]

dfty9e19  于 2023-10-20  发布在  Spring
关注(0)|答案(1)|浏览(161)

此问题已在此处有答案

CORS issue - No 'Access-Control-Allow-Origin' header is present on the requested resource(11个回答)
8小时前关闭
CORS策略已阻止从源“http://localhost:3000”访问位于“https://b304-210-121-223-90.ngrok.io/members/sign-up”的XMLHttpRequest:对印前检查请求的响应未通过访问控制检查:请求的资源上不存在“Access-Control-Allow-Origin”标头。
我正在研究 Spring Boot 和React。
当前端在浏览器上发送请求时,我会遇到这个问题。不过,在 Postman 里也能用。
我在ngork只得到200个选项。
这是我的密码

安全配置

@RequiredArgsConstructor
@Configuration
@EnableWebSecurity
public class SecurityConfig
{
    private final JwtService jwtService;
    private final RedisRepository redisRepository;
    private final CustomAuthorityUtils authorityUtils;
    private final MemberDetailsService memberDetailsService;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception
    {
        http
                .headers((headers) ->
                        headers.frameOptions((frameOptions) -> frameOptions.disable()))
                .csrf(csrf -> csrf.disable())
                .httpBasic(httpBasic -> httpBasic.disable())
                .formLogin(formLogin -> formLogin.disable())
                .sessionManagement(sessionManagement ->
                        sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .exceptionHandling(exceptionHandling ->
                        exceptionHandling
                                .accessDeniedHandler(new CustomAccessDeniedHandler())
                                .authenticationEntryPoint(new CustomAuthenticationEntryPoint()))
                .authorizeHttpRequests(authorize -> authorize
                        .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                        .requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
                        .requestMatchers(HttpMethod.GET).permitAll()
                        .requestMatchers(HttpMethod.PATCH, "/members/help/reset-password").permitAll()
                        .requestMatchers(HttpMethod.POST, "/members/login", "/members/sign-up").permitAll()
                        .requestMatchers(HttpMethod.POST).hasAnyRole("USER", "ADMIN")
                        .requestMatchers(HttpMethod.PATCH).hasAnyRole("USER", "ADMIN")
                        .requestMatchers(HttpMethod.DELETE).hasAnyRole("USER", "ADMIN")
                        .anyRequest().permitAll())
                .apply(new CustomFilterConfigurer());

        return http.build();
    }

    @Bean
    public PasswordEncoder passwordEncoder()
    {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }
    

    public class CustomFilterConfigurer extends AbstractHttpConfigurer<CustomFilterConfigurer, HttpSecurity>
    {
        @Override
        public void configure(HttpSecurity builder) throws Exception
        {
            AuthenticationManager authenticationManager = builder.getSharedObject(AuthenticationManager.class);

            JwtAuthenticationFilter jwtAuthenticationFilter = new JwtAuthenticationFilter(authenticationManager, jwtService, redisRepository);
            jwtAuthenticationFilter.setFilterProcessesUrl("/members/login");
            jwtAuthenticationFilter.setAuthenticationSuccessHandler(new CustomAuthenticationSuccessfulHandler());
            jwtAuthenticationFilter.setAuthenticationFailureHandler(new CustomAuthenticationFailureHandler());

            JwtVerificationFilter jwtVerificationFilter = new JwtVerificationFilter(jwtService, authorityUtils, memberDetailsService);

            builder
                    .addFilter(jwtAuthenticationFilter)
                    .addFilterAfter(jwtVerificationFilter, JwtAuthenticationFilter.class);
        }
    }
}

在CustomFilerConfigurer中,我添加了扩展UsernamePasswordAuthenticationFilter和OncePerRequestFilter的Filter类

VerificationFilter

@Slf4j
@RequiredArgsConstructor
public class JwtVerificationFilter extends OncePerRequestFilter
{
    private final JwtService jwtService;
    private final CustomAuthorityUtils authorityUtils;
    private final MemberDetailsService memberDetailsService;
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException
    {
        log.info("===doFilterInternal===");

        try {
            String accessToken = resolveAccessToken(request);
            jwtService.isValidToken(accessToken);
            setAuthenticationToContext(jwtService.getClaims(accessToken));

            log.info("accessToken = {}", accessToken);
        } catch (InvalidTokenException e){
            log.error("Error processing JWT: {}", e.getMessage());
            throw e;
        }
        filterChain.doFilter(request, response);
    }

    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException
    {
        String authorization = request.getHeader(AUTHORIZATION);
        log.info("===shouldNotFilter===");
        log.info("authorization = {}", authorization);

        return authorization == null || !authorization.startsWith("Bearer ");
    }

    private void setAuthenticationToContext(Claims claims)
    {
        String email = claims.getSubject();
        UserDetails userDetails = memberDetailsService.loadUserByUsername(email);

        List<GrantedAuthority> authorities = authorityUtils.createAuthorities((List) claims.get("roles"));

        log.info("===setAuthenticationToContext===");
        log.info("authorities = {}", authorities);

        Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authentication);
    }

    private String resolveAccessToken(HttpServletRequest request)
    {
        return request.getHeader(AUTHORIZATION).substring(7);
    }
}

在shouldNotFilter中,如果请求在HttpHeader中不包含jwt token或Authorization,则返回true。使得
doFilterInternal不会为请求执行。

身份验证过滤器

@Slf4j
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter
{
    private final AuthenticationManager authenticationManager;
    private final JwtService jwtService;
    private final RedisRepository redisRepository;

    @SneakyThrows
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
    {
        try
        {
            ObjectMapper objectMapper = new ObjectMapper();
            LoginDto loginDto = objectMapper.readValue(request.getInputStream(), LoginDto.class);

            UsernamePasswordAuthenticationToken authenticationToken
                    = new UsernamePasswordAuthenticationToken(loginDto.getEmail(), loginDto.getPassword());

            return authenticationManager.authenticate(authenticationToken);
        }

        catch (RuntimeException e)
        {
            throw new RuntimeException(e.getMessage());
        }
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request,
                                            HttpServletResponse response,
                                            FilterChain chain,
                                            Authentication authResult) throws ServletException, IOException
    {
        String email = authResult.getName();

        List<String> roles = authResult.getAuthorities().stream()
                .map(GrantedAuthority::getAuthority)
                .collect(Collectors.toList());

        log.info("===successfulAuthentication===");
        log.info("authorities = {}", authResult.getAuthorities());
        String accessToken = jwtService.generateAccessToken(email, roles);
        String refreshToken = jwtService.generateRefreshToken(email);

        redisRepository.saveRefreshToken(refreshToken, email);

        response.setHeader("Authorization", "Bearer " + accessToken);
        response.setHeader("Refresh", refreshToken);

        this.getSuccessHandler().onAuthenticationSuccess(request, response, authResult);
    }
}

CorsConfiguration

@Configuration
public class CorsConfiguration
{
    @Bean
    public WebMvcConfigurer corsConfig()
    {
        return new WebMvcConfigurer()
        {
            @Override
            public void addCorsMappings(CorsRegistry registry)
            {
                registry.addMapping("/**")
                        .allowedOriginPatterns("http://localhost:3000", "https://b304-210-121-223-90.ngrok.io")
                        .allowedMethods(HttpMethod.GET.name(),
                                HttpMethod.POST.name(),
                                HttpMethod.OPTIONS.name(),
                                HttpMethod.PATCH.name(),
                                HttpMethod.DELETE.name())
                        .allowCredentials(true)
                        .allowedHeaders("*");
            }
        };
    }
}

安全日志:TRACE

2023-10-19T11:34:42.070+09:00  INFO 18568 --- [           main] o.s.d.j.r.query.QueryEnhancerFactory     : Hibernate is in classpath; If applicable, HQL parser will be used.

2023-10-19T11:34:43.091+09:00  WARN 18568 --- [           main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning

2023-10-19T11:34:43.099+09:00 TRACE 18568 --- [           main] eGlobalAuthenticationAutowiredConfigurer : Eagerly initializing {securityConfig=com.jbaacount.config.SecurityConfig$$SpringCGLIB$$0@27ee8493}

2023-10-19T11:34:43.257+09:00  INFO 18568 --- [           main] o.s.s.web.DefaultSecurityFilterChain     : Will secure any request with [org.springframework.security.web.session.DisableEncodeUrlFilter@439a0f0, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@6ac3ce34, org.springframework.security.web.context.SecurityContextHolderFilter@453a699b, org.springframework.security.web.header.HeaderWriterFilter@482bdb02, org.springframework.security.web.authentication.logout.LogoutFilter@6b7ff02a, com.jbaacount.global.security.filter.JwtAuthenticationFilter@6444bf53, com.jbaacount.global.security.filter.JwtVerificationFilter@634a3a2, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@6169c15d, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@6fc31f6, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@4fc3bdb4, org.springframework.security.web.session.SessionManagementFilter@23f2bfdb, org.springframework.security.web.access.ExceptionTranslationFilter@4dac863d, org.springframework.security.web.access.intercept.AuthorizationFilter@15d313d3]

2023-10-19T11:34:43.452+09:00  INFO 18568 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''

2023-10-19T11:34:43.459+09:00  INFO 18568 --- [           main] com.jbaacount.ServerApplication          : Started ServerApplication in 6.136 seconds (process running for 6.543)

2023-10-19T11:34:49.569+09:00  INFO 18568 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'

2023-10-19T11:34:49.576+09:00 TRACE 18568 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Trying to match request against DefaultSecurityFilterChain [RequestMatcher=any request, Filters=[org.springframework.security.web.session.DisableEncodeUrlFilter@439a0f0, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@6ac3ce34, org.springframework.security.web.context.SecurityContextHolderFilter@453a699b, org.springframework.security.web.header.HeaderWriterFilter@482bdb02, org.springframework.security.web.authentication.logout.LogoutFilter@6b7ff02a, com.jbaacount.global.security.filter.JwtAuthenticationFilter@6444bf53, com.jbaacount.global.security.filter.JwtVerificationFilter@634a3a2, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@6169c15d, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@6fc31f6, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@4fc3bdb4, org.springframework.security.web.session.SessionManagementFilter@23f2bfdb, org.springframework.security.web.access.ExceptionTranslationFilter@4dac863d, org.springframework.security.web.access.intercept.AuthorizationFilter@15d313d3]] (1/1)

2023-10-19T11:34:49.576+09:00 DEBUG 18568 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Securing OPTIONS /members/sign-up

2023-10-19T11:34:49.577+09:00 TRACE 18568 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Invoking DisableEncodeUrlFilter (1/13)

2023-10-19T11:34:49.577+09:00 TRACE 18568 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Invoking WebAsyncManagerIntegrationFilter (2/13)

2023-10-19T11:34:49.578+09:00 TRACE 18568 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Invoking SecurityContextHolderFilter (3/13)

2023-10-19T11:34:49.579+09:00 TRACE 18568 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Invoking HeaderWriterFilter (4/13)

2023-10-19T11:34:49.580+09:00 TRACE 18568 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Invoking LogoutFilter (5/13)

2023-10-19T11:34:49.580+09:00 TRACE 18568 --- [nio-8080-exec-1] o.s.s.w.a.logout.LogoutFilter            : Did not match request to Or [Ant [pattern='/logout', GET], Ant [pattern='/logout', POST], Ant [pattern='/logout', PUT], Ant [pattern='/logout', DELETE]]

2023-10-19T11:34:49.580+09:00 TRACE 18568 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Invoking JwtAuthenticationFilter (6/13)

2023-10-19T11:34:49.580+09:00 TRACE 18568 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Invoking JwtVerificationFilter (7/13)

2023-10-19T11:34:49.580+09:00  INFO 18568 --- [nio-8080-exec-1] c.j.g.s.filter.JwtVerificationFilter     : ===shouldNotFilter===

2023-10-19T11:34:49.580+09:00  INFO 18568 --- [nio-8080-exec-1] c.j.g.s.filter.JwtVerificationFilter     : authorization = null

2023-10-19T11:34:49.580+09:00 TRACE 18568 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Invoking RequestCacheAwareFilter (8/13)

2023-10-19T11:34:49.580+09:00 TRACE 18568 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Invoking SecurityContextHolderAwareRequestFilter (9/13)

2023-10-19T11:34:49.581+09:00 TRACE 18568 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Invoking AnonymousAuthenticationFilter (10/13)

2023-10-19T11:34:49.581+09:00 TRACE 18568 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Invoking SessionManagementFilter (11/13)

2023-10-19T11:34:49.581+09:00 TRACE 18568 --- [nio-8080-exec-1] .s.s.w.c.SupplierDeferredSecurityContext : Created SecurityContextImpl [Null authentication]

2023-10-19T11:34:49.582+09:00 TRACE 18568 --- [nio-8080-exec-1] o.s.s.w.a.AnonymousAuthenticationFilter  : Set SecurityContextHolder to AnonymousAuthenticationToken [Principal=anonymousUser, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=null], Granted Authorities=[ROLE_ANONYMOUS]]

2023-10-19T11:34:49.582+09:00 TRACE 18568 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Invoking ExceptionTranslationFilter (12/13)

2023-10-19T11:34:49.582+09:00 TRACE 18568 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Invoking AuthorizationFilter (13/13)

2023-10-19T11:34:49.582+09:00 TRACE 18568 --- [nio-8080-exec-1] estMatcherDelegatingAuthorizationManager : Authorizing SecurityContextHolderAwareRequestWrapper[ org.springframework.security.web.header.HeaderWriterFilter$HeaderWriterRequest@3e08b966]

2023-10-19T11:34:49.589+09:00 TRACE 18568 --- [nio-8080-exec-1] estMatcherDelegatingAuthorizationManager : Checking authorization on SecurityContextHolderAwareRequestWrapper[ org.springframework.security.web.header.HeaderWriterFilter$HeaderWriterRequest@3e08b966] using org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer$$Lambda$1596/0x0000000801c1a730@1b7a1007

2023-10-19T11:34:49.589+09:00 DEBUG 18568 --- [nio-8080-exec-1] o.s.security.web.FilterChainProxy        : Secured OPTIONS /members/sign-up

2023-10-19T11:34:49.603+09:00 TRACE 18568 --- [nio-8080-exec-1] o.s.s.w.header.writers.HstsHeaderWriter  : Not injecting HSTS header since it did not match request to [Is Secure]

我检查了开发工具。这里是标题信息。
enter image description here
enter image description here
我猜请求被发送到shouldNotFilter,但我不知道为什么请求仍然是Options,尽管我允许SecurityConfig中的OPTIONS和PreFlightRequest
如果我错过了什么或应用错误,请让我知道。

0sgqnhkj

0sgqnhkj1#

将这行代码添加到控制器的顶部,它将允许您到达端点。

@CrossOrigin(origins = "*", maxAge = 3600)

相关问题