如何针对特定域名绕过已认证端点的Spring Security校验?
实现方案
该需求可正常实现,核心逻辑是在权限校验流程中增加「请求端点+来源域名」的组合判断分支,对符合条件的请求跳过JWT认证,具体操作如下:
步骤1:修改SecurityConfig,新增自定义放行规则
不需要把/sample-endpoint加入全局公开路径列表,通过自定义RequestMatcher匹配来自xyz.com的目标端点请求,单独放行:
SecurityConfig修改后代码
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { public static final String[] unauthedUrls = { "healthcheck","some-other-endpoint"} // 自定义匹配器:匹配来自xyz.com的/sample-endpoint请求 private RequestMatcher xyzDomainSampleRequestMatcher() { return request -> { // 先匹配请求路径 if (!"/sample-endpoint".equals(request.getRequestURI())) { return false; } // 匹配来源域名,优先取跨域请求的Origin头,无Origin头时取请求来源主机名 String origin = request.getHeader(HttpHeaders.ORIGIN); if (origin != null) { // 去除协议、端口后匹配主域名,需要匹配子域名可改为endsWith("xyz.com") return origin.replaceFirst("^https?://", "") .replaceFirst(":\\d+$", "") .equals("xyz.com"); } return "xyz.com".equals(request.getRemoteHost()); }; } @Override protected void configure(HttpSecurity http) throws Exception { http .httpBasic().disable() .csrf().disable() .cors().and() .headers().frameOptions().disable().and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() .exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and() .addFilterAfter(jwtSecurityFilter, UsernamePasswordAuthenticationFilter.class) .authorizeRequests() // 新增规则:匹配到xyz.com访问sample-endpoint的请求直接放行 .requestMatchers(xyzDomainSampleRequestMatcher()).permitAll() .antMatchers(unauthedUrls).permitAll() .anyRequest().authenticated(); } }
步骤2:修改JwtSecurityFilter,避免放行请求报错
需要调整过滤器的异常判断逻辑,避免符合放行条件的请求因为没有携带JWT被抛出异常、打印错误日志:
JwtSecurityFilter修改后代码(仅修改catch块部分)
private void setAuthenticationContext(HttpServletRequest request) { try { String token = getJwt(request); if (StringUtils.isBlank(token)) { throw new RuntimeException("Authorization token not provided"); } // 此处省略原有业务逻辑 } catch (Exception ex) { boolean needSkipAuth = false; if (request != null) { // 判断是否为公开路径 boolean isPublicUrl = Arrays.stream(SecurityConfig.unauthedUrls) .anyMatch(url -> request.getRequestURI().contains(url)); // 判断是否为xyz.com访问sample-endpoint的放行请求 boolean isXyzDomainRequest = "/sample-endpoint".equals(request.getRequestURI()); if (isXyzDomainRequest) { String origin = request.getHeader(HttpHeaders.ORIGIN); if (origin != null) { isXyzDomainRequest = origin.replaceFirst("^https?://", "") .replaceFirst(":\\d+$", "") .equals("xyz.com"); } else { isXyzDomainRequest = "xyz.com".equals(request.getRemoteHost()); } } needSkipAuth = isPublicUrl || isXyzDomainRequest; } if (needSkipAuth) { // 符合放行条件,直接返回不处理异常 return; } LOGGER.warn("Unable to authenticate request: {} {}", ex.getMessage(), request == null ? null : request.getRequestURI()); } }
注意事项
- 如果需要支持
xyz.com的所有子域名(如a.xyz.com、b.xyz.com),可以把域名匹配逻辑从equals("xyz.com")改为endsWith("xyz.com") - 如果是服务端之间的调用场景(无Origin头),建议配合IP白名单校验,提前解析
xyz.com对应的IP段进行匹配,避免域名伪造的安全问题 - 如果你使用的是Spring Security 5.7+版本,
WebSecurityConfigurerAdapter已经被标记为废弃,可以改为用SecurityFilterChain的方式实现配置,逻辑和上述方案一致
内容的提问来源于stack exchange,提问作者K17
相关产品推荐
相关产品推荐

