8.Spring security中的HttpFirewall
文章目录
- *`HttpFirewall`*
- *8.1`HttpFirewall`简介*
- *8.2`HttpFirewall`严格模式*
- *8.2.1`rejectForbiddenHttpMethod`*
- *8.2.2`rejectedBlocklistedUrls`*
- *8.2.3`rejectedUntrustedHosts`*
- *8.2.4`isNormalized`*
- *8.2.5`containsOnlyPrintableAsciiCharacters`*
- *8.3`HttpFirewall`普通模式*
HttpFirewall
HttpFirewall
是spring security提供的HTTP防火墙,它可以用于拒绝潜在的危险请求或者包装这些请求进而控制其行为。HttpFirewall
被注入到FilterChainProxy
中,并在spring security过滤器链执行之前被触发。
8.1HttpFirewall
简介
Spring security中通过
HttpFirewall
来检查请求路径以及参数是否合法,如果合法,才会进入到过滤器链中进行处理。
public interface HttpFirewall {// 对请求对象进行检验并封装FirewalledRequest getFirewalledRequest(HttpServletRequest request) throws RequestRejectedException;// 对响应对象进行封装HttpServletResponse getFirewalledResponse(HttpServletResponse response);
}
FirewalledRequest
是封装后的请求类,但实际上该类只是在HttpServletRequestWrapper
的基础上增加了reset
方法。当spring security过滤器链执行完毕时,由FilterChainProxy
负责调用该方法,以便重置全部或者部分属性。
FirewalledResponse
是封装后的响应类,该类主要重写了sendRedirect
、setHeader
、addHeader
以及addCookie
四个方法,在每一个方法中都对其参数进行校验,以确保参数中不含有\r
和\n
。
HttpFirewall
一共有两个实现类:
DefaultHttpFirewall
:虽然名字中包含default,但这并不是框架默认使用的HTTP防火墙,它只是一个检查相对宽松的防火墙。StrictHttpFirewall
:这是一个检查严格的HTTP防火墙,默认即此。
HttpFirewall
中对请求的合法性校验在FilterChainProxy#doFilterInternal
方法中触发。
需要注意的是
HttpFirewall
的配置位置,在spring security框架中有两个地方涉及了HttpFirewall
实例的获取:
- 在
FilterChainProxy
属性定义中,默认创建的HttpFirewall
实例就是StrictHttpFirewall
。FilterChainProxy
是在WebSecurity#performBuild
方法中构建的,而WebSecurity
实现了ApplicationContextAware
接口,并实现了接口中的setApplicationContext
方法,在该方法中,从spring容器中查找到HttpFirewall
对并赋值给httpFirewall
属性。最终在performBuild
方法中,将FilterChainProxy
对象构建成功后,如果httpFirewall
不为空,就把httpFirewall
配置给FilterChainProxy
对象。因此,如果spring容器中存在
HttpFirewall
实例,则最终使用spring容器提供的实例;如果不存在,则使用FilterChainProxy
中默认定义的StrictHttpFirewall
。
8.2HttpFirewall
严格模式
在
FilterChainProxy#doFilterInternal
中触发请求校验的方法如下:
private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {// 请求的校验主要是在getFirewalledRequest方法中完成的。在进入spring security过滤器链之前,请求对象和响应对象// 都分别换成FirewalledRequest和FirewalledResponse了FirewalledRequest firewallRequest = this.firewall.getFirewalledRequest((HttpServletRequest) request);HttpServletResponse firewallResponse = this.firewall.getFirewalledResponse((HttpServletResponse) response);List<Filter> filters = getFilters(firewallRequest);// 省略其他virtualFilterChain.doFilter(firewallRequest, firewallResponse);
}
需要注意的是,无论是
FirewalledRequest
还是FirewalledResponse
,在经过spring security过滤器链的时候,还会通过装饰器模式增强其功能,所以开发者最终在接口中拿到的HttpServletRequest
和HttpServletResponse
对象,并不是这里的FirewalledRequest
和FirewalledResponse
。
重点分析getFirewalledRequest
方法:
// StrictHttpFirewall
@Override
public FirewalledRequest getFirewalledRequest(HttpServletRequest request) throws RequestRejectedException {// 校验请求方法是否合法rejectForbiddenHttpMethod(request);// 校验请求中的非法字符rejectedBlocklistedUrls(request);// 校验主机信息rejectedUntrustedHosts(request);// 判断参数格式是否合法if (!isNormalized(request)) {throw new RequestRejectedException("The request was rejected because the URL was not normalized.");}String requestUri = request.getRequestURI();// 判断请求字符是否合法if (!containsOnlyPrintableAsciiCharacters(requestUri)) {throw new RequestRejectedException("The requestURI was rejected because it can only contain printable ASCII characters.");}return new StrictFirewalledRequest(request);
}
接下来会逐一分析这五个校验方法。
8.2.1rejectForbiddenHttpMethod
主要用来判断请求方法是否合法:
private void rejectForbiddenHttpMethod(HttpServletRequest request) {if (this.allowedHttpMethods == ALLOW_ANY_HTTP_METHOD) {return;}if (!this.allowedHttpMethods.contains(request.getMethod())) {throw new RequestRejectedException("The request was rejected because the HTTP method \"" + request.getMethod()+ "\" was not included within the list of allowed HTTP methods " + this.allowedHttpMethods);}
}
allowedHttpMethods
是一个Set
集合,默认情况下该集合中包含七个常见的方法:DELETE
、GET
、HEAD
、OPTIONS
、PATCH
、POST
、PUT
,ALLOW_ANY_HTTP_METHOD
变量默认情况下则是一个空的Set
集合。
开发者可以根据实际需求修改allowedHttpMethods
变量的值,进而调整允许的请求方法。
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {/*** 第一种方式:通过设置allowedHttpMethods进行修改,此时只允许POST请求。*/// @Bean// HttpFirewall httpFirewall() {// StrictHttpFirewall strictHttpFirewall = new StrictHttpFirewall();// Set<String> allowedHttpMethods = new HashSet<>();// allowedHttpMethods.add(HttpMethod.POST.name());// strictHttpFirewall.setAllowedHttpMethods(allowedHttpMethods);// return strictHttpFirewall;// }/*** 第二种方式:设置参数为true,让allowedHttpMethods等于ALLOW_ANY_HTTP_METHOD,进而允许所有请求通过。*/@BeanHttpFirewall httpFirewall() {StrictHttpFirewall strictHttpFirewall = new StrictHttpFirewall();strictHttpFirewall.setUnsafeAllowAnyHttpMethod(true);return strictHttpFirewall;}// 省略其他
}
8.2.2rejectedBlocklistedUrls
主要用来校验请求URL是否规范:
- 如果请求URL地址中在编码之前或者之后,包含了分号,即
;
、%3b
、%3B
,则该请求会被拒绝,可以通过setAllowSemicolon
方法开启或者关闭这一规则。- 如果请求URL地址中在编码之前或者之后,包含了斜杠,即
%2f
、%2F
,则该请求会被拒绝,可以通过setAllowUrlEncodedSlash
方法开启或者关闭这一规则。- 如果请求URL地址中在编码之前或者之后,包含了反斜杠,即
\\
、%5c
、%5C
,则该请求会被拒绝,可以通过setAllowBackSlash
方法开启或者关闭这一规则。- 如果请求URL地址中在编码之后包含了
%25
或者在编码之前包含了%
,则该请求会被拒绝,可以通过setAllowUrlEncodedPercent
方法开启或者关闭这一规则。- 如果请求URL在编码后包含了英文句号
%2e
或者%2E
,则该请求会被拒绝,可以通过setAllowUrlEncodedPeriod
方法开启或者关闭这一规则。
private void rejectedBlocklistedUrls(HttpServletRequest request) {// 校验编码后的请求地址for (String forbidden : this.encodedUrlBlocklist) {// 主要是校验了contextPath和requestURI两个属性,这两个属性是客户端传递来的字符串,未做任何更改if (encodedUrlContains(request, forbidden)) {throw new RequestRejectedException("The request was rejected because the URL contained a potentially malicious String \""+ forbidden + "\"");}}// 校验解码后的请求地址for (String forbidden : this.decodedUrlBlocklist) {// 主要校验了servletPath、pathInfo两个属性,需要注意的是,这个是经过解码后的if (decodedUrlContains(request, forbidden)) {throw new RequestRejectedException("The request was rejected because the URL contained a potentially malicious String \""+ forbidden + "\"");}}
}
8.2.3rejectedUntrustedHosts
主要用来校验host是否受信任:
private void rejectedUntrustedHosts(HttpServletRequest request) {String serverName = request.getServerName();if (serverName != null && !this.allowedHostnames.test(serverName)) {throw new RequestRejectedException("The request was rejected because the domain " + serverName + " is untrusted.");}
}
allowedHostnames
默认总是返回true
,即默认信任所有的host,可以根据实际需求对此进行配置:
@Bean
HttpFirewall httpFirewall() {StrictHttpFirewall strictHttpFirewall = new StrictHttpFirewall();strictHttpFirewall.setAllowedHostnames((hostname) -> hostname.equalsIgnoreCase("local.javaboy.org"));return strictHttpFirewall;
}
8.2.4isNormalized
主要用来检查请求地址是否规范,即不包含"./"、"/…/“以及”/."三种字符。
private static boolean isNormalized(HttpServletRequest request) {// 对requestURI、contextPath、servletPath以及pathInfo分别进行了校验if (!isNormalized(request.getRequestURI())) {return false;}if (!isNormalized(request.getContextPath())) {return false;}if (!isNormalized(request.getServletPath())) {return false;}if (!isNormalized(request.getPathInfo())) {return false;}return true;
}
8.2.5containsOnlyPrintableAsciiCharacters
用来校验请求地址中是否包含不可打印的ASCII字符。
private static boolean containsOnlyPrintableAsciiCharacters(String uri) {int length = uri.length();for (int i = 0; i < length; i++) {char ch = uri.charAt(i);if (ch < '\u0020' || ch > '\u007e') {return false;}}return true;
}
StrictHttpFirewall
中的校验规则,前三种可以通过相关方法调整,后面两种不可调整。
8.3HttpFirewall
普通模式
HttpFirewall
普通模式就是使用DefaultHttpFirewall
,该类的校验规则就要简单很多:
// DefaultHttpFirewall
@Override
public FirewalledRequest getFirewalledRequest(HttpServletRequest request) throws RequestRejectedException {// 构建RequestWrapper对象对原始请求中的功能进行了增强,例如将请求地址中的//格式化为/,以及将请求中的servletPath和// pathInfo中用分号隔开的参数提取出来,只保留路径即可FirewalledRequest firewalledRequest = new RequestWrapper(request);if (!isNormalized(firewalledRequest.getServletPath()) || !isNormalized(firewalledRequest.getPathInfo())) {throw new RequestRejectedException("Un-normalized paths are not supported: " + firewalledRequest.getServletPath()+ ((firewalledRequest.getPathInfo() != null) ? firewalledRequest.getPathInfo() : ""));}String requestURI = firewalledRequest.getRequestURI();// 判断requestURI中是否包含编码后的斜杠if (containsInvalidUrlEncodedSlash(requestURI)) {throw new RequestRejectedException("The requestURI cannot contain encoded slash. Got " + requestURI);}return firewalledRequest;
}
一般来说,并不建议在项目中使用
DefaultHttpFirewall
,如果一定要用,只需要提供一个实例即可:
@Bean
HttpFirewall httpFirewall() {return new DefaultHttpFirewall();
}
8.Spring security中的HttpFirewall相关推荐
- Spring Security中文文档
Spring Security中文文档 来源:https://www.springcloud.cc/spring-security.html#overall-architecture 作者 Ben A ...
- Spring Security 中最流行的权限管理模型!
前面和大家说了 ACL,讲了理论,也给了一个完整的案例,相信小伙伴们对于 ACL 权限控制模型都已经比较了解了. 本文我要和大家聊一聊另外一个非常流行的权限管理模型,那就是 RBAC. 1.RBAC ...
- 一起搞清楚 Spring Security 中的 UserDetails
点击蓝色"程序猿DD"关注我 回复"资源"获取独家整理的学习资料! 170元买400元书的机会又来啦! 1. 前言 前一篇介绍了 Spring Security ...
- springsecurity sessionregistry session共享_要学就学透彻!Spring Security 中 CSRF 防御源码解析...
今日干货 刚刚发表查看:66666回复:666 公众号后台回复 ssm,免费获取松哥纯手敲的 SSM 框架学习干货. 上篇文章松哥和大家聊了什么是 CSRF 攻击,以及 CSRF 攻击要如何防御.主要 ...
- Spring Security 中取得 RememberMe 的 cookie 值
为什么80%的码农都做不了架构师?>>> Spring Security 中的 RememberMe 对应的 cookie 名称是可配置的--相信一般情况下大家也不会使用那个默 ...
- Http基本身份验证在Spring Security中如何工作?
在上一篇文章中,您学习了如何在基于Spring安全性的Java应用程序中启用Http基本身份验证 ,现在,我们将进一步进一步了解http基本身份验证在Spring安全性中的工作原理. 如果您还记得的话 ...
- Spring Security中的SecurityContext和SecurityContextHolder是什么?
SecurityContext和SecurityContextHolder是Spring Security的两个基本类. SecurityContext用于存储当前经过身份验证的用户的详细信息,也称为 ...
- 如何使用Java和XML Config在Spring Security中启用HTTP基本身份验证
在上一篇文章中,我向您展示了如何在Java应用程序中启用Spring安全性 ,今天我们将讨论如何使用Spring Security 在Java Web应用程序中启用Basic HTTP身份验证 . 如 ...
- java按钮权限控制_详解Spring Security 中的四种权限控制方式
Spring Security 中对于权限控制默认已经提供了很多了,但是,一个优秀的框架必须具备良好的扩展性,恰好,Spring Security 的扩展性就非常棒,我们既可以使用 Spring Se ...
最新文章
- 属于窄带噪声的是热噪声_时钟201系列: 非相位噪声的情况 (第一篇)
- Windows核心编程 第27章 硬件输入模型和局部输入状态
- 拦截器 java_在Java后端如何添加拦截器
- python安装pyinstaller出现错误_pyinstaller 3.6版本通过pip安装失败的解决办法(推荐)...
- rsync 服务快速部署手册
- maven jar包冲突的发现与解决[工具篇]
- Python3_基础部分_第一个Python程序
- python中button中command_tkinter模块的button控件不点击也运行command
- php 小说采集系统,YGBOOK小说采集系统 php版 v1.4
- 久其报表软件基本操作指引
- matlab找零钱,自动售货系统
- APIO 2017 考拉的游戏 题解
- iOS开发-ios7样式绕圈活动指示器(自定义Activity Indicator View)
- 【金融】新成立基金建仓时点、行业分布与市场行情关系探究
- REDIS哨兵【Sentinel】模式+哨兵的核心知识点+redis哨兵主从切换的数据丢失问题+上一章铺垫的【异步复制数据丢失问题】+【集群脑裂】
- 计算机毕业设计ssm基于ssm流浪宠物领养系统8xg84系统+程序+源码+lw+远程部署
- es时间对象(Date)
- 联想微型计算机安装系统,联想笔记本做系统,手把手教你联想笔记本安装win10系统...
- 劳动合同到期提醒怎么设置,怎么在便签上设置合同到期提醒
- 【第五届集创赛备赛】一、黄乐天老师赛事宣讲及各个赛题分析
热门文章
- python 数据库性能提升 - TCP聊天+传输文件服务器服务器套接字v2.7
- ICCV 2021 | High-Fidelity Pluralistic Image Completion with Transformers 阅读笔记(部分翻译)
- 程序员如何正确应对压力
- Dockerfile RUN指令 语法解析
- 解决No architectures to compile for...
- Appium学习日记(一)——Appium工作原理及其主要组件
- redis根据前缀批量查找key
- 【Paper Note】基于情感分析和关系网络的影视产品评论数据文本挖掘研究
- 20189215 2018-2019-2 《密码与安全新技术专题》课程总结
- 最简单的JAVA解法-----无聊的逗