Spring Security:基于请求体的SOAP服务权限控制问题——过滤器中无法获取用户认证信息
问题分析与解决方案
首先,你遇到的Authentication为null的问题,核心原因是自定义过滤器的执行顺序不对——你把它加在了UsernamePasswordAuthenticationFilter之后,但如果你的认证方式不是表单登录(比如JWT、OAuth2等),这个过滤器根本不会触发,导致SecurityContextHolder还没被填充认证信息。就算是表单登录,也可能存在其他认证过滤器在更后面执行,所以你的过滤器拿到的还是空的上下文。
一、解决过滤器中获取Authentication的问题
1. 调整过滤器顺序
把自定义过滤器放在Spring Security的最后一个安全过滤器FilterSecurityInterceptor之后,确保所有认证流程都已经完成,SecurityContextHolder里已经有了用户的认证信息:
@Configuration public class MyAppConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { // 先配置你的认证规则(比如表单登录、JWT认证等) http.authorizeRequests() .anyRequest().authenticated() .and() // 将自定义过滤器放在FilterSecurityInterceptor之后 .addFilterAfter(new MyFilter(), FilterSecurityInterceptor.class); } }
如果你的项目用了特定的认证过滤器(比如JWT的JwtAuthenticationFilter),也可以直接指定放在它之后,这样更精准。
2. 确保RequestWrapper正确处理请求体
你的MyRequestWrapper需要正确缓存请求体内容,避免流被消费后后续过滤器/控制器无法读取。这里给你一个标准的实现:
public class MyRequestWrapper extends HttpServletRequestWrapper { private final String body; public MyRequestWrapper(HttpServletRequest request) throws IOException { super(request); // 读取并缓存请求体 StringBuilder sb = new StringBuilder(); try (BufferedReader reader = request.getReader()) { String line; while ((line = reader.readLine()) != null) { sb.append(line); } } body = sb.toString(); } public String getBody() { return body; } // 重写getReader和getInputStream,返回缓存的内容 @Override public BufferedReader getReader() throws IOException { return new BufferedReader(new StringReader(body)); } @Override public ServletInputStream getInputStream() throws IOException { final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8)); return new ServletInputStream() { @Override public int read() throws IOException { return byteArrayInputStream.read(); } @Override public boolean isFinished() { return byteArrayInputStream.available() == 0; } @Override public boolean isReady() { return true; } @Override public void setReadListener(ReadListener readListener) { // 无需实现,若需要可自行补充 } }; } }
二、针对SOAP权限控制的更优方案
其实对于SOAP服务的场景,用自定义过滤器不是最优雅的方式,更推荐使用Spring Security方法级安全,结合Spring-WS的端点方法来实现:
1. 启用方法级安全
在配置类上添加@EnableGlobalMethodSecurity(prePostEnabled = true):
@Configuration @EnableGlobalMethodSecurity(prePostEnabled = true) public class MyAppConfig extends WebSecurityConfigurerAdapter { // 其他配置... }
2. 在SOAP端点方法上使用@PreAuthorize
直接在处理SOAP请求的端点方法上,通过SpEL表达式判断请求体内容和用户角色:
@Endpoint public class MySoapEndpoint { @PayloadRoot(namespace = "http://your-namespace.com/soap", localPart = "YourRequest") @PreAuthorize("hasRole('MANAGER') or #request.searchKeyOnBody == '允许的关键词'") @ResponsePayload public YourResponse handleSoapRequest(@RequestPayload YourRequest request) { // 处理SOAP请求逻辑 return new YourResponse(); } }
这种方式的优势:
- 完全贴合Spring Security的设计,无需手动处理
SecurityContextHolder - 直接将权限规则与业务逻辑绑定,代码更清晰
- SpEL表达式可以直接访问请求对象的属性,无需手动解析XML/JSON请求体
额外注意事项
- 如果你的应用使用异步处理,需要设置
SecurityContextHolder的策略为MODE_INHERITABLETHREADLOCAL,确保子线程能继承认证上下文:SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
内容的提问来源于stack exchange,提问作者Mem
相关产品推荐
相关产品推荐

