首页 > 解决方案 > 从自定义 WebFlux loginForm 传递额外的登录参数

问题描述

我已经实现了一个自定义ReactiveAuthenticationManager来使用我的formLogin.

@Bean
public ReactiveAuthenticationManager reactiveAuthenticationManager() {
    return new ReactiveAuthenticationManagerAdapter(this::authenticate);
}

private Authentication authenticate(Authentication authentication) throws AuthenticationException {

    return new UsernamePasswordAuthenticationToken(
            userId, password, authorities);

}

这工作正常,该authenticate函数在正确的时间被调用。但是,我现在必须从登录表单中发送一个附加参数。我已经能够创建一个自定义登录表单,它将额外的参数发送到端点,但是我如何将它包含在Authentication我进入的对象中authenticate???

我发现这个示例在实现中看起来很简单,但它适用于 Spring MVC,我需要在 WebFlux 中进行。

更新:
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
    http.csrf()
            .disable()
            .authorizeExchange()
            .pathMatchers("/login/**")
            .permitAll()
            .pathMatchers("/**")
            .authenticated()
            .and()
            .formLogin()
            .loginPage("/login")
            .authenticationSuccessHandler(new RedirectServerAuthenticationSuccessHandler("/"))
            .authenticationFailureHandler(this::onAuthenticationFailure)
            .and()
            .logout()
            .logoutUrl("/logout")
            .logoutSuccessHandler(logoutSuccessHandler("/bye"));

    return http.build();
}

更新

我现在有了一些进展。通过不使用默认值formLoginServerHttpSecurity而是使用我自己WebFilter的扩展AuthenticationWebFilter我能够做我想做的事。我现在遇到的问题是我没有默认入口点,而且很难知道我是否错过了该ServerHttpSecurity课程为我做的其他事情。

标签: spring-bootspring-securityspring-webflux

解决方案


我试图让它更优雅,但它会做更多的工作。所以我满足于这个insed。你必须像你一样构建你的 SecurityWebFiler 链。但是在你创建它之后你必须发现AuthenticationWebFilter它是一个负责创建authentication对象的网络过滤器。并设置您自己的身份验证转换器。

@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
       http.authorizeExchange()
           .anyExchange().authenticated()
           .and()
           .formLogin()
       ;

    final SecurityWebFilterChain build = http.build();

    build.getWebFilters().collectList().subscribe(
            webFilters -> {
                for (WebFilter filter : webFilters){
                    if(filter instanceof AuthenticationWebFilter){
                        AuthenticationWebFilter awf = (AuthenticationWebFilter) filter;
                        awf.setAuthenticationConverter(new CustomHttpBasicAuthenticationConverter());
                    }
                }
            }
    );

    return build;
}

的例子HttpBasicAuthenticationConverter。老实说,我只是从 spring 源代码中复制了它。希望有帮助。

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.function.Function;

public class CustomHttpBasicAuthenticationConverter implements Function<ServerWebExchange, Mono<Authentication>> {

private String usernameParameter = "username";
private String passwordParameter = "password";

@Override
public Mono<Authentication> apply(ServerWebExchange exchange) {
    return exchange.getFormData()
            .map( data -> createAuthentication(data));
}

private UsernamePasswordAuthenticationToken createAuthentication(
        MultiValueMap<String, String> data) {
    String username = data.getFirst(this.usernameParameter);
    String password = data.getFirst(this.passwordParameter);
    return new UsernamePasswordAuthenticationToken(username, password);
}

public void setUsernameParameter(String usernameParameter) {
    Assert.notNull(usernameParameter, "usernameParameter cannot be null");
    this.usernameParameter = usernameParameter;
}

public void setPasswordParameter(String passwordParameter) {
    Assert.notNull(passwordParameter, "passwordParameter cannot be null");
    this.passwordParameter = passwordParameter;
}
}

推荐阅读