shiro_Realm缓存机制
源码下载地址:配套源码地址
redission集成
yaml文件中redis配置部分
framework:
shiro:
redis:
nodes: redis://192.168.1.112:6379,redis://192.168.1.112:6380,redis://192.168.1.112:6381,redis://192.168.1.112:7379,redis://192.168.1.112:7380,redis://192.168.1.112:7381
connect-timeout: 6000
connect-pool-size: 150
connection-minimumidle-size: 30
timeout: 6000
global-session-timeout: 360000
添加ShiroRedisProperites
此类主要负责yaml文件的配置类。
package com.itheima.shiro.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.io.Serializable;
/**
* @Description redis配置文件
*/
@Data
@ConfigurationProperties(prefix = "itheima.framework.shiro.redis")
public class ShiroRedisProperties implements Serializable {
/**
* redis连接地址
*/
private String nodes ;
/**
* 获取连接超时时间
*/
private int connectTimeout ;
/**
* 最小空闲连接数
*/
private int connectPoolSize;
/**
* 最大连接数
*/
private int connectionMinimumidleSize ;
/**
* 等待数据返回超时时间
*/
private int timeout ;
/**
* 全局超时时间
*/
private long globalSessionTimeout;
}
在shiroConfig中声明
这里的redis仅供认证鉴权使用,业务使用的redis需要另外配置,因为认证鉴权操作非常频繁,因此在使用中最好和业务的redis分开。
package com.itheima.shiro.config;
import com.itheima.shiro.core.ShiroDbRealm;
import com.itheima.shiro.core.filter.RolesOrAuthorizationFilter;
import com.itheima.shiro.core.impl.ShiroDbRealmImpl;
import com.itheima.shiro.properties.PropertiesUtil;
import lombok.extern.log4j.Log4j2;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* @Description 权限配置类
*/
@Configuration
@ComponentScan(basePackages = "com.itheima.shiro.core")
@EnableConfigurationProperties({ShiroRedisProperties.class})
@Log4j2
public class ShiroConfig {
// 注入配置
@Autowired
private ShiroRedisProperties shiroRedisProperties;
@Bean("redissonClientForShiro")
public RedissonClient redissonClient(){
//获取当前redis节点信息
String[] nodes = shiroRedisProperties.getNodes().split(",");
//创建配置信息:1、单机redis配置 2、集群redis配置
Config config = new Config();
if (nodes.length==1){
//1、单机redis配置
config.useSingleServer().setAddress(nodes[0])
.setConnectTimeout(shiroRedisProperties.getConnectTimeout())
.setConnectionMinimumIdleSize(shiroRedisProperties.getConnectionMinimumidleSize())
.setConnectionPoolSize(shiroRedisProperties.getConnectPoolSize())
.setTimeout(shiroRedisProperties.getTimeout());
}else if(nodes.length>1) {
//2、集群redis配置
config.useClusterServers().addNodeAddress(nodes)
.setConnectTimeout(shiroRedisProperties.getConnectTimeout())
.setMasterConnectionMinimumIdleSize(shiroRedisProperties.getConnectionMinimumidleSize())
.setMasterConnectionPoolSize(shiroRedisProperties.getConnectPoolSize())
.setTimeout(shiroRedisProperties.getTimeout());
}else {
return null;
}
//创建redission的客户端,交于spring管理
RedissonClient client = Redisson.create(config);
return client;
}
}
定义缓存对象SimpleMapCache
package com.itheima.shiro.core.base;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.util.CollectionUtils;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
/**
* @Description:实现Cache、Serializable 接口
*/
public class SimpleMapCache implements Cache<Object,Object>, Serializable {
/**
* Backing instance.
*/
private final Map<Object, Object> map;
/**
* The name of this cache.
*/
private final String name;
public SimpleMapCache(String name, Map<Object, Object> backingMap) {
if (name == null) {
throw new IllegalArgumentException("Cache name cannot be null.");
}
if (backingMap == null) {
throw new IllegalArgumentException("Backing map cannot be null.");
}
this.name = name;
this.map = backingMap;
}
public Object get(Object key) throws CacheException {
return map.get(key);
}
public Object put(Object key, Object value) throws CacheException {
return map.put(key, value);
}
public Object remove(Object key) throws CacheException {
return map.remove(key);
}
public void clear() throws CacheException {
map.clear();
}
public int size() {
return map.size();
}
public Set<Object> keys() {
Set<Object> keys = map.keySet();
if (!keys.isEmpty()) {
return Collections.unmodifiableSet(keys);
}
return Collections.emptySet();
}
public Collection<Object> values() {
Collection<Object> values = map.values();
if (!CollectionUtils.isEmpty(values)) {
return Collections.unmodifiableCollection(values);
}
return Collections.emptySet();
}
@Override
public String toString() {
return "SimpleMapCache{" +
"map=" + map +
", name='" + name + '\'' +
'}';
}
}
序列化工具
为了把对象转换成字符串来缓存到redis ;从redis读取的字符串转换成对象。
package com.itheima.shiro.utils;
import lombok.extern.log4j.Log4j2;
import java.io.*;
/**
* @Description:自定义序列化工具
*/
@Log4j2
public class ShiroRedissionSerialize {
//序列化方法
public static String serialize(Object object){
//判断对象是否为空
if (EmptyUtil.isNullOrEmpty(object)){
return null;
}
//流的操作
ByteArrayOutputStream bos = null;
ObjectOutputStream oos =null;
String encodeBase64 = null;
bos = new ByteArrayOutputStream();
try {
oos = new ObjectOutputStream(bos);
oos.writeObject(object);
//转换字符串
encodeBase64 = EncodesUtil.encodeBase64(bos.toByteArray());
} catch (IOException e) {
e.printStackTrace();
log.error("流写入异常:{}",e);
}finally {
//关闭流
try {
bos.close();
oos.close();
} catch (IOException e) {
e.printStackTrace();
log.error("流写入异常:{}",e);
}
}
return encodeBase64;
}
//反序列化方法
public static Object deserialize(String str){
//判断是否为空
if (EmptyUtil.isNullOrEmpty(str)){
return null;
}
//流从操作
ByteArrayInputStream bis =null;
ObjectInputStream ois = null;
Object object = null;
//转换对象
bis = new ByteArrayInputStream(EncodesUtil.decodeBase64(str));
try {
ois = new ObjectInputStream(bis);
object = ois.readObject();
} catch (Exception e) {
e.printStackTrace();
log.error("流读取异常:{}",e);
}finally {
//关闭流
try {
bis.close();
ois.close();
} catch (IOException e) {
e.printStackTrace();
log.error("流读取异常:{}",e);
}
}
return object;
}
}
缓存服务接口SimpleCacheService
主要是为了新增、更新、移除、获取缓存
SimpleCacheService 接口
package com.itheima.shiro.core;
import org.apache.shiro.cache.Cache;
/**
* @Description:实现缓存管理服务
*/
public interface SimpleCacheService {
/**
* @Description 创建缓存
* @param cacheName 缓存名称
* @param cache 缓存对象
* @return
*/
void creatCache(String cacheName, Cache<Object,Object> cache);
/**
* @Description 获得缓存
* @param cacheName 缓存名称
* @return 缓存对象
*/
Cache<Object,Object> getCache(String cacheName);
/**
* @Description 删除缓存
* @param cacheName 缓存名称
* @return
*/
void removeCache(String cacheName);
/**
* @Description 更新缓存
* @param cacheName 缓存名称
* @param cache 新的缓存对象
* @return
*/
void updateCache(String cacheName,Cache<Object,Object> cache);
}
SimpleCacheServiceImpl 实现类
package com.itheima.shiro.core.impl;
import com.itheima.shiro.core.SimpleCacheService;
import com.itheima.shiro.utils.ShiroRedissionSerialize;
import com.itheima.shiro.utils.ShiroUtil;
import org.apache.shiro.cache.Cache;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
/**
* @Description:实现缓存管理服务
*/
@Component
public class SimpleCacheServiceImpl implements SimpleCacheService {
@Resource(name = "redissonClientForShiro")
RedissonClient redissonClient;
@Override
public void creatCache(String cacheName, Cache<Object, Object> cache) {
RBucket<String> bucket = redissonClient.getBucket(cacheName);
// 缓存中没有则创建,有则更新
bucket.trySet(ShiroRedissionSerialize.serialize(cache), ShiroUtil.getShiroSession().getTimeout()/1000, TimeUnit.SECONDS);
}
@Override
public Cache<Object, Object> getCache(String cacheName) {
RBucket<String> bucket = redissonClient.getBucket(cacheName);
return (Cache<Object, Object>) ShiroRedissionSerialize.deserialize(bucket.get());
}
@Override
public void removeCache(String cacheName) {
RBucket<String> bucket = redissonClient.getBucket(cacheName);
bucket.delete();
}
@Override
public void updateCache(String cacheName, Cache<Object, Object> cache) {
RBucket<String> bucket = redissonClient.getBucket(cacheName);
bucket.set(ShiroRedissionSerialize.serialize(cache), ShiroUtil.getShiroSession().getTimeout()/1000, TimeUnit.SECONDS);
}
}
ShiroUtil工具类 (说明)
/*
* <b>文件名</b>:ShiroUtil.java
*
* 文件描述:
*
*
* 2017-10-11 下午2:28:25
*/
package com.itheima.shiro.utils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
/**
* @Description shiro工具类
*/
public class ShiroUtil {
/**
* @Description 获得shiro的session
* @param
* @return
*/
public static Session getShiroSession() {
return SecurityUtils.getSubject().getSession();
}
/**
* @Description 获得shiro的sessionId
* @param
* @return
*/
public static String getShiroSessionId() {
return getShiroSession().getId().toString();
}
/**
* @Description 是否登陆
* @param
* @return
*/
public static Boolean isAuthenticated(){
Subject subject = SecurityUtils.getSubject();
return subject.isAuthenticated();
}
}
整合桥接器BridgeService
UserBridgeService接口
package com.itheima.shiro.core.bridge;
import com.itheima.shiro.core.base.ShiroUser;
import com.itheima.shiro.pojo.User;
import org.apache.shiro.authz.AuthorizationInfo;
import java.util.List;
/**
* @Description:用户信息桥接(后期会做缓存)
*/
public interface UserBridgeService {
/**
* @Description 查找用户信息
* @param loginName 用户名称
* @return user对象
*/
User findUserByLoginName(String loginName);
/**
* @Description 鉴权方法
* @param shiroUser 令牌对象
* @return 鉴权信息
*/
AuthorizationInfo getAuthorizationInfo(ShiroUser shiroUser);
/**
* @Description 查询用户对应角色标识list
* @param userId 用户id
* @return 角色标识集合
*/
List<String> findRoleList(String key,String userId);
/**
* @Description 查询用户对应资源标识list
* @param userId 用户id
* @return 资源标识集合
*/
List<String> findResourcesList(String key,String userId);
/**
* @Description 查询资源ids
* @param userId 用户id
* @return 资源id集合
*/
List<String> findResourcesIds(String userId);
/**
* @Description 登录成后加载缓存信息
* @param shiroUser 令牌对象
* @return
*/
void loadUserAuthorityToCache(ShiroUser shiroUser);
}
UserBridgeServiceImpl实现类
package com.itheima.shiro.core.bridge.impl;
import com.itheima.shiro.constant.CacheConstant;
import com.itheima.shiro.core.SimpleCacheService;
import com.itheima.shiro.core.adapter.UserAdapter;
import com.itheima.shiro.core.base.ShiroUser;
import com.itheima.shiro.core.base.SimpleMapCache;
import com.itheima.shiro.core.bridge.UserBridgeService;
import com.itheima.shiro.pojo.Resource;
import com.itheima.shiro.pojo.Role;
import com.itheima.shiro.pojo.User;
import com.itheima.shiro.utils.EmptyUtil;
import com.itheima.shiro.utils.ShiroUtil;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cache.Cache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @Description:用户信息桥接(后期会做缓存)
*/
@Component("userBridgeService")
public class UserBridgeServiceImpl implements UserBridgeService {
@Autowired
UserAdapter userAdapter;
@Autowired
SimpleCacheService simpleCacheService;
@Override
public User findUserByLoginName(String loginName) {
String key = CacheConstant.FIND_USER_BY_LOGINNAME + loginName;
//获取缓存
Cache<Object, Object> cache = simpleCacheService.getCache(key);
//缓存存在
if (!EmptyUtil.isNullOrEmpty(cache)){
return (User) cache.get(key);
}
//缓存不存在
User user = userAdapter.findUserByLoginName(loginName);
if (!EmptyUtil.isNullOrEmpty(user)){
Map<Object,Object> map = new HashMap<>();
map.put(key, user);
SimpleMapCache simpleMapCache = new SimpleMapCache(key, map);
simpleCacheService.creatCache(key, simpleMapCache);
}
return user;
}
@Override
public List<String> findResourcesIds(String userId) {
String sessionId = ShiroUtil.getShiroSessionId();
String key = CacheConstant.RESOURCES_KEY_IDS+sessionId;
List<Resource> resources = new ArrayList<>();
//获取缓存
Cache<Object, Object> cache = simpleCacheService.getCache(key);
//缓存存在
if (!EmptyUtil.isNullOrEmpty(cache)){
resources = (List<Resource>) cache.get(key);
}else {
//缓存不存在
resources = userAdapter.findResourceByUserId(userId);
if (!EmptyUtil.isNullOrEmpty(resources)){
Map<Object,Object> map = new HashMap<>();
map.put(key, resources);
SimpleMapCache simpleMapCache = new SimpleMapCache(key, map);
simpleCacheService.creatCache(key,simpleMapCache );
}
}
List<String> ids = new ArrayList<>();
for (Resource resource : resources) {
ids.add(resource.getId());
}
return ids;
}
@Override
public AuthorizationInfo getAuthorizationInfo(ShiroUser shiroUser) {
String sessionId = ShiroUtil.getShiroSessionId();
String roleKey = CacheConstant.ROLE_KEY+sessionId;
String resourcesKey = CacheConstant.RESOURCES_KEY+sessionId;
//查询用户对应的角色标识
List<String> roleList = this.findRoleList(roleKey,shiroUser.getId());
//查询用户对于的资源标识
List<String> resourcesList = this.findResourcesList(resourcesKey,shiroUser.getId());
//构建鉴权信息对象
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.addRoles(roleList);
simpleAuthorizationInfo.addStringPermissions(resourcesList);
return simpleAuthorizationInfo;
}
@Override
public List<String> findRoleList(String key,String userId){
List<Role> roles = new ArrayList<>();
//获得缓存
Cache<Object, Object> cache = simpleCacheService.getCache(key);
//缓存存在
if (!EmptyUtil.isNullOrEmpty(cache)){
roles = (List<Role>) cache.get(key);
}else {
//缓存不存在
roles = userAdapter.findRoleByUserId(userId);
if (!EmptyUtil.isNullOrEmpty(roles)){
Map<Object,Object> map = new HashMap<>();
map.put(key, roles);
SimpleMapCache simpleMapCache = new SimpleMapCache(key, map);
simpleCacheService.creatCache(key,simpleMapCache );
}
}
List<String> roleLabel = new ArrayList<>();
for (Role role : roles) {
roleLabel.add(role.getLabel());
}
return roleLabel;
}
@Override
public List<String> findResourcesList(String key,String userId){
List<Resource> resources = new ArrayList<>();
//获得缓存
Cache<Object, Object> cache = simpleCacheService.getCache(key);
//缓存存在
if (!EmptyUtil.isNullOrEmpty(cache)){
resources = (List<Resource>) cache.get(key);
}else {
//缓存不存在
resources = userAdapter.findResourceByUserId(userId);
if (!EmptyUtil.isNullOrEmpty(resources)){
Map<Object,Object> map = new HashMap<>();
map.put(key, resources);
SimpleMapCache simpleMapCache = new SimpleMapCache(key, map);
simpleCacheService.creatCache(key,simpleMapCache );
}
}
List<String> resourceLabel = new ArrayList<>();
for (Resource resource : resources) {
resourceLabel.add(resource.getLabel());
}
return resourceLabel;
}
@Override
public void loadUserAuthorityToCache(ShiroUser shiroUser) {
String sessionId = ShiroUtil.getShiroSessionId();
String roleKey = CacheConstant.ROLE_KEY+sessionId;
String resourcesKey = CacheConstant.RESOURCES_KEY+sessionId;
//查询用户对应的角色标识
List<String> roleList = this.findRoleList(roleKey,shiroUser.getId());
//查询用户对于的资源标识
List<String> resourcesList = this.findResourcesList(resourcesKey,shiroUser.getId());
}
}
CacheConstant缓存键值常量类
package com.itheima.shiro.constant;
/**
* @Description 缓存键值常量类
*/
public class CacheConstant extends SuperConstant{
public final static String GROUP_CAS="group_shiro:";
public static final String ROLE_KEY =GROUP_CAS+"role_key:";
public static final String RESOURCES_KEY =GROUP_CAS+"resources_key:";
public static final String RESOURCES_KEY_IDS =GROUP_CAS+"resources_key_ids:";
public final static String FIND_USER_BY_LOGINNAME =GROUP_CAS+"findUserByLoginName";
}
清理缓存
在用户退出登录时清理缓存。在ShiroDbRealmImpl实现类中重写doClearCache方法,在用户退出登录时,执行subject.logout()会触发此方法。
@Override
protected void doClearCache(PrincipalCollection principals) {
ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal();
String sessionId = ShiroUtil.getShiroSessionId();
String roleKey = CacheConstant.ROLE_KEY+sessionId;
String resourcesKey = CacheConstant.RESOURCES_KEY+sessionId;
String loginNamekey = CacheConstant.FIND_USER_BY_LOGINNAME + shiroUser.getLoginName();
String resourcesIdKey = CacheConstant.RESOURCES_KEY_IDS+sessionId;
simpleCacheService.removeCache(roleKey);
simpleCacheService.removeCache(resourcesKey);
simpleCacheService.removeCache(loginNamekey);
simpleCacheService.removeCache(resourcesIdKey);
super.doClearCache(principals);
}
本文来自博客园,作者:NE_STOP,转载请注明原文链接:https://www.cnblogs.com/alineverstop/p/19476357
浙公网安备 33010602011771号