从自定义WebFlux loginForm传递其他登录参数

时间:2019-01-22 14:40:18

标签: spring-boot spring-security spring-webflux

我已经实现了自定义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对象中?

我发现this的示例在实现中看起来很直接,但是对于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();
}

更新

我现在有一些进步。通过不使用formLogin上的默认ServerHttpSecurity而是使用我自己的扩展WebFilter的{​​{1}}不能执行我想要的事情。我现在遇到的问题是,我没有默认的入口点,也很难知道我是否会神奇地缺少AuthenticationWebFilter类对我有用的其他东西。

1 个答案:

答案 0 :(得分:0)

我尝试使它更优雅,但这将需要更多工作。因此,我为此受助了。您必须像构建一样构建SecurityWebFiler链。但是创建它之后,您必须找到此AuthenticationWebFilter,它是一个负责创建authentication对象的Web过滤器。并设置自己的身份验证转换器。

@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;
}
}