【发布时间】:2026-01-13 19:50:01
【问题描述】:
Java 11、Spring Boot 2.1.3、Spring 5.1.5
我有一个 Spring Boot 项目,其中某些端点由 API 密钥保护。目前使用此代码可以正常工作:
@Component("securityConfig")
@ConfigurationProperties("project.security")
@EnableWebSecurity
@Order(1)
public class SecurityJavaConfig extends WebSecurityConfigurerAdapter {
private static final Logger LOG = LoggerFactory.getLogger(SecurityJavaConfig.class);
private static final String API_KEY_HEADER = "x-api-key";
private String apiKey;
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
APIKeyFilter filter = new APIKeyFilter(API_KEY_HEADER);
filter.setAuthenticationManager(authentication -> {
String apiKey = (String) authentication.getPrincipal();
if (this.apiKey != null && !this.apiKey.isEmpty() && this.apiKey.equals(apiKey)) {
authentication.setAuthenticated(true);
return authentication;
} else {
throw new BadCredentialsException("Access Denied.");
}
});
httpSecurity
.antMatcher("/v1/**")
.csrf()
.disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilter(filter)
.authorizeRequests()
.anyRequest()
.authenticated();
}
}
这成功地需要一个包含 API 密钥的标头,但仅适用于 /v1/... 中的端点
我有一个新要求,需要证书进行身份验证。我按照这些指南在我的项目中设置了 X.509 身份验证:
不过,我遇到了一些问题:
- 始终需要证书,而不仅仅是
/v1/*端点 - API 密钥过滤器不再起作用
这是我更新的application.properties 文件:
server.port=8443
server.ssl.enabled=true
server.ssl.key-store-type=PKCS12
server.ssl.key-store=classpath:cert/keyStore.p12
server.ssl.key-store-password=<redacted>
server.ssl.trust-store=classpath:cert/trustStore.jks
server.ssl.trust-store-password=<redacted>
server.ssl.trust-store-type=JKS
server.ssl.client-auth=need
还有我更新的SecurityJavaConfig 类:
@Component("securityConfig")
@ConfigurationProperties("project.security")
@EnableWebSecurity
@Order(1) //Safety first.
public class SecurityJavaConfig extends WebSecurityConfigurerAdapter {
private static final Logger LOG = LoggerFactory.getLogger(SecurityJavaConfig.class);
private static final String API_KEY_HEADER = "x-api-key";
private static final RequestMatcher PUBLIC_URLS = new OrRequestMatcher(
new AntPathRequestMatcher("/ping")
);
private String apiKey;
@Value("#{'${project.security.x509clients}'.split(',')}")
private List<String> x509clients;
@Override
public void configure(final WebSecurity web) {
web.ignoring().requestMatchers(PUBLIC_URLS);
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
APIKeyFilter filter = new APIKeyFilter(API_KEY_HEADER);
filter.setAuthenticationManager(authentication -> {
String apiKey = (String) authentication.getPrincipal();
if (this.apiKey != null && !this.apiKey.isEmpty() && this.apiKey.equals(apiKey)) {
authentication.setAuthenticated(true);
return authentication;
} else {
throw new BadCredentialsException("Access Denied.");
}
});
httpSecurity
.antMatcher("/v1/**")
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilter(filter)
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.x509()
.subjectPrincipalRegex("CN=(.*?)(?:,|$)")
.userDetailsService(userDetailsService())
.and()
.csrf()
.disable();
}
@Bean
public UserDetailsService userDetailsService() {
return new UserDetailsService() {
@Override
public UserDetails loadUserByUsername(String username) {
if (x509clients.contains(username)) {
return new User(
username,
"",
AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER")
);
} else {
throw new UsernameNotFoundException("Access Denied.");
}
}
};
}
}
我感觉httpSecurity 方法中的链顺序存在问题,但我不确定那是什么。另外,我尝试添加第二个configure() 方法,忽略PUBLIC_URLS,但这没有任何帮助。我还尝试将server.ssl.client-auth 更改为want,但它允许客户端连接到我的/v1/* API,根本不需要证书。
不需要证书的示例输出:
$ curl -k -X GET https://localhost:8443/ping
curl: (35) error:1401E412:SSL routines:CONNECT_CR_FINISHED:sslv3 alert bad certificate
需要证书和 api-key 的示例输出:
$ curl -k -X GET https://localhost:8443/v1/clients
curl: (35) error:1401E412:SSL routines:CONNECT_CR_FINISHED:sslv3 alert bad certificate
$ curl -k -X GET https://localhost:8443/v1/clients --cert mycert.crt --key mypk.pem
[{"clientId":1,"clientName":"Sample Client"}]
【问题讨论】:
-
要支持两种身份验证,您需要创建自己的身份验证提供程序并自己处理。
标签: java spring-boot spring-security x509certificate api-key