[英]Migrating Spring Security AbstractAuthenticationProcessingFilter to WebFlux
I'm updating an old application to use WebFlux but I've gotten a bit lost when it comes to handling JWT validation with Spring Security.我正在更新一个旧应用程序以使用 WebFlux,但是在使用 Spring Security 处理 JWT 验证时我有点迷失了。
The existing code (which works with standard Spring Web) looks like:现有代码(适用于标准 Spring Web)如下所示:
(Validating a Firebase Token) (验证 Firebase 令牌)
public class FirebaseAuthenticationTokenFilter extends AbstractAuthenticationProcessingFilter {
private static final String TOKEN_HEADER = "X-Firebase-Auth";
public FirebaseAuthenticationTokenFilter() {
super("/v1/**");
}
@Override
public Authentication attemptAuthentication(
final HttpServletRequest request, final HttpServletResponse response) {
for (final Enumeration<?> e = request.getHeaderNames(); e.hasMoreElements(); ) {
final String nextHeaderName = (String) e.nextElement();
final String headerValue = request.getHeader(nextHeaderName);
}
final String authToken = request.getHeader(TOKEN_HEADER);
if (Strings.isNullOrEmpty(authToken)) {
throw new RuntimeException("Invaild auth token");
}
return getAuthenticationManager().authenticate(new FirebaseAuthenticationToken(authToken));
}
However when switching to WebFlux we lose HttpServletRequest
and HttpServletResponse
.然而,当切换到 WebFlux 时,我们丢失了
HttpServletRequest
和HttpServletResponse
。 There is a GitHub issue which suggests there is an alternative method/fix https://github.com/spring-projects/spring-security/issues/5328 however following it through I'm not able to identify what was actually changed to make this work.有一个 GitHub 问题表明有一个替代方法/修复https://github.com/spring-projects/spring-security/issues/5328但是通过它我无法确定实际更改的内容这项工作。
The Spring Security docs while great, don't really explain how to handle the use-case. Spring Security 文档虽然很棒,但并没有真正解释如何处理用例。
Any tips on how to proceed?有关如何进行的任何提示?
Got there in the end:最后到达那里:
First need to update the filter chain with a custom filter just like before首先需要像以前一样使用自定义过滤器更新过滤器链
@Configuration
public class SecurityConfig {
private final FirebaseAuth firebaseAuth;
public SecurityConfig(final FirebaseAuth firebaseAuth) {
this.firebaseAuth = firebaseAuth;
}
@Bean
public SecurityWebFilterChain springSecurityFilterChain(final ServerHttpSecurity http) {
http.authorizeExchange()
.and()
.authorizeExchange()
.pathMatchers("/v1/**")
.authenticated()
.and()
.addFilterAt(firebaseAuthenticationFilter(), SecurityWebFiltersOrder.AUTHENTICATION)
.csrf()
.disable();
return http.build();
}
private AuthenticationWebFilter firebaseAuthenticationFilter() {
final AuthenticationWebFilter webFilter =
new AuthenticationWebFilter(new BearerTokenReactiveAuthenticationManager());
webFilter.setServerAuthenticationConverter(new FirebaseAuthenticationConverter(firebaseAuth));
webFilter.setRequiresAuthenticationMatcher(ServerWebExchangeMatchers.pathMatchers("/v1/**"));
return webFilter;
}
}
The main workhorse of the process is FirebaseAuthenticationConverter
where I validate the incoming JWT against Firebase, and perform some standard logic against it.该过程的主要工作是
FirebaseAuthenticationConverter
,我在其中根据 Firebase 验证传入的 JWT,并对其执行一些标准逻辑。
@Slf4j
@Component
@RequiredArgsConstructor
public class FirebaseAuthenticationConverter implements ServerAuthenticationConverter {
private static final String BEARER = "Bearer ";
private static final Predicate<String> matchBearerLength =
authValue -> authValue.length() > BEARER.length();
private static final Function<String, Mono<String>> isolateBearerValue =
authValue -> Mono.justOrEmpty(authValue.substring(BEARER.length()));
private final FirebaseAuth firebaseAuth;
private Mono<FirebaseToken> verifyToken(final String unverifiedToken) {
try {
final ApiFuture<FirebaseToken> task = firebaseAuth.verifyIdTokenAsync(unverifiedToken);
return Mono.justOrEmpty(task.get());
} catch (final Exception e) {
throw new SessionAuthenticationException(e.getMessage());
}
}
private Mono<FirebaseUserDetails> buildUserDetails(final FirebaseToken firebaseToken) {
return Mono.just(
FirebaseUserDetails.builder()
.email(firebaseToken.getEmail())
.picture(firebaseToken.getPicture())
.userId(firebaseToken.getUid())
.username(firebaseToken.getName())
.build());
}
private Mono<Authentication> create(final FirebaseUserDetails userDetails) {
return Mono.justOrEmpty(
new UsernamePasswordAuthenticationToken(
userDetails.getEmail(), null, userDetails.getAuthorities()));
}
@Override
public Mono<Authentication> convert(final ServerWebExchange exchange) {
return Mono.justOrEmpty(exchange)
.flatMap(AuthorizationHeaderPayload::extract)
.filter(matchBearerLength)
.flatMap(isolateBearerValue)
.flatMap(this::verifyToken)
.flatMap(this::buildUserDetails)
.flatMap(this::create);
}
}
To the previous answer there could be added that this method also works fine:对于上一个答案,可以补充说这种方法也可以正常工作:
private Mono<FirebaseToken> verifyToken(final String unverifiedToken) {
try {
return Mono.just(FirebaseAuth.getInstance().verifyIdToken(unverifiedToken));
} catch (final Exception e) {
throw new SessionAuthenticationException(e.getMessage());
}
}
And this one does not provid warnings regarding unnecessary use of blocking methods (like get()
)并且这个不提供关于不必要使用阻塞方法的警告(如
get()
)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.