ThreadLocal的应用
springsecurity中通过线程上下文存储认证信息
在我的个人博客项目中引入了springsecurity做认证授权处理,但是springsecurity原生内置的认证过滤器基本上是跟登录操作强绑定的,也就是UsernamePasswordAuthenticationFilter
每次请求都需要输入用户名和密码才能进行认证,而我项目中采用jwt保存登录状态来进行认证,所以我们自定义一个public class TokenAuthenticationFiliter extends OncePerRequestFilter过滤器
然后通过
http.addFilterBefore(new TokenAuthenticationFiliter(), UsernamePasswordAuthenticationFilter.class)
将其添加到UsernamePasswordAuthenticationFilter过滤器的前面
在TokenAuthenticationFiliter 中我们进行token解析之后,将提取出来的用户名和权限信息存入UsernamePasswordAuthenticationToken中,然后将UsernamePasswordAuthenticationToken存入SecurityContextHolder所持有的SecurityContext中,存入当前线程上下文,以供后续过滤器使用,而无需多次解析jwt影响性能
将从jwt中提取的信息存入自定义的UserContext中
springsecurity的整个 过滤器链对于后续springmvc中的handler方法是不可见的,在security的过滤器链中解析的token信息我们有必要存入线程上下文,以方便controller层的方法去获取这些信息进行相关处理
所以这里我们先自定义一个接口UserContext
public interface UserContext extends Serializable {
MyUserVO getMyUserVO();
// 对用户进行赋值的方法
void setCurrentUserVO(MyUserVO myUserVO);
// 获取变量
Object getProperty(Object var);
// 添加变量
void addProperty(Object varKey, Object varValue);
// 移出变量
void removeProperty(Object var);
// 获取所有变量
Map<Object, Object> getProperties();
// 初始化变量
void setProperties(Map<Object, Object> map);
// 移出所有变量
void removeAllProperties();
// 添加业务变量
void addBusiProperty(Object busiVarKey, Object busiVarValue);
// 获取业务变量
Object getBusiProperty(Object busiVarKey);
// 获取所有业务变量
Map<Object, Object> getBusiProperties();
// 初始化业务变量
void setBusiProperties(Map<Object, Object> map);
}
然后再定义一个类去实现它
/**
* 用户信息的上下文信息
*/
public class UserContextImpl implements UserContext {
private static final long serialVersionUID = 8383356012441014698L;
// 用户类
private MyUserVO myUserVO = new MyUserVO();
// 变量
private Map<Object, Object> properties = new HashMap<>();
// 业务变量
private Map<Object, Object> busiProperties = new HashMap<>();
public UserContextImpl() {
}
@Override
public MyUserVO getMyUserVO() {
return this.myUserVO;
}
@Override
public void setCurrentUserVO(MyUserVO myUserVO) {
this.myUserVO = myUserVO;
}
@Override
public Object getProperty(Object var) {
return this.properties;
}
@Override
public void addProperty(Object varKey, Object varValue) {
this.properties.put(varKey, varValue);
}
@Override
public void removeProperty(Object var) {
this.properties.remove(var);
}
@Override
public Map<Object, Object> getProperties() {
return this.properties;
}
@Override
public void setProperties(Map<Object, Object> map) {
this.properties = map;
}
@Override
public void removeAllProperties() {
this.properties.clear();
}
@Override
public void addBusiProperty(Object busiVarKey, Object busiVarValue) {
this.busiProperties.put(busiVarKey, busiVarValue);
}
@Override
public Object getBusiProperty(Object busiVarKey) {
return this.busiProperties.get(busiVarKey);
}
@Override
public Map<Object, Object> getBusiProperties() {
return this.busiProperties;
}
@Override
public void setBusiProperties(Map<Object, Object> map) {
this.busiProperties = map;
}
}
再定义一个类UserContextHolder ,并且设置一个localthread成员变量,这才是我们保存线程上下文信息的核心,spring中其实大量使用了ThreadLocal来进行上下文保存一些重要信息
/**
* 管理存储用户信息的上下文
*/
public class UserContextHolder {
private static final ThreadLocal<UserContext> contextHolder = new ThreadLocal<>();
public UserContextHolder() {
}
public static void setContext(UserContext userContext){
contextHolder.set(userContext);
}
public static UserContext getContext(){
UserContext userContext=contextHolder.get();
if(userContext==null){
userContext=new UserContextImpl();
setContext(userContext);
}
return userContext;
}
}