shiro+redis实现用户权限集缓存功能


本文开发环境:springboot2+thymeleaf+shiro+redis

通过redis实现对用户权限集的缓存功能。

1,pom.xml引入包

①在使用shiro的情况下集成redis

 
 <dependency>
     <groupId>org.crazycakegroupId>
     <artifactId>shiro-redisartifactId>
     <version>2.4.2.1-RELEASEversion>
 dependency>

②springboot集成redis

 
 <dependency>
     <groupId>org.springframework.bootgroupId>
     <artifactId>spring-boot-starter-data-redisartifactId>
 dependency>
 
  <dependency>
         <groupId>org.springframework.bootgroupId>
         <artifactId>spring-boot-starter-redisartifactId>
         <version>1.4.7.RELEASEversion>
  dependency>

③ shiro-redis中引入的有jedis3.1.0包,访问控制符是protected,需要引用低版本才能使用2.9.0 public

 <dependency>
     <groupId>redis.clientsgroupId>
     <artifactId>jedisartifactId>
     <version>2.9.0version>
 dependency>

④注释devTools热部署插件,和redis缓存冲突(可能会发生,发生就注释)

 
     
     
     
     
 

2,redis连接配置

#### redis服务器配置 ####
##redis服务器地址
spring.redis.host = localhost
##端口
spring.redis.port = 6379
# Redis数据库索引(默认为0)
spring.redis.database=0

3,启动redis服务

 

4,doGetAuthenticationInfo身份认证代码略。

5,ShiroRealm配置doGetAuthorizationInfo授权认证,获取权限集

@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { 
    SysEmployee employee = (SysEmployee) principals.getPrimaryPrincipal();
    Long id = employee.getId();
    SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
    // 获取用户角色集
    List roleList = this.roleService.findUserRole(id);
    Set roleSet = roleList.stream().map(HospPermissionRole::getName).collect(Collectors.toSet());
    simpleAuthorizationInfo.setRoles(roleSet);
    // 获取用户权限集
    List permissionList = this.menuService.findUserPermissions(id);
    Set permissionSet = permissionList.stream().map(HospPermissionMenu::getMark).collect(Collectors.toSet());
    simpleAuthorizationInfo.setStringPermissions(permissionSet);
    return simpleAuthorizationInfo;
}

6,ShiroConfig

@Bean
public SecurityManager securityManager() {
  DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
  securityManager.setRealm(shiroRealm());
  // 自定义缓存实现 使用redis
  securityManager.setCacheManager(cacheManager());
  //注入记住我管理器
  securityManager.setRememberMeManager(rememberMeManager());
  return securityManager;
}

@Bean
public ShiroRealm shiroRealm() {
  // 配置 Realm,需自己实现
  ShiroRealm shiroRealm = new ShiroRealm();
  shiroRealm.setCredentialsMatcher(new MyCredentialsMatcher());
  return shiroRealm;
}

/**
* cacheManager 缓存 redis实现
* @return
*/
public RedisCacheManager cacheManager() {
  RedisCacheManager redisCacheManager = new RedisCacheManager();
  // redisCacheManager.setRedisManager(redisManager());
  return redisCacheManager;
}

7,自定义redis缓存管理器RedisCacheManager

import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.cache.CacheManager;
//自定义shiro缓存管理器
public class RedisCacheManager implements CacheManager {

  //参数1:认证或者是授权缓存的统一名称
  @Override
  public Cache getCache(String cacheName) throws CacheException {
  //System.out.println(cacheName);
  return new RedisCache(cacheName);
  }
}

8,自定义redis缓存实现 RediCache

import com.mly.oa.hospital.util.SpringContextUtil;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.util.Collection;
import java.util.Set;
//自定义redisCache缓存实现
public class RedisCache implements Cache {

  private String cacheName;

  public RedisCache() {
  }

  public RedisCache(String cacheName) {
    this.cacheName = cacheName;
  }

  @Override
  public v get(k k) throws CacheException {
    // System.out.println(" key:"+k);
    return (v) getRedisTemplate().opsForHash().get(this.cacheName,k.toString());
  }

  @Override
  public v put(k k, v v) throws CacheException {
    // System.out.println(" key: "+k);
    // System.out.println(" value:"+v);
    getRedisTemplate().opsForHash().put(this.cacheName,k.toString(),v);
    return null;
  }

  @Override
  public v remove(k k) throws CacheException {
    return (v) getRedisTemplate().opsForHash().delete(this.cacheName,k.toString());
  }

  @Override
  public void clear() throws CacheException {
    getRedisTemplate().delete(this.cacheName);
  }

  @Override
  public int size() {
    return getRedisTemplate().opsForHash().size(this.cacheName).intValue();
  }

  @Override
  public Set keys() {
    return getRedisTemplate().opsForHash().keys(this.cacheName);
  }

  @Override
  public Collection values() {
    return getRedisTemplate().opsForHash().values(this.cacheName);
  }

  private RedisTemplate getRedisTemplate(){
    RedisTemplate redisTemplate = (RedisTemplate) SpringContextUtil.getBean("redisTemplate");
    redisTemplate.setKeySerializer(new StringRedisSerializer());
    redisTemplate.setHashKeySerializer(new StringRedisSerializer());
    return redisTemplate;
  }

}

9,创建上下文对象工具类

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/**
* @author John Lee
* @date 2021/11/14 22:22
*/
@Component
public class SpringContextUtil implements ApplicationContextAware {

/**
* 上下文对象实例
*/
  private static ApplicationContext applicationContext;

  @Autowired
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    this.applicationContext = applicationContext;
  }

/**
* 获取applicationContext
* @return
*/
  public static ApplicationContext getApplicationContext() {
    return applicationContext;
  }

/**
* 通过name获取 Bean.
* @param name
* @return
*/
  public static Object getBean(String name){
    return getApplicationContext().getBean(name);
  }

/**
* 通过class获取Bean.
* @param clazz
* @param
* @return
*/
  public static T getBean(Class clazz){
    return getApplicationContext().getBean(clazz);
  }

/**
* 通过name,以及Clazz返回指定的Bean
* @param name
* @param clazz
* @param
* @return
*/
  public static T getBean(String name,Class clazz){
    return getApplicationContext().getBean(name, clazz);
  }
}

10,自定义key-value序列化方式

添加前后对比:

 

 

 

 

 

 

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
*/
@Configuration
public class RedisConfig {

  @Bean
  public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
    RedisTemplate template = new RedisTemplate();
    template.setConnectionFactory(factory);
    Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
    ObjectMapper om = new ObjectMapper();
    om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
    om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
    jackson2JsonRedisSerializer.setObjectMapper(om);
    StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
    // key采用String的序列化方式
    template.setKeySerializer(stringRedisSerializer);
    // hash的key也采用String的序列化方式
    template.setHashKeySerializer(stringRedisSerializer);
    // value序列化方式采用jackson
    template.setValueSerializer(jackson2JsonRedisSerializer);
    // hash的value序列化方式采用jackson
    template.setHashValueSerializer(jackson2JsonRedisSerializer);
    template.afterPropertiesSet();
    return template;
  }

}

11,系统退出,调用自定义中remove,进行删除当前用户redis缓存

@Override
public v remove(k k) throws CacheException {
  System.out.println("=============remove=============");
  return (v) getRedisTemplate().opsForHash().delete(this.cacheName,k.toString());
}

 

附加redis其他相关配置

① bind 127.0.0.1

bind绑定ip只支持这个ip地址连接的请求,在生产环境中,请求外部redis需要注释该项。

② redis3.2版本后新增 protected-mode配置。设置外部网络连接redis服务:

 关闭protected-mode,此时外部网络可以直接访问
 开启protected-mode,需要配置bind IP 或者设置访问密码

③ daemonize (守护进程),默认请款下redis不是后台运行的,如需要后台运行,设置值为 yes。

windows系统不支持。

④ pidfile /var/run/redis.pid

当redis在后台运行时,redis默认是把pid文件放在/var/run/redis.pid,可以修改到其他位置。当运行多个redis服务时,需要指定不同的pid文件和端口。

windows不支持

⑤ port 监听端口,默认是6379

⑥ timeout 0

设置客户端连接时的超时时间,单位为秒。当客户端在这段时间内没有发出任何指令,那么关闭该连接。

⑦ tcp-keepalive 0

指定TCP连接是否为长连接,“侦探”信号有server端维护。默认为0,表示禁用

⑧ loglevel notice

log等级为4级,debug,verbose,notice和warning。生产环境下一半开启notice。

⑨ databases 16

设置数据库的个数,默认16个库,默认使用0号库。

⑩ save 900 1 save 300 10 save 60 10000

保存数据快照的频率,将数据持久化到dump.rdb文件中的频率。

 默认设置,意思是:
 if(在60 秒之内有10000 个keys 发生变化时){     进行镜像备份
 }else if(在300 秒之内有10 个keys 发生了变化){ 进行镜像备份
 }else if(在900 秒之内有1 个keys 发生了变化){ 进行镜像备份}

redis更多详细配置:推荐链接https://blog.csdn.net/neubuffer/article/details/17003909