首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >还在用 Session?Spring Security 6 + JWT 打造全终端通用认证架构,彻底告别多端登录难题!

还在用 Session?Spring Security 6 + JWT 打造全终端通用认证架构,彻底告别多端登录难题!

作者头像
行者全栈架构师
发布于 2026-05-21 16:08:00
发布于 2026-05-21 16:08:00
4780
举报

JWT+Spring Security 6 多终端认证系统实战

导读: 本文详解基于 JWT 和 Spring Security 6 的多终端认证系统设计与实现,包括 Token 设计、刷新机制、权限控制以及生产环境最佳实践。


🎯 一、为什么选择 JWT?

1.1 传统 Session 认证的痛点

问题:

•❌ Session 存储在服务器,占用内存•❌ 集群环境需要 Session 共享 (Redis)•❌ CSRF 攻击风险•❌ 跨域支持复杂•❌ 移动端适配困难

1.2 JWT 的优势

对比表:

特性

Session 方案

JWT 方案

优势

存储位置

服务器 + Redis

客户端

✅ 节省资源

扩展性

需要 Session 共享

天然支持

✅ 易于扩展

性能

每次查询数据库

本地验证

✅ 性能提升 5 倍

跨域

复杂配置

原生支持

✅ 简单

安全性

CSRF 风险

更安全

✅ 无 CSRF


🔐 二、JWT Token 设计

2.1 Token 结构设计

代码语言:javascript
复制
/**
 * JWT Token 结构
 */
public class TokenInfo {

    /**
     * Header (Base64 编码)
     * {
     *   "alg": "HS256",
     *   "typ": "JWT"
     * }
     */
    private String header;

    /**
     * Payload (Base64 编码)
     * {
     *   "userId": 1001,           // 用户 ID
     *   "userName": "admin",       // 用户名
     *   "deviceId": "xxx",         // 设备标识
     *   "iat": 1234567890,         // 签发时间
     *   "exp": 1234571490          // 过期时间 (2 小时)
     * }
     */
    private String payload;

    /**
     * Signature (签名)
     * HMACSHA256(
     *   base64UrlEncode(header) + "." + 
     *   base64UrlEncode(payload),
     *   secret
     * )
     */
    private String signature;
}

2.2 双 Token 机制

代码语言:javascript
复制
sequenceDiagram
    participant Client as 客户端
    participant Server as 服务端

    Client->>Server: 登录 (用户名 + 密码)
    Server->>Server: 验证通过
    Server-->>Client: 返回 AccessToken(2h) + RefreshToken(7d)

    Note over Client,Server: 正常请求阶段
    Client->>Server: 请求 API + AccessToken
    Server->>Server: 验证 Token 有效性
    Server-->>Client: 返回数据

    Note over Client,Server: Token 过期处理
    Client->>Server: 请求 API + 过期 AccessToken
    Server-->>Client: 返回 401 Token 已过期

    Client->>Server: 刷新 Token + RefreshToken
    Server->>Server: 验证 RefreshToken
    Server-->>Client: 返回新的 AccessToken + RefreshToken

    Note over Client,Server: 继续使用
    Client->>Server: 请求 API + 新 AccessToken
    Server-->>Client: 返回数据

代码实现:

代码语言:javascript
复制
/**
 * JWT Token 工具类
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class JwtUtil {

    @Value("${jwt.secret}")
    private String secret;

    @Value("${jwt.expiration}")
    private Long expiration; // AccessToken 过期时间 (2 小时)

    @Value("${jwt.refreshExpiration}")
    private Long refreshExpiration; // RefreshToken 过期时间 (7 天)

    /**
     * 生成 Token
     */
    public String generateToken(LoginUser loginUser, String deviceId) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("userId", loginUser.getUserId());
        claims.put("userName", loginUser.getUsername());
        claims.put("deviceId", deviceId);

        Date now = new Date();
        Date expireDate = new Date(now.getTime() + expiration);

        return Jwts.builder()
            .setClaims(claims)
            .setId(UUID.randomUUID().toString())
            .setIssuedAt(now)
            .setExpiration(expireDate)
            .signWith(SignatureAlgorithm.HS256, secret)
            .compact();
    }

    /**
     * 生成 RefreshToken
     */
    public String generateRefreshToken(String username) {
        Date now = new Date();
        Date expireDate = new Date(now.getTime() + refreshExpiration);

        return Jwts.builder()
            .setSubject(username)
            .setId(UUID.randomUUID().toString())
            .setIssuedAt(now)
            .setExpiration(expireDate)
            .signWith(SignatureAlgorithm.HS256, secret)
            .compact();
    }

    /**
     * 解析 Token
     */
    public Claims parseToken(String token) {
        try {
            return Jwts.parser()
                .setSigningKey(secret)
                .parseClaimsJws(token)
                .getBody();
        } catch (Exception e) {
            log.error("解析 Token 失败:{}", e.getMessage());
            return null;
        }
    }

    /**
     * 从 Token 中获取用户 ID
     */
    public Long getUserId(String token) {
        Claims claims = parseToken(token);
        if (claims == null) {
            return null;
        }
        return Long.valueOf(claims.get("userId").toString());
    }

    /**
     * 验证 Token 是否过期
     */
    public boolean isTokenExpired(String token) {
        Claims claims = parseToken(token);
        if (claims == null) {
            return true;
        }
        return claims.getExpiration().before(new Date());
    }

    /**
     * 验证 Token 是否有效
     */
    public boolean validateToken(String token) {
        try {
            Claims claims = parseToken(token);
            return claims != null && !isTokenExpired(token);
        } catch (Exception e) {
            log.error("验证 Token 失败", e);
            return false;
        }
    }
}

🔧 三、Spring Security 6 配置

3.1 安全配置类

代码语言:javascript
复制
/**
 * Spring Security 6 配置
 */
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true)
@RequiredArgsConstructor
public class SecurityConfig {

    private final JwtAuthenticationFilter jwtAuthenticationFilter;
    private final AuthenticationProvider authenticationProvider;

    /**
     * 配置安全过滤器链
     */
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            // 禁用 CSRF
            .csrf(csrf -> csrf.disable())

            // 禁用 Session
            .sessionManagement(session -> 
                session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))

            // 配置授权规则
            .authorizeHttpRequests(auth -> auth
                // 放行静态资源和公开接口
                .requestMatchers("/login", "/register", "/captchaImage")
                    .permitAll()
                .requestMatchers("/common/download/**", "/common/download/resource")
                    .permitAll()
                .requestMatchers("/swagger-ui.html", "/swagger-ui/**", "/v3/api-docs/**")
                    .permitAll()
                // 其他请求需要认证
                .anyRequest().authenticated()
            )

            // 添加 JWT 过滤器
            .addFilterBefore(jwtAuthenticationFilter, 
                UsernamePasswordAuthenticationFilter.class)

            // 配置异常处理
            .exceptionHandling(exceptions -> exceptions
                .authenticationEntryPoint((request, response, authException) -> {
                    // 未认证处理
                    response.setContentType("application/json;charset=UTF-8");
                    response.getWriter().write("{\"code\":401,\"msg\":\"未认证\"}");
                })
                .accessDeniedHandler((request, response, accessDeniedException) -> {
                    // 无权限处理
                    response.setContentType("application/json;charset=UTF-8");
                    response.getWriter().write("{\"code\":403,\"msg\":\"无权限\"}");
                })
            );

        return http.build();
    }

    /**
     * 配置认证提供者
     */
    @Bean
    public AuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
        authProvider.setUserDetailsService(userDetailsService());
        authProvider.setPasswordEncoder(passwordEncoder());
        return authProvider;
    }

    /**
     * 密码编码器
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 用户详情服务
     */
    @Bean
    public UserDetailsService userDetailsService() {
        return username -> {
            // 从数据库加载用户信息
            SysUser user = userService.selectUserByUserName(username);
            if (user == null) {
                throw new UsernameNotFoundException("用户不存在");
            }
            return new org.springframework.security.core.userdetails.User(
                username,
                user.getPassword(),
                getAuthorities(user)
            );
        };
    }

    /**
     * 获取用户权限列表
     */
    private Collection<? extends GrantedAuthority> getAuthorities(SysUser user) {
        List<GrantedAuthority> authorities = new ArrayList<>();

        // 添加角色权限
        for (SysRole role : user.getRoles()) {
            authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getRoleKey()));
        }

        // 添加操作权限
        for (String permission : user.getPermissions()) {
            authorities.add(new SimpleGrantedAuthority(permission));
        }

        return authorities;
    }
}

3.2 JWT 认证过滤器

代码语言:javascript
复制
/**
 * JWT 认证过滤器
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final JwtUtil jwtUtil;
    private final TokenService tokenService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                   HttpServletResponse response, 
                                   FilterChain filterChain) 
            throws ServletException, IOException {

        // 1. 获取 Token
        String token = getTokenFromRequest(request);

        // 2. 没有 Token,直接放行
        if (StringUtils.isEmpty(token)) {
            filterChain.doFilter(request, response);
            return;
        }

        // 3. 验证 Token
        if (!jwtUtil.validateToken(token)) {
            log.warn("Token 无效或已过期");
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write("{\"code\":401,\"msg\":\"Token 无效或已过期\"}");
            return;
        }

        // 4. 解析 Token,获取用户信息
        Claims claims = jwtUtil.parseToken(token);
        Long userId = Long.valueOf(claims.get("userId").toString());
        String username = claims.getSubject();

        // 5. 从 Redis 中获取登录用户信息
        LoginUser loginUser = tokenService.getLoginUser(token);
        if (loginUser == null) {
            log.warn("登录用户不存在");
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            response.getWriter().write("{\"code\":401,\"msg\":\"登录用户不存在\"}");
            return;
        }

        // 6. 设置 Spring Security 上下文
        UsernamePasswordAuthenticationToken authenticationToken = 
            new UsernamePasswordAuthenticationToken(loginUser, null, 
                loginUser.getAuthorities());
        authenticationToken.setDetails(new WebAuthenticationDetailsSource()
            .buildDetails(request));

        SecurityContextHolder.getContext().setAuthentication(authenticationToken);

        log.debug("用户 [{}] 认证成功", username);

        // 7. 继续过滤链
        filterChain.doFilter(request, response);
    }

    /**
     * 从请求中获取 Token
     */
    private String getTokenFromRequest(HttpServletRequest request) {
        // 从 Header 中获取
        String token = request.getHeader("Authorization");

        // 支持 Bearer Token 格式
        if (StringUtils.isNotEmpty(token) && token.startsWith("Bearer ")) {
            token = token.substring(7);
        }

        // 如果 Header 中没有,尝试从参数中获取
        if (StringUtils.isEmpty(token)) {
            token = request.getParameter("token");
        }

        return token;
    }
}

💡 四、登录认证流程

4.1 登录接口实现

代码语言:javascript
复制
/**
 * 用户认证 Controller
 */
@RestController
public class AuthController {

    @Autowired
    private AuthService authService;

    /**
     * 登录接口
     */
    @PostMapping("/login")
    public R<Map<String, Object>> login(@RequestBody LoginBody loginBody) {
        // 1. 验证码校验 (可选)
        if (loginBody.getCaptchaCode() != null) {
            captchaService.verifyCaptcha(loginBody.getUsername(), 
                                        loginBody.getCaptchaCode());
        }

        // 2. 执行登录认证
        LoginResult result = authService.login(
            loginBody.getUsername(),
            loginBody.getPassword(),
            loginBody.getCode(),
            loginBody.getUuid()
        );

        // 3. 返回 Token 和用户信息
        Map<String, Object> response = new HashMap<>();
        response.put("accessToken", result.getAccessToken());
        response.put("refreshToken", result.getRefreshToken());
        response.put("userInfo", result.getUserInfo());

        return R.success(response);
    }

    /**
     * 退出登录
     */
    @PostMapping("/logout")
    public R<Void> logout(HttpServletRequest request) {
        String token = getTokenFromRequest(request);
        authService.logout(token);
        return R.success();
    }

    /**
     * 刷新 Token
     */
    @PostMapping("/refresh")
    public R<Map<String, String>> refreshToken(
            @RequestParam String refreshToken) {
        Map<String, String> tokens = authService.refreshToken(refreshToken);
        return R.success(tokens);
    }
}

4.2 认证 Service 实现

代码语言:javascript
复制
/**
 * 认证服务
 */
@Service
@RequiredArgsConstructor
public class AuthService {

    private final AuthenticationManager authenticationManager;
    private final JwtUtil jwtUtil;
    private final TokenService tokenService;
    private final UserService userService;

    /**
     * 登录认证
     */
    @Transactional
    public LoginResult login(String username, String password, 
                            String code, String uuid) {
        // 1. 创建认证 Token
        UsernamePasswordAuthenticationToken authToken = 
            new UsernamePasswordAuthenticationToken(username, password);

        // 2. 执行认证 (会调用 UserDetailsService)
        Authentication authenticate;
        try {
            authenticate = authenticationManager.authenticate(authToken);
        } catch (BadCredentialsException e) {
            throw new ServiceException("用户名或密码错误");
        } catch (DisabledException e) {
            throw new ServiceException("账号已被停用");
        } catch (Exception e) {
            log.error("登录认证失败", e);
            throw new ServiceException("登录失败:" + e.getMessage());
        }

        // 3. 获取登录用户
        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();

        // 4. 记录登录日志
        recordLogininfor(username, Constants.LOGIN_SUCCESS, "登录成功");

        // 5. 生成 Token
        String deviceId = UUID.randomUUID().toString();
        String accessToken = jwtUtil.generateToken(loginUser, deviceId);
        String refreshToken = jwtUtil.generateRefreshToken(username);

        // 6. 将用户信息缓存到 Redis
        tokenService.saveLoginUser(loginUser, accessToken, deviceId);

        // 7. 构建返回结果
        LoginResult result = new LoginResult();
        result.setAccessToken(accessToken);
        result.setRefreshToken(refreshToken);
        result.setUserInfo(buildUserInfo(loginUser));

        return result;
    }

    /**
     * 退出登录
     */
    public void logout(String token) {
        // 从 Redis 中删除登录信息
        tokenService.deleteLoginUser(token);

        // 记录登出日志
        Claims claims = jwtUtil.parseToken(token);
        if (claims != null) {
            String username = claims.getSubject();
            recordLogininfor(username, Constants.LOGOUT, "退出成功");
        }
    }

    /**
     * 刷新 Token
     */
    @Transactional
    public Map<String, String> refreshToken(String refreshToken) {
        // 1. 验证 RefreshToken
        if (!jwtUtil.validateToken(refreshToken)) {
            throw new ServiceException("RefreshToken 已过期");
        }

        // 2. 获取用户名
        Claims claims = jwtUtil.parseToken(refreshToken);
        String username = claims.getSubject();

        // 3. 重新生成 Token
        LoginUser loginUser = loadUserByUsername(username);
        String deviceId = UUID.randomUUID().toString();
        String newAccessToken = jwtUtil.generateToken(loginUser, deviceId);
        String newRefreshToken = jwtUtil.generateRefreshToken(username);

        // 4. 更新 Redis缓存
        tokenService.saveLoginUser(loginUser, newAccessToken, deviceId);

        // 5. 返回新 Token
        Map<String, String> tokens = new HashMap<>();
        tokens.put("accessToken", newAccessToken);
        tokens.put("refreshToken", newRefreshToken);

        return tokens;
    }

    /**
     * 加载用户信息
     */
    private LoginUser loadUserByUsername(String username) {
        SysUser user = userService.selectUserByUserName(username);
        if (user == null) {
            throw new ServiceException("用户不存在");
        }
        return userDetailsService.loadUserByUsername(username);
    }
}

🚀 五、多终端支持

5.1 设备类型识别

代码语言:javascript
复制
/**
 * 设备类型枚举
 */
public enum DeviceType {

    WEB("web", "PC 网页"),
    APP("app", "手机 App"),
    MINI_PROGRAM("mp", "小程序"),
    WECHAT("wechat", "微信公众号");

    private final String code;
    private final String desc;

    DeviceType(String code, String desc) {
        this.code = code;
        this.desc = desc;
    }

    /**
     * 从 User-Agent 中识别设备类型
     */
    public static DeviceType fromUserAgent(String userAgent) {
        if (StringUtils.isEmpty(userAgent)) {
            return DeviceType.WEB;
        }

        userAgent = userAgent.toLowerCase();

        // 判断是否为 App
        if (userAgent.contains("android") || userAgent.contains("iphone")) {
            if (userAgent.contains("micromessenger")) {
                return DeviceType.WECHAT;
            }
            return DeviceType.APP;
        }

        // 判断是否为小程序
        if (userAgent.contains("miniprogram")) {
            return DeviceType.MINI_PROGRAM;
        }

        // 默认为 Web
        return DeviceType.WEB;
    }
}

5.2 多终端登录策略

代码语言:javascript
复制
/**
 * 多终端登录管理器
 */
@Component
@RequiredArgsConstructor
public class MultiTerminalManager {

    private final RedisUtil redisUtil;

    /**
     * 同一账号允许同时登录的设备数
     */
    private static final int MAX_LOGIN_DEVICES = 5;

    /**
     * 检查是否允许登录
     */
    public boolean allowLogin(Long userId, DeviceType deviceType) {
        String key = "login:user:" + userId;

        // 获取该用户所有登录设备
        Set<String> devices = redisUtil.get(key, Set.class);
        if (devices == null) {
            return true;
        }

        // 检查是否超过最大设备数
        if (devices.size() >= MAX_LOGIN_DEVICES) {
            // 踢出最早登录的设备
            String earliestDevice = getEarliestDevice(devices);
            kickoutDevice(userId, earliestDevice);
        }

        return true;
    }

    /**
     * 记录设备登录
     */
    public void recordDeviceLogin(Long userId, String deviceId, DeviceType deviceType) {
        String key = "login:user:" + userId;

        Map<String, Object> deviceInfo = new HashMap<>();
        deviceInfo.put("deviceId", deviceId);
        deviceInfo.put("deviceType", deviceType.getCode());
        deviceInfo.put("loginTime", System.currentTimeMillis());

        redisUtil.hashSet(key, deviceId, deviceInfo);
        redisUtil.expire(key, 7, TimeUnit.DAYS);
    }

    /**
     * 踢出指定设备
     */
    public void kickoutDevice(Long userId, String deviceId) {
        String key = "login:user:" + userId + ":token:" + deviceId;
        String token = redisUtil.get(key, String.class);

        if (token != null) {
            // 将 Token 加入黑名单
            redisUtil.set("token:blacklist:" + token, "", 
                         2, TimeUnit.HOURS);
        }

        // 删除设备信息
        redisUtil.hashDelete(key, deviceId);
    }

    /**
     * 获取最早登录的设备
     */
    private String getEarliestDevice(Set<String> devices) {
        long earliestTime = Long.MAX_VALUE;
        String earliestDevice = null;

        for (String deviceId : devices) {
            // TODO: 获取设备登录时间并比较
        }

        return earliestDevice;
    }
}

📊 六、性能优化

6.1 Token 缓存策略

代码语言:javascript
复制
/**
 * Token 服务优化版
 */
@Service
@RequiredArgsConstructor
public class TokenService {

    private final RedisUtil redisUtil;

    /**
     * 保存登录用户 (带缓存预热)
     */
    public void saveLoginUser(LoginUser user, String token, String deviceId) {
        String userKey = "login:token:" + token;
        String deviceKey = "login:device:" + deviceId;

        // 1. 缓存用户信息
        redisUtil.set(userKey, user, 2, TimeUnit.HOURS);

        // 2. 建立 Token 和设备映射
        redisUtil.set(deviceKey, token, 2, TimeUnit.HOURS);

        // 3. 预热用户权限数据
        warmUpUserPermissions(user.getUserId());
    }

    /**
     * 获取登录用户 (多级缓存)
     */
    public LoginUser getLoginUser(String token) {
        // 1. 检查 Token 黑名单
        if (isTokenBlacklisted(token)) {
            return null;
        }

        // 2. 从 Redis 获取
        String userKey = "login:token:" + token;
        LoginUser user = redisUtil.get(userKey, LoginUser.class);

        if (user != null) {
            // 3. 续期
            redisUtil.expire(userKey, 2, TimeUnit.HOURS);
        }

        return user;
    }

    /**
     * 预热用户权限数据
     */
    private void warmUpUserPermissions(Long userId) {
        // 提前加载用户权限到缓存
        String permKey = "user:perms:" + userId;
        // ... 加载权限数据
    }

    /**
     * 检查 Token 是否在黑名单中
     */
    private boolean isTokenBlacklisted(String token) {
        return Boolean.TRUE.equals(redisUtil.hasKey("token:blacklist:" + token));
    }
}

6.2 并发登录控制

代码语言:javascript
复制
/**
 * 登录限流器
 */
@Component
public class LoginRateLimiter {

    private final RedisUtil redisUtil;

    /**
     * 检查是否允许登录
     */
    public boolean allowLogin(String ip, String username) {
        // IP 维度限流:每分钟最多 10 次
        String ipKey = "login:limit:ip:" + ip;
        Long ipCount = redisUtil.increment(ipKey);
        if (ipCount == 1) {
            redisUtil.expire(ipKey, 1, TimeUnit.MINUTES);
        }
        if (ipCount > 10) {
            log.warn("IP 登录过于频繁:{}", ip);
            return false;
        }

        // 用户名维度限流:每分钟最多 5 次
        String userKey = "login:limit:user:" + username;
        Long userCount = redisUtil.increment(userKey);
        if (userCount == 1) {
            redisUtil.expire(userKey, 1, TimeUnit.MINUTES);
        }
        if (userCount > 5) {
            log.warn("用户名登录过于频繁:{}", username);
            return false;
        }

        return true;
    }
}

🛡️ 七、安全加固

7.1 Token 防篡改

代码语言:javascript
复制
/**
 * Token 签名验证增强
 */
public class SecureJwtUtil extends JwtUtil {

    /**
     * 双重签名验证
     */
    public boolean validateTokenSecure(String token) {
        // 1. 基础验证
        if (!validateToken(token)) {
            return false;
        }

        // 2. 检查签名算法
        Claims claims = parseToken(token);
        String alg = (String) claims.get("alg");
        if (!"HS256".equals(alg)) {
            log.warn("不支持的签名算法:{}", alg);
            return false;
        }

        // 3. 检查必要字段
        if (claims.get("userId") == null || claims.getSubject() == null) {
            log.warn("Token 缺少必要字段");
            return false;
        }

        return true;
    }

    /**
     * 添加指纹验证
     */
    public String generateTokenWithFingerprint(LoginUser user, String fingerprint) {
        Map<String, Object> claims = buildClaims(user);
        claims.put("fingerprint", fingerprint); // 设备指纹

        return Jwts.builder()
            .setClaims(claims)
            .signWith(SignatureAlgorithm.HS256, secret)
            .compact();
    }
}

7.2 XSS 和 CSRF 防护

代码语言:javascript
复制
/**
 * 安全响应头配置
 */
@Configuration
public class SecurityHeaderConfig {

    @Bean
    public Filter securityHeaderFilter() {
        return (request, response, chain) -> {
            // 防止 XSS 攻击
            response.setHeader("X-XSS-Protection", "1; mode=block");
            response.setHeader("X-Content-Type-Options", "nosniff");
            response.setHeader("X-Frame-Options", "DENY");

            // 内容安全策略
            response.setHeader("Content-Security-Policy", 
                "default-src 'self'; script-src 'self' 'unsafe-inline'");

            // HSTS
            response.setHeader("Strict-Transport-Security", 
                "max-age=31536000; includeSubDomains");

            chain.doFilter(request, response);
        };
    }
}

📝 总结

本文详细介绍了 JWT+Spring Security 6 多终端认证系统的完整实现:

✅ JWT 原理: Token 结构 + 双 Token 机制 ✅ Security6 配置: 过滤器链 + 认证提供者 ✅ 登录流程: 完整的认证时序 ✅ 多终端: 设备识别 + 并发控制 ✅ 性能优化: 缓存策略 + 限流 ✅ 安全加固: 防篡改 + XSS/CSRF


如果这篇文章对你有帮助,请:

•👍 点赞支持•⭐ 收藏备用•💖 关注我,获取更多认证安全干货•💬 评论区聊聊:你在认证系统中遇到过哪些坑?

🚀 祝您的认证系统安全稳定!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-03-12,如有侵权请联系 cloudcommunity@tencent.com 删除
目录
  • JWT+Spring Security 6 多终端认证系统实战
  • 🎯 一、为什么选择 JWT?
    • 1.1 传统 Session 认证的痛点
    • 1.2 JWT 的优势
  • 🔐 二、JWT Token 设计
    • 2.1 Token 结构设计
    • 2.2 双 Token 机制
  • 🔧 三、Spring Security 6 配置
    • 3.1 安全配置类
    • 3.2 JWT 认证过滤器
  • 💡 四、登录认证流程
    • 4.1 登录接口实现
    • 4.2 认证 Service 实现
  • 🚀 五、多终端支持
    • 5.1 设备类型识别
    • 5.2 多终端登录策略
  • 📊 六、性能优化
    • 6.1 Token 缓存策略
    • 6.2 并发登录控制
  • 🛡️ 七、安全加固
    • 7.1 Token 防篡改
    • 7.2 XSS 和 CSRF 防护
  • 📝 总结
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档