【发布时间】:2021-10-05 08:55:06
【问题描述】:
我有一个 Spring Boot Webflux 应用程序。我正在将 Kafka 与 Apache camel 一起使用,并且还使用 websockets。
我最近添加了Spring security。因为我不需要身份验证,因此我不进行身份验证,而只是授权传入的请求。
由于添加了安全性,它阻止了 Kafka Connect 的 REST API 端点,例如。 GET /connectors call。 IMO,它不应该阻止它们,因为它们会从应用程序内部引发。
错误日志
2021-07-29 22:20:51.654 DEBUG 68377 --- [ctor-http-nio-4] o.s.w.s.adapter.HttpWebHandlerAdapter : [adf2f930-1, L:/127.0.0.1:8083 - R:/127.0.0.1:60653] HTTP GET "/connectors"
2021-07-29 22:20:51.659 DEBUG 68377 --- [ parallel-3] o.s.w.s.s.DefaultWebSessionManager : Created new WebSession.
2021-07-29 22:20:51.660 DEBUG 68377 --- [ parallel-3] o.s.s.w.s.u.m.OrServerWebExchangeMatcher : Trying to match using PathMatcherServerWebExchangeMatcher{pattern='/weather-data/api/**', method=GET}
2021-07-29 22:20:51.660 DEBUG 68377 --- [ parallel-3] athPatternParserServerWebExchangeMatcher : Request 'GET /connectors' doesn't match 'GET /weather-data/api/**'
2021-07-29 22:20:51.660 DEBUG 68377 --- [ parallel-3] o.s.s.w.s.u.m.OrServerWebExchangeMatcher : No matches found
2021-07-29 22:20:51.660 DEBUG 68377 --- [ parallel-3] o.s.s.w.s.u.m.OrServerWebExchangeMatcher : Trying to match using PathMatcherServerWebExchangeMatcher{pattern='/weather-data/api/**', method=POST}
2021-07-29 22:20:51.660 DEBUG 68377 --- [ parallel-3] athPatternParserServerWebExchangeMatcher : Request 'GET /connectors' doesn't match 'POST /weather-data/api/**'
2021-07-29 22:20:51.660 DEBUG 68377 --- [ parallel-3] o.s.s.w.s.u.m.OrServerWebExchangeMatcher : No matches found
2021-07-29 22:20:51.660 DEBUG 68377 --- [ parallel-3] o.s.s.w.s.u.m.OrServerWebExchangeMatcher : Trying to match using PathMatcherServerWebExchangeMatcher{pattern='/weather-data/api/events', method=GET}
2021-07-29 22:20:51.660 DEBUG 68377 --- [ parallel-3] athPatternParserServerWebExchangeMatcher : Request 'GET /connectors' doesn't match 'GET /weather-data/api/events'
2021-07-29 22:20:51.660 DEBUG 68377 --- [ parallel-3] o.s.s.w.s.u.m.OrServerWebExchangeMatcher : No matches found
2021-07-29 22:20:51.660 DEBUG 68377 --- [ parallel-3] o.s.s.w.s.a.AuthorizationWebFilter : Authorization failed: Access Denied
2021-07-29 22:20:51.669 INFO 68377 --- [ parallel-3] c.s.M.w.WebFluxSecurityConfiguration : Authentication exception
org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: Not Authenticated
at org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter.commenceAuthentication(ExceptionTranslationWebFilter.java:70) ~[spring-security-web-5.5.0.jar:5.5.0]
at org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter.lambda$filter$1(ExceptionTranslationWebFilter.java:45) ~[spring-security-web-5.5.0.jar:5.5.0]
1. Webflux 安全配置
package com.reactive.sse.security
import mu.KotlinLogging
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.HttpMethod
import org.springframework.http.HttpStatus
import org.springframework.security.access.AccessDeniedException
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity
import org.springframework.security.config.web.server.SecurityWebFiltersOrder
import org.springframework.security.config.web.server.ServerHttpSecurity
import org.springframework.security.core.AuthenticationException
import org.springframework.security.web.server.SecurityWebFilterChain
import org.springframework.security.web.server.authentication.AuthenticationWebFilter
import org.springframework.security.web.server.authentication.ServerAuthenticationConverter
import org.springframework.web.server.ServerWebExchange
import reactor.core.publisher.Mono
@Configuration
@EnableWebFluxSecurity
class SecurityConfiguration {
private val logger = KotlinLogging.logger {}
val READ_ROLE = "DATA_READ"
@Bean
fun securityWebFilterChain(
http: ServerHttpSecurity,
weatherAuthenticatedRequestManager: WeatherAuthenticatedRequestManager,
authenticationConverter: ServerAuthenticationConverter
): SecurityWebFilterChain {
val authenticationWebFilter = AuthenticationWebFilter(weatherAuthenticatedRequestManager)
authenticationWebFilter.setServerAuthenticationConverter(authenticationConverter)
return http
.exceptionHandling()
.authenticationEntryPoint { swe: ServerWebExchange, ex: AuthenticationException? ->
logger.error(ex) { "Authentication exception" }
Mono.fromRunnable { swe.response.statusCode = HttpStatus.UNAUTHORIZED }
}.accessDeniedHandler { swe: ServerWebExchange, ex: AccessDeniedException? ->
logger.error(ex) { "Authorization exception" }
Mono.fromRunnable { swe.response.statusCode = HttpStatus.FORBIDDEN }
}.and()
.authorizeExchange()
.pathMatchers(HttpMethod.GET,"/weather-data/api/**").hasRole(READ_ROLE)
.and()
.addFilterAt(authenticationWebFilter, SecurityWebFiltersOrder.AUTHENTICATION)
.httpBasic().disable()
.csrf().disable()
.formLogin().disable()
.logout().disable()
.build()
}
}
2. WeatherAuthenticationConverter
package com.reactive.sse.security
import org.springframework.http.HttpCookie
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.Authentication
import org.springframework.security.web.server.authentication.ServerAuthenticationConverter
import org.springframework.stereotype.Component
import org.springframework.web.server.ServerWebExchange
import reactor.core.publisher.Mono
@Component
class WeatherAuthenticationConverter : ServerAuthenticationConverter {
override fun convert(exchange: ServerWebExchange?): Mono<Authentication> {
return Mono.justOrEmpty(exchange)
.flatMap { serverWebExchange: ServerWebExchange ->
Mono.justOrEmpty(serverWebExchange.request.cookies["X-Auth"])
}
.filter { cookies: List<HttpCookie> -> cookies.isNotEmpty() }
.map { cookies: List<HttpCookie> -> cookies[0].value }
.map { authenticationStr: String ->
UsernamePasswordAuthenticationToken(
authenticationStr,
authenticationStr
)
}
}
}
3. WeatherAuthenticatedRequestManager
package com.reactive.sse.security
import org.springframework.security.authentication.ReactiveAuthenticationManager
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.core.Authentication
import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.stereotype.Component
import reactor.core.publisher.Mono
@Component
class WeatherAuthenticatedRequestManager : ReactiveAuthenticationManager {
/**
* Authenticate based on the Jwt token
*
* @property authentication
* @return Authentication Publisher
*/
override fun authenticate(authentication: Authentication): Mono<Authentication> {
return Mono.just(authentication)
.map { authenticationObj: Authentication ->
val roles: List<String> = (authenticationObj.credentials as List<*>).filterIsInstance<String>()
val rolesAuthority = roles.map { value -> SimpleGrantedAuthority(value) }
UsernamePasswordAuthenticationToken(
roles,
roles,
rolesAuthority
)
}
}
}
spring security应该如何配置才不会像这样阻塞内部调用。
【问题讨论】:
标签: spring-boot spring-security apache-kafka spring-webflux