1. Realm缓存机制意义

2. Realm缓存机制实现思路

原理分析
此时我们对UserBridgeServiceImpl的实现类里面的逻辑加入了自定义的SimpleCacheService缓存服务接口,简单来说实现了在认证和鉴权时不需要每次都去查询数据库,而是把认证和鉴权信息放入到redis缓存中,以减低数据库的访问压力;
1、集成redis服务器,作为集中存储认证和鉴权信息 2、改写UserBridgeServiceImpl使其优先从缓存中读取
3. redission集成
ShiroRedisProperties
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
package com.itheima.shiro.config; import com.itheima.shiro.core.ShiroDbRealm; import com.itheima.shiro.core.impl.ShiroDbRealmImpl; import com.itheima.shiro.filter.RolesOrAuthorizationFilter; 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; /** * @Description redission客户端 */ @Bean("redissonClientForShiro") public RedissonClient redissonClient() { log.info("=====初始化redissonClientForShiro开始======"); String[] nodeList = shiroRedisProperties.getNodes().split(","); Config config = new Config(); if (nodeList.length == 1) { config.useSingleServer().setAddress(nodeList[0]) .setConnectTimeout(shiroRedisProperties.getConnectTimeout()) .setConnectionMinimumIdleSize(shiroRedisProperties.getConnectionMinimumidleSize()) .setConnectionPoolSize(shiroRedisProperties.getConnectPoolSize()).setTimeout(shiroRedisProperties.getTimeout()); } else { config.useClusterServers().addNodeAddress(nodeList) .setConnectTimeout(shiroRedisProperties.getConnectTimeout()) .setMasterConnectionMinimumIdleSize(shiroRedisProperties.getConnectionMinimumidleSize()) .setMasterConnectionPoolSize(shiroRedisProperties.getConnectPoolSize()).setTimeout(shiroRedisProperties.getTimeout()); } RedissonClient redissonClient = Redisson.create(config); log.info("=====初始化redissonClientForShiro完成======"); return redissonClient; } /** * @Description 创建cookie对象 */ @Bean(name="sessionIdCookie") public SimpleCookie simpleCookie(){ SimpleCookie simpleCookie = new SimpleCookie(); simpleCookie.setName("ShiroSession"); return simpleCookie; } /** * @Description 权限管理器 * @param * @return */ @Bean(name="securityManager") public DefaultWebSecurityManager defaultWebSecurityManager(){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(shiroDbRealm()); securityManager.setSessionManager(shiroSessionManager()); return securityManager; } /** * @Description 自定义RealmImpl */ @Bean(name="shiroDbRealm") public ShiroDbRealm shiroDbRealm(){ return new ShiroDbRealmImpl(); } /** * @Description 会话管理器 */ @Bean(name="sessionManager") public DefaultWebSessionManager shiroSessionManager(){ DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); sessionManager.setSessionValidationSchedulerEnabled(false); sessionManager.setSessionIdCookieEnabled(true); sessionManager.setSessionIdCookie(simpleCookie()); sessionManager.setGlobalSessionTimeout(3600000); return sessionManager; } /** * @Description 保证实现了Shiro内部lifecycle函数的bean执行 */ @Bean(name = "lifecycleBeanPostProcessor") public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } /** * @Description AOP式方法级权限检查 */ @Bean @DependsOn("lifecycleBeanPostProcessor") public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); defaultAdvisorAutoProxyCreator.setProxyTargetClass(true); return defaultAdvisorAutoProxyCreator; } /** * @Description 配合DefaultAdvisorAutoProxyCreator事项注解权限校验 */ @Bean public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor() { AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor(); aasa.setSecurityManager(defaultWebSecurityManager()); return new AuthorizationAttributeSourceAdvisor(); } /** * @Description 过滤器链 */ private Map<String, String> filterChainDefinition(){ List<Object> list = PropertiesUtil.propertiesShiro.getKeyList(); Map<String, String> map = new LinkedHashMap<>(); for (Object object : list) { String key = object.toString(); String value = PropertiesUtil.getShiroValue(key); log.info("读取防止盗链控制:---key{},---value:{}",key,value); map.put(key, value); } return map; } /** * @Description 自定义过滤器定义 */ private Map<String, Filter> filters() { Map<String, Filter> map = new HashMap<String, Filter>(); map.put("roleOr", new RolesOrAuthorizationFilter()); return map; } /** * @Description Shiro过滤器 */ @Bean("shiroFilter") public ShiroFilterFactoryBean shiroFilterFactoryBean(){ ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean(); shiroFilter.setSecurityManager(defaultWebSecurityManager()); //使自定义过滤器生效 shiroFilter.setFilters(filters()); shiroFilter.setFilterChainDefinitionMap(filterChainDefinition()); shiroFilter.setLoginUrl("/login"); shiroFilter.setUnauthorizedUrl("/login"); return shiroFilter; } }
SimpleMapCache
package com.itheima.shiro.core.base; import com.itheima.shiro.utils.EmptyUtil; import org.apache.shiro.cache.Cache; import org.apache.shiro.cache.CacheException; import java.io.Serializable; import java.util.Collection; import java.util.Collections; import java.util.Map; import java.util.Set; /** * @Description 缓存实现类, 实现序列 接口方便对象存储于第三方容器(Map存放键值对) */ public class SimpleMapCache implements Cache<Object, Object>, Serializable { private final Map<Object, Object> attributes; 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."); } else { this.name = name; attributes = backingMap; } } public Object get(Object key) throws CacheException { return attributes.get(key); } public Object put(Object key, Object value) throws CacheException { return attributes.put(key, value); } public Object remove(Object key) throws CacheException { return attributes.remove(key); } public void clear() throws CacheException { attributes.clear(); } public int size() { return attributes.size(); } public Set<Object> keys() { Set<Object> keys = attributes.keySet(); if (!keys.isEmpty()) return Collections.unmodifiableSet(keys); else return Collections.emptySet(); } public Collection<Object> values() { Collection<Object> values = attributes.values(); if (!EmptyUtil.isNullOrEmpty(values)) return Collections.unmodifiableCollection(values); else return Collections.emptySet(); } @Override public String toString() { return "SimpleMapCache [attributes=" + attributes + ", name=" + name + ", keys()=" + keys() + ", size()=" + size() + ", values()=" + values() + "]"; } }
ShiroRedissionSerialize序列化工具
package com.itheima.shiro.utils; import lombok.extern.log4j.Log4j2; import org.apache.shiro.codec.Base64; import java.io.*; /** * @Description:实现shiro会话的序列化存储 */ @Log4j2 public class ShiroRedissionSerialize { public static Object deserialize(String str) { if (EmptyUtil.isNullOrEmpty(str)) { return null; } ByteArrayInputStream bis = null; ObjectInputStream ois = null; Object object=null; try { bis = new ByteArrayInputStream(EncodesUtil.decodeBase64(str)); ois = new ObjectInputStream(bis); object = ois.readObject(); } catch (IOException |ClassNotFoundException e) { log.error("流读取异常:{}",e); } finally { try { bis.close(); ois.close(); } catch (IOException e) { log.error("流读取异常:{}",e); } } return object; } public static String serialize(Object obj) { if (EmptyUtil.isNullOrEmpty(obj)) { return null; } ByteArrayOutputStream bos = null; ObjectOutputStream oos = null; String base64String = null; try { bos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(bos); oos.writeObject(obj); base64String = EncodesUtil.encodeBase64(bos.toByteArray()); } catch (IOException e) { log.error("流写入异常:{}",e); } finally { try { bos.close(); oos.close(); } catch (IOException e) { log.error("流写入异常:{}",e); } } return base64String; } }
缓存服务接口SimpleCacheService
package com.itheima.shiro.core; import org.apache.shiro.cache.Cache; import org.apache.shiro.cache.CacheException; /** * @Description 简单的缓存管理接口 */ public interface SimpleCacheService { /** * <b>功能说明:</b>:新增缓存堆到管理器<br> */ void createCache(String cacheName, Cache<Object, Object> cache) throws CacheException; /** * <b>方法名:</b>:getCache<br> * <b>功能说明:</b>:获取缓存堆<br> */ Cache<Object, Object> getCache(String cacheName) throws CacheException; /** * <b>方法名:</b>:removeCache<br> * <b>功能说明:</b>:移除缓存堆<br> */ void removeCache(String cacheName) throws CacheException; /** * <b>方法名:</b>:updateCahce<br> * <b>功能说明:</b>:更新缓存堆<br> */ void updateCahce(String cacheName, Cache<Object, Object> cache) throws CacheException; }
SimpleCacheServiceImpl
调用RedissionClient去实现缓存,同时使用ShiroRedissionSerialize实现序列化
package com.itheima.shiro.core.impl; import com.itheima.shiro.constant.CacheConstant; import com.itheima.shiro.core.SimpleCacheService; import com.itheima.shiro.utils.ShiroRedissionSerialize; import lombok.extern.log4j.Log4j2; import org.apache.shiro.SecurityUtils; import org.apache.shiro.cache.Cache; import org.apache.shiro.cache.CacheException; 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 简单的缓存管理接口的实现 */ @Log4j2 @Component public class SimpleCacheServiceImpl implements SimpleCacheService { @Resource(name = "redissonClientForShiro") RedissonClient redissonClient; @Override public void createCache(String name, Cache<Object, Object> cache){ RBucket<String> bucket = redissonClient.getBucket(CacheConstant.GROUP_CAS+name); bucket.trySet(ShiroRedissionSerialize.serialize(cache), SecurityUtils.getSubject().getSession().getTimeout()/1000, TimeUnit.SECONDS); } @SuppressWarnings("unchecked") @Override public Cache<Object, Object> getCache(String name) throws CacheException { RBucket<String> bucket = redissonClient.getBucket(CacheConstant.GROUP_CAS+name); return (Cache<Object, Object>) ShiroRedissionSerialize.deserialize(bucket.get()); } @Override public void removeCache(String name) throws CacheException { RBucket<String> bucket = redissonClient.getBucket(CacheConstant.GROUP_CAS+name); bucket.delete(); } @Override public void updateCahce(String name, Cache<Object, Object> cache){ RBucket<String> bucket = redissonClient.getBucket(CacheConstant.GROUP_CAS+name); bucket.set(ShiroRedissionSerialize.serialize(cache), SecurityUtils.getSubject().getSession().getTimeout()/1000, TimeUnit.MILLISECONDS); } }
桥接器BridgeService
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); }
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()); } }
缓存的清理
package com.itheima.shiro.core.impl; import com.itheima.shiro.constant.CacheConstant; import com.itheima.shiro.constant.SuperConstant; import com.itheima.shiro.core.ShiroDbRealm; import com.itheima.shiro.core.SimpleCacheService; import com.itheima.shiro.core.base.ShiroUser; import com.itheima.shiro.core.base.SimpleToken; import com.itheima.shiro.core.bridge.UserBridgeService; import com.itheima.shiro.pojo.User; import com.itheima.shiro.utils.BeanConv; import com.itheima.shiro.utils.EmptyUtil; import com.itheima.shiro.utils.ShiroUtil; import org.apache.shiro.authc.*; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.util.ByteSource; import org.springframework.beans.factory.annotation.Autowired; /** * @Description:自定义realm的抽象类实现 */ public class ShiroDbRealmImpl extends ShiroDbRealm { @Autowired UserBridgeService userBridgeService; @Autowired SimpleCacheService simpleCacheService; @Override public AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { //token令牌信息 SimpleToken simpleToken = (SimpleToken) token; //查询user对象 User user = userBridgeService.findUserByLoginName(simpleToken.getUsername()); if (EmptyUtil.isNullOrEmpty(user)){ throw new UnknownAccountException("账号不存在!"); } //构建认证令牌对象 ShiroUser shiroUser = BeanConv.toBean(user, ShiroUser.class); shiroUser.setResourceIds(userBridgeService.findResourcesIds(shiroUser.getId())); String slat = shiroUser.getSalt(); String password = shiroUser.getPassWord(); //构建认证信息对象:1、令牌对象 2、密文密码 3、加密因子 4、当前realm的名称 return new SimpleAuthenticationInfo(shiroUser, password, ByteSource.Util.bytes(slat), getName()); } @Override public AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal(); return userBridgeService.getAuthorizationInfo(shiroUser); } @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); } @Override public void initCredentialsMatcher() { //指定密码算法 HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(SuperConstant.HASH_ALGORITHM); //指定迭代次数 hashedCredentialsMatcher.setHashIterations(SuperConstant.HASH_INTERATIONS); //生效密码比较器 setCredentialsMatcher(hashedCredentialsMatcher); } }
浙公网安备 33010602011771号