Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/spring-boot/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Spring boot 使用spring安全性和反应式spring的自定义身份验证_Spring Boot_Spring Security_Kotlin_Spring Webflux - Fatal编程技术网

Spring boot 使用spring安全性和反应式spring的自定义身份验证

Spring boot 使用spring安全性和反应式spring的自定义身份验证,spring-boot,spring-security,kotlin,spring-webflux,Spring Boot,Spring Security,Kotlin,Spring Webflux,我有一个自定义的身份验证方案。我有一个REST端点,它在http uri路径中有userId,在http头中有token。我想检查这样的请求是否由具有有效令牌的有效用户执行。用户和令牌存储在mongo集合中 我不知道应该在哪个类中授权用户 我的SecurityConfig: @EnableWebFluxSecurity class SecurityConfig { @Bean fun securityWebFilterChain(http: ServerHttpSecurity): S

我有一个自定义的身份验证方案。我有一个REST端点,它在http uri路径中有
userId
,在http头中有
token
。我想检查这样的请求是否由具有有效令牌的有效用户执行。用户和令牌存储在mongo集合中

我不知道应该在哪个类中授权用户

我的
SecurityConfig

@EnableWebFluxSecurity
class SecurityConfig {

  @Bean
  fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {

    val build = http
        .httpBasic().disable()
        .formLogin().disable()
        .csrf().disable()
        .logout().disable()
        .authenticationManager(CustomReactiveAuthenticationManager())
        .securityContextRepository(CustomServerSecurityContextRepository())
        .authorizeExchange().pathMatchers("/api/measurement/**").hasAuthority("ROLE_USER")
        .anyExchange().permitAll().and()

    return build.build()
  }

  @Bean
  fun userDetailsService(): MapReactiveUserDetailsService {
    val user = User.withDefaultPasswordEncoder()
        .username("sampleDeviceIdV1")
        .password("foo")
        .roles("USER")
        .build()

    return MapReactiveUserDetailsService(user)
  }
}
My
ServerSecurityContextRepository

class CustomServerSecurityContextRepository : ServerSecurityContextRepository {

  override fun load(exchange: ServerWebExchange): Mono<SecurityContext> {
    val authHeader = exchange.request.headers.getFirst(HttpHeaders.AUTHORIZATION)
    val path = exchange.request.uri.path


    return if (path.startsWith("/api/measurement/") && authHeader != null && authHeader.startsWith(prefix = "Bearer ")) {
      val deviceId = path.drop(17)

      val authToken = authHeader.drop(7)
      val auth = UsernamePasswordAuthenticationToken(deviceId, authToken)
      Mono.just(SecurityContextImpl(auth))
    } else {
      Mono.empty()
    }
  }

  override fun save(exchange: ServerWebExchange?, context: SecurityContext?): Mono<Void> {
    return Mono.empty()
  }
}
类CustomServerSecurityContextRepository:ServerSecurityContextRepository{
覆盖乐趣加载(exchange:ServerWebExchange):Mono{
val authHeader=exchange.request.headers.getFirst(HttpHeaders.AUTHORIZATION)
val path=exchange.request.uri.path
返回if(path.startsWith(“/api/measurement/”)&&authHeader!=null&&authHeader.startsWith(prefix=“Bearer”)){
val deviceId=路径。删除(17)
val authToken=authHeader.drop(7)
val auth=UsernamePasswordAuthenticationToken(deviceId,authToken)
Mono.just(SecurityContextImpl(auth))
}否则{
Mono.empty()
}
}
覆盖有趣的保存(exchange:ServerWebExchange?,上下文:SecurityContext?):Mono{
返回Mono.empty()
}
}
出现了两个问题:

  • ServerSecurityContextRepository
    是从exchange获取用户名和令牌的好地方,还是有更好的地方

  • 我应该在哪里执行身份验证(根据mongo集合检查令牌和用户名)? 我的自定义
    AuthenticationManager
    在任何地方都不会被调用。我应该在
    ServerSecurityContextRepository
    中执行所有操作,还是在
    ReactiveAuthenticationManager
    中执行用户和令牌验证?或者其他班级更合适


  • 事实证明,网络上的一些教程是完全错误的

    我已使用以下代码成功配置了所有内容:

    class DeviceAuthenticationConverter : Function<ServerWebExchange, Mono<Authentication>> {
      override fun apply(exchange: ServerWebExchange): Mono<Authentication> {
        val authHeader: String? = exchange.request.headers.getFirst(HttpHeaders.AUTHORIZATION)
        val path: String? = exchange.request.uri.path
    
        return when {
          isValidPath(path) && isValidHeader(authHeader) -> Mono.just(UsernamePasswordAuthenticationToken(path?.drop(17), authHeader?.drop(7)))
          else -> Mono.empty()
        }
      }
    
      private fun isValidPath(path: String?) = path != null && path.startsWith(API_MEASUREMENT)
    
      private fun isValidHeader(authHeader: String?) = authHeader != null && authHeader.startsWith(prefix = "Bearer ")
    
    }
    
    @EnableWebFluxSecurity
    class SecurityConfig {
    
      companion object {
        const val API_MEASUREMENT = "/api/measurement/"
        const val API_MEASUREMENT_PATH = "$API_MEASUREMENT**"
        const val DEVICE = "DEVICE"
        const val DEVICE_ID = "deviceId"
      }
    
      @Bean
      fun securityWebFilterChain(http: ServerHttpSecurity, authenticationManager: ReactiveAuthenticationManager) =
          http
              .httpBasic().disable()
              .formLogin().disable()
              .csrf().disable()
              .logout().disable()
              .authorizeExchange().pathMatchers(API_MEASUREMENT_PATH).hasRole(DEVICE)
              .anyExchange().permitAll().and().addFilterAt(authenticationWebFilter(authenticationManager), AUTHENTICATION).build()
    
      @Bean
      fun userDetailsService(tokenRepository: TokenRepository) = MongoDeviceTokenReactiveUserDetailsService(tokenRepository)
    
      @Bean
      fun tokenRepository(template: ReactiveMongoTemplate, passwordEncoder: PasswordEncoder) = MongoTokenRepository(template, passwordEncoder)
    
      @Bean
      fun tokenFacade(tokenRepository: TokenRepository) = TokenFacade(tokenRepository)
    
      @Bean
      fun authManager(userDetailsService: ReactiveUserDetailsService) = UserDetailsRepositoryReactiveAuthenticationManager(userDetailsService)
    
      private fun authenticationWebFilter(reactiveAuthenticationManager: ReactiveAuthenticationManager) =
          AuthenticationWebFilter(reactiveAuthenticationManager).apply {
            setAuthenticationConverter(DeviceAuthenticationConverter())
            setRequiresAuthenticationMatcher(
                ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST, API_MEASUREMENT_PATH)
            )
          }
    
      @Bean
      fun passwordEncoder() = PasswordEncoderFactories.createDelegatingPasswordEncoder()
    }