175 lines
4.6 KiB
Markdown
175 lines
4.6 KiB
Markdown
# 通用基础设施开发文档 - Part 2(JWT + 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
|