threadlocal定义--定义什么的看看就行了==================
ThreadLocal 是 Java 中的一个类,它提供了线程局部变量。为每个线程分配独立的变量副本来工作。每个线程在访问 ThreadLocal 变量时,都会获取该线程特有的值,而不会影响或被其他线程的值影响。
就是说threadlocal就像线程的口袋,每个线程都有口袋,可以往里面放东西,也可以取东西,你的口袋是你的,我的口袋是我的,我的口袋跟你没关系,你的口袋跟我也没关系。
应用场景:========================================
在单体架构项目中,有许多情况都需要用到--当前用户身份信息,比如说删除某个数据我要判断当前登录用户是否有权限删除,比如说购物环境中不可能为每个用户都创建一个购物车表,所有的用户共用一个购物车表,用户查询购物车时就要根据当前用户信息查询出该用户的购物车数据,总不能把表中所有的购物车商品都查出来把。
想要获取当前登录用户信息,肯定要在登录时做些手脚,将用户信息保存下来。保存在令牌中,然后每次请求都可以携带到后端,后端再去存储,这样在不同的线程中都能获取到当前用户信息了。
实现=============================================
我们先看一下jwt令牌的工作流程:
用户首先进行登录==》
请求到后端controller==》
(用户名密码正确)生成令牌返回给前端==》
前端保存令牌并在以后每次请求中携带令牌
用户发起其他请求(请求头中携带了令牌)==》
后端拦截器拦截用户请求==》
验证请求中令牌是否合法==》
合法放行,不合法拒绝访问
原文链接:https://blog.csdn.net/weixin_75001968/article/details/138923794
================================================
在生成令牌时将用户信息保存在其中:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
public class AuthController {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtUtil jwtUtil;
@Autowired
private UserMapper userMapper;
@PostMapping("/login")
public Map<String, String> login(@RequestBody AuthRequest authRequest) {
try {
//验证用户名密码是否正确--查询数据库
User user = userMapper.select(authRequest);
//密码正确才解析令牌,否则直接返回
if(user == null) throw new Exception("用户名或者密码不正确!");
//生成令牌-userId作为数据载荷部分
//如果username唯一,将username作为数据载荷部分也是一样
String token = jwtUtil.generateToken(authRequest.getUserId());
//将生成的令牌返回给前端
Map<String, String> response = new HashMap<>();
response.put("token", token);
return response;
} catch (AuthenticationException e) {
throw new RuntimeException("Invalid login credentials");
}
}
}
class AuthRequest {
private String username;
private String password;
// getters and setters
}
解析令牌时将用户信息(userId)存入threadlocal
1.创建threadlocal类:
public class UserContext {
private static final ThreadLocal<String> currentUser = new ThreadLocal<>();
public static void setCurrentUser(Long userId) {
currentUser.set(userId);
}
public static Long getCurrentUser() {
return currentUser.get();
}
public static void clear() {
currentUser.remove();
}
}
2.修改jwt工具类
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.SignatureException;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.MalformedJwtException;
import java.util.Date;
public class JwtUtil {
private static final String SECRET_KEY = "yourSecretKey"; // 替换为你的密钥
public static String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 86400000)) // 1 天有效期
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
public static boolean validateToken(String token) {
try {
Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token);
return true;
} catch (SignatureException | ExpiredJwtException | MalformedJwtException e) {
return false;
}
}
public static Claims getClaims(String token) {
return Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();
}
}
3.修改jwt拦截器
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import io.jsonwebtoken.Claims;
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 {
// 获取到请求头中的令牌
String authorizationHeader = request.getHeader("Authorization");
// 校验令牌是否合法
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
String token = authorizationHeader.substring(7);
if (jwtUtil.validateToken(token)) {
// 解析用户信息并存入 ThreadLocal
Claims claims = jwtUtil.getClaims(token);
Long userId= claims.getSubject();
//将用户id存入threadlocal
UserContext.setCurrentUser(userId);
return true;
}
}
// 返回不合法信息
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid or missing Authorization header");
return false;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 清理 ThreadLocal,防止内存泄漏
UserContext.clear();
}
}
4.使用
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api")
public class ApiController {
@GetMapping("/userinfo")
public Map<String, String> getUserInfo() {
//从threadlocal中获取用户信息--userId
String username = UserContext.getCurrentUser();
Map<String, String> response = new HashMap<>();
response.put("username", username);
return response;
}
}