Spring-Security:使用 CORS Preflights 获得 401(尽管 http.cors())

Posted

技术标签:

【中文标题】Spring-Security:使用 CORS Preflights 获得 401(尽管 http.cors())【英文标题】:Spring-Security: Getting 401s with CORS Preflights (despite http.cors()) 【发布时间】:2020-08-26 17:22:20 【问题描述】:

对于初学者,我想使用基本身份验证来保护我的 rest-api 的一部分。 当我尝试从反应客户端访问端点时,我在预检请求中不断收到 401。

我尝试遵循本指南但没有成功: https://www.baeldung.com/spring-security-cors-preflight

我不确定这是否是问题的一部分,但另一部分只能通过某些自定义 http 标头访问。

我正在使用方法安全性:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = false)
class MethodSecurityConfig : GlobalMethodSecurityConfiguration() 
    override fun customMethodSecurityMetadataSource(): MethodSecurityMetadataSource = SecurityMetadataSource()

    override fun accessDecisionManager(): AccessDecisionManager = super.accessDecisionManager().apply 
        this as AbstractAccessDecisionManager
        decisionVoters.add(PrivilegeVoter())
    

这是我的安全配置:

@Configuration
@EnableJpaAuditing(auditorAwareRef = "auditorProvider")
class SecurityConfig : WebSecurityConfigurerAdapter() 
    private val deviceRequestHeaderName: String = "X-DEVICE-ID"    
    private val platformRequestHeaderName: String = "X-PLATFORM-ID"

    @Autowired
    lateinit var users: AppUserRepository

    @Autowired
    lateinit var backendUsers: BackendUserRepository

    @Autowired
    lateinit var roles: RoleRepository

    val authManager by lazy  authenticationManager() 

    private val authProvider by lazy 
        PreAuthenticatedAuthenticationProvider().apply 
            setPreAuthenticatedUserDetailsService 
                val authId = it.principal as UserAuthId
                if (authId.deviceId == null) throw UsernameNotFoundException("No device-id to search for.")
                if (authId.platform == null) throw UsernameNotFoundException("Platform not specified.")
                val platform = try 
                    ApplicationPlatform.valueOf(authId.platform)
                 catch (e: IllegalArgumentException) 
                    throw UsernameNotFoundException("Unknown platform $authId.platform.")
                
                val existingUser = users.findByUserDeviceIdAndPlatform(authId.deviceId, platform)
                if (existingUser != null) return@setPreAuthenticatedUserDetailsService existingUser

                users.save(AppUser(authId.deviceId, platform, roles))
            
        
    

    val passwordEncoder by lazy  BCryptPasswordEncoder() 

    private val deviceIdFilter by lazy 
        HeaderFieldAuthFilter(deviceRequestHeaderName, platformRequestHeaderName).apply 
            setAuthenticationManager(authManager)
        
    

    override fun configure(auth: AuthenticationManagerBuilder) = auth 
        authenticationProvider(authProvider)

        val userDetailsService = BackendUserDetailsService(backendUsers)
        userDetailsService(userDetailsService).passwordEncoder(passwordEncoder)
    

    override fun configure(http: HttpSecurity) = http 
        session 
            sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        
        exceptionHandling()

        addFilter(deviceIdFilter)
        authorizeRequests().anyRequest().authenticated()
        csrf().disable()
        httpBasic()

        cors().configurationSource  request ->
            CorsConfiguration().apply 
                allowedOrigins = listOf(ALL)
                allowedMethods = listOf(GET, POST, DELETE, PUT, OPTIONS).map  it.name 
                allowedHeaders = listOf(ALL)
                allowCredentials = true
                maxAge = 3600
            
        
    

    @Bean
    fun auditorProvider(): AuditorAware<User> = AuditorAware<User> 
        val authentication = SecurityContextHolder.getContext().authentication
        val user = authentication.run  if (isAuthenticated) principal as? User else null 
        return@AuditorAware Optional.ofNullable(user)
    

【问题讨论】:

Baeldung 教程严重过时,请使用官方文档 【参考方案1】:

我可以通过手动从身份验证中排除预检请求来解决。 添加这个

antMatchers(OPTIONS, "/**").permitAll()

authorizeRequests() 配置可以实现这一点。 请注意,Options 是对 HttpMethod 枚举值的直接引用,像这样导入

import org.springframework.http.HttpMethod.*

帮助我实现目标的 *** 帖子:

Response for preflight has invalid HTTP status code 401 - Spring How to configure CORS in a Spring Boot + Spring Security application?

最初我认为这应该由 cors 配置处理 - 显然不是。

【讨论】:

【参考方案2】:

要为单个休息端点启用 CORS,您可以使用以下命令对其进行注释:

@CrossOrigin

要允许所有端点使用 CORS,您可以使用这样的 bean:

  @Bean
  public WebMvcConfigurer corsConfigurer() 
    return new WebMvcConfigurerAdapter() 
      @Override
      public void addCorsMappings(CorsRegistry registry) 
        registry.addMapping("/**").allowedMethods("GET", "POST", "PUT", "DELETE").allowedOrigins("*")
                .allowedHeaders("*");
      
    ;

【讨论】:

将@CrossOrigin 放在单个端点上并不能解决我项目中该端点的问题。选项请求得到 401 响应。 我明天将测试 WebMvcConfigurerAdapter 建议。

以上是关于Spring-Security:使用 CORS Preflights 获得 401(尽管 http.cors())的主要内容,如果未能解决你的问题,请参考以下文章

Spring-Security OAuth WebMVC 无效的 CORS 请求

如何为 Webflux、Spring-Security、okta-spring-boot-starter 和 Okta-React 正确使用 CORS?

在 spring 安全标签中启用 CORS

将标头添加到 oauth/token 响应(spring-security)

Spring Security CORS 不工作

spring CORS 和 Angular 不起作用:HTTP 状态代码 403 错误