Files
number/后端架构设计/11-通用基础设施-part2-JWT与拦截器.md
2026-03-17 12:09:43 +08:00

175 lines
4.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 通用基础设施开发文档 - Part 2JWT + UserContext + 拦截器)
---
## 一、JwtUtil.java
```java
package com.openclaw.util;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.security.Key;
import java.util.Date;
@Component
public class JwtUtil {
private final Key key;
private final long expireMs;
public JwtUtil(
@Value("${jwt.secret}") String secret,
@Value("${jwt.expire-ms}") long expireMs) {
this.key = Keys.hmacShaKeyFor(secret.getBytes());
this.expireMs = expireMs;
}
public String generate(Long userId, String role) {
return Jwts.builder()
.setSubject(userId.toString())
.claim("role", role)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + expireMs))
.signWith(key, SignatureAlgorithm.HS256)
.compact();
}
public Claims parse(String token) {
return Jwts.parserBuilder()
.setSigningKey(key).build()
.parseClaimsJws(token)
.getBody();
}
public Long getUserId(String token) {
return Long.parseLong(parse(token).getSubject());
}
public String getRole(String token) {
return parse(token).get("role", String.class);
}
}
```
```yaml
# application.yml
jwt:
secret: change-this-to-a-256-bit-random-secret-in-prod
expire-ms: 86400000 # 24 小时
```
---
## 二、UserContext.java
```java
package com.openclaw.util;
public class UserContext {
private static final ThreadLocal<Long> USER_ID = new ThreadLocal<>();
private static final ThreadLocal<String> ROLE = new ThreadLocal<>();
public static void set(Long userId, String role) {
USER_ID.set(userId);
ROLE.set(role);
}
public static Long getUserId() { return USER_ID.get(); }
public static String getRole() { return ROLE.get(); }
public static void clear() {
USER_ID.remove();
ROLE.remove();
}
}
```
---
## 三、AuthInterceptor.java
```java
package com.openclaw.interceptor;
import com.openclaw.constant.ErrorCode;
import com.openclaw.exception.BusinessException;
import com.openclaw.util.JwtUtil;
import com.openclaw.util.UserContext;
import jakarta.servlet.http.*;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
@Component
@RequiredArgsConstructor
public class AuthInterceptor implements HandlerInterceptor {
private final JwtUtil jwtUtil;
@Override
public boolean preHandle(HttpServletRequest req,
HttpServletResponse res,
Object handler) {
String auth = req.getHeader("Authorization");
if (auth == null || !auth.startsWith("Bearer "))
throw new BusinessException(ErrorCode.UNAUTHORIZED);
try {
String token = auth.substring(7);
Long userId = jwtUtil.getUserId(token);
String role = jwtUtil.getRole(token);
UserContext.set(userId, role);
} catch (Exception e) {
throw new BusinessException(ErrorCode.UNAUTHORIZED);
}
return true;
}
@Override
public void afterCompletion(HttpServletRequest req, HttpServletResponse res,
Object handler, Exception ex) {
UserContext.clear(); // 防止 ThreadLocal 内存泄漏
}
}
```
---
## 四、WebMvcConfig.java注册拦截器
```java
package com.openclaw.config;
import com.openclaw.interceptor.AuthInterceptor;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.*;
@Configuration
@RequiredArgsConstructor
public class WebMvcConfig implements WebMvcConfigurer {
private final AuthInterceptor authInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authInterceptor)
.addPathPatterns("/api/**")
.excludePathPatterns(
"/api/v1/users/register",
"/api/v1/users/login",
"/api/v1/users/sms-code",
"/api/v1/payments/callback/**",
"/api/v1/skills", // 公开浏览
"/api/v1/skills/{id}" // 公开详情
);
}
}
```
---
**文档版本**v1.0 | **创建日期**2026-03-16