SpringBoot + Sa-Token 实现浏览器级 CSRF 防御(基础篇)

前言

在前后端分离项目中,跨域请求与 CSRF 攻击一直是安全防护的重点。很多人只关注 Token 登录态,却忽略了浏览器层面的防护手段。本文将结合 Sa-Token 框架,手把手教你实现 4 种核心防御手段:CSRF 令牌、Referer/Origin 校验、Cookie SameSite/HttpOnly 配置,以及跨域请求拦截。


一、为什么这些配置很重要?

先快速回顾 CSRF 攻击的原理:攻击者通过诱导用户访问恶意网站,利用用户已登录的 Cookie,在目标网站执行未授权操作。防御的核心思路就是:

  1. 让攻击者无法拿到有效的身份凭证(Cookie/Token)
  2. 让浏览器拒绝携带凭证发起跨域请求
  3. 让服务器能识别并拦截非法来源的请求

1. Sa-Token 核心配置(适配前后端分离)

application.yml 中配置基础登录态规则,这里我们选择用 Header 传递 Token,同时开启 Cookie 安全属性(用于其他场景的防护):

yaml

yaml 复制代码
sa-token:
  token-name: Authorization
  timeout: 28800
  activity-timeout: 14400
  is-concurrent: true
  is-share: false
  is-read-header: true
  is-read-cookie: false  # 前后端分离场景,不通过 Cookie 读 Token
  token-prefix: "Bearer"
  jwt-secret-key: system$2023-05CV-982131711
  cookie:
    same-site: Strict   # 跨域请求不携带 Cookie
    http-only: true     # 禁止 JS 读取 Cookie,防 XSS 窃取
    secure: false       # 生产环境 HTTPS 必须设为 true
    path: /

2. 关键属性解释

表格

配置项 作用 安全意义
same-site: Strict 只有同站请求才会携带 Cookie 直接阻断跨站请求的 Cookie 携带,从根源防 CSRF
http-only: true 浏览器禁止 JS 读取 Cookie 防止 XSS 攻击窃取会话 Cookie
secure: true 仅在 HTTPS 环境下携带 Cookie 防止中间人攻击窃取 Cookie

三、第一道防线:CORS 跨域配置,拒绝非法来源

CORS 是浏览器层面的第一道拦截,配置正确的跨域规则,能让浏览器主动拒绝非白名单的跨域请求。

完整配置代码

java

运行

kotlin 复制代码
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.Arrays;
import java.util.List;

@Slf4j
@Configuration
public class CorsConfig implements WebMvcConfigurer {

    @Value("${spring.profiles.active:dev}")
    private String activeProfile;

    @Value("#{'${system.cors.origin_url:}'.split(',')}")
    private List<String> allowedOrigins;

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        log.info("[CorsConfig] 当前环境:{},允许的 Origin:{}", activeProfile, allowedOrigins);

        registry.addMapping("/**")
                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
                .allowedHeaders("*")
                .allowCredentials(true)
                .maxAge(3600);

        var config = registry.getCorsConfigurations().get("/**");

        // 测试/预发布/生产环境使用通配(仅内部环境,请勿对外暴露)
        if (Arrays.asList("test", "stg", "prod").contains(activeProfile)) {
            config.addAllowedOriginPattern("*");
        } else {
            // 开发环境严格校验白名单域名
            if (allowedOrigins == null || allowedOrigins.isEmpty()) {
                log.error("[CorsConfig] 开发环境允许的 Origin 为空,所有跨域请求将被拒绝");
                config.setAllowedOrigins(List.of());
            } else {
                allowedOrigins.stream()
                        .map(String::trim)
                        .filter(s -> !s.isEmpty())
                        .forEach(config::addAllowedOrigin);
            }
        }
    }
}

避坑指南

  • 不要在生产环境使用 addAllowedOriginPattern("*"),会绕过浏览器的 CORS 校验。
  • allowCredentials(true) 必须配合明确的 allowedOrigins,否则浏览器会拒绝携带 Cookie。
  • 测试跨域时,工具请求(如 Apifox)默认不带 Origin 头,无法触发 CORS 校验,必须手动添加 Origin 头测试。

四、第二道防线:Sa-Token 拦截器,实现 Origin/Referer 校验

CORS 只能拦截浏览器发起的跨域请求,无法拦截工具请求。我们需要在 Sa-Token 拦截器中,手动校验请求来源,实现双重防护。

核心拦截逻辑

java

运行

java 复制代码
import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Arrays;
import java.util.List;

@Slf4j
@Configuration
public class SaTokenSecurityConfig {

    @Value("${spring.profiles.active:dev}")
    private String activeProfile;

    @Value("#{'${system.cors.origin_url:}'.split(',')}")
    private List<String> allowedOrigins;

    @Bean
    public SaInterceptor saInterceptor() {
        return new SaInterceptor(handler -> {
            // 1. 基础登录校验
            StpUtil.checkLogin();

            // 2. 开发环境 Origin/Referer 校验
            if (!Arrays.asList("test", "stg", "prod").contains(activeProfile)) {
                String origin = handler.getRequest().getHeader("Origin");
                String referer = handler.getRequest().getHeader("Referer");
                boolean isAllowed = false;

                if (origin != null) {
                    isAllowed = allowedOrigins.stream().anyMatch(origin::startsWith);
                } else if (referer != null) {
                    isAllowed = allowedOrigins.stream().anyMatch(referer::startsWith);
                }

                if (!isAllowed) {
                    log.warn("[安全拦截] 非法请求来源:Origin={}, Referer={}", origin, referer);
                    throw new RuntimeException("非法请求来源,拒绝访问");
                }
            }
        });
    }
}

五、基础防护效果验证

完成以上配置后,你的项目已经具备了基础的浏览器安全防护能力:

  1. 非白名单域名的跨域请求会被浏览器拦截。
  2. 工具请求不带 Origin/Referer 头时,会被服务器拦截。
  3. Cookie 已开启 HttpOnlySameSite,降低 XSS 和 CSRF 风险。

在下一篇文章中,我们将进阶实现 Sa-Token 自带的 CSRF 令牌校验,完成完整的安全防护闭环。

相关推荐
Amy187021118239 分钟前
守好第一道门:馈线保护装置如何为海上风光固态变压器筑牢安全防线
安全
上海锝秉工控2 小时前
免调校抗干扰 激光甲烷传感守护工业安全
安全
asaotomo5 小时前
从抓包插件到浏览器安全 Agent:Hx0 鹰眼 v1.0.6,正式接入 MCP
安全·渗透测试·agent·浏览器插件·ai工具·mcp
卓怡学长7 小时前
w210基于springboot反诈平台设计与实现
java·spring boot·spring·intellij-idea
大模型丫丫8 小时前
如何提升单体 Spring Boot 应用的并发数?
java·spring boot·后端
吴声子夜歌9 小时前
ApacheCommons——commons-exec(外部进程与系统命令安全执行)
java·安全·apache
RuoyiOffice9 小时前
SpringBoot3+Vue3 最推荐的开源 OA 系统 2026:流程驱动、资源闭环、多端互通
spring boot·vue3·flowable·oa·协同办公·spring boot 3·开源oa
请输入昵称3359 小时前
Wazuh-检测实验室实操
安全
底层信号9 小时前
沙箱管得住 Agent 的手,管不住它的目标 —— 从 OpenAI 7 月逃逸事件看 Agent 安全的真正瓶颈
人工智能·安全
计算机毕设定制辅导-无忧学长9 小时前
基于Spring Boot的玄幻小说个性化推荐平台设计与实现
java·vue.js·spring boot·后端·mysql·推荐算法