shiro使用redis实现将菜单权限进行缓存

一 redis的安装

参考文章:https://blog.csdn.net/u011066470/article/details/114794800

二  redis整合shiro

2.1 启动redis服务端

[root@localhost ~]# cd /usr/local/redis
[root@localhost redis]# ps -ef|grep redis
root       2947   2898  0 09:48 pts/0    00:00:00 grep --color=auto redis
[root@localhost redis]# ls
bin
[root@localhost redis]# cd bin
[root@localhost bin]# ls
dump.rdb         redis-check-aof  redis-cli       redis-server
redis-benchmark  redis-check-rdb  redis-sentinel
[root@localhost bin]# ./redis-server  /bonc-kecheng-jingxiang/redis-6.2.1/redis.conf
3106:C 02 Jun 2021 09:59:05.144 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
3106:C 02 Jun 2021 09:59:05.144 # Redis version=6.2.1, bits=64, commit=00000000, modified=0, pid=3106, just started
3106:C 02 Jun 2021 09:59:05.144 # Configuration loaded
3106:M 02 Jun 2021 09:59:05.144 * Increased maximum number of open files to 10032 (it was originally set to 1024).
3106:M 02 Jun 2021 09:59:05.144 * monotonic clock: POSIX clock_gettime
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 6.2.1 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                   
 (    '      ,       .-`  | `,    )     Running in standalone mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379
 |    `-._   `._    /     _.-'    |     PID: 3106
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           http://redis.io        
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               

3106:M 02 Jun 2021 09:59:05.145 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
3106:M 02 Jun 2021 09:59:05.145 # Server initialized
3106:M 02 Jun 2021 09:59:05.145 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
3106:M 02 Jun 2021 09:59:05.146 * Loading RDB produced by version 6.2.1
3106:M 02 Jun 2021 09:59:05.146 * RDB age 36704 seconds
3106:M 02 Jun 2021 09:59:05.146 * RDB memory usage when created 0.85 Mb
3106:M 02 Jun 2021 09:59:05.146 * DB loaded from disk: 0.001 seconds
3106:M 02 Jun 2021 09:59:05.146 * Ready to accept connections

2.2 启动redis客户端

[root@localhost ~]# cd /usr/local/redis
[root@localhost redis]# cd bin
[root@localhost bin]# ls
dump.rdb         redis-check-aof  redis-cli       redis-server
redis-benchmark  redis-check-rdb  redis-sentinel
[root@localhost bin]# ./redis-cli
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> 

2.3 配置pom文件

      <!--redis整合springboot-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

2.4 配置redis连接

#redis
spring.redis.port=6379
spring.redis.host=172.16.71.22
spring.redis.database=0

2.5 开发RedisCacheManager

package com.shiro.ljf.demo.sptshirodemo.shiro.cache;

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 <K, V> Cache<K, V> getCache(String cacheName) throws CacheException {
        System.out.println(cacheName);
        return new RedisCache<K,V>(cacheName);
    }
}

 2.6 开RedisCache实现

package com.shiro.ljf.demo.sptshirodemo.shiro.cache;

import com.shiro.ljf.demo.sptshirodemo.utils.ApplicationContextUtils;
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;

//自定义redis缓存的实现
public class RedisCache<k,v> implements Cache<k,v> {

    private String cacheName;

    public RedisCache() {
    }

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

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

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

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

    @Override
    public void clear() throws CacheException {
        System.out.println("=============clear==============");
        getRedisTemplate().delete(this.cacheName);
    }

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

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

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

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

2.7 自定义salt序列化

  • 由于shiro中提供的simpleByteSource实现没有实现序列化,所有在认证时出现错误信息

  • 解决方案: 需要自动salt实现序列化

2.自定义的myByteSource类:

package com.shiro.ljf.demo.sptshirodemo.utils;

import org.apache.shiro.codec.Base64;
import org.apache.shiro.codec.CodecSupport;
import org.apache.shiro.codec.Hex;
import org.apache.shiro.util.ByteSource;

import java.io.File;
import java.io.InputStream;
import java.io.Serializable;
import java.util.Arrays;

//自定义salt实现  实现序列化接口
public class MyByteSource implements ByteSource,Serializable {

    private  byte[] bytes;
    private String cachedHex;
    private String cachedBase64;

    public MyByteSource(){

    }

    public MyByteSource(byte[] bytes) {
        this.bytes = bytes;
    }

    public MyByteSource(char[] chars) {
        this.bytes = CodecSupport.toBytes(chars);
    }

    public MyByteSource(String string) {
        this.bytes = CodecSupport.toBytes(string);
    }

    public MyByteSource(ByteSource source) {
        this.bytes = source.getBytes();
    }

    public MyByteSource(File file) {
        this.bytes = (new BytesHelper()).getBytes(file);
    }

    public MyByteSource(InputStream stream) {
        this.bytes = (new BytesHelper()).getBytes(stream);
    }

    public static boolean isCompatible(Object o) {
        return o instanceof byte[] || o instanceof char[] || o instanceof String || o instanceof ByteSource || o instanceof File || o instanceof InputStream;
    }

    public byte[] getBytes() {
        return this.bytes;
    }

    public boolean isEmpty() {
        return this.bytes == null || this.bytes.length == 0;
    }

    public String toHex() {
        if (this.cachedHex == null) {
            this.cachedHex = Hex.encodeToString(this.getBytes());
        }

        return this.cachedHex;
    }

    public String toBase64() {
        if (this.cachedBase64 == null) {
            this.cachedBase64 = Base64.encodeToString(this.getBytes());
        }

        return this.cachedBase64;
    }

    public String toString() {
        return this.toBase64();
    }

    public int hashCode() {
        return this.bytes != null && this.bytes.length != 0 ? Arrays.hashCode(this.bytes) : 0;
    }

    public boolean equals(Object o) {
        if (o == this) {
            return true;
        } else if (o instanceof ByteSource) {
            ByteSource bs = (ByteSource)o;
            return Arrays.equals(this.getBytes(), bs.getBytes());
        } else {
            return false;
        }
    }

    private static final class BytesHelper extends CodecSupport {
        private BytesHelper() {
        }

        public byte[] getBytes(File file) {
            return this.toBytes(file);
        }

        public byte[] getBytes(InputStream stream) {
            return this.toBytes(stream);
        }
    }

}

 2.8 验证

1.关闭防火墙

[root@localhost ~]# systemctl status firewalld
● firewalld.service - firewalld - dynamic firewall daemon
   Loaded: loaded (/usr/lib/systemd/system/firewalld.service; enabled; vendor preset: enabled)
   Active: active (running) since Wed 2021-06-02 09:47:20 CST; 1h 17min ago
     Docs: man:firewalld(1)
 Main PID: 783 (firewalld)
    Tasks: 2
   CGroup: /system.slice/firewalld.service
           └─783 /usr/bin/python -Es /usr/sbin/firewalld --nofork --nopid

Jun 02 09:47:45 localhost.localdomain firewalld[783]: WARNING: COMMAND_FAILED...
Jun 02 09:47:45 localhost.localdomain firewalld[783]: WARNING: COMMAND_FAILED...
Jun 02 09:47:45 localhost.localdomain firewalld[783]: WARNING: COMMAND_FAILED...
Jun 02 09:47:45 localhost.localdomain firewalld[783]: WARNING: COMMAND_FAILED...
Jun 02 09:47:45 localhost.localdomain firewalld[783]: WARNING: COMMAND_FAILED...
Jun 02 09:47:45 localhost.localdomain firewalld[783]: WARNING: COMMAND_FAILED...
Jun 02 09:47:45 localhost.localdomain firewalld[783]: WARNING: COMMAND_FAILED...
Jun 02 09:47:45 localhost.localdomain firewalld[783]: WARNING: COMMAND_FAILED...
Jun 02 09:47:45 localhost.localdomain firewalld[783]: WARNING: COMMAND_FAILED...
Jun 02 09:47:46 localhost.localdomain firewalld[783]: WARNING: COMMAND_FAILED...
Hint: Some lines were ellipsized, use -l to show in full.
[root@localhost ~]# systemctl stop  firewalld
[root@localhost ~]# 

2.将服务启动起来

3.使用用户ljf进行登录

 

跳转到登录页面

 

多次刷页面

可以看到:不再查询数据库,没有查询数据库的日志,

查看redis数据库:

127.0.0.1:6379> keys *
1) "my-authenticationCache"
2) "com.shiro.ljf.demo.sptshirodemo.shiro.CustomerRealm.authorizationCache"
127.0.0.1:6379> hgetall my-authenticationCache
1) "ljf"
2) "\xac\xed\x00\x05sr\x00/org.apache.shiro.authc.SimpleAuthenticationInfo\x83\xc0\x15@`\xea%,\x02\x00\x03L\x00\x0bcredentialst\x00\x12Ljava/lang/Object;L\x00\x0fcredentialsSaltt\x00\"Lorg/apache/shiro/util/ByteSource;L\x00\nprincipalst\x00.Lorg/apache/shiro/subject/PrincipalCollection;xpt\x00 d12dfd006876d2539291d47d802b3c66sr\x002com.shiro.ljf.demo.sptshirodemo.utils.MyByteSource\xaf\x00\xe6\xbb\x05\x86\xc8\xf0\x02\x00\x03[\x00\x05bytest\x00\x02[BL\x00\x0ccachedBase64t\x00\x12Ljava/lang/String;L\x00\tcachedHexq\x00~\x00\bxpur\x00\x02[B\xac\xf3\x17\xf8\x06\bT\xe0\x02\x00\x00xp\x00\x00\x00\bQWW!RgLwppsr\x002org.apache.shiro.subject.SimplePrincipalCollection\xa8\x7fX%\xc6\xa3\bJ\x03\x00\x01L\x00\x0frealmPrincipalst\x00\x0fLjava/util/Map;xpsr\x00\x17java.util.LinkedHashMap4\xc0N\\\x10l\xc0\xfb\x02\x00\x01Z\x00\x0baccessOrderxr\x00\x11java.util.HashMap\x05\a\xda\xc1\xc3\x16`\xd1\x03\x00\x02F\x00\nloadFactorI\x00\tthresholdxp?@\x00\x00\x00\x00\x00\x0cw\b\x00\x00\x00\x10\x00\x00\x00\x01t\x005com.shiro.ljf.demo.sptshirodemo.shiro.CustomerRealm_0sr\x00\x17java.util.LinkedHashSet\xd8l\xd7Z\x95\xdd*\x1e\x02\x00\x00xr\x00\x11java.util.HashSet\xbaD\x85\x95\x96\xb8\xb74\x03\x00\x00xpw\x0c\x00\x00\x00\x10?@\x00\x00\x00\x00\x00\x01t\x00\x03ljfxx\x00w\x01\x01q\x00~\x00\x11x"
127.0.0.1:6379> hget my-authenticationCache ljf
"\xac\xed\x00\x05sr\x00/org.apache.shiro.authc.SimpleAuthenticationInfo\x83\xc0\x15@`\xea%,\x02\x00\x03L\x00\x0bcredentialst\x00\x12Ljava/lang/Object;L\x00\x0fcredentialsSaltt\x00\"Lorg/apache/shiro/util/ByteSource;L\x00\nprincipalst\x00.Lorg/apache/shiro/subject/PrincipalCollection;xpt\x00 d12dfd006876d2539291d47d802b3c66sr\x002com.shiro.ljf.demo.sptshirodemo.utils.MyByteSource\xaf\x00\xe6\xbb\x05\x86\xc8\xf0\x02\x00\x03[\x00\x05bytest\x00\x02[BL\x00\x0ccachedBase64t\x00\x12Ljava/lang/String;L\x00\tcachedHexq\x00~\x00\bxpur\x00\x02[B\xac\xf3\x17\xf8\x06\bT\xe0\x02\x00\x00xp\x00\x00\x00\bQWW!RgLwppsr\x002org.apache.shiro.subject.SimplePrincipalCollection\xa8\x7fX%\xc6\xa3\bJ\x03\x00\x01L\x00\x0frealmPrincipalst\x00\x0fLjava/util/Map;xpsr\x00\x17java.util.LinkedHashMap4\xc0N\\\x10l\xc0\xfb\x02\x00\x01Z\x00\x0baccessOrderxr\x00\x11java.util.HashMap\x05\a\xda\xc1\xc3\x16`\xd1\x03\x00\x02F\x00\nloadFactorI\x00\tthresholdxp?@\x00\x00\x00\x00\x00\x0cw\b\x00\x00\x00\x10\x00\x00\x00\x01t\x005com.shiro.ljf.demo.sptshirodemo.shiro.CustomerRealm_0sr\x00\x17java.util.LinkedHashSet\xd8l\xd7Z\x95\xdd*\x1e\x02\x00\x00xr\x00\x11java.util.HashSet\xbaD\x85\x95\x96\xb8\xb74\x03\x00\x00xpw\x0c\x00\x00\x00\x10?@\x00\x00\x00\x00\x00\x01t\x00\x03ljfxx\x00w\x01\x01q\x00~\x00\x11x"
127.0.0.1:6379> 

 

本项目详细介绍请看:http://www.sojson.com/shiro (强烈推荐) Demo已经部署到线上,地址是http://shiro.itboy.net, 管理员帐号:admin,密码:sojson.com 如果密码错误,请用sojson。 PS:你可以注册自己的帐号,然后用管理员赋权限给你自己的帐号,但是,每20分钟会把数据初始化一次。建议自己下载源码,让Demo跑起来,然后跑的更快,有问题加群解决。 声明: 本人提供这个Shiro + SpringMvc + Mybatis + Redis 的Demo 本着学习的态度,如果有欠缺和不足的地方,给予指正,并且多多包涵。 “去其糟粕取其精华”。如果觉得写的好的地方就给个赞,写的不好的地方,也请多多包涵。 使用过程: 1.创建数据库。 创建语句 :tables.sql 2.插入初始化数据 插入初始化数据:init.data.sql 3.运行。 管理员帐号:admin 密码:sojson ps:定时任务的sql会把密码改变为sojson.com 新版本说明:http://www.sojson.com/blog/164.html 和 http://www.sojson.com/blog/165.html 主要解决是之前说的问题:Shiro 教程,关于最近反应的相关异常问题,解决方法合集。 项目在本页面的附件中提取。 一、Cache配置修改。 配置文件(spring-cache.xml )中已经修改为如下配置: <!-- redis 配置,也可以把配置挪到properties配置文件中,再读取 --> <!-- 这种 arguments 构造的方式,之前配置有缺点。 这里之前的配置有问题,因为参数类型不一致,有时候jar和环境的问题,导致参数根据index对应,会处理问题, 理论上加另一个 name,就可以解决,现在把name 和type都加上,更保险。 --> 二、登录获取上一个URL地址报错。 当没有获取到退出前的request ,为null 的时候会报错。在(UserLoginController.java )135行处有所修改。 /** * shiro 获取登录之前的地址 * 之前0.1版本这个没判断空。 */ SavedRequest savedRequest = WebUtils.getSavedRequest(request); String url = null ; if(null != savedRequest){ url = savedRequest.getRequestUrl(); } /** * 我们平常用的获取上一个请求的方式,在Session不一致的情况下是获取不到的 * String url = (String) request.getAttribute(WebUtils.FORWARD_REQUEST_URI_ATTRIBUTE); */ 三、删除了配置文件中的cookie写入域的问题。 在配置文件里(spring-shiro.xml )中的配置有所修改。 <!-- 会话Cookie模板 --> <!--cookie的name,我故意取名叫xxxxbaidu --> <!--cookie的有效时间 --> <!-- 配置存储Session Cookie的domain为 一级域名 --> 上面配置是去掉了 Session 的存储Key 的作用域,之前设置的.itboy.net ,是写到当前域名的 一级域名 下,这样就可以做到N 个 二级域名 下,三级、四级....下 Session 都是共享的。 <!-- 用户信息记住我功能的相关配置 --> <!-- 配置存储rememberMe Cookie的domain为 一级域名 --> <!-- 30天时间,记住我30天 --> 记住我登录的信息配置。和上面配置是一样的道理,可以在相同 一级域名 下的所有域名都可以获取到登录的信息。 四、简单实现了单个帐号只能在一处登录。 我们在其他的系统中可以看到,单个帐号只允许一人使用,在A处登录了,B处再登录,那A处就被踢出了。如下图所示。 但是此功能不是很完美,当A处被踢出后,再重新登录,这时候B处反应有点慢,具体我还没看,因为是之前加的功能,现在凌晨了,下次我有空再瞧瞧,同学你也可以看看,解决了和我说一声,我把功能修复。 五、修复功能(BUG) 1.修复权限添加功能BUG。 之前功能有问题,每当添加一个权限的时候,默认都给角色为“管理员”的角色默认添加当前新添加的权限。这样达到管理员的权限永远是最大的。由于代码有BUG ,导致所有权限删除了。现已修复。 2.修复项目只能部署到Root目录下的问题。 问题描述:之前项目只能部署到Root 下才能正常运行,目前已经修复,可以带项目路径进行访问了,之前只能这样访问,http://localhost:8080 而不能http://localhost:8080/shiro.demo/ 访问,目前是可以了。 解决方案:在 FreeMarkerViewExtend.java 33行处 增加了BasePath ,通过BasePath 来控制请求目录,在 Freemarker 中可以自由使用,而 JSP 中是直接在 JSP 中获取BasePath 使用。 解决后遗症:因为我们的权限是通过URL 来控制的,那么增加了项目的目录,导致权限不能正确的判断,再加上我们的项目名称(目录)可以自定义,导致更不好判断。 后遗症解决方案:PermissionFilter.java 50行处 解决了这个问题,详情请看代码和注释,其实就是replace 了一下。 HttpServletRequest httpRequest = ((HttpServletRequest)request); /** * 此处是改版后,为了兼容项目不需要部署到root下,也可以正常运行,但是权限没设置目前必须到root 的URI, * 原因:如果你把这个项目叫 ShiroDemo,那么路径就是 /ShiroDemo/xxxx.shtml ,那另外一个人使用,又叫Shiro_Demo,那么就要这么控制/Shiro_Demo/xxxx.shtml * 理解了吗? * 所以这里替换了一下,使用根目录开始的URI */ String uri = httpRequest.getRequestURI();//获取URI String basePath = httpRequest.getContextPath();//获取basePath if(null != uri && uri.startsWith(basePath)){ uri = uri.replace(basePath, ""); } 3.项目启动的时候报错,关于JNDI的错误提示。 其实也不是错,但是看着不舒服,所以还得解决这个问题。解决这个问题需要在web.xml 中的开始部位加入以下代码。 spring.profiles.active dev spring.profiles.default dev spring.liveBeansView.mbeanDomain dev 4.项目Maven打包问题。 打包的时候,不同版本的 Eclipse 还有IDEA 会有打包打不进去Mapper.xml 文件,这个时候要加如下代码(群里同学提供的)。 src/main/java **/*.properties **/*.xml false 在 标签内加入即可,如果还是不能解决,那么请你加群(改名后)说明你的问题,有人会回答你。 5.Tomcat7以上在访问JSP页面的时候,提示JSTL错误。 这个错误是因为Tomcat7 中没有 JSTL 的jar包,现在已经在项目pom.xml 中增加了如下 jar 的引入管理。 javax.servlet jstl 1.2 javax.servlet jsp-api 2.0 provided 如果还是不能解决问题,请在官方群(群号:259217951)内搜索“jstl” 如图下载依赖包。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值