【问题标题】:Spring-Security: Getting 401s with CORS Preflights (despite http.cors())Spring-Security:使用 CORS Preflights 获得 401(尽管 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 教程严重过时,请使用官方文档

标签: spring-boot kotlin spring-security cors


【解决方案1】:

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

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

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

import org.springframework.http.HttpMethod.*

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

最初我认为这应该由 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 建议。
    猜你喜欢
    • 2017-10-11
    • 2019-03-25
    • 2016-10-31
    • 1970-01-01
    • 2019-07-16
    • 2014-03-22
    • 2018-12-15
    • 1970-01-01
    • 2022-08-17
    相关资源
    最近更新 更多