Spring WebFlux Kotlin OAuth2 CORS

Spring WebFlux Kotlin OAuth2 CORS

所以这是配置...

我有 localhost:8080 作为我的后端,localhost:3000 作为我的前端。后端的身份验证仅通过 OAuth2 处理。基本上,前端用户单击“使用 Spotify 登录”,被重定向到处理 OAuth2 的后端,然后重定向回前端。所有后续请求都来自前端到后端。 (前端在 Next.JS 顺便说一句)

这是我的问题...

如果我未通过身份验证,对我后端的任何请求都会引发 CORS 错误。 (如果我通过身份验证,它会按预期工作)。所以 GET /users/{id},虽然没有登录,returns CORS,而不是普通的 401。这没关系,因为我可以解决它,但是现在我不能发出 PUT 请求由于 CORS 而导致的端点,即使在登录时......我已经尝试过 @CrossOrigin 注释,但这没有帮助。我还在 SecurityConfig 中尝试了 .cors().disable(),但是我 仍然 在前端抛出 CORS 错误...

这是我用于 Security/WebFlux 的内容:

       <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
       </dependency>
       <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-oauth2-client</artifactId>
            <version>5.4.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
            <version>2.3.4.RELEASE</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
        </dependency>

这是我的 CorsConfig:

@Configuration
@Order(Ordered.HIGHEST_PRECEDENCE)
@WebFilter("/*")
class CorsConfig: WebFluxConfigurer {
    override fun addCorsMappings(registry: CorsRegistry) {
        registry.addMapping("/**")
                .allowedMethods("HEAD", "GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS")
                .allowCredentials(true)
        super.addCorsMappings(registry)
    }
}

这是我的安全配置:


@EnableWebFluxSecurity
class SecurityConfig(
        @Qualifier("globalErrorHandler")
        private val exceptionHandler: WebExceptionHandler,
        private val authenticationFailureHandler: ServerAuthenticationFailureHandler,
        private val authenticationSuccessHandler: AuthenticationSuccessHandler,
        private val authorizationRequestResolver: ServerOAuth2AuthorizationRequestResolver,
        private val logoutSuccessHandler: LogoutSuccessHandler
) {
    @Bean
    fun securityFilterChain(
            httpSecurity: ServerHttpSecurity
    ): SecurityWebFilterChain = httpSecurity
            .authorizeExchange()
            .pathMatchers("/login", "/register", "/logout").permitAll()
            .anyExchange().authenticated()
            .and()
            .cors().disable()
            .exceptionHandling(::withConfiguration)
            .oauth2Login(::withConfiguration)
            .logout(::withConfiguration)
            .csrf().disable()
            .build()

    fun withConfiguration(spec: ServerHttpSecurity.ExceptionHandlingSpec): Unit =
            spec.accessDeniedHandler(exceptionHandler::handle)
                    .authenticationEntryPoint(exceptionHandler::handle)
                    .run {}

    fun withConfiguration(spec: ServerHttpSecurity.OAuth2LoginSpec): Unit =
            spec.authenticationFailureHandler(authenticationFailureHandler)
                    .authenticationSuccessHandler(authenticationSuccessHandler)
                    .authorizationRequestResolver(authorizationRequestResolver)
                    .run { }

    fun withConfiguration(spec: ServerHttpSecurity.LogoutSpec): Unit =
            spec.logoutUrl("/logout")
                    .logoutSuccessHandler(logoutSuccessHandler)
                    .run { }
}

如果您需要更多信息,请随时询问。

这是我的解决方案,我需要添加:

    fun corsConfigurationSource(): CorsConfigurationSource {
        val cors = CorsConfiguration()
        cors.allowedOrigins = List.of("*")
        cors.allowedMethods = List.of("*")
        cors.allowedHeaders = List.of("*")
        cors.allowCredentials = true
        cors.maxAge = 3600L
        val source = UrlBasedCorsConfigurationSource()
        source.registerCorsConfiguration("/**", cors)
        return source
    }

然后,在我的 SecurityWebFilterChain 中,我需要让 Cors 使用我的配置:

    @Bean
    fun securityFilterChain(
            httpSecurity: ServerHttpSecurity
    ): SecurityWebFilterChain = httpSecurity
            .cors(::withConfiguration)
            .csrf().disable()
            .authorizeExchange()
            .pathMatchers("/login", "/register", "/logout").permitAll()
            .anyExchange().authenticated()
            .and()
            .exceptionHandling(::withConfiguration)
            .oauth2Login(::withConfiguration)
            .logout(::withConfiguration)
            .build()

并且 ::withConfiguration 解析为:

    fun withConfiguration(spec: ServerHttpSecurity.CorsSpec): Unit =
            spec.configurationSource(corsConfigurationSource())
                    .run { }