程序员scholar 程序员scholar
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • Web 标准

    • HTML
    • CSS
    • JavaScript
  • 前端框架

    • Vue2
    • Vue3
    • Vue3 + TS
    • 微信小程序
    • uni-app
  • 工具与库

    • jQuery
    • Ajax
    • Axios
    • Webpack
    • Vuex
    • WebSocket
    • 第三方登录
  • 后端与语言扩展

    • ES6
    • Typescript
    • node.js
  • Element-UI
  • Apache ECharts
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • Web 标准

    • HTML
    • CSS
    • JavaScript
  • 前端框架

    • Vue2
    • Vue3
    • Vue3 + TS
    • 微信小程序
    • uni-app
  • 工具与库

    • jQuery
    • Ajax
    • Axios
    • Webpack
    • Vuex
    • WebSocket
    • 第三方登录
  • 后端与语言扩展

    • ES6
    • Typescript
    • node.js
  • Element-UI
  • Apache ECharts
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
npm

(进入注册为作者充电)

  • SpringSecurity

    • Spring Security是什么
    • 认证与授权的基本概念
    • Spring Security 的默认配置
    • SpringSecurity的默认登录页
    • Spring Security 的 Filter 机制
    • HttpSecurity 与自定义登录页面
    • Spring Security 的核心组件与扩展
    • Spring Security 中的用户认证与角色管理
    • Spring Security 的授权机制与安全表达式
    • Security 的 Session 与 Token 管理
      • 1. Session 管理
        • 1.1 Session 失效与超时处理
        • 1.2 Session 固定攻击防护
      • 2. CSRF 保护
        • 2.1 什么是 CSRF 保护?
        • 2.2 配置 CSRF 保护
      • 3. Token 认证(JWT)
        • 3.1 什么是 JWT?
        • 3.2 如何生成与验证 JWT?
      • 4. JWT 进行 Token 验证 实战
        • 1. JWT 认证的基本流程
        • 2. 项目依赖配置
        • 3. JWT 工具类的实现
        • 4. JWT 过滤器的实现
        • 5. Spring Security 配置整合 JWT 过滤器
        • 6. 用户登录成功后生成 JWT 并返回
      • 5. 前端如何保存和携带 JWT Token
        • 1. 登录成功后保存 Token
        • 2. 在请求中携带 Token
        • 2.1 使用 Axios 请求拦截器
        • 2.2 在单个请求中手动添加 Token
        • 3. 处理 Token 过期的情况
        • 4. 通过响应拦截器保存 Token
    • Spring Security 集成第三方登录
    • Spring Security 集成 QQ 登录与 JWT 认证
    • Spring Security 登录认证源码
    • Spring Security - JWT认证实战
    • Spring Security - JWT授权实战
    • Spring Security 异常处理与自定义逻辑
  • SpringSecurity
  • SpringSecurity
scholar
2024-08-20
目录

Security 的 Session 与 Token 管理

# Security 的 Session 与 Token 管理

前言

在 Web 应用中,用户的身份认证和会话管理是保障系统安全的核心部分。Spring Security 提供了多种方式来管理 Session、保护 CSRF 攻击以及实现基于 Token 的无状态认证。

# 1. Session 管理

在默认情况下,Spring Security 通过 Session 管理用户的登录状态。Session 管理包括会话超时处理、并发登录控制、Session 固定攻击防护等。

# 1.1 Session 失效与超时处理

Spring Security 允许你配置会话超时和失效后的处理方式。

代码示例:配置 Session 超时与失效处理

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .sessionManagement()
                .invalidSessionUrl("/session-invalid")  // 当 Session 失效时,重定向到指定页面
                .maximumSessions(1)  // 限制每个用户只能有一个会话,防止并发登录
                .maxSessionsPreventsLogin(true);  // 当达到最大会话数时,禁止新登录
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

重要参数说明:

  • invalidSessionUrl(String url):当 Session 失效时,重定向到指定的页面(如登录页面)。
  • maximumSessions(int maxSessions):配置单个用户的最大会话数,通常用于限制并发登录。
  • maxSessionsPreventsLogin(boolean preventsLogin):如果设置为 true,当达到最大会话数时,阻止用户再次登录。如果为 false,则允许新的登录,旧的会话会失效。

使用场景: 适用于需要严格控制用户并发登录、以及对 Session 失效进行精细化处理的应用场景。

# 1.2 Session 固定攻击防护

Session 固定攻击(Session Fixation)是一种攻击方式,攻击者通过设置一个固定的 Session ID 来冒充用户。Spring Security 默认会在用户成功登录时重新生成一个新的 Session ID,以防止这种攻击。

代码示例:Session 固定攻击防护配置

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .sessionManagement()
            .sessionFixation().migrateSession();  // 默认配置,用户登录成功后重新生成 Session ID
}
1
2
3
4
5
6

其他选项:

  • migrateSession():重新生成 Session ID(默认)。
  • newSession():创建一个全新的 Session。
  • none():不处理 Session ID,保留原有 Session ID(不推荐)。

使用场景: 适用于需要保护用户登录过程免受 Session 固定攻击的应用。

# 2. CSRF 保护

CSRF(跨站请求伪造)是一种常见的 Web 攻击方式,攻击者诱导用户在未授权的情况下执行恶意操作。Spring Security 默认启用了 CSRF 防护,通过生成和验证 CSRF Token 来防止此类攻击。

# 2.1 什么是 CSRF 保护?

CSRF 防护机制通过在每个表单请求中嵌入一个随机生成的 Token,并在服务器端进行验证,确保请求的合法性。如果请求中缺少或包含错误的 CSRF Token,服务器会拒绝该请求。

# 2.2 配置 CSRF 保护

Spring Security 默认开启 CSRF 防护。如果你的应用是 RESTful API,可以选择禁用它,因为 API 通常通过 JWT 或其他无状态方式认证,不依赖 Session。

代码示例:禁用 CSRF 防护(适用于 RESTful API)

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .csrf().disable();  // 禁用 CSRF 防护
}
1
2
3
4
5

使用场景: 在传统的 Web 应用中,CSRF 防护是必不可少的,尤其是表单提交较多的场景。但在基于 Token 的无状态 API 中,可以安全地禁用 CSRF 防护。

# 3. Token 认证(JWT)

在微服务架构和前后端分离的项目中,Session 认证会带来一定的负担,特别是在无状态服务中,JWT(JSON Web Token)被广泛使用来实现无状态的用户认证。

# 3.1 什么是 JWT?

JWT 是一种轻量级的 Token 格式,包含了用户的认证信息,服务器在认证用户后生成 JWT,并将其返回给客户端,客户端在后续请求中携带该 Token 来进行认证。由于 JWT 是自包含的,因此可以实现无状态的认证。

# 3.2 如何生成与验证 JWT?

生成和验证 JWT 通常使用第三方库,如 jjwt。以下是一个简单的 JWT 生成与验证示例:

代码示例:JWT 生成与验证

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;

import java.util.Date;

@Component
public class JwtTokenProvider {

    private final String jwtSecret = "secretKey";  // 密钥,用于签名 JWT
    private final long jwtExpirationMs = 86400000;  // Token 有效期(24小时)

    // 生成 JWT
    public String generateToken(String username) {
        return Jwts.builder()
                   .setSubject(username)  // 设置主题为用户名
                   .setIssuedAt(new Date())  // 设置签发时间
                   .setExpiration(new Date((new Date()).getTime() + jwtExpirationMs))  // 设置过期时间
                   .signWith(SignatureAlgorithm.HS512, jwtSecret)  // 使用 HS512 算法和密钥进行签名
                   .compact();
    }

    // 验证 JWT
    public boolean validateToken(String token) {
        try {
            Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token);
            return true;
        } catch (Exception e) {
            return false;  // 如果验证失败,返回 false
        }
    }

    // 从 JWT 中获取用户名
    public String getUsernameFromToken(String token) {
        return Jwts.parser()
                   .setSigningKey(jwtSecret)
                   .parseClaimsJws(token)
                   .getBody()
                   .getSubject();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

重要参数说明:

  • setSubject(String subject):设置 JWT 的主体(通常为用户名)。
  • setIssuedAt(Date issuedAt):设置 JWT 的签发时间。
  • setExpiration(Date expiration):设置 JWT 的过期时间。
  • signWith(SignatureAlgorithm algorithm, String secret):使用指定的签名算法和密钥对 JWT 进行签名。

使用场景: 适用于需要无状态认证的场景,如前后端分离的项目或微服务架构,使用 JWT 可以减少服务器的负担并提高系统的可扩展性。

总结

  • Session 管理:通过配置 Spring Security 的 Session 管理,可以实现对会话的精细化控制,如并发登录限制、Session 失效处理、Session 固定攻击防护等。
  • CSRF 保护:CSRF 是常见的 Web 攻击,Spring Security 默认开启 CSRF 防护。在基于 Token 的无状态应用中可以禁用该防护。
  • Token 认证(JWT):JWT 是一种轻量级的无状态认证方式,广泛应用于微服务和前后端分离项目。通过生成和验证 JWT,可以实现用户认证和授权的无状态化。

# 4. JWT 进行 Token 验证 实战

在基于 Spring Security 的应用中,JWT(JSON Web Token)是一种常见的无状态认证方式,特别适用于前后端分离的架构。通过 JWT,可以将用户认证信息保存在 Token 中,而不再依赖传统的 Session,减少了服务器的状态管理压力。

# 1. JWT 认证的基本流程

  1. 用户登录时,服务器验证用户名和密码,成功后生成 JWT (同时设置过期时间),然后将其返回给客户端。
  2. 客户端在后续请求中携带 JWT(通常放在 Authorization 头部)。
  3. 服务器在接收到请求后,从请求头中解析 JWT,验证其有效性,并根据 Token 中的信息决定是否允许访问受保护的资源。

# 2. 项目依赖配置

在使用 JWT 时,需要添加一些相关依赖,例如 jjwt 库。

pom.xml 配置:

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>
1
2
3
4
5

# 3. JWT 工具类的实现

首先,需要一个工具类来生成和验证 JWT。该类负责创建、解析和验证 JWT。

代码示例:JWT 工具类

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;
import java.util.Date;

@Component
public class JwtTokenProvider {

    // 密钥,用于签名和验证 JWT
    private final String jwtSecret = "mySecretKey";
    // Token 有效期(单位:毫秒)
    private final long jwtExpirationMs = 86400000;  // 24小时

    // 生成 JWT Token
    public String generateToken(String username) {
        return Jwts.builder()
                   .setSubject(username)  // 设置主题为用户名
                   .setIssuedAt(new Date())  // 设置签发时间
                   .setExpiration(new Date((new Date()).getTime() + jwtExpirationMs))  // 设置过期时间
                   .signWith(SignatureAlgorithm.HS512, jwtSecret)  // 使用 HS512 算法和密钥进行签名
                   .compact();
    }

    // 从 JWT 中获取用户名
    public String getUsernameFromToken(String token) {
        return Jwts.parser()
                   .setSigningKey(jwtSecret)
                   .parseClaimsJws(token)
                   .getBody()
                   .getSubject();  // 从 Claims 中获取用户名
    }

    // 验证 JWT Token 是否有效
    public boolean validateToken(String token) {
        try {
            Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token);
            return true;
        } catch (Exception e) {
            // 解析失败说明 Token 无效(可能已过期或签名不正确)
            return false;
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

重要参数说明:

  • setSubject(String subject):设置 JWT 的主体,通常是用户名。
  • setIssuedAt(Date issuedAt):设置 JWT 的签发时间。
  • setExpiration(Date expiration):设置 JWT 的过期时间。过期后,Token 将不再有效。
  • signWith(SignatureAlgorithm algorithm, String secret):使用指定的签名算法和密钥对 JWT 进行签名。常用的算法是 HS512。

# 4. JWT 过滤器的实现

为了在每次请求时验证 Token,需要在 Spring Security 的过滤器链中添加一个自定义过滤器,该过滤器从请求头中提取 JWT,验证其有效性,并将用户信息注入到 Spring Security 的上下文中。

代码示例:JWT 过滤器

import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final JwtTokenProvider jwtTokenProvider;
    private final UserDetailsService userDetailsService;

    public JwtAuthenticationFilter(JwtTokenProvider jwtTokenProvider, UserDetailsService userDetailsService) {
        this.jwtTokenProvider = jwtTokenProvider;
        this.userDetailsService = userDetailsService;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        // 从请求头中获取 Authorization 字段
        String authHeader = request.getHeader("Authorization");
        String token = null;
        String username = null;

        // 检查是否包含 Bearer Token
        if (authHeader != null && authHeader.startsWith("Bearer ")) {
            token = authHeader.substring(7);  // 去掉 "Bearer " 前缀
            username = jwtTokenProvider.getUsernameFromToken(token);
        }

        // 验证 Token 并设置认证信息
        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            UserDetails userDetails = userDetailsService.loadUserByUsername(username);

            // 验证 Token 是否有效
            if (jwtTokenProvider.validateToken(token)) {
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                
                // 将认证信息设置到 Spring Security 的上下文中
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        }
        // 继续过滤器链
        chain.doFilter(request, response);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

重要参数说明:

  • getHeader(String name):从请求头中获取指定的字段,这里用于获取 Authorization 字段。
  • SecurityContextHolder.getContext().getAuthentication():获取当前的认证信息,防止重复认证。
  • UsernamePasswordAuthenticationToken:Spring Security 的认证对象,包含用户的身份和权限信息。

使用场景: 当需要在每个请求中验证用户身份时,使用自定义过滤器将 JWT 验证逻辑集成到 Spring Security 的认证流程中。

# 5. Spring Security 配置整合 JWT 过滤器

最后,需要将自定义的 JWT 过滤器集成到 Spring Security 的过滤器链中。

代码示例:Spring Security 配置

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final JwtTokenProvider jwtTokenProvider;
    private final MyUserDetailsService myUserDetailsService;

    public SecurityConfig(JwtTokenProvider jwtTokenProvider, MyUserDetailsService myUserDetailsService) {
        this.jwtTokenProvider = jwtTokenProvider;
        this.myUserDetailsService = myUserDetailsService;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()  // 禁用 CSRF,因为我们使用的是无状态的 Token 认证
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)  // 不创建或使用 Session
            .and()
            .authorizeRequests()
                .antMatchers("/login", "/register").permitAll()  // 允许所有用户访问登录和注册接口
                .anyRequest().authenticated()  // 其他请求都需要认证
            .and()
            .addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider, myUserDetailsService), 
                             UsernamePasswordAuthenticationFilter.class);  // 在 UsernamePasswordAuthenticationFilter 之前添加 JWT 过滤器
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

重要参数说明:

  • csrf().disable():禁用 CSRF 防护,适用于使用 JWT 的无状态认证。
  • sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS):配置为无状态,Spring Security 不再创建或使用 Session。
  • addFilterBefore():在指定的过滤器之前添加自定义过滤器,这里在 UsernamePasswordAuthenticationFilter 之前添加 JWT 过滤器。

# 6. 用户登录成功后生成 JWT 并返回

当用户登录成功后,服务器需要生成一个 JWT,并将其返回给前端。这个过程通常发生在处理用户登录请求的 Controller 中。以下是一个完整的流程:

  1. 用户通过前端发送登录请求(包含用户名和密码)。
  2. 服务器接收请求后,通过 Spring Security 验证用户名和密码。
  3. 验证成功后,生成 JWT 并将其返回给前端。

代码示例:用户登录并生成 JWT

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/auth")
public class AuthController {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtTokenProvider jwtTokenProvider;

    @PostMapping("/login")
    public ResponseEntity<?> authenticateUser(@RequestBody LoginRequest loginRequest) {
        // 进行用户认证
        Authentication authentication = authenticationManager.authenticate(
            new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword()));

        // 设置认证信息到上下文中
        SecurityContextHolder.getContext().setAuthentication(authentication);

        // 生成 JWT Token
        String jwt = jwtTokenProvider.generateToken(authentication.getName());

        // 返回 Token 给前端
        return ResponseEntity.ok(new JwtResponse(jwt));
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

关键点说明

  • authenticationManager.authenticate(...):通过用户名和密码进行认证,这是 Spring Security 的默认认证机制。
  • SecurityContextHolder.getContext().setAuthentication(authentication):将认证信息保存到 Spring Security 的上下文中。
  • jwtTokenProvider.generateToken(authentication.getName()):使用之前实现的 JwtTokenProvider 类生成 JWT,通常使用用户名作为 Token 的主体。
  • ResponseEntity.ok(new JwtResponse(jwt)):将生成的 JWT 包装在响应对象中返回给前端。

LoginRequest 类

用于接收前端传递的登录请求:

public class LoginRequest {
    private String username;
    private String password;

    // Getters and setters...
}
1
2
3
4
5
6

JwtResponse 类

用于包装返回给前端的 JWT:

public class JwtResponse {
    private String token;

    public JwtResponse(String token) {
        this.token = token;
    }

    // Getters and setters...
}
1
2
3
4
5
6
7
8
9

总结

通过以上配置,Spring Security 可以无缝支持基于 JWT 的无状态认证。JWT 认证的核心包括:

  • Token 的生成与解析:通过工具类生成和解析 JWT,并在请求中验证其有效性。
  • 自定义过滤器:在每次请求中通过过滤器验证 JWT,确保用户身份的合法性。
  • Spring Security 的整合:通过将 JWT 过滤器集成到 Spring Security 的认证流程中,实现全面的无状态安全管理。

# 5. 前端如何保存和携带 JWT Token

在使用 Spring Security 进行 JWT 认证时,前端(基于 Vue 和 Axios)需要在登录成功后保存 Token,并在每次请求时将 Token 携带在请求头中。本文将详细介绍如何在 Vue 项目中处理 JWT 的保存、获取和携带,包括关键 API 的使用说明和详细代码注释。

# 1. 登录成功后保存 Token

当用户通过登录接口认证成功后,后端会返回一个 JWT。前端需要将这个 Token 保存到本地存储中,通常使用 localStorage 或 sessionStorage。

代码示例:登录接口调用并保存 Token

// 登录页面中的方法
methods: {
  async login() {
    try {
      const response = await axios.post('/login', {
        username: this.username,  // 用户输入的用户名
        password: this.password   // 用户输入的密码
      });

      // 假设后端返回的数据格式为 { token: 'jwt-token-value' }
      const token = response.data.token;

      // 将 Token 保存到 localStorage(适用于长期登录)或 sessionStorage(适用于会话级别登录)
      localStorage.setItem('jwtToken', token);  // 保存 Token 到 localStorage

      // 设置 Axios 默认请求头,后续所有请求都会自动携带该 Token
      axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;

      // 跳转到主页或其他页面
      this.$router.push('/home');
    } catch (error) {
      console.error('登录失败', error);
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

重要参数说明:

  • localStorage.setItem('jwtToken', token):将 Token 保存到 localStorage 中,适用于用户希望在页面刷新或重新打开浏览器时仍然保持登录状态。
  • axios.defaults.headers.common['Authorization'] = 'Bearer ' + token:设置 Axios 的全局请求头,这样所有后续请求都会自动携带 Token。

使用场景: 适用于用户登录后需要保持会话状态并进行多次请求的场景。

# 2. 在请求中携带 Token

为了确保每次请求都携带 Token,可以通过全局拦截器或在具体请求中手动添加 Authorization 头。

# 2.1 使用 Axios 请求拦截器

通过请求拦截器可以自动为所有请求添加 Authorization 头部。

代码示例:配置 Axios 请求拦截器

// 在 Vue 项目中(如 main.js)配置全局拦截器
axios.interceptors.request.use(
  config => {
    // 从 localStorage 或 sessionStorage 中获取 Token
    const token = localStorage.getItem('jwtToken');
    if (token) {
      // 在请求头中携带 Token
      config.headers['Authorization'] = `Bearer ${token}`;
    }
    return config;  // 返回修改后的配置
  },
  error => {
    return Promise.reject(error);
  }
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

重要参数说明:

  • axios.interceptors.request.use():设置请求拦截器,所有请求在发出之前都会经过这个拦截器,用于动态添加或修改请求配置。
  • config.headers['Authorization'] = 'Bearer ' + token:将 Token 添加到请求头中,确保后端能够识别和验证用户身份。

使用场景: 适用于所有请求都需要携带 JWT Token 的场景。

# 2.2 在单个请求中手动添加 Token

如果不希望所有请求都携带 Token,可以在具体的请求中手动添加 Authorization 头。

代码示例:在单个请求中手动添加 Token

axios.get('/protected-resource', {
  headers: {
    Authorization: `Bearer ${localStorage.getItem('jwtToken')}`
  }
})
.then(response => {
  console.log('数据获取成功', response.data);
})
.catch(error => {
  console.error('请求失败', error);
});
1
2
3
4
5
6
7
8
9
10
11

重要说明: 手动添加请求头适合在某些特殊场景下,仅需要在特定请求中携带 Token。

# 3. 处理 Token 过期的情况

Token 通常有有效期,前端需要处理 Token 过期的情况。当后端返回 401(未授权)状态时,可以清除失效的 Token 并重定向到登录页面。

代码示例:处理 401 错误

axios.interceptors.response.use(
  response => response,  // 如果响应正常,直接返回
  error => {
    if (error.response.status === 401) {
      // 清除过期的 Token
      localStorage.removeItem('jwtToken');
      // 跳转到登录页面
      this.$router.push('/login');
    }
    return Promise.reject(error);  // 返回错误信息
  }
);
1
2
3
4
5
6
7
8
9
10
11
12

重要参数说明:

  • localStorage.removeItem('jwtToken'):清除过期的 Token,确保用户重新登录。
  • this.$router.push('/login'):通过 Vue Router 跳转到登录页面,提示用户重新进行认证。

使用场景: 适用于处理用户登录过期或失效时的场景。

# 4. 通过响应拦截器保存 Token

如果希望在每次登录时自动保存 Token,可以通过 Axios 的响应拦截器实现。

代码示例:通过响应拦截器保存 Token

axios.interceptors.response.use(
  response => {
    // 判断是否为登录请求,并检查是否包含 token
    if (response.config.url === '/login' && response.data.token) {
      const token = response.data.token;
      // 将 Token 保存到 localStorage
      localStorage.setItem('jwtToken', token);
      // 设置 Axios 默认请求头,后续所有请求都会携带该 Token
      axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
    }
    return response;
  },
  error => {
    // 处理响应错误,例如 401 未授权错误
    if (error.response.status === 401) {
      localStorage.removeItem('jwtToken');
      this.$router.push('/login');
    }
    return Promise.reject(error);
  }
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

适用场景: 当需要在全局自动处理登录响应并保存 Token 时,这种方式更为简洁。

总结

在基于 Vue 和 Axios 的前端应用中,JWT 的管理包括:

  • 保存 Token:登录成功后,将 Token 保存到 localStorage 或 sessionStorage 中。
  • 请求中携带 Token:通过全局拦截器或手动设置请求头,将 Token 添加到 Authorization 头部。
  • 处理 Token 过期:通过响应拦截器捕获 401 错误,并执行相应的重定向和清除操作。
编辑此页 (opens new window)
上次更新: 2025/01/05, 02:09:04
Spring Security 的授权机制与安全表达式
Spring Security 集成第三方登录

← Spring Security 的授权机制与安全表达式 Spring Security 集成第三方登录→

Theme by Vdoing | Copyright © 2019-2025 程序员scholar
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式