Spring Webflux+;X509证书验证问题的Spring安全性:X509.X509CertImpl无法强制转换为类java.lang.String

Spring Webflux+;X509证书验证问题的Spring安全性:X509.X509CertImpl无法强制转换为类java.lang.String,java,spring-security,spring-webflux,x509certificate,Java,Spring Security,Spring Webflux,X509certificate,我面临Spring Security+SpringWebflux+X509客户端证书验证的问题。 我能得到一些帮助吗 使用以下最低工作代码: @RestController @SpringBootApplication public class X509AuthenticationServer { public static void main(String[] args) { SpringApplication.run(X509AuthenticationServer

我面临Spring Security+SpringWebflux+X509客户端证书验证的问题。 我能得到一些帮助吗

使用以下最低工作代码:

@RestController
@SpringBootApplication
public class X509AuthenticationServer {

    public static void main(String[] args) {
        SpringApplication.run(X509AuthenticationServer.class, args);
    }

    @RequestMapping(value = "/test")
    public Mono<String> test() {
        return Mono.just("\n test \n");
    }

    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity serverHttpSecurity, UserDetailsRepositoryReactiveAuthenticationManager authenticationManager, X509PrincipalExtractor principalExtractor) {
        return serverHttpSecurity.authorizeExchange().anyExchange().authenticated().and().x509().principalExtractor(principalExtractor).authenticationManager(authenticationManager).and().build();
    }

    @Bean
    public X509PrincipalExtractor principalExtractor() {
        return new SubjectDnX509PrincipalExtractor();
    }

    @Bean
    public UserDetailsRepositoryReactiveAuthenticationManager authenticationManager(ReactiveUserDetailsService reactiveUserDetailsService) {
        return new UserDetailsRepositoryReactiveAuthenticationManager(reactiveUserDetailsService);
    }

    @Bean
    public MapReactiveUserDetailsService mapReactiveUserDetailsService() {
        return new MapReactiveUserDetailsService(User.withUsername("USER").authorities(new SimpleGrantedAuthority("USER")).password("").build());
    }

}
我希望这能起作用,也就是说,我可以进行X509证书验证。 然而,我面临着一个我不理解的相当奇怪的例外

2020-10-15 20:04:57.773 DEBUG [,9dfd3873894ac7fe,9dfd3873894ac7fe,true] 8646 --- [ctor-http-nio-3] .w.a.p.x.SubjectDnX509PrincipalExtractor : Subject DN is 'CN=(I see the correct CN), OU=zzzzzz, OU=xxxxxx, O=cccccc, C=US'
2020-10-15 20:04:57.773 DEBUG [,9dfd3873894ac7fe,9dfd3873894ac7fe,true] 8646 --- [ctor-http-nio-3] .w.a.p.x.SubjectDnX509PrincipalExtractor : Extracted Principal name is(I see the correct CN)
2020-10-15 20:04:57.785 DEBUG [,9dfd3873894ac7fe,9dfd3873894ac7fe,true] 8646 --- [ctor-http-nio-3] a.w.r.e.AbstractErrorWebExceptionHandler : [a8f68710/1-1] Resolved [ClassCastException: class sun.security.x509.X509CertImpl cannot be cast to class java.lang.String (sun.security.x509.X509CertImpl and java.lang.String are in module java.base of loader 'bootstrap')] for HTTP GET /test
2020-10-15 20:04:57.786 ERROR [,9dfd3873894ac7fe,9dfd3873894ac7fe,true] 8646 --- [ctor-http-nio-3] a.w.r.e.AbstractErrorWebExceptionHandler : [a8f68710/1-1]  500 Server Error for HTTP GET "/test

java.lang.ClassCastException: class sun.security.x509.X509CertImpl cannot be cast to class java.lang.String (sun.security.x509.X509CertImpl and java.lang.String are in module java.base of loader 'bootstrap')
    at org.springframework.security.authentication.AbstractUserDetailsReactiveAuthenticationManager.authenticate(AbstractUserDetailsReactiveAuthenticationManager.java:99) ~[spring-security-core-5.3.4.RELEASE.jar:5.3.4.RELEASE]
    Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
    |_ checkpoint ⇢ org.springframework.security.web.server.authentication.AuthenticationWebFilter [DefaultWebFilterChain]

问题:这个“sun.security.x509.X509CertImpl和java.lang.String在加载程序“bootstrap”的模块java.base中”是什么意思,我是如何解决这个问题的,以及如何解决它的


谢谢

问题在于,
AbstractUserDetailsReactiveAuthenticationManager
旨在验证用户名/密码,而不是X509证书,因此它试图将X509凭据转换为字符串,以便像验证密码一样验证它

相反,请删除
ReactiveAuthenticationManager
Bean,并允许使用默认的
reactivepreauthenticationdauthenticationmanager
,它是从
ReactiveUserDetailsService
Bean自动创建的。
SubjectDnX509PrincipalExtractor
是默认值,因此无需显式提供它,但如果需要自定义它,只需将其作为Bean公开,默认情况下将使用它

@RestController
@SpringBootApplication
public class X509AuthenticationServer {

    public static void main(String[] args) {
        SpringApplication.run(X509AuthenticationServer.class, args);
    }

    @RequestMapping(value = "/test")
    public Mono<String> test() {
        return Mono.just("\n test \n");
    }

    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
        // @formatter:off
        http
            .authorizeExchange()
                .anyExchange().authenticated()
                .and()
            .x509();
        // @formatter:on
        return http.build();
    }

    @Bean
    public MapReactiveUserDetailsService mapReactiveUserDetailsService() {
        return new MapReactiveUserDetailsService(User.withUsername("USER").authorities("USER").password("").build());
    }

}
@RestController
@SpringBoot应用程序
公共类X509AuthenticationServer{
公共静态void main(字符串[]args){
run(X509AuthenticationServer.class,args);
}
@请求映射(value=“/test”)
公共单声道测试(){
返回Mono.just(“\n test\n”);
}
@豆子
公共安全WebFilterChain安全WebFilterChain(ServerHttpSecurity http){
//@formatter:off
http
.授权交易所()
.anyExchange().authenticated()
.及()
.x509();
//@formatter:on
返回http.build();
}
@豆子
公共MapReactiveUserDetails服务MapReactiveUserDetails服务(){
返回新的MapReactiveUserDetailsService(User.withUsername(“User”).authorities(“User”).password(“”.build());
}
}

您还可以在

上找到完整的示例问题在于,
AbstractUserDetailsReactiveAuthenticationManager
旨在验证用户名/密码,而不是X509证书,因此它试图将X509凭据转换为字符串,以便像验证密码一样验证它

相反,请删除
ReactiveAuthenticationManager
Bean,并允许使用默认的
reactivepreauthenticationdauthenticationmanager
,它是从
ReactiveUserDetailsService
Bean自动创建的。
SubjectDnX509PrincipalExtractor
是默认值,因此无需显式提供它,但如果需要自定义它,只需将其作为Bean公开,默认情况下将使用它

@RestController
@SpringBootApplication
public class X509AuthenticationServer {

    public static void main(String[] args) {
        SpringApplication.run(X509AuthenticationServer.class, args);
    }

    @RequestMapping(value = "/test")
    public Mono<String> test() {
        return Mono.just("\n test \n");
    }

    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
        // @formatter:off
        http
            .authorizeExchange()
                .anyExchange().authenticated()
                .and()
            .x509();
        // @formatter:on
        return http.build();
    }

    @Bean
    public MapReactiveUserDetailsService mapReactiveUserDetailsService() {
        return new MapReactiveUserDetailsService(User.withUsername("USER").authorities("USER").password("").build());
    }

}
@RestController
@SpringBoot应用程序
公共类X509AuthenticationServer{
公共静态void main(字符串[]args){
run(X509AuthenticationServer.class,args);
}
@请求映射(value=“/test”)
公共单声道测试(){
返回Mono.just(“\n test\n”);
}
@豆子
公共安全WebFilterChain安全WebFilterChain(ServerHttpSecurity http){
//@formatter:off
http
.授权交易所()
.anyExchange().authenticated()
.及()
.x509();
//@formatter:on
返回http.build();
}
@豆子
公共MapReactiveUserDetails服务MapReactiveUserDetails服务(){
返回新的MapReactiveUserDetailsService(User.withUsername(“User”).authorities(“User”).password(“”.build());
}
}

您也可以在

Hello@Rob上找到完整的示例,非常感谢您的清晰详细的解释。向上投票。我想知道,如果我只想在CN上添加实际的检查(比如只允许包含特定字符串的CN),那么如何将其应用到您的示例中?发现它非常感谢Rob对异常的清晰解释,以及解决它的步骤。AcceptedHello@Rob,非常感谢您的清晰详细的解释。向上投票。我想知道,如果我只想在CN上添加实际的检查(比如只允许包含特定字符串的CN),那么如何将其应用到您的示例中?发现它非常感谢Rob对异常的清晰解释,以及解决它的步骤。认可的