Spring Webflux + Spring Security for X509 certificate validation issue: x509.X509CertImpl cannot be cast to class java.lang.String

huangapple go评论80阅读模式
英文:

Spring Webflux + Spring Security for X509 certificate validation issue: x509.X509CertImpl cannot be cast to class java.lang.String

问题

I am facing an issue with Spring Security + Spring Webflux + X509 Client certificate validation.
May I get some assistance please?

With the minimal working code below:

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

}
server.ssl.key-store=/keystore.jks
server.ssl.key-store-password=123456
server.ssl.key-alias=localhost
server.ssl.key-password=123456
server.ssl.enabled=true
server.ssl.trust-store=/truststore.jks
server.ssl.trust-store-password=123456
server.ssl.client-auth=need
server.port=8443
spring.security.user.name=test
spring.security.user.password=test

logging.level.root=DEBUG

I was expecting this to work, i.e. that I can do the X509 certificate validation.
However, I am facing this rather strange exception I am not understanding

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]

Question: What does this "sun.security.x509.X509CertImpl and java.lang.String are in module java.base of loader 'bootstrap'" mean, how did I end up with this issue, and how to fix it please?

Thank you

英文:

I am facing an issue with Spring Security + Spring Webflux + X509 Client certificate validation.
May I get some assistance please?

With the minimal working code below:

@RestController
@SpringBootApplication
public class X509AuthenticationServer {
public static void main(String[] args) {
SpringApplication.run(X509AuthenticationServer.class, args);
}
@RequestMapping(value = &quot;/test&quot;)
public Mono&lt;String&gt; test() {
return Mono.just(&quot;\n test \n&quot;);
}
@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(&quot;USER&quot;).authorities(new SimpleGrantedAuthority(&quot;USER&quot;)).password(&quot;&quot;).build());
}
}
server.ssl.key-store=/keystore.jks
server.ssl.key-store-password=123456
server.ssl.key-alias=localhost
server.ssl.key-password=123456
server.ssl.enabled=true
server.ssl.trust-store=/truststore.jks
server.ssl.trust-store-password=123456
server.ssl.client-auth=need
server.port=8443
spring.security.user.name=test
spring.security.user.password=test
logging.level.root=DEBUG

I was expecting this to work, i.e. that I can do the X509 certificate validation.
However, I am facing this rather strange exception I am not understanding

2020-10-15 20:04:57.773 DEBUG [,9dfd3873894ac7fe,9dfd3873894ac7fe,true] 8646 --- [ctor-http-nio-3] .w.a.p.x.SubjectDnX509PrincipalExtractor : Subject DN is &#39;CN=(I see the correct CN), OU=zzzzzz, OU=xxxxxx, O=cccccc, C=US&#39;
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 &#39;bootstrap&#39;)] 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 &quot;/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 &#39;bootstrap&#39;)
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]

Question: What does this "sun.security.x509.X509CertImpl and java.lang.String are in module java.base of loader 'bootstrap'" means, how did I end up with this issue, and how to fix it please?

Thank you

答案1

得分: 2

问题在于 AbstractUserDetailsReactiveAuthenticationManager 旨在验证用户名/密码,而不是 X509 证书,因此它试图将 X509 凭据强制转换为字符串,以类似于验证密码的方式进行验证。

相反,删除 ReactiveAuthenticationManager Bean,允许使用默认的 ReactivePreAuthenticatedAuthenticationManager,它会自动从 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());
    }

}

您还可以在以下链接找到完整示例:https://github.com/spring-projects/spring-security-samples/blob/592e4f89d2819f255340518a2c91b7e6eab6ed3e/reactive/webflux/java/authentication/x509/src/main/java/example/WebfluxX509Application.java

英文:

The problem is that the AbstractUserDetailsReactiveAuthenticationManager is intended to validate a username/password not a X509 certificate, so it is trying to cast the X509 credential to a String to validate it like a password.

Instead, remove the ReactiveAuthenticationManager Bean and allow the default ReactivePreAuthenticatedAuthenticationManager to be used which is automatically created from the ReactiveUserDetailsService bean. The SubjectDnX509PrincipalExtractor is the default, so there is no need to explicitly provide it, but if you need to customize it just expose it as a Bean and it will be used by default.

@RestController
@SpringBootApplication
public class X509AuthenticationServer {
public static void main(String[] args) {
SpringApplication.run(X509AuthenticationServer.class, args);
}
@RequestMapping(value = &quot;/test&quot;)
public Mono&lt;String&gt; test() {
return Mono.just(&quot;\n test \n&quot;);
}
@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(&quot;USER&quot;).authorities(&quot;USER&quot;).password(&quot;&quot;).build());
}
}

You can also find a complete sample at https://github.com/spring-projects/spring-security-samples/blob/592e4f89d2819f255340518a2c91b7e6eab6ed3e/reactive/webflux/java/authentication/x509/src/main/java/example/WebfluxX509Application.java

huangapple
  • 本文由 发表于 2020年10月16日 02:11:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/64377448.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定