创建公共控制器,方便所有继承者都能够从中方便的获取用户信息
/**
* 公共控制器以方便继承者获取用户信息
*
* @Author xiang;
* @Date 2020/12/17 15:54
*/
public class BaseController {
protected Long userId;
protected String name;
protected String unionid;
public void init() {
this.userId = UserContext.getUser().getUserId();
this.unionid = UserContext.getUser().getUnionid();
}
}
创建token解析用户信息存储类
/**
* token解析用户信息储存类
*
* @author xyl
* @date 2023/4/25
*/
@Data
public class LoginInfo {
/**
* 平台统一ID
*/
private String unionid;
/**
* 登录类型
*/
private String userType;
/**
* 用户ID
*/
private Long userId;
}
利用thradLocal 存储用户信息
客户端发送的每次Http请求,对应的在服务端都会分配一个新的线程来处理,在处理过程中设计到的后端代码都属于同一个线程
多线程访问同一个共享变量的时候容易出现并发问题,特别是多个线程对一个变量进行写入的时候,为了保证线程安全,一般使用者在访问共享变量的时候需要进行额外的同步措施才能保证线程安全性
ThreadLocal是JDK包提供的,它提供线程本地变量,如果创建一乐ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个副本,在实际多线程操作的时候,操作的是自己本地内存中的变量,从而规避了线程安全问题,如下图所示
/**
* 记录用户信息
*
* @author xyl
* @date 2023/4/25
*/
public class UserContext {
/**
* ThreadLocal对象常用于防止对可变的单实例变量或全局变量进行共享。set和get方法为每个使用该变量的线程都保存一份独立的副本,因此,
* get总是返回当前线程在调用set时设置的最新值。
* 避免每次调用方法时都需要传递上下文信息(该处使用的是ThreadLocal的这个用法)。
*/
private static final ThreadLocal<LoginInfo> userHolder = new ThreadLocal<LoginInfo>();
public static void setUser(LoginInfo user) {
userHolder.set(user);
}
public static LoginInfo getUser() {
return userHolder.get();
}
public static void removeUser() {
userHolder.remove();
}
}
利用切面在用户访问时先写入,这边截取部分代码展示,大多校验token时写入
/**
* 校验token
*
* @param request
*/
private void validateToken(HttpServletRequest request) {
//获取前端传来的Token
String token = request.getHeader("token");
if (token == null) {
throw new TokenException(Constant.TOKEN_ERROR);
}
//解析token
Claims claims = JwtUtil.parseJWT(token);
//获取用户名称
String username = (String) claims.get("name");
//获取用户ID
long id = Long.parseLong(claims.get("id").toString());
//用户登录类型 0:后台用户 1:前台用户
String userType = claims.get("userType").toString();
//获取统一平台ID
String unionid = claims.get("unionid").toString();
//写入用户解析存储类
LoginInfo loginInfo = new LoginInfo();
loginInfo.setUserType(userType);
loginInfo.setUserId(id);
loginInfo.setUnionid(unionid);
//记录该现场下用户信息副本
UserContext.setUser(loginInfo);
Object o = redisUtil.get(RedisKey.USER_TOKEN + claims.get("userType").toString() + "_" + id);
如何调用,当然在其他地方也都可以调用,只要在这次请求的线程内,还可以应用在一些不能够更改传参的方法里
@RestController
@RequestMapping("/api/authUser")
@Api(value = "个人用户相关", tags = "个人用户相关")
@Auth
public class AuthUserController extends BaseController {
@Autowired
private ITyUserService service;
@Autowired
private ContactsService contactsService;
@Resource
private VisitorService visitorService;
@ApiOperation("获取用户详情")
@GetMapping("findById")
@PreAuthorize("hasRole('ROLE_NORMAL')")
public BaseResponse<UnifyUserVo> findById() {
init();
return BaseResponse.success(service.findByAoYangUnionid(unionid));
}
}