spring-security中的csrf防御机制(跨域请求伪造)
什么是csrf?
spring-servlet中配置csrf
<!-- Spring csrf 拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/login" />
<bean class="com.wangzhixuan.commons.csrf.CsrfInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
在类中声明Csrf拦截器,用来生成或去除CsrfToken
import java.io.IOException; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import com.wangzhixuan.commons.scan.ExceptionResolver;
import com.wangzhixuan.commons.utils.WebUtils; /**
* Csrf拦截器,用来生成或去除CsrfToken
*
* @author L.cm
*/
public class CsrfInterceptor extends HandlerInterceptorAdapter {
private static final Logger logger = LogManager.getLogger(ExceptionResolver.class); @Autowired
private CsrfTokenRepository csrfTokenRepository; @Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HandlerMethod handlerMethod = (HandlerMethod) handler;
// 非控制器请求直接跳出
if (!(handler instanceof HandlerMethod)) {
return true;
}
CsrfToken csrfToken = handlerMethod.getMethodAnnotation(CsrfToken.class);
// 判断是否含有@CsrfToken注解
if (null == csrfToken) {
return true;
}
// create、remove同时为true时异常
if (csrfToken.create() && csrfToken.remove()) {
logger.error("CsrfToken attr create and remove can Not at the same time to true!");
return renderError(request, response, Boolean.FALSE, "CsrfToken attr create and remove can Not at the same time to true!");
}
// 创建
if (csrfToken.create()) {
CsrfTokenBean token = csrfTokenRepository.generateToken(request);
csrfTokenRepository.saveToken(token, request, response);
// 缓存一个表单页面地址的url
csrfTokenRepository.cacheUrl(request, response);
request.setAttribute(token.getParameterName(), token);
return true;
}
// 判断是否ajax请求
boolean isAjax = WebUtils.isAjax(handlerMethod);
// 校验,并且清除
CsrfTokenBean tokenBean = csrfTokenRepository.loadToken(request);
if (tokenBean == null) {
return renderError(request, response, isAjax, "CsrfToken is null!");
}
String actualToken = request.getHeader(tokenBean.getHeaderName());
if (actualToken == null) {
actualToken = request.getParameter(tokenBean.getParameterName());
}
if (!tokenBean.getToken().equals(actualToken)) {
return renderError(request, response, isAjax, "CsrfToken not eq!");
}
return true;
} private boolean renderError(HttpServletRequest request, HttpServletResponse response,
boolean isAjax, String message) throws IOException {
// 获取缓存的cacheUrl
String cachedUrl = csrfTokenRepository.getRemoveCacheUrl(request, response);
// ajax请求直接抛出异常,因为{@link ExceptionResolver}会去处理
if (isAjax) {
throw new RuntimeException(message);
}
// 非ajax CsrfToken校验异常,先清理token
csrfTokenRepository.saveToken(null, request, response);
logger.info("Csrf[redirectUrl]:\t" + cachedUrl);
response.sendRedirect(cachedUrl);
return false;
} /**
* 用于清理@CsrfToken保证只能请求成功一次
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
HandlerMethod handlerMethod = (HandlerMethod) handler;
// 非控制器请求直接跳出
if (!(handler instanceof HandlerMethod)) {
return;
}
CsrfToken csrfToken = handlerMethod.getMethodAnnotation(CsrfToken.class);
if (csrfToken == null || !csrfToken.remove()) {
return;
}
csrfTokenRepository.getRemoveCacheUrl(request, response);
csrfTokenRepository.saveToken(null, request, response);
} }
声明Csrf过滤注解,通过标注来过滤对应的请求
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; /**
* Csrf过滤注解
* @author L.cm
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CsrfToken {
boolean create() default false;
boolean remove() default false;
}
建立实例对象(操作对象)
import java.io.Serializable;
import org.springframework.util.Assert;
public class CsrfTokenBean implements Serializable {
private static final long serialVersionUID = -6865031901744243607L;
private final String token;
private final String parameterName;
private final String headerName;
/**
* Creates a new instance
* @param headerName the HTTP header name to use
* @param parameterName the HTTP parameter name to use
* @param token the value of the token (i.e. expected value of the HTTP parameter of
* parametername).
*/
public CsrfTokenBean(String headerName, String parameterName, String token) {
Assert.hasLength(headerName, "headerName cannot be null or empty");
Assert.hasLength(parameterName, "parameterName cannot be null or empty");
Assert.hasLength(token, "token cannot be null or empty");
this.headerName = headerName;
this.parameterName = parameterName;
this.token = token;
}
public String getHeaderName() {
return this.headerName;
}
public String getParameterName() {
return this.parameterName;
}
public String getToken() {
return this.token;
}
}
过滤过程中需要的仓库
package com.wangzhixuan.commons.csrf; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; public interface CsrfTokenRepository {
/**
* Generates a {@link CsrfTokenBean}
*
* @param request the {@link HttpServletRequest} to use
* @return the {@link CsrfTokenBean} that was generated. Cannot be null.
*/
CsrfTokenBean generateToken(HttpServletRequest request); /**
* Saves the {@link CsrfTokenBean} using the {@link HttpServletRequest} and
* {@link HttpServletResponse}. If the {@link CsrfTokenBean} is null, it is the same as
* deleting it.
*
* @param token the {@link CsrfTokenBean} to save or null to delete
* @param request the {@link HttpServletRequest} to use
* @param response the {@link HttpServletResponse} to use
*/
void saveToken(CsrfTokenBean token, HttpServletRequest request,
HttpServletResponse response); /**
* Loads the expected {@link CsrfTokenBean} from the {@link HttpServletRequest}
*
* @param request the {@link HttpServletRequest} to use
* @return the {@link CsrfTokenBean} or null if none exists
*/
CsrfTokenBean loadToken(HttpServletRequest request); /**
* 缓存来源的url
* @param request request the {@link HttpServletRequest} to use
* @param response the {@link HttpServletResponse} to use
*/
void cacheUrl(HttpServletRequest request, HttpServletResponse response); /**
* 获取并清理来源的url
* @param request the {@link HttpServletRequest} to use
* @param response the {@link HttpServletResponse} to use
* @return 来源url
*/
String getRemoveCacheUrl(HttpServletRequest request, HttpServletResponse response); }
HttpSessionCsrfTokenRepository
package com.wangzhixuan.commons.csrf; import java.util.UUID; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession; import com.wangzhixuan.commons.utils.StringUtils; public final class HttpSessionCsrfTokenRepository implements CsrfTokenRepository {
private static final String DEFAULT_CSRF_PARAMETER_NAME = "_csrf";
private static final String DEFAULT_CSRF_HEADER_NAME = "X-CSRF-TOKEN";
private static final String DEFAULT_CSRF_TOKEN_ATTR_NAME = HttpSessionCsrfTokenRepository.class
.getName().concat(".CSRF_TOKEN");
private static final String DEFAULT_CACHE_URL_ATTR_NAME = HttpSessionCsrfTokenRepository.class
.getName().concat(".CACHE_URL"); private String parameterName = DEFAULT_CSRF_PARAMETER_NAME;
private String headerName = DEFAULT_CSRF_HEADER_NAME;
private String sessionAttributeName = DEFAULT_CSRF_TOKEN_ATTR_NAME;
private String cacheUrlAttributeName = DEFAULT_CACHE_URL_ATTR_NAME; /*
* (non-Javadoc)
*
* @see org.springframework.security.web.csrf.CsrfTokenRepository#saveToken(org.
* springframework .security.web.csrf.CsrfToken,
* javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
public void saveToken(CsrfTokenBean token, HttpServletRequest request,
HttpServletResponse response) {
if (token == null) {
HttpSession session = request.getSession(false);
if (session != null) {
session.removeAttribute(this.sessionAttributeName);
}
}
else {
HttpSession session = request.getSession();
session.setAttribute(this.sessionAttributeName, token);
}
} /*
* (non-Javadoc)
*
* @see
* org.springframework.security.web.csrf.CsrfTokenRepository#loadToken(javax.servlet
* .http.HttpServletRequest)
*/
public CsrfTokenBean loadToken(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session == null) {
return null;
}
return (CsrfTokenBean) session.getAttribute(this.sessionAttributeName);
} /*
* (non-Javadoc)
*
* @see org.springframework.security.web.csrf.CsrfTokenRepository#generateToken(javax.
* servlet .http.HttpServletRequest)
*/
public CsrfTokenBean generateToken(HttpServletRequest request) {
return new CsrfTokenBean(this.headerName, this.parameterName,
createNewToken());
} private String createNewToken() {
return UUID.randomUUID().toString();
} @Override
public void cacheUrl(HttpServletRequest request, HttpServletResponse response) {
String queryString = request.getQueryString();
// 被拦截前的请求URL
String redirectUrl = request.getRequestURI();
if (StringUtils.isNotBlank(queryString)) {
redirectUrl = redirectUrl.concat("?").concat(queryString);
}
HttpSession session = request.getSession();
session.setAttribute(this.cacheUrlAttributeName, redirectUrl);
} @Override
public String getRemoveCacheUrl(HttpServletRequest request, HttpServletResponse response) {
HttpSession session = request.getSession(false);
if (session == null) {
return null;
}
String redirectUrl = (String) session.getAttribute(this.cacheUrlAttributeName);
if (StringUtils.isBlank(redirectUrl)) {
return null;
}
session.removeAttribute(this.cacheUrlAttributeName);
return redirectUrl;
} }
spring-security中的csrf防御机制(跨域请求伪造)的更多相关文章
- spring boot 中 2.X 的跨域请求
解决跨域: @Configuration @EnableAutoConfiguration public class ZooConfiguration { @Bean public FilterReg ...
- Spring Boot Web应用开发 CORS 跨域请求支持:
Spring Boot Web应用开发 CORS 跨域请求支持: 一.Web开发经常会遇到跨域问题,解决方案有:jsonp,iframe,CORS等等CORS与JSONP相比 1. JSONP只能实现 ...
- js中ajax如何解决跨域请求
js中ajax如何解决跨域请求,在讲这个问题之前先解释几个名词 1.跨域请求 所有的浏览器都是同源策略,这个策略能保证页面脚本资源和cookie安全 ,浏览器隔离了来自不同源的请求,防上跨域不安全的操 ...
- Django框架 之 跨域请求伪造
Django框架 之 跨域请求伪造 浏览目录 同源策略与Jsonp 同源策略 Jsonp jQuery对JSONP的实现 CORS 简介 两种请求 同源策略与Jsonp 同源策略 同源策略(Same ...
- CSRF/XSRF 跨站请求伪造
CSRF/XSRF 跨站请求伪造 CSRF(Cross Site Request Forgery, 跨站域请求伪造)也称 XSRF, 是一种网络的攻击方式,它在 2007 年曾被列为互联网 20 大安 ...
- web 安全 & web 攻防: XSS(跨站脚本攻击)和 CSRF(跨站请求伪造)
web 安全 & web 攻防: XSS(跨站脚本攻击)和 CSRF(跨站请求伪造) XSS(跨站脚本攻击)和CSRF(跨站请求伪造) Cross-site Scripting (XSS) h ...
- ValidateAntiForgeryToken 防止CSRF(跨网站请求伪造)
用途:防止CSRF(跨网站请求伪造). 用法:在View->Form表单中:<%:Html.AntiForgeryToken()%> 在Controller->Action动作 ...
- revel框架教程之CSRF(跨站请求伪造)保护
revel框架教程之CSRF(跨站请求伪造)保护 CSRF是什么?请看这篇博文“浅谈CSRF攻击方式”,说的非常清楚. 现在做网站敢不防CSRF的我猜只有两种情况,一是没什么人访问,二是局域网应用.山 ...
- CSRF(跨站请求伪造)攻击
CSRF(跨站请求伪造)攻击 CSRF(Cross Site Request Forgery,跨站请求伪造)是一种近年来才逐渐被大众了解的网络攻击方式,又被称为One-Click Attack或Ses ...
随机推荐
- ORA-22992:没法使用从远程表选择的LOB定位器
OLB 问题 ORA-22992:没法使用从远程表选择的LOB定位器 Create global temporary table temp on commit preserve rows as sel ...
- Filezilla配置FTP中的坑以及出坑办法
做本科生助教,老板让配置一个FTP传资料交作业,找了一台Windows服务器捣鼓,开始按网上教程自己配置特别麻烦,何西西说用Filezilla比较方便,就去Filezilla官网下载了Filezill ...
- 大白话5分钟带你走进人工智能-第二十九节集成学习之随机森林随机方式 ,out of bag data及代码(2)
大白话5分钟带你走进人工智能-第二十九节集成学习之随机森林随机方式 ,out of bag data及代码(2) 上一节中我们讲解了随机森林的基本概念,本节的话我们讲解随机森 ...
- Linux下mysql启动失败
原因:强制重启服务器之后,发现mysql启动 失败 解决方法:强制清空 报错路径下的tmp文件,清空之后在tmp下面新建一个文件夹,文件夹的名字在你清空tmp之后启动mysql系统会给你提示 欧克,完 ...
- SpringBoot2.0 整合 RocketMQ ,实现请求异步处理
一.RocketMQ 1.架构图片 2.角色分类 (1).Broker RocketMQ 的核心,接收 Producer 发过来的消息.处理 Consumer 的消费消息请求.消息的持 久化存储.服务 ...
- GenericKeychain
KeychainItemWrapper是apple官方例子“GenericKeychain”里一个访问keychain常用操作的封装类,在官网上 下载了GenericKeychain项目后,只需要把“ ...
- php-fpm 高并发 参数调整
工作中经常会遇到会给客户配置服务器,其中有的客户还会有并发量要求,其中也会必须要用负载均衡承载压力的.增加服务器数量肯定能有效的提升服务器承载能力,但只有根据目前已有配置设置好单台服务器才能更好的发挥 ...
- Response.Redirect 产生的“正在中止线程”错误
Response.Redirect 产生的“正在中止线程”错误 今天在开发调试过程中,出现在一个 "正在中止线程"异常信息. 调用Response.Redirect()方法产生的, ...
- python 基础(三) 程序基本流程
流程控制 流程结构分为3种 顺序结构 分支结构 循环结构 一 分支结构 (1) 单一条件分支 主体结构: if 条件表达式: #为真得代码块 (2) 双向条件分支 主体结构: if 条件表达 ...
- Tinghua Data Mining 8
聚类 Clustering 根据评论信息做一些聚类,挖掘关系. bug 期望最大法 peek高峰的个数与高斯函数的个数不一定相同 Z隐含参数 不需要求 每个点属于哪个簇 类似于辅助线 跳板 借力 模型 ...