You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何针对特定端点忽略SSL客户端认证(mTLS)

问题:Spring Boot mTLS配置下实现部分API公开访问

背景

应用采用客户端-服务器双向认证(mTLS),核心配置如下:

# application.properties
server.ssl.enabled=true
server.ssl.protocol=TLS
server.ssl.enabled-protocols=TLSv1.3
server.ssl.client-auth=need

# ... 以及密钥库相关配置

核心需求

多数API在当前mTLS配置下正常工作,但需将特定API(如/hello)设为公开,无需客户端证书即可访问;其余API(如/secret)仍需强制mTLS认证。

问题现象

尝试通过HttpSecurity配置过滤器链后,无客户端证书的请求访问/hello时直接返回SSL错误,无法达到公开访问的预期。

测试方式

使用curl命令验证:

# 带客户端证书访问(正常返回Hello)
curl https://localhost:8080/hello -k -v --cert ./client.cert --key ./client.key

# 不带客户端证书访问(返回SSL错误)
curl https://localhost:8080/hello -k -v

参数说明:

  • -k:忽略自签名服务器证书
  • -v:输出详细请求日志
  • --cert/--key:指定客户端证书及密钥文件

已尝试的无效方案

  • 将server.ssl.client-auth设为want或none:所有API均变为公开,不符合部分API需认证的需求;
  • 配置单条SecurityFilterChain:仅设置/hello为permitAll(),但mTLS认证在Spring Security过滤器之前的SSL握手阶段就已生效,无证书请求直接被拦截;
  • 配置两条未指定优先级的SecurityFilterChain:无法区分公开与需认证的API,仍呈现全公开或全认证的结果。

后续尝试的配置代码:

@Bean
SecurityFilterChain publicApiSecurityFilterChain(HttpSecurity http) throws Exception {
    http.authorizeRequests().antMatchers("/hello").permitAll();
    return http.build();
}

@Bean
SecurityFilterChain privateApiSecurityFilterChain(HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
            .antMatchers("/**")
            .authenticated().and()
        .csrf().disable()
        .x509()
            .subjectPrincipalRegex("CN=(.*?)(?:,|$)")
            .userDetailsService(userDetailsService());
    return http.build();
}

@Bean
UserDetailsService userDetailsService() {
    return username -> {
        if (username == null || username.isEmpty()) {
            throw new UsernameNotFoundException(username);
        }
        return new User(username, "", AuthorityUtils.createAuthorityList("ROLE_SSL_USER"));
    };
}

版本信息

  • Spring Boot:2.7.7(计划迁移至v3)
  • Java:17

解决方案

问题根源:当server.ssl.client-auth=need时,SSL握手阶段强制要求客户端证书,请求无法到达Spring Security过滤器链;设为want时,SSL层接受有/无证书的请求,此时可通过Spring Security区分验证逻辑。

1. 修改application.properties配置

将客户端认证模式改为want,允许SSL层接受无证书请求:

server.ssl.client-auth=want

2. 配置带优先级的双SecurityFilterChain

通过@Order注解指定过滤器链执行顺序,确保公开API的规则优先生效:

@Configuration
@EnableWebSecurity(debug = true)
public class WebSecurityConfig {

    // 公开API过滤器链,优先级更高
    @Bean
    @Order(1)
    SecurityFilterChain publicApiFilterChain(HttpSecurity http) throws Exception {
        http
            .requestMatchers(matchers -> matchers.antMatchers("/hello"))
            .authorizeHttpRequests(auth -> auth.anyRequest().permitAll())
            .csrf().disable()
            .cors().disable()
            .httpBasic().disable()
            .formLogin().disable();
        return http.build();
    }

    // 私有API过滤器链,强制mTLS认证
    @Bean
    @Order(2)
    SecurityFilterChain privateApiFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
            .csrf().disable()
            .cors().disable()
            .httpBasic().disable()
            .formLogin().disable()
            .x509(x509 -> x509
                .subjectPrincipalRegex("CN=(.*?)(?:,|$)")
                .userDetailsService(userDetailsService()));
        return http.build();
    }

    @Bean
    UserDetailsService userDetailsService() {
        return username -> {
            if (username == null || username.isEmpty()) {
                throw new UsernameNotFoundException(username);
            }
            return new User(username, "", AuthorityUtils.createAuthorityList("ROLE_SSL_USER"));
        };
    }
}

关键说明

  • @Order(1):让公开API的过滤器链优先执行,匹配到/hello时直接放行,不进入后续认证逻辑;
  • requestMatchers:限定每条过滤器链仅处理指定路径的请求,避免规则冲突;
  • x509()配置:在私有API的过滤器链中启用X.509证书认证,只有携带有效客户端证书的请求才能通过。

验证效果:

  • 访问/hello:无需客户端证书即可正常返回内容;
  • 访问/secret:必须携带有效客户端证书,否则返回401未授权。

内容的提问来源于stack exchange,提问作者Henry Fung

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 19:43:16