SecurityContextHolder简介说明
下文笔者讲述Spring Security中SecurityContextHolder简介说明,如下所示
SecurityContextHolder简介
SecurityContextHolder是SpringSecurity最基础组件
用于存放SecurityContext对象
默认使用ThreadLocal实现
保证本线程内所有的方法都可以获得SecurityContext对象
SecurityContextHolder 有其他两种模式
分别SecurityContextHolder.MODE_GLOBAL 和 SecurityContextHolder.MODE_INHERITABLETHREADLOCAL
SecurityContextHolder.MODE_GLOBAL表示SecurityContextHolder对象的全局的,应用中所有线程都可以访问
SecurityContextHolder.MODE_INHERITABLETHREADLOCAL用于线程有父子关系的情境中,线程希望自己的子线程和自己有相同的安全性
// 获取安全上下文对象 ,此处同Shiro一样保存在ThreadLocal中
//如果不存在,则创建一个authentication属性为null的empty安全上下文对象
SecurityContext securityContext = SecurityContextHolder.getContext();
// 获取当前认证principal(当事人),或request token (令牌)
// 如果没有认证,会是 null,该例子是认证之后的情况
Authentication authentication = securityContext.getAuthentication()
//获取当事人信息对象,返回结果是Object类型
//实际上可以是应用程序自定义带有更多应用相关信息的某个类型
//该对象是 Spring Security 核心接口 UserDetails一个实现类
//数据库中保存一个用户信息到SecurityContextHolder中Spring Security需要的用户信息格式
// 一个适配器。
Object principal = authentication.getPrincipal();
if (principal instanceof UserDetails) {
String username = ((UserDetails)principal).getUsername();
} else {
String username = principal.toString();
}
修改SecurityContextHolder的工作模式
综上所述,SecurityContextHolder可以工作在以下三种模式之一:
1.MODE_THREADLOCAL(缺省工作模式)
2.MODE_GLOBAL
3.MODE_INHERITABLETHREADLOCAL
4.修改SecurityContextHolder的工作模式有两种方法
a:设置一个系统属性(system.properties):spring.security.strategy;
SecurityContextHolder会自动从该系统属性中尝试获取被设定的工作模式
b:调用SecurityContextHolder静态方法setStrategyName()
程序化方式主动设置工作模式的方法
SecurityContextHolder存储SecurityContext的方式(默认就是mode_threadlocal)
1.单机系统
即应用从开启到关闭的整个生命周期只有一个用户在使用
由于整个应用只需要保存一个SecurityContext(安全上下文即可)
2.多用户系统
如Web系统
整个生命周期可能同时有多个用户在使用
这时候应用需要保存多个SecurityContext(安全上下文)
需要利用ThreadLocal进行保存
每个线程都可以利用ThreadLocal
获取其自己SecurityContext,及安全上下文。
如
使用jwt实现登录功能时候
可将用户信息保存在安全上下文中
@Override
public String login(String username, String password) {
String token = null;
//密码需要客户端加密后传递
try {
UserDetails userDetails = loadUserByUsername(username);
if(!password.equals(BasicEncryptUtils.decryptByRSA(userDetails.getPassword()))){
throw new ServiceException("password.is.not.correct", ResultCode.VALIDATE_FAILED.getCode());
}
//根据userDetails构建新的Authentication
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
//存放authentication到SecurityContextHolder
SecurityContextHolder.getContext().setAuthentication(authentication);
//使用jwt生成token
token = jwtTokenUtil.generateToken(userDetails, RsaUtils.getPrivateKey(propertiesUtils.getPrivateKeyUrl()));
//到redis中查看该账号是否之前已经登录
if (redisUtil.getValueByKey(CommonConstant.ormis_online_login_user_info+username)!=null){
//删掉对应的数据
redisUtil.deleteCache(CommonConstant.ormis_online_login_user_info+username);
}
//重新存储
redisUtil.set(CommonConstant.ormis_online_login_user_info+username,token,propertiesUtils.getExpiration());
} catch (Exception e) {
log.error("login error is {}", e.getMessage());
throw new ServiceException(e.getMessage(), ResultCode.VALIDATE_FAILED.getCode());
}
return token;
}
版权声明
本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。


