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 ...
随机推荐
- 原生JS操作 table object HTMLTableSectionElement 对象,获取行数
<tbody id="infoTab"> <tr class="fomat"> <td class="blank&quo ...
- java读取properties文件的配置信息
项目开发中,我们一般来向 application.properties 文件中放一些全局配置变量,以便程序中读取使用,本篇内容来演示从properties配置文件中读取键值. 当然,我们不一定写入 a ...
- HTTP请求信息和响应信息的格式
请求: (1)请求行信息 Request URL: 发起请求的路径 Request Method:请求的方式(get post head put delete trace options等) ...
- Android studio改包名
http://www.cnblogs.com/Kyouhui/p/4632813.html Android Studio,咱们开发安卓的利器,自推出就受到移动开发者的追捧,但一路走来,大家谈到他,充满 ...
- Root Motion深度解析[Unity]
http://blog.csdn.net/cubesky/article/details/39478207 在很多动画当中,模型的位置.角度往往会发生变化,我们需要决定是否将模型再动画中发生的这些变换 ...
- PHP保留小数的相关方法
结合一下网上的例子 $num = 10.4567; //第一种:利用round()对浮点数进行四舍五入 但是这个如果没有两位小数也不会"两位精度" echo round($num, ...
- 51nod 1562 玻璃切割 (set)
#include<stdio.h> #include<iostream> #include<set> using namespace std; typedef lo ...
- python操作rabbitmq实现广播效果
生产方(Fanout_Publisher.py) # __author__ = 'STEVEN' import pika #开启socket connection = pika.BlockingCon ...
- scrapy分布式原理
scrapy分布式原理 关于Scrapy工作流程回顾 Scrapy单机架构 上图的架构其实就是一种单机架构,只在本机维护一个爬取队列,Scheduler进行调度,而要实现多态服务器共同爬取数据关键 ...
- Codeforces 1142B(倍增)
1.先预处理出在循环中某数前面的数是谁. 2.读入a数列时贪心选取最晚的父亲. 3.链上倍增预处理二进制祖先. 4.对于每个位置,预处理第n-1个祖先位置最早要从哪里开始,技巧上再顺手与前一位的最早位 ...