上篇【Apache-Shiro+Zookeeper系统集群安全解决方案之会话管理】,解决了Shiro在系统集群开发时安全的会话共享问题,系统在使用过程中会有大量的权限检查和用户身份检验动作,为了不频繁访问储存容器,Shiro提供了三个缓存机制:

  1. 用户登录Session缓存,默认是不开启的,在Realm配置中设置authenticationCachingEnabled开启,
    使用原则是用户在登录成功后缓存登录校验信息,如 admin 登录成功后将用户名密码等缓存,在超时退出或直接关闭浏览器需要重新登录时不访问数据库。
    在用户退出或超时自动清理缓存数据。
    缓存数据通过配置的cacheManager存储。

  2. 用户权限缓存,用户访问到在配置URL规则范围内的链接时触发读取当前用户的权限并缓存
    同样缓存数据是通过配置的cacheManager存储。

  3. 还有一个是SessionDao中实现的用户登录后的身份信息缓存到本地比如内存,在一定时间内不会到Session存储容器中取用户信息,可以减少Shiro对存储容器的大量读取,在上文有提到并有实现方法。

所以本文的说到解决方案是Shiro + Zookeeper,Shiro权限框架,利用Zookeeper做存储容器。

用到的框架技术:

Spring + Shiro + Zookeeper

SHIRO整合SPRING配置

applicationContext-shiro.xml 伪代码:

  1. <!-- 自定义shiro的realm -->
  2. <bean id="jdbcAuthenticationRealm" class="..jdbcAuthenticationRealm" depends-on="...">
  3. ...
  4. <!-- 自定义缓存名 -->
  5. <property name="authorizationCacheName" value="shiroAuthorizationCache"/>
  6. <property name="authenticationCacheName" value="shiroAuthenticationCache"/>
  7. <!--
  8. 缓存用户登录信息,默认不缓存
  9. 缓存后用户在不点退出的情况下再次登录直接使用缓存中数据
  10. 注意修改密码后要自动退出用户, 否则用户直接关闭浏览器下次使用缓存数据将无法登录!
  11. -->
  12. <property name="authenticationCachingEnabled" value="true"/>
  13. <!--缓存用户权限信息-->
  14. <property name="authorizationCachingEnabled" value="true"/>
  15. </bean>
  16. <!-- 缓存配置 -->
  17. <bean id="zkShiroSessionCache" class="..ZKShiroCacheManager">
  18. <property name="zookeeperTemplate" ref="zookeeperTemplate"/>
  19. <!-- 结尾不加/ -->
  20. <property name="shiroSessionCacheZKPath" value="/SHIROSESSIONCACHE"/>
  21. <property name="sessionCachePrefix" value="cache-"/>
  22. </bean>
  23. <!-- SHIRO安全管理器 -->
  24. <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
  25. <property name="realm" ref="jdbcAuthenticationRealm"/>
  26. <property name="cacheManager" ref="zkShiroSessionCache"/>
  27. <property name="sessionManager" ref="sessionManager"/>
  28. </bean>

缓存管理器

Shiro会通过缓存管理器和缓存名获取缓存DAO,比如上面配置的shiroAuthorizationCacheshiroAuthenticationCache会产生两个缓存DAO用来存取不同类型的缓存数据。

ZKShiroCacheManager.java

  1. import bgonline.foundation.hadoop.zk.IZookeeperTemplate;
  2. import org.apache.shiro.ShiroException;
  3. import org.apache.shiro.cache.Cache;
  4. import org.apache.shiro.cache.CacheException;
  5. import org.apache.shiro.cache.CacheManager;
  6. import org.apache.shiro.util.Destroyable;
  7. import org.apache.shiro.util.Initializable;
  8. import org.slf4j.Logger;
  9. import org.slf4j.LoggerFactory;
  10. import org.springframework.util.Assert;
  11. /**
  12. * SHIRO缓存Manager
  13. *
  14. */
  15. public class ZKShiroCacheManager implements CacheManager, Initializable, Destroyable {
  16. Logger logger = LoggerFactory.getLogger(this.getClass());
  17. /**
  18. * zk操作工具类
  19. */
  20. private IZookeeperTemplate zookeeperTemplate;
  21. /**
  22. * 缓存根路径
  23. */
  24. private String shiroSessionCacheZKPath = "/SHIROSESSIONS2";
  25. /**
  26. * 缓存项前缀
  27. */
  28. private String sessionCachePrefix = "cache-";
  29. @Override
  30. public void destroy() throws Exception {
  31. }
  32. /**
  33. * 根据名字返回CACHE
  34. * @param s
  35. * @param <K>
  36. * @param <V>
  37. * @return
  38. * @throws CacheException
  39. */
  40. @Override
  41. public <K, V> Cache<K, V> getCache(String s) throws CacheException {
  42. logger.info("shiro get cache, return : ZKShiroCache, cache name: {}", s);
  43. Assert.notNull(zookeeperTemplate, "zookeeperTemplate must be set !");
  44. return new ZKShiroCache<K, V>(zookeeperTemplate, shiroSessionCacheZKPath, sessionCachePrefix, s);
  45. }
  46. @Override
  47. public void init() throws ShiroException {
  48. }
  49. public void setZookeeperTemplate(IZookeeperTemplate zookeeperTemplate) {
  50. this.zookeeperTemplate = zookeeperTemplate;
  51. }
  52. public void setShiroSessionCacheZKPath(String shiroSessionCacheZKPath) {
  53. this.shiroSessionCacheZKPath = shiroSessionCacheZKPath;
  54. }
  55. public void setSessionCachePrefix(String sessionCachePrefix) {
  56. this.sessionCachePrefix = sessionCachePrefix;
  57. }
  58. }

缓存DAO

真正的缓存CRUD等操作类。

ZKShiroCache.java

  1. import bgonline.foundation.hadoop.zk.IZookeeperTemplate;
  2. import bgonline.foundation.hadoop.zk.ZNode;
  3. import org.apache.shiro.cache.Cache;
  4. import org.apache.shiro.cache.CacheException;
  5. import org.slf4j.Logger;
  6. import org.slf4j.LoggerFactory;
  7. import org.springframework.util.SerializationUtils;
  8. import java.util.*;
  9. /**
  10. * SHIRO集群缓存储存类
  11. *
  12. */
  13. public class ZKShiroCache<K, V> implements Cache<K, V> {
  14. Logger logger = LoggerFactory.getLogger(this.getClass());
  15. private IZookeeperTemplate zookeeperTemplate;
  16. private String shiroSessionCacheZKPath;
  17. private String sessionCachePrefix;
  18. private String cacheName;
  19. /**
  20. * 根据cacheName创建ZK CACHE 实例
  21. *
  22. * @param zookeeperTemplate zk操作工具类
  23. * @param shiroSessionCacheZKPath 缓存根路径, 结尾不加/
  24. * @param sessionCachePrefix 缓存项前缀
  25. * @param cacheName 缓存名字
  26. */
  27. public ZKShiroCache(IZookeeperTemplate zookeeperTemplate, String shiroSessionCacheZKPath, String sessionCachePrefix, String cacheName) {
  28. this.zookeeperTemplate = zookeeperTemplate;
  29. this.shiroSessionCacheZKPath = shiroSessionCacheZKPath;
  30. this.sessionCachePrefix = sessionCachePrefix;
  31. this.cacheName = cacheName;
  32. }
  33. @Override
  34. public V get(K k) throws CacheException {
  35. String path = getPath(k);
  36. logger.debug("get cache for key: {}", path);
  37. byte[] byteValue = zookeeperTemplate.getData(path).getByteData();
  38. if (byteValue.length > 0)
  39. return (V) SerializationUtils.deserialize(byteValue);
  40. else
  41. return null;
  42. }
  43. @Override
  44. public V put(K k, V v) throws CacheException {
  45. V previous = get(k);
  46. String path = getPath(k);
  47. ZNode node = new ZNode();
  48. node.setPath(path);
  49. node.setByteData(SerializationUtils.serialize(v));
  50. String nodepath = zookeeperTemplate.createNode(node);
  51. logger.debug("put cache for key: {}, return path: {}", path, nodepath);
  52. return previous;
  53. }
  54. @Override
  55. public V remove(K k) throws CacheException {
  56. String path = getPath(k);
  57. logger.debug("remove cache for key: {}", path);
  58. V previous = get(k);
  59. zookeeperTemplate.deleteNode(path);
  60. return previous;
  61. }
  62. @Override
  63. public void clear() throws CacheException {
  64. logger.debug("clear cache");
  65. zookeeperTemplate.deleteNode(shiroSessionCacheZKPath);
  66. }
  67. @Override
  68. public int size() {
  69. ZNode node = new ZNode();
  70. node.setPath(shiroSessionCacheZKPath);
  71. int size = zookeeperTemplate.getChildren(node).size();
  72. logger.debug("get cache size: {}", size);
  73. return size;
  74. }
  75. @Override
  76. public Set<K> keys() {
  77. List<String> cl = cacheList();
  78. logger.debug("get cache keys, size : {}", cl.size());
  79. return (Set<K>) cl;
  80. }
  81. @Override
  82. public Collection<V> values() {
  83. Collection<V> cs = new ArrayList<V>();
  84. List<String> cl = cacheList();
  85. for (String k : cl) {
  86. //读取的key形如: cache-shiroAuthenticationCacheadmin
  87. if (k.startsWith(sessionCachePrefix)) {
  88. String noPrefixId = k.replace(sessionCachePrefix, "");
  89. String path = getPath(noPrefixId);
  90. cs.add((V) zookeeperTemplate.getData(path).getByteData());
  91. }
  92. }
  93. logger.debug("get cache values, size : {}", cs.size());
  94. return cs;
  95. }
  96. /**
  97. * 获取所有缓存列表
  98. *
  99. * @return list
  100. */
  101. private List<String> cacheList() {
  102. ZNode node = new ZNode();
  103. node.setPath(shiroSessionCacheZKPath);
  104. List<String> children = zookeeperTemplate.getChildren(node);
  105. logger.debug("get cache list, size : {}", children.size());
  106. return children;
  107. }
  108. /**
  109. * 生成缓存项存储全路径KEY
  110. * 根路径/缓存前缀+缓存名字+缓存KEY
  111. *
  112. * @param key
  113. * @return
  114. */
  115. private String getPath(Object key) {
  116. return shiroSessionCacheZKPath + '/' + sessionCachePrefix + cacheName + key;
  117. }
  118. }

小结

Shiro提供了很全面的接口,方便我们根据自己的需要任意扩展,关于自定义缓存还可以参考Shiro自带的MemoryConstrainedCacheManagerMapCache 这也是一个非常好的例子。

文中提到几个自己封装的有关Zookeeper的类可以替换成自己的实现类,或者也可能直接使用其它的工具做存储容器比如数据库等,本文主要是起到抛砖引玉的作用,说明Shiro在缓存共享自定义实现的过程和配置。

Apache-Shiro+Zookeeper系统集群安全解决方案之缓存管理的更多相关文章

  1. Apache-Shiro+Zookeeper系统集群安全解决方案之会话管理

    如今的系统多不是孤军奋战,在多结点会话共享管理方面有着各自的解决办法,比如Session粘连,基于Web容器的各种处理等或者类似本文说的完全接管Web容器的Session管理,只是做法不尽相同. 而本 ...

  2. 一键运行CIS安全扫描,集群安全无忧!

    CIS安全扫描是Rancher 2.4推出的其中一个重磅功能,旨在帮助用户快速.有效地加强集群的安全性.本文将详细介绍CIS安全扫描这一功能,包含详细的操作demo. 本文来自Rancher Labs ...

  3. JAVA系统架构高并发解决方案 分布式缓存 分布式事务解决方案

    JAVA系统架构高并发解决方案 分布式缓存 分布式事务解决方案

  4. mongodb副本集加分片集群安全认证使用账号密码登录

    mongodb副本集加分片集群搭建网上资料有很多.粘贴一个写的比较好的.副本集加分片搭建 对于搭建好的mongodb副本集加分片集群,为了安全,启动安全认证,使用账号密码登录. 默认的mongodb是 ...

  5. kafka集群安全化之启用kerberos与acl

    一.背景 在我们部署完kafka之后,虽然我们已经可以“肆意”的用kafka了,但是在一个大公司的实际生产环境中,kafka集群往往十分庞大,每个使用者都应该只关心自己所负责的Topic,并且对其他人 ...

  6. kubernetes实战(八):k8s集群安全机制RBAC

    1.基本概念 RBAC(Role-Based Access Control,基于角色的访问控制)在k8s v1.5中引入,在v1.6版本时升级为Beta版本,并成为kubeadm安装方式下的默认选项, ...

  7. Kubernetes集群安全概述

    API的访问安全性 API Server的端口和地址 在默认情况下,API Server通过本地端口和安全端口两个不同的HTTP端口,对外提供API服务,其中本地端口是基于HTTP协议的,用于在本机( ...

  8. kubernetes(k8s)集群安全机制RBAC

    1.基本概念 RBAC(Role-Based Access Control,基于角色的访问控制)在k8s v1.5中引入,在v1.6版本时升级为Beta版本,并成为kubeadm安装方式下的默认选项, ...

  9. elasticsearch集群安全重启节点

    es2.x 关闭集群的动态分片:(动态分片开启状态如果节点宕机了,会导致集群重新分配数据进行数据转移,会导致节点直接大量传输数据)curl -XPUT 'http://192.168.248.193: ...

随机推荐

  1. select、poll、epoll之间的区别

    select.poll.epoll之间的区别总结[整理]   select,poll,epoll都是IO多路复用的机制.I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就 ...

  2. mmap DMA【转】

    转自:http://blog.csdn.net/lihaoweiv/article/details/6275241 第 13 章  mmap 和 DMA 本章将深入探讨 Linux 内存管理部分,并强 ...

  3. Oracle PL/SQL之LOOP循环控制语句

    在PL/SQL中可以使用LOOP语句对数据进行循环处理,利用该语句可以循环执行指定的语句序列.常用的LOOP循环语句包含3种形式:基本的LOOP.WHILE...LOOP和FOR...LOOP. LO ...

  4. “wsimport -keep ”生成客户端报错“Use of SOAP Encoding is not supported.”

    本来想用 “wsimport -keep ” 生成客户端,结果报错“Use of SOAP Encoding is not supported.” 应该是缺jar包, 闲麻烦就发现了百度经验上的 这个 ...

  5. 转Class.forName()用法详解

    主要功能 Class.forName(xxx.xx.xx)返回的是一个类 Class.forName(xxx.xx.xx)的作用是要求JVM查找并加载指定的类, 也就是说JVM会执行该类的静态代码段 ...

  6. node-webkit教程<>Native UI API 之Menu(菜单)

    node-webkit教程(6)Native UI API 之Menu(菜单)1 前言... 2 6.1  Menu 概述... 3 6.2  menu api6 6.2.1  new Menu([o ...

  7. linux命令:du 命令

    Linux du命令也是查看使用空间的,但是与df命令不同的是Linux du命令是对文件和目录磁盘使用的空间的查看,还是和df命令有一些区别的. 1.命令格式: du [选项][文件] 2.命令功能 ...

  8. ACM题目————棋盘问题

    Description 在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别.要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子 ...

  9. 【转】MYSQL入门学习之十:视图的基本操作

    转载地址:http://www.2cto.com/database/201212/176775.html 一.视图的基本介绍  www.2cto.com           视图是虚拟的表.与包含数据 ...

  10. Axure一点2

    4:找茬:先安装图片,在不同的地方安上动态面板(命名为左一),在动态面板上双击,添加动态,进去动态后添加矩形改色,设置透明度为0,回到主页面.并设为隐藏,复制到另一面(命名为右一).在右一上添加图像热 ...