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);
}
posted @ 2026-01-13 12:04  NE_STOP  阅读(8)  评论(0)    收藏  举报