JWT token无效invalid signature

本文探讨了在使用Django和JWT进行身份验证时遇到的token无效问题,具体表现为invalidsignature错误。分析了JWT生成token时对username字段的依赖及唯一性的要求,提出了两种解决方案:一是确保username字段具有唯一性;二是重写UserManager的get_by_natural_key方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

django+ JWT 生成发token无效

请求需要验证的接口,返回

invalid signature

 

我的user模型是这样的

 

 

后面看https://www.jianshu.com/p/f0a55f39dfa8链接说,jwt生成token时默认是需要用到username,并且username需要保证唯一,或者重写UserManager的get_by_natural_key方法

所以解决方法有两个:

方法1.username需要修改为唯一约束,修改迁移后只对后续用户有效,以前同username的token还是会无效,修改username也还是会无效

 

方法2.

重写UserManager的get_by_natural_key方法,查询的必须是唯一值

 

<think>我们使用拦截器实现JWT Token登录验证的核心步骤: 1. 创建拦截器类,在preHandle方法中实现Token验证逻辑 2. 配置拦截器,指定拦截路径和排除路径(如登录接口) 3. 在拦截器中解析和验证Token,并将解析结果(如用户信息)存入请求属性,供后续使用 具体步骤: 步骤1:创建JWT工具类(用于生成和解析Token) 通常我们会有一个JWT工具类,用于生成Token和解析Token。这里假设我们已经有了一个JwtUtil类,它包含了解析Token的方法。 步骤2:创建登录验证拦截器 在拦截器的preHandle方法中,我们执行以下操作: a. 从请求头中获取Token(通常放在Authorization头中,格式为Bearer <token>) b. 检查Token是否存在和有效(非空且以Bearer开头) c. 提取Token字符串,使用JwtUtil解析Token d. 如果解析成功,说明Token有效,将解析出的用户信息(Claims)存入请求属性,以便后续Controller使用 e. 如果解析失败(如Token过期或无效),返回错误响应(如401未授权)并中断请求 步骤3:配置拦截器 在配置类中注册拦截器,并设置拦截路径(例如拦截所有请求)和排除路径(例如登录接口、注册接口等不需要验证的接口) 步骤4:在Controller中获取用户信息 在需要用户信息的Controller方法中,我们可以通过请求属性获取之前存入的用户信息。 下面我们详细展开: 1. JWT工具类(示例): 假设我们已经有一个JwtUtil类,其中包含了解析Token的方法parseToken,该方法返回Claims对象(包含用户信息)或者抛出异常。 2. 拦截器实现: ```java import io.jsonwebtoken.Claims; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Component public class JwtInterceptor implements HandlerInterceptor { @Autowired private JwtUtil jwtUtil; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 1. 从请求头中获取token String token = request.getHeader("Authorization"); // 2. 检查token格式 if (token == null || !token.startsWith("Bearer ")) { // 如果没有token或者格式不对,返回未授权错误 response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); response.getWriter().write("No token or token format error"); return false; } // 提取真正的token字符串(去掉Bearer前缀) String tokenStr = token.substring(7); try { // 3. 解析token Claims claims = jwtUtil.parseToken(tokenStr); // 4. 将解析出的claims存入请求属性,以便后续使用 request.setAttribute("claims", claims); return true; // 放行 } catch (Exception e) { // 解析失败,返回未授权 response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); response.getWriter().write("Invalid token: " + e.getMessage()); return false; } } } ``` 3. 配置拦截器(使用WebMvcConfigurer): ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebConfig implements WebMvcConfigurer { @Autowired private JwtInterceptor jwtInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(jwtInterceptor) .addPathPatterns("/**") // 拦截所有请求 .excludePathPatterns("/user/login", "/user/register"); // 排除登录和注册接口 } } ``` 4. 在Controller中使用: 在需要用户信息的地方,我们可以从请求属性中获取claims: ```java @RestController @RequestMapping("/user") public class UserController { @GetMapping("/info") public ResponseEntity<?> getUserInfo(HttpServletRequest request) { // 从请求属性中获取claims Claims claims = (Claims) request.getAttribute("claims"); String userId = claims.getSubject(); // 假设subject存储用户ID // 根据userId查询用户信息... return ResponseEntity.ok(userInfo); } } ``` 注意事项: - 在拦截器中,我们只做Token的格式检查和解析,不涉及数据库查询(验证Token的有效性由JWT的签名和过期时间保证)。如果需要进一步验证用户状态(如用户是否被禁用),可以在Controller中或通过另一个拦截器/过滤器进行。 - 排除路径一定要包括生成Token的登录接口,否则会形成死循环(因为登录接口也需要被拦截,但此时还没有Token)。 - 如果使用RESTful API,返回的错误信息应该是JSON格式,上面的示例中直接返回了字符串,实际应用中可以使用统一错误处理来返回JSON。 优化: 如果希望返回统一的JSON错误格式,可以在拦截器中构造一个JSON字符串返回,例如: ```java response.setContentType("application/json;charset=utf-8"); PrintWriter out = response.getWriter(); out.write("{\"code\": 401, \"message\": \"未授权:无效token\"}"); out.flush(); out.close(); ``` 另外,也可以使用全局异常处理来统一处理拦截器中抛出的异常,但注意在拦截器中抛出异常并不会被@ControllerAdvice捕获,所以需要在拦截器内部处理。 通过以上步骤,我们就在拦截器中实现了JWT Token的登录验证逻辑。 相关问题: 1. 如何生成JWT Token? 2. 如何在拦截器中实现更细粒度的权限控制(如角色验证)? 3. 如何刷新JWT Token? 4. JWT Token的安全性如何保障? 5. 拦截器和过滤器在实现登录验证时有什么区别?</think>### 在拦截器中实现 JWT Token 登录验证方案 在 Spring Boot 中使用拦截器实现 JWT Token 登录验证,需通过以下步骤实现: #### 1. **创建 JWT 工具类** 实现 Token 的生成、解析和验证逻辑[^1]: ```java import io.jsonwebtoken.*; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.util.Date; @Component public class JwtUtil { @Value("${jwt.secret}") private String secret; // 密钥,从配置读取 @Value("${jwt.expiration}") private long expiration; // 有效期(毫秒) // 生成Token public String generateToken(String userId) { return Jwts.builder() .setSubject(userId) // 用户标识 .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() + expiration)) .signWith(SignatureAlgorithm.HS512, secret) .compact(); } // 解析Token public Claims parseToken(String token) { return Jwts.parser() .setSigningKey(secret) .parseClaimsJws(token) .getBody(); } // 验证Token public boolean validateToken(String token) { try { parseToken(token); // 解析成功即有效 return true; } catch (JwtException | IllegalArgumentException e) { return false; // Token无效或过期 } } } ``` #### 2. **创建 JWT 验证拦截器** 在 `preHandle` 中实现验证逻辑[^2]: ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class JwtAuthInterceptor implements HandlerInterceptor { @Autowired private JwtUtil jwtUtil; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 1. 从Header获取Token (格式: Authorization: Bearer <token>) String tokenHeader = request.getHeader("Authorization"); if (tokenHeader == null || !tokenHeader.startsWith("Bearer ")) { response.sendError(401, "缺少有效的Token凭证"); return false; // 中断请求 } // 2. 提取Token字符串 String token = tokenHeader.substring(7); // 3. 验证Token有效性 if (!jwtUtil.validateToken(token)) { response.sendError(401, "Token无效或已过期"); return false; } // 4. 解析Token并将用户信息存入请求属性 Claims claims = jwtUtil.parseToken(token); request.setAttribute("userClaims", claims); // 供Controller使用 return true; // 验证通过,放行请求 } } ``` #### 3. **注册拦截器配置** 配置拦截路径和排除路径[^4]: ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebConfig implements WebMvcConfigurer { @Autowired private JwtAuthInterceptor jwtAuthInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(jwtAuthInterceptor) .addPathPatterns("/api/**") // 拦截API路径 .excludePathPatterns("/api/login") // 排除登录接口 .excludePathPatterns("/api/public/**"); // 排除公开接口 } } ``` #### 4. **在 Controller 中使用验证结果** 通过请求属性获取用户信息[^4]: ```java @RestController @RequestMapping("/api/user") public class UserController { @GetMapping("/profile") public ResponseEntity<?> getUserProfile(HttpServletRequest request) { // 从拦截器设置的属性获取用户信息 Claims claims = (Claims) request.getAttribute("userClaims"); String userId = claims.getSubject(); // 获取用户ID // 查询用户信息... return ResponseEntity.ok(userProfile); } } ``` #### 5. **登录接口生成 Token** ```java @RestController public class AuthController { @Autowired private JwtUtil jwtUtil; @PostMapping("/api/login") public ResponseEntity<?> login(@RequestBody LoginRequest request) { // 1. 验证用户名密码(伪代码) User user = userService.authenticate(request); // 2. 生成Token String token = jwtUtil.generateToken(user.getId()); // 3. 返回Token return ResponseEntity.ok(Map.of("token", token)); } } ``` ### 关键实现细节 1. **Token 存储位置**: - 推荐使用 `Authorization: Bearer <token>` 请求头传递 - 避免 URL 参数传递(防止泄露) 2. **验证逻辑优化**: ```java // 在拦截器中添加详细错误处理 try { Claims claims = jwtUtil.parseToken(token); request.setAttribute("userClaims", claims); } catch (ExpiredJwtException ex) { response.sendError(401, "Token已过期"); return false; } catch (JwtException ex) { response.sendError(401, "无效Token"); return false; } ``` 3. **安全性增强**: - 使用 HTTPS 传输 - Token 设置合理有效期(通常 2-24 小时) - 敏感操作要求重新认证 4. **性能考虑**: - JWT 验证是本地计算($O(1)$ 复杂度) - 避免在拦截器中进行数据库查询 ### 测试流程 1. 调用 `/api/login` 获取 Token 2. 访问受限接口时携带 Token: ```bash curl -H "Authorization: Bearer <your_token>" http://localhost:8080/api/user/profile ``` ### 常见问题解决 - **Token 过期**:客户端需重新登录获取新 Token - **跨域问题**:确保前端配置 `Access-Control-Expose-Headers: Authorization` - **分布式部署**:所有实例使用相同的 JWT 密钥 ### 相关优化方向 1. 结合 Redis 实现 Token 黑名单(用于立即失效) 2. 实现 Token 自动刷新机制 3. 添加双因素认证支持 --- **相关问题** 1. 如何实现 JWT Token 的自动刷新机制? 2. 在微服务架构中如何统一处理 JWT 认证? 3. JWT 和 OAuth2 在认证方案中如何选择? 4. 如何防止 JWT Token 被窃取后的安全问题? 5. 拦截器中如何处理 Token 失效时的统一错误响应?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值