使用 Sa-Token CORS 策略处理跨域问题(三种方式全版)

前言

之前写了一篇 使用 Sa-Token 的全局过滤器解决跨域问题(三种方式全版) 详解了如何使用 Sa-Token 的全局过滤器解决各种跨域问题,正好最近 Sa-Token 更新了版本,最新版本简化了跨域处理方案, 本文章将介绍一下最新版本的写法,以便给正在使用新版本的同学做个参考。

注:本文章仅针对新版本(v1.42.0 及以上版本)提供参考,低于 v1.42.0 版本可以移步上述文章进行了解。


在 web 开发中,跨域绝对是比较折磨新同学的一个问题,本文将讲解三种常见的跨域情形,并讲解如何使用 Sa-Token 框架解决跨域问题。

什么情况下会发生跨域

简单理解,就是你在 A 域名下的页面,去调用 B 域名的接口,浏览器感觉你这次调用可能是不安全的请求行为,于是它需要用 cors 安全策略来确认一下这个请求是由用户真实的意愿发出的,而不是被 csrf 伪造请求攻击偷偷发送的。(这么说只是为了方便大家理解,不是特别严谨,实际上同域名下部分情形也会出现跨域问题)

请仔细理解上面这段话,因为它说明了两点:

  • 跨域不是后端接口对前端浏览器的限制,而是前端浏览器对用户的限制。
  • 跨域不是在保护后端接口免受攻击,而是浏览器在保护用户,避免用户发送自己不想发送的请求。

请一定要记住上面跨域的本质,明白了症状和原因,我们才能对症下药。

一般情况下,我们会碰到三种跨域场景:

  • 1、本地页面调用测试服务器,只在项目开发阶段会有跨域问题。(比较简单)
  • 2、使用 header 头提交 token,产生的跨域问题。(比较常见+通用,推荐使用)
  • 3、使用第三方 Cookie 提交 token,产生的跨域问题。(最古老的方案,目前新版浏览器对此方案限制越来越严格,非必要不选择此方案,如果对此方案不是很熟悉就贸然使用也容易出现安全问题)

跨域情形一:只在项目开发阶段会有跨域问题

有些公司项目的开发方式为:

  • 在项目开发时:使用本地页面调用测试服务器接口。(域名不同,存在跨域问题)
  • 在项目部署时:将后端接口和前端页面部署在同一域名下。(域名一致,不存在跨域问题)

这种情况下比较好解决,在代码层面我们无需任何更改,只在前端客户端做出一定的更改就行了。比如说:在前端配置一个代理服务器,或者修改一下 Chrome 客户端使其去除跨域限制。

具体的方案有很多,大家可参考这篇博客:手把手教你解决web前端跨域问题

上面是说的普通前后端分离开发,而在APP、小程序 开发中,其天然就是个没有跨域限制的客户端,我们什么都不用做就能解决跨域问题。

跨域情形二:使用 header 头提交 token,产生的跨域问题(比较常见+通用,推荐使用)

当你使用 header 头提交 token 时,会产生跨域问题。此方案比较常见+通用,推荐使用。

jquery 代码示例:

js 复制代码
	$.ajax({
		url: "/user/getInfo",
		type: "post", 
		data: {},
		dataType: 'json',
		headers: {
			"X-Requested-With": "XMLHttpRequest",
			// 重点处:请求的 header 头里塞入自定义参数
			"satoken": localStorage.getItem("satoken")
		},
		success: function(res){
			console.log(res);
		},
		error: function(xhr, type, errorThrown){
			return alert("异常:" + JSON.stringify(xhr));
		}
	});

Axios 代码示例:

js 复制代码
    axios({
        url: "/user/getInfo",
        method: 'post',
        data: {},
        headers: {
            "Content-Type": "application/x-www-form-urlencoded",
			// 重点处:请求的 header 头里塞入自定义参数
            "satoken": localStorage.getItem("satoken")
        }
    }).
    then(function (response) { // 成功时执行
        const res = response.data;
		console.log(res);
    }).
    catch(function (error) {
        return alert("异常:" + JSON.stringify(error));
    })

此时在后端,我们应该添加以下响应头:

java 复制代码
/**
 * [Sa-Token 权限认证] 配置类 
 *
 * @author click33
 */
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {

	/**
	 * CORS 跨域处理策略
	 */
	@Bean
	public SaCorsHandleFunction corsHandle() {
		return (req, res, sto) -> {
			res.
				// 允许指定域访问跨域资源
				setHeader("Access-Control-Allow-Origin", "*")
				// 允许所有请求方式
				.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE")
				// 有效时间
				.setHeader("Access-Control-Max-Age", "3600")
				// 允许的header参数
				.setHeader("Access-Control-Allow-Headers", "*");

			// 如果是预检请求,则立即返回到前端
			SaRouter.match(SaHttpMethod.OPTIONS)
				.free(r -> System.out.println("--------OPTIONS预检请求,不做处理"))
				.back();
		};
	}
}

WebFlux 同理,此处理策略全环境通用。

这是最古老的方案,目前新版浏览器对此方案限制越来越严格,非必要不选择此方案,如果对此方案不是很熟悉就贸然使用也容易出现安全问题。

jquery 代码示例:

js 复制代码
	$.ajax({
		url: "/user/getInfo",
		type: "post", 
		data: {},
		dataType: 'json',
		// 重点处:指定是跨域模式,需要提交第三方 Cookie 
		crossDomain: true,
		xhrFields:{
			withCredentials: true
		},
		headers: {
			"X-Requested-With": "XMLHttpRequest"
		},
		success: function(res){
			console.log(res);
		},
		error: function(xhr, type, errorThrown){
			return alert("异常:" + JSON.stringify(xhr));
		}
	});

Axios 代码示例:

js 复制代码
    axios({
        url: "/user/getInfo",
        method: 'post',
        data: {},
		// 重点处:开启第三方 Cookie 
		withCredentials: true,
        headers: {
            "Content-Type": "application/x-www-form-urlencoded"
        }
    }).
    then(function (response) { // 成功时执行
        console.log(res);
    }).
    catch(function (error) {
        return alert("异常:" + JSON.stringify(error));
    })

此时在后端,我们应该添加以下响应头:

java 复制代码
/**
 * [Sa-Token 权限认证] 配置类 
 *
 * @author click33
 */
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {

	/**
	 * CORS 跨域处理策略
	 */
	@Bean
	public SaCorsHandleFunction corsHandle() {
		return (req, res, sto) -> {
			// 获得客户端domain
			String origin = req.getHeader("Origin");
			if (origin == null) {
				origin = req.getHeader("Referer");
			}

			// ---------- 设置跨域响应头 ----------
			res
				// 允许第三方 Cookie
				.setHeader("Access-Control-Allow-Credentials", "true")
				// 允许指定域访问跨域资源
				.setHeader("Access-Control-Allow-Origin", origin)
				// 允许所有请求方式
				.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE")
				// 允许的header参数
				.setHeader("Access-Control-Allow-Headers", "x-requested-with,satoken")
				// 有效时间
				.setHeader("Access-Control-Max-Age", "3600")
			;

			// 如果是预检请求,则立即返回到前端
			SaRouter.match(SaHttpMethod.OPTIONS)
				.free(r -> System.out.println("--------OPTIONS预检请求,不做处理"))
				.back();
		};
	}

}

WebFlux 同理,此处理策略全环境通用。

注意:根据浏览器最新标准,Cookie 模式认证需要做到以下两点:

1、Cookie 属性指定 secure=true 以及 sameSite=None

yml 复制代码
sa-token:
    is-log: true
    cookie:
        # 指明当前为 https 安全连接
        secure: true
        # 指明第三方 Cookie 限制级别为:不限制
        sameSite: None

2、后端的服务必须是 https 形式,前端无要求。

相关推荐
猫头虎几秒前
手动部署开源OpenClaw汉化中文版过程中常见问题排查手册
人工智能·langchain·开源·github·aigc·agi·openclaw
SimonKing4 分钟前
分享一款可以管理本地端口的IDEA插件:Port Manager
java·后端·程序员
索荣荣9 分钟前
Maven配置文件(pom.xml)终极指南
java·开发语言
一只大侠的侠19 分钟前
Flutter开源鸿蒙跨平台训练营 Day14React Native表单开发
flutter·开源·harmonyos
代码栈上的思考23 分钟前
SpringBoot 拦截器
java·spring boot·spring
送秋三十五27 分钟前
一次大文件处理性能优化实录————Java 优化过程
java·开发语言·性能优化
雨中飘荡的记忆29 分钟前
千万级数据秒级对账!银行日终批处理对账系统从理论到实战
java
jbtianci35 分钟前
Spring Boot管理用户数据
java·spring boot·后端
Sylvia-girl38 分钟前
线程池~~
java·开发语言
猫头虎42 分钟前
OpenClaw开源汉化发行版:介绍、下载、安装、配置教程
运维·windows·开源·aigc·ai编程·agi·csdn