401无Token错误全解析:从原理到实战的JWT认证指南

1次阅读
没有评论

共计 2468 个字符,预计需要花费 7 分钟才能阅读完成。

image.webp

当浏览器突然弹出 401

昨天测试同事又给我发了个截图——熟悉的红色错误提示框,带着那个刺眼的 {"code":401,"msg":"无 token,请重新登录","data":null}。这已经是本周第三次收到这类反馈了。用户正在填写的长表单突然失效,辛苦输入的数据瞬间消失,这种体验简直比断网还让人崩溃。

401 无 Token 错误全解析:从原理到实战的 JWT 认证指南

认证方式的世代更迭

Session 的烦恼

早些年用 Session-Cookie 方案时,我们得在服务端维护会话存储,每次请求都要查数据库验证 sessionId。当用户量上来后:

  • 服务器内存开始告急
  • 集群环境下要处理会话同步
  • 跨域场景下 Cookie 携带受限

JWT 的曙光

JWT(JSON Web Token)就像一张自带防伪标识的电子门票:

+---------------------+
| Header.Payload.Sign |
+---------------------+

实际解码后的样子:

{
  "alg": "HS256",
  "typ": "JWT"
}
{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}

Spring Security 实战手册

核心工具类

public class JwtUtils {
    private static final String SECRET_KEY = "你的 256 位密钥";

    public static String generateToken(UserDetails user) {return Jwts.builder()
            .setSubject(user.getUsername())
            .setIssuedAt(new Date())
            .setExpiration(new Date(System.currentTimeMillis() + 3600000))
            .signWith(Keys.hmacShaKeyFor(SECRET_KEY.getBytes()))
            .compact();}

    public static boolean validateToken(String token) {
        try {Jwts.parserBuilder()
                .setSigningKey(SECRET_KEY.getBytes())
                .build()
                .parseClaimsJws(token);
            return true;
        } catch (Exception e) {
            // 具体异常处理逻辑
            return false;
        }
    }
}

认证过滤器

public class JwtFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                   HttpServletResponse response,
                                   FilterChain chain) throws IOException, ServletException {String token = request.getHeader("Authorization");

        if (token == null || !token.startsWith("Bearer")) {response.setContentType("application/json");
            response.getWriter().write("{\"code\":401,\"msg\":\" 无 token,请重新登录 \",\"data\":null}");
            return;
        }

        String jwt = token.substring(7);
        if (!JwtUtils.validateToken(jwt)) {
            // 处理过期 / 篡改等不同异常类型
            sendError(response, "Token 已失效");
            return;
        }

        chain.doFilter(request, response);
    }
}

安全进阶指南

Token 存储的抉择

  • LocalStorage
  • 容易被 XSS 攻击窃取
  • 需设置 HttpOnly=false

  • Cookie

  • 要处理 CSRF 防护
  • 推荐配置:
    ResponseCookie cookie = ResponseCookie.from("token", jwt)
        .httpOnly(true)
        .secure(true)
        .sameSite("Strict")
        .build();

双 Token 续期策略

  1. 登录时返回 accessToken(30 分钟) 和 refreshToken(7 天)
  2. accessToken 过期后,用 refreshToken 获取新 token
  3. refreshToken 过期则强制重新登录
// 刷新接口示例
@PostMapping("/refresh")
public ResponseEntity<?> refreshToken(@RequestBody RefreshRequest request) {if (redisTemplate.opsForValue().get(request.getRefreshToken()) == null) {throw new InvalidTokenException();
    }
    // 验证 refreshToken 有效性
    // 生成新的 accessToken
}

测试与优化

Postman 测试集

1. 未带 Token 请求受保护接口
   - 预期:返回 401 错误

2. 使用过期 Token 请求
   - 预期:返回 401 并提示 "Token 已过期"

3. 使用正确 Token 请求
   - 预期:正常返回业务数据 

性能压测建议

  • 使用 JMeter 模拟 1000 并发验证认证模块
  • 重点关注:
  • Token 校验的响应时间
  • 异常情况下的错误率
  • 内存泄漏风险

思考与延伸

当我们需要在分布式系统中实现立即失效某个 Token 时(如用户主动登出),可以考虑:

  • 将黑名单存入 Redis 并设置 TTL
  • 使用 JWT 的 jti(JWT ID)作为唯一标识
  • 每次校验时额外检查黑名单

推荐扩展阅读:
RFC 7519
OAuth 2.0 与 JWT 的关系
Spring Security 官方文档

下次当你再看到 401 错误时,希望想到的不只是简单的重新登录,而是背后这套精密的认证机制在守护着系统安全。

正文完
 0
评论(没有评论)