个人记录,如有错误,敬请指出

项目环境:spring boot+shiro+jwt

简单方式直接通过nginx对Referer进行校验拦截即可,本文不做讲解

1、创建CsrfFilter

import org.springframework.web.filter.OncePerRequestFilter;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;public class CsrfFilter extends OncePerRequestFilter {private static final String CSRF_TOKEN = "X-Csrf-Token";/*** 需要排除的接口*/private static final List<String> ignoreCsrfList = new ArrayList<String>();private static final List<String> accessRequestList = Arrays.asList(new String[]{"GET", "HEAD", "TRACE", "OPTIONS"});private Collection<String> domains;static {ignoreCsrfList.add("/sys/login");ignoreCsrfList.add("/sys/logout");}public CsrfFilter(Collection<String> domains) {this.domains = domains;}@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {String csrfToken = null;//获取cookie中Csrf-Token的值Cookie[] cookies = request.getCookies();if (cookies != null && cookies.length > 0) {for (Cookie cookie : cookies) {if (cookie.getName().equals(CSRF_TOKEN)) {csrfToken = cookie.getValue();}}}boolean missingToken = csrfToken == null;// GET 等方式不用提供Token,自动放行,不能用于修改数据。修改数据必须使用 POST、PUT、DELETE、PATCH 方式并且Referer要合法。if (accessRequestList.contains(request.getMethod())) {filterChain.doFilter(request, response);return;}String uri = request.getRequestURI();if (uri == null || verifyIgnoreApi(uri)) {filterChain.doFilter(request, response);return;}if (!domains.isEmpty() && !verifyDomains(request)) {response.sendError(HttpServletResponse.SC_FORBIDDEN, "CSRF Protection: Referer Illegal");return;}if (!verifyToken(request, csrfToken)) {response.sendError(HttpServletResponse.SC_FORBIDDEN, missingToken ? "CSRF Token Missing" : "CSRF Token Invalid");return;}filterChain.doFilter(request, response);}private boolean verifyDomains(HttpServletRequest request) {// 从 HTTP 头中取得 Referer 值String referer = request.getHeader("Referer");// 判断 Referer 是否以 合法的域名 开头。if (referer != null) {if (referer.indexOf("://") > 0) {referer = referer.substring(referer.indexOf("://") + 3);}if (referer.indexOf("/") > 0) {referer = referer.substring(0, referer.indexOf("/"));}if (referer.indexOf(":") > 0) {referer = referer.substring(0, referer.indexOf(":"));}for (String domain : domains) {if (referer.endsWith(domain)) {return true;}}}return false;}private boolean verifyToken(HttpServletRequest request, String token) {if (token == null) {return false;}//与前端约定的加密方式int csrfToken = MD5Util.MD5Encode(token, "utf-8").hashCode();String hToken = request.getHeader(CSRF_TOKEN);String rToken = request.getParameter(CSRF_TOKEN);if (hToken != null && Integer.parseInt(hToken) == csrfToken) {return true;}if (rToken != null && Integer.parseInt(rToken) == csrfToken) {return true;}return false;}private boolean verifyIgnoreApi(String uri) {for (String ignoreApi : ignoreCsrfList) {if (uri.endsWith(ignoreApi)) {return true;}}return false;}

2、注册Filter,在ShiroConfig类中添加如下配置

    //配置文件中配置允许的Referer@Value("${access.referer}")private List<String> accessReferer;@Beanpublic FilterRegistrationBean csrfFilter() {FilterRegistrationBean filterRegistration = new FilterRegistrationBean();// 这里使用的是配置文件信息,获取配置文件中的csrf.domains相关值信息if (accessReferer != null && accessReferer.size() > 0) {filterRegistration.setFilter(new CsrfFilter(accessReferer));} else {filterRegistration.setFilter(new CsrfFilter(Collections.emptyList()));}filterRegistration.setEnabled(true);filterRegistration.addUrlPatterns("/*");return filterRegistration;}

3、在登录接口生成X-Access-Token,并通过设置Cookie的方式返回前端

//配置文件中配置前后端共同的域名(如果没有共同域名,cookie无法正常传递,暂时没考虑怎么实现),如前端域名为a.test.com,后端域名为b.test.com,则配置为.test.com,前面的.不可省略
@Value("${csp.access.domain}")
private String cspDomain;Cookie cookie = new Cookie(CSRF_TOKEN, csrfToken);
cookie.setPath("/");
if (StringUtils.isNotBlank(cspDomain)) {cookie.setDomain(cspDomain);
}
//cookie.setHttpOnly(true);
response.addCookie(cookie);

4、通过第三步设置后,请求登录,设置Cookie的时候会提示错误信息如下:

Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.There was an unexpected error (type=Internal Server Error, status=500).
An invalid domain [.test.com] was specified for this cookie

原因是spinrboot内嵌的tomcat默认不支持这种写法,但是springboot的官方文档是支持的,添加CookieConfig配置类

import org.apache.tomcat.util.http.LegacyCookieProcessor;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** cookie配置类**/
@Configuration
public class CookieConfig {/*** 解决问题:* There was an unexpected error (type=Internal Server Error, status=500).* An invalid domain [.test.com] was specified for this cookie** @return*/@Beanpublic WebServerFactoryCustomizer<TomcatServletWebServerFactory> cookieProcessorCustomizer() {return (factory) -> factory.addContextCustomizers((context) -> context.setCookieProcessor(new LegacyCookieProcessor()));}}

5、Cookie能够正常传输,前端读取到Cookie的X-Csrf-Token后,通过约定的加密方式,在后续请求时在请求头或请求体中携带加密后的值,后端通过对比Cookie中的值是否与请求携带上来的值是否一直来判断是否是跨域伪造请求。

前后端分离解决CSRF问题相关推荐

  1. Vue flask前后端分离解决跨域

    Vue flask前后端分离解决跨域 安装axios 在项目目录下输入:npm install axios--save-dev 配置axios 在main.js中引入axios import axio ...

  2. Spring Boot 前后端分离解决跨越问题

    一.起源 当我们在开发前后端分离项目的时候,出于安全原因,浏览器禁止Ajax调用驻留在当前原点之外的资源.跨源资源共享(CORS)是由大多数浏览器实现的W3C规范,允许您灵活地指定什么样的跨域请求被授 ...

  3. 跨域详解!前后端分离解决跨域问题

    文章目录 一.为什么会出现跨域问题 二.什么是跨域 三.非同源限制 四.跨域问题的解决方式 Jsonp前后端配合 前端修改 后端修改 CORS 详解响应头 5. SpringBoot解决 [方式一]全 ...

  4. vue解决线上跨域的问题_vue前后端分离解决跨域问题

    用Vue-cli脚手架搭建了个demo,前后分离就有跨域问题的出现. vue-clie搭建demo步骤(传送门):https://www.cnblogs.com/wangenbo/p/8487764. ...

  5. Django CSRF(什么是CSRF?)\Django前后端分离csrf token获取方式

    文章目录 Django CSRF 什么是CSRF? Django CSRF Django CSRF 中间件 Django,Ajax提交csrf_token处理 Django 设置 cookie 中的 ...

  6. springmvc集成cas,并解决前后端分离情况

    2019独角兽企业重金招聘Python工程师标准>>> 1.最近项目需要集成已经存在的cas系统. 但是目前已集成的系统都是jsp.而我们项目是前后端分离开发(伪),没有分开部署. ...

  7. JWT(解决前后端分离和微服务的用户会话跟踪问题)

    这里写目录标题 JWT:解决前后端分离和微服务的用户会话跟踪问题 与传统sessio验证的区别: 基于 token 的鉴权机制 JWT的主要引用场景及优点 JWT的构成: JWT搭建 案例: JWT: ...

  8. 解决java前后端分离端口跨域问题

    解决java前后端分离端口跨域问题 参考文章: (1)解决java前后端分离端口跨域问题 (2)https://www.cnblogs.com/mollie-x/p/10449686.html 备忘一 ...

  9. springBoot 解决前后端分离项目中跨越请求,同源策略

    今天在做项目的过程,采用前后端分离技术的时遇到采用ajax请求无法访问后台接口,按F12,查看浏览器运行状态时,报如下错误 为了解决浏览的同源策略,就必须了解什么是同源策略. 1.什么是同源策略 同源 ...

最新文章

  1. java实现将汉语转换为拼音
  2. java.lang.NullPointerException: Attempt to invoke virtual method ‘boolean java.lang.String.equals(j
  3. python十九:map,filter,reduce函数
  4. Win32 Application和Win32 Console Application的区别
  5. css未知尺寸的图片的水平和垂直居中
  6. js 循环拆词_javascript forEach通用循环遍历方法
  7. JUnit单元测试笔记
  8. 信息学奥赛一本通(1178:成绩排序)——选择排序
  9. SparkStreaming项目(实时统计每个品类被点击的次数)
  10. 正则表达式匹配html标签
  11. idea开发java前端_Web前端开发神器 Intellij IDEA
  12. 使用深度图重建世界坐标
  13. 2022-06-26 笔记本新机重装系统
  14. 吉林大学高级程序设计(红皮书例题)(1~7章)
  15. [转载] 晓说——第17期:揭秘战争秘闻 朝鲜战争62年祭(下)
  16. socketDemo套接字
  17. 关于ElasticSearch (ES)
  18. SCORM标准及支持SCORM标准学习平台的设计
  19. 虚拟地址 虚拟内存 物理地址
  20. Redhat6.5离线配置Zabbix,含自定义Zabbix监控项

热门文章

  1. 从npm、npx说起,到shell
  2. python列表过滤的方法
  3. Webpack前端资源加载/打包工具
  4. MySQL数据库知识点
  5. 总线概述及常见总线(转)
  6. 【java】删除文件夹及文件夹中的所有文件
  7. oracle 错误代码
  8. 菲尔人格测试今天你测了吗?
  9. epoch和iteration的区别
  10. 过孔为什么不能打焊盘上?我就想打,怎么办?