全局动态数据
这里主要涉及到 SpringBoot 授权对话基于认证规范该怎么全局获取到玩家信息实体.
一般授权完成授权之后会以 Header 形式追加以下格式结构:
Authorization: <type> <credentials>
type 字段包含有:
Basic: 基本明文账号密码之后base64编码Bearer:OAuth|JWT授权机制, 目前最常见- ….., 其他可以自行了解
可以按照自身需求构建 JWT 或者直接采用唯一哈希对应放置在 Redis 关联即可,
用户重新登录会去 Redis 注销上一次的 token 映射.
言归正传就是当登录完全通过 HandlerInterceptor 检索出来 Token 在数据库|Redis查询到对应数据实体之后怎么让全局被访问.
网上很多说采用 ThreadLocal 直接线程本地变量保存, 这样其实最大问题是内部出现 Exception 会导致内存没办法回收,
最后到达一定量及就会出现大量内存泄露.
另外设置和释放都需要手动进行分配和注销, 比如在 HandlerInterceptor 当中:
public class HandlerInterceptorConfigurer implements HandlerInterceptor {
private static final ThreadLocal<UserModel> USER = new ThreadLocal<>();
/**
* 请求拦截
*/
@Override
public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler) throws Exception {
User.set(new UserModel()); // 读取加载线程本地变量
}
/**
* 响应拦截
*/
@Override
public void afterCompletion(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler, Exception ex) throws Exception {
User.remove();// 手动释放
}
}
只要内部出现 Exception 级别, 那么就无法保证 afterCompletion 会被拦截到, 所以就开始出现内存泄露.
SpringBoot 则提供 ThreadLocal 做底层的另外回收方案: RequestContextHolder.
RequestContextHolder 是内部与请求上下文绑定, 也就是每次访问都会由底层生成新的 RequestContextHolder 实例,
请求结束之后就会自动开始回收该实例数据对象, 好处就是直接底层帮你维护请求过程的全局变量, 坏处是不能跨线程和依赖 Spring.
那么之后按照自己要求定义请求过程的变量读取器:
/**
* 静态获取授权对象, 利用 Spring 自带的 RequestContextHolder 构建会话过程的全局玩家数据
*/
public class AuthorizedService {
private static final String AuthorizedAttributeUser = "AuthorizedUser";
private static final String AuthorizedAttributeToken = "AuthorizedToken";
public static void setUser(User user) {
RequestContextHolder.currentRequestAttributes().setAttribute(
AuthorizedAttributeUser,
user,
RequestAttributes.SCOPE_REQUEST
);
}
public static User getUser() {
Object handler = RequestContextHolder.currentRequestAttributes().getAttribute(
AuthorizedAttributeUser,
RequestAttributes.SCOPE_REQUEST
);
if (handler instanceof User) {
return (User) handler;
} else {
return null;
}
}
public static void removeUser() {
RequestContextHolder.currentRequestAttributes().removeAttribute(
AuthorizedAttributeUser,
RequestAttributes.SCOPE_REQUEST
);
}
public static void setToken(String token) {
RequestContextHolder.currentRequestAttributes().setAttribute(
AuthorizedAttributeToken,
token,
RequestAttributes.SCOPE_REQUEST
);
}
public static String getToken() {
Object handler = RequestContextHolder.currentRequestAttributes().getAttribute(
AuthorizedAttributeToken,
RequestAttributes.SCOPE_REQUEST
);
if (handler instanceof String) {
return (String) handler;
} else {
return null;
}
}
public static void removeToken() {
RequestContextHolder.currentRequestAttributes().removeAttribute(
AuthorizedAttributeToken,
RequestAttributes.SCOPE_REQUEST
);
}
}
这样就能全局保存好对应玩家登录实体数据, 不需要访问需要玩家实体都从数据库当中获取检索.