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

    • 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
    • 最佳实践

JWT

定义与作用

JSON Web Token(JWT)是一种基于 JSON 的开放标准(RFC 7519),用于在各方之间安全传输声明(Claims)。与传统 Session 认证相比,JWT 具有天然的无状态特性,使其在分布式系统和微服务架构中成为首选的认证凭证载体。

Session 认证的痛点

痛点说明
服务端存储压力每个用户会话需在服务端存储 Session 数据,用户量增大时内存或数据库压力剧增
横向扩展困难多实例部署时必须引入 Redis 等共享 Session 存储,增加架构复杂度
CSRF 风险基于 Cookie 的 Session 机制天然携带 Cookie,需额外配置 CSRF 防护
跨域限制Cookie 的同源策略使前后端分离架构下的跨域认证变得复杂

JWT 通过自包含的结构将用户身份和权限信息直接编码在 Token 中,服务端无需存储会话状态,仅凭 Token 自身即可完成身份验证。

核心原理

三部分结构

JWT 由 Header、Payload、Signature 三部分组成,以点号(.)连接:

Base64URL(Header).Base64URL(Payload).Base64URL(Signature)

Header(头部)

声明 Token 类型和签名算法:

{
  "alg": "HS256",
  "typ": "JWT"
}

常用算法:

  • HS256 / HS512:HMAC 对称签名,密钥双方共享
  • RS256 / RS512:RSA 非对称签名,私钥签发、公钥验证
  • ES256 / ES512:ECDSA 非对称签名,密钥更短、性能更高

Payload(载荷)

承载 Claims(声明),即 Token 中包含的数据:

Registered Claims(注册声明):

Claim含义示例
issIssuer,签发者"iss": "auth-server"
subSubject,主题(用户标识)"sub": "user123"
audAudience,受众"aud": "api-client"
expExpiration Time,过期时间戳"exp": 1718000000
nbfNot Before,生效时间戳"nbf": 1717900000
iatIssued At,签发时间戳"iat": 1717900000
jtiJWT ID,唯一标识"jti": "uuid-xxx"

Public / Private Claims:业务自定义声明,如 roles、userId、permissions 等。

Signature(签名)

防止 Token 被篡改。以 HS256 为例:

HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  secret
)

RS256 场景下,授权服务器使用私钥签名,资源服务器使用公钥验证,私钥不外泄。

无状态特性

JWT 的无状态意味着服务端不保存 Token 副本,每次请求携带 Token,服务端通过签名验证和 Claims 解析即可确认身份。这一特性消除了会话存储,但也带来了无法主动吊销的局限——Token 在过期前始终有效。

签发与验证时序

示例一:观察 JWT 的完整结构

场景:手工拆解一个 JWT,理解三部分的具体内容。

操作:

在 jwt.io 调试工具中输入标准 JWT:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiJ1c2VyMTIzIiwiaXNzIjoiYXV0aC1zZXJ2ZXIiLCJleHAiOjE3MTgwMDAwMDAsInJvbGVzIjpbIlJPTEVfVVNFUiJdfQ.
SflKxwRJSMeKKF2QT4fwpMe...

结果分析:

部分Base64URL 解码后内容
Header{"alg":"HS256","typ":"JWT"}
Payload{"sub":"user123","iss":"auth-server","exp":1718000000,"roles":["ROLE_USER"]}
SignatureHMACSHA256 计算结果,由前两部分 + 密钥生成
  • 任何对 Header 或 Payload 的修改都会导致 Signature 验证失败
  • Payload 中的 exp 决定了 Token 的存活窗口,过期后资源服务器拒绝接受
  • roles 是自定义 Private Claim,Spring Security 的 JwtAuthenticationConverter 可据此提取权限

示例二:从 Session 认证迁移到 JWT 无状态认证

场景:一个电商系统原有 Session 认证,需改造为前后端分离架构下的 JWT 认证。

操作前配置(Session 认证):

@EnableWebSecurity
public class SessionSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/login", "/public/**").permitAll()
                .antMatchers("/api/orders/**").authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .defaultSuccessUrl("/home")
                .and()
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
    }
}

问题分析:

  • 用户登录后 Session 存储在服务端内存
  • 订单服务部署 3 个实例,用户请求被负载均衡到不同实例时 Session 丢失
  • 引入 Redis 解决共享 Session,增加运维成本

操作后配置(JWT 无状态认证):

@EnableWebSecurity
public class JwtSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
            .authorizeRequests()
                .antMatchers("/api/public/**").permitAll()
                .antMatchers("/api/orders/**").authenticated()
                .and()
            .oauth2ResourceServer()
                .jwt();
    }
}

application.yml 配置:

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://auth-server.example.com

结果分析:

  • sessionCreationPolicy(SessionCreationPolicy.STATELESS) 禁用 Session 创建,服务端不再存储会话状态
  • oauth2ResourceServer().jwt() 启用 JWT 认证,从请求头 Authorization: Bearer <token> 解析 JWT
  • 三个实例均可独立验证 JWT 签名,无需共享 Session 存储
  • 前端每次请求在 Header 中携带 Token,天然支持跨域

易错场景:JWT 中存放敏感信息 + 密钥泄露

面试高频考点

错误做法:将用户密码、银行卡号等敏感信息放入 JWT Payload,并使用弱密钥或硬编码密钥。

问题分析:

  1. Payload 不是加密存储:JWT 的 Payload 仅做 Base64URL 编码,任何人可解码读取。以下结构看似安全,实则是明文暴露:

    {
      "sub": "user123",
      "password": "123456",
      "creditCard": "622202************"
    }
    

    攻击者截取 Token 后可直接解码获取敏感信息。

  2. 弱密钥导致签名伪造:HS256 使用短密钥或常见字符串(如 "secret"、"123456")时,攻击者可通过字典暴力破解密钥,进而伪造任意 Token,冒充任意用户身份。

  3. 无法主动撤销:JWT 一旦签发,在 exp 过期前服务端无法强制作废。若 Token 泄露,攻击者可长期冒用,直到 Token 自然过期。

正确做法:

  • Payload 只放用户标识、角色、权限等非敏感声明,禁止放入密码、Token、个人隐私数据
  • 使用强随机密钥(HS256 推荐至少 256 位),生产环境通过环境变量或密钥管理服务注入,禁止硬编码
  • 敏感数据应通过服务端根据 sub 查询数据库获取
  • 设置合理的 exp 过期时间(通常 15 分钟 ~ 2 小时),配合 Refresh Token 机制实现长期会话
上一页
本章定位
下一页
JwtDecoder