Apache-Shiro+Zookeeper系统集群安全解决方案之缓存管理
上篇【Apache-Shiro+Zookeeper系统集群安全解决方案之会话管理】,解决了Shiro在系统集群开发时安全的会话共享问题,系统在使用过程中会有大量的权限检查和用户身份检验动作,为了不频繁访问储存容器,Shiro提供了三个缓存机制:
用户登录Session缓存,默认是不开启的,在Realm配置中设置authenticationCachingEnabled开启,
使用原则是用户在登录成功后缓存登录校验信息,如 admin 登录成功后将用户名密码等缓存,在超时退出或直接关闭浏览器需要重新登录时不访问数据库。
在用户退出或超时自动清理缓存数据。
缓存数据通过配置的cacheManager存储。用户权限缓存,用户访问到在配置URL规则范围内的链接时触发读取当前用户的权限并缓存
同样缓存数据是通过配置的cacheManager存储。还有一个是SessionDao中实现的用户登录后的身份信息缓存到本地比如内存,在一定时间内不会到Session存储容器中取用户信息,可以减少Shiro对存储容器的大量读取,在上文有提到并有实现方法。
所以本文的说到解决方案是Shiro + Zookeeper,Shiro权限框架,利用Zookeeper做存储容器。
用到的框架技术:
Spring + Shiro + Zookeeper
SHIRO整合SPRING配置
applicationContext-shiro.xml 伪代码:
<!-- 自定义shiro的realm -->
<bean id="jdbcAuthenticationRealm" class="..jdbcAuthenticationRealm" depends-on="...">
...
<!-- 自定义缓存名 -->
<property name="authorizationCacheName" value="shiroAuthorizationCache"/>
<property name="authenticationCacheName" value="shiroAuthenticationCache"/>
<!--
缓存用户登录信息,默认不缓存
缓存后用户在不点退出的情况下再次登录直接使用缓存中数据
注意修改密码后要自动退出用户, 否则用户直接关闭浏览器下次使用缓存数据将无法登录!
-->
<property name="authenticationCachingEnabled" value="true"/>
<!--缓存用户权限信息-->
<property name="authorizationCachingEnabled" value="true"/>
</bean>
<!-- 缓存配置 -->
<bean id="zkShiroSessionCache" class="..ZKShiroCacheManager">
<property name="zookeeperTemplate" ref="zookeeperTemplate"/>
<!-- 结尾不加/ -->
<property name="shiroSessionCacheZKPath" value="/SHIROSESSIONCACHE"/>
<property name="sessionCachePrefix" value="cache-"/>
</bean>
<!-- SHIRO安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="jdbcAuthenticationRealm"/>
<property name="cacheManager" ref="zkShiroSessionCache"/>
<property name="sessionManager" ref="sessionManager"/>
</bean>
缓存管理器
Shiro会通过缓存管理器和缓存名获取缓存DAO,比如上面配置的shiroAuthorizationCache和shiroAuthenticationCache会产生两个缓存DAO用来存取不同类型的缓存数据。
ZKShiroCacheManager.java
import bgonline.foundation.hadoop.zk.IZookeeperTemplate;
import org.apache.shiro.ShiroException;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.util.Destroyable;
import org.apache.shiro.util.Initializable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
/**
* SHIRO缓存Manager
*
*/
public class ZKShiroCacheManager implements CacheManager, Initializable, Destroyable {
Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* zk操作工具类
*/
private IZookeeperTemplate zookeeperTemplate;
/**
* 缓存根路径
*/
private String shiroSessionCacheZKPath = "/SHIROSESSIONS2";
/**
* 缓存项前缀
*/
private String sessionCachePrefix = "cache-";
@Override
public void destroy() throws Exception {
}
/**
* 根据名字返回CACHE
* @param s
* @param <K>
* @param <V>
* @return
* @throws CacheException
*/
@Override
public <K, V> Cache<K, V> getCache(String s) throws CacheException {
logger.info("shiro get cache, return : ZKShiroCache, cache name: {}", s);
Assert.notNull(zookeeperTemplate, "zookeeperTemplate must be set !");
return new ZKShiroCache<K, V>(zookeeperTemplate, shiroSessionCacheZKPath, sessionCachePrefix, s);
}
@Override
public void init() throws ShiroException {
}
public void setZookeeperTemplate(IZookeeperTemplate zookeeperTemplate) {
this.zookeeperTemplate = zookeeperTemplate;
}
public void setShiroSessionCacheZKPath(String shiroSessionCacheZKPath) {
this.shiroSessionCacheZKPath = shiroSessionCacheZKPath;
}
public void setSessionCachePrefix(String sessionCachePrefix) {
this.sessionCachePrefix = sessionCachePrefix;
}
}
缓存DAO
真正的缓存CRUD等操作类。
ZKShiroCache.java
import bgonline.foundation.hadoop.zk.IZookeeperTemplate;
import bgonline.foundation.hadoop.zk.ZNode;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.SerializationUtils;
import java.util.*;
/**
* SHIRO集群缓存储存类
*
*/
public class ZKShiroCache<K, V> implements Cache<K, V> {
Logger logger = LoggerFactory.getLogger(this.getClass());
private IZookeeperTemplate zookeeperTemplate;
private String shiroSessionCacheZKPath;
private String sessionCachePrefix;
private String cacheName;
/**
* 根据cacheName创建ZK CACHE 实例
*
* @param zookeeperTemplate zk操作工具类
* @param shiroSessionCacheZKPath 缓存根路径, 结尾不加/
* @param sessionCachePrefix 缓存项前缀
* @param cacheName 缓存名字
*/
public ZKShiroCache(IZookeeperTemplate zookeeperTemplate, String shiroSessionCacheZKPath, String sessionCachePrefix, String cacheName) {
this.zookeeperTemplate = zookeeperTemplate;
this.shiroSessionCacheZKPath = shiroSessionCacheZKPath;
this.sessionCachePrefix = sessionCachePrefix;
this.cacheName = cacheName;
}
@Override
public V get(K k) throws CacheException {
String path = getPath(k);
logger.debug("get cache for key: {}", path);
byte[] byteValue = zookeeperTemplate.getData(path).getByteData();
if (byteValue.length > 0)
return (V) SerializationUtils.deserialize(byteValue);
else
return null;
}
@Override
public V put(K k, V v) throws CacheException {
V previous = get(k);
String path = getPath(k);
ZNode node = new ZNode();
node.setPath(path);
node.setByteData(SerializationUtils.serialize(v));
String nodepath = zookeeperTemplate.createNode(node);
logger.debug("put cache for key: {}, return path: {}", path, nodepath);
return previous;
}
@Override
public V remove(K k) throws CacheException {
String path = getPath(k);
logger.debug("remove cache for key: {}", path);
V previous = get(k);
zookeeperTemplate.deleteNode(path);
return previous;
}
@Override
public void clear() throws CacheException {
logger.debug("clear cache");
zookeeperTemplate.deleteNode(shiroSessionCacheZKPath);
}
@Override
public int size() {
ZNode node = new ZNode();
node.setPath(shiroSessionCacheZKPath);
int size = zookeeperTemplate.getChildren(node).size();
logger.debug("get cache size: {}", size);
return size;
}
@Override
public Set<K> keys() {
List<String> cl = cacheList();
logger.debug("get cache keys, size : {}", cl.size());
return (Set<K>) cl;
}
@Override
public Collection<V> values() {
Collection<V> cs = new ArrayList<V>();
List<String> cl = cacheList();
for (String k : cl) {
//读取的key形如: cache-shiroAuthenticationCacheadmin
if (k.startsWith(sessionCachePrefix)) {
String noPrefixId = k.replace(sessionCachePrefix, "");
String path = getPath(noPrefixId);
cs.add((V) zookeeperTemplate.getData(path).getByteData());
}
}
logger.debug("get cache values, size : {}", cs.size());
return cs;
}
/**
* 获取所有缓存列表
*
* @return list
*/
private List<String> cacheList() {
ZNode node = new ZNode();
node.setPath(shiroSessionCacheZKPath);
List<String> children = zookeeperTemplate.getChildren(node);
logger.debug("get cache list, size : {}", children.size());
return children;
}
/**
* 生成缓存项存储全路径KEY
* 根路径/缓存前缀+缓存名字+缓存KEY
*
* @param key
* @return
*/
private String getPath(Object key) {
return shiroSessionCacheZKPath + '/' + sessionCachePrefix + cacheName + key;
}
}
小结
Shiro提供了很全面的接口,方便我们根据自己的需要任意扩展,关于自定义缓存还可以参考Shiro自带的MemoryConstrainedCacheManager和 MapCache 这也是一个非常好的例子。
文中提到几个自己封装的有关Zookeeper的类可以替换成自己的实现类,或者也可能直接使用其它的工具做存储容器比如数据库等,本文主要是起到抛砖引玉的作用,说明Shiro在缓存共享自定义实现的过程和配置。
Apache-Shiro+Zookeeper系统集群安全解决方案之缓存管理的更多相关文章
- Apache-Shiro+Zookeeper系统集群安全解决方案之会话管理
如今的系统多不是孤军奋战,在多结点会话共享管理方面有着各自的解决办法,比如Session粘连,基于Web容器的各种处理等或者类似本文说的完全接管Web容器的Session管理,只是做法不尽相同. 而本 ...
- 一键运行CIS安全扫描,集群安全无忧!
CIS安全扫描是Rancher 2.4推出的其中一个重磅功能,旨在帮助用户快速.有效地加强集群的安全性.本文将详细介绍CIS安全扫描这一功能,包含详细的操作demo. 本文来自Rancher Labs ...
- JAVA系统架构高并发解决方案 分布式缓存 分布式事务解决方案
JAVA系统架构高并发解决方案 分布式缓存 分布式事务解决方案
- mongodb副本集加分片集群安全认证使用账号密码登录
mongodb副本集加分片集群搭建网上资料有很多.粘贴一个写的比较好的.副本集加分片搭建 对于搭建好的mongodb副本集加分片集群,为了安全,启动安全认证,使用账号密码登录. 默认的mongodb是 ...
- kafka集群安全化之启用kerberos与acl
一.背景 在我们部署完kafka之后,虽然我们已经可以“肆意”的用kafka了,但是在一个大公司的实际生产环境中,kafka集群往往十分庞大,每个使用者都应该只关心自己所负责的Topic,并且对其他人 ...
- kubernetes实战(八):k8s集群安全机制RBAC
1.基本概念 RBAC(Role-Based Access Control,基于角色的访问控制)在k8s v1.5中引入,在v1.6版本时升级为Beta版本,并成为kubeadm安装方式下的默认选项, ...
- Kubernetes集群安全概述
API的访问安全性 API Server的端口和地址 在默认情况下,API Server通过本地端口和安全端口两个不同的HTTP端口,对外提供API服务,其中本地端口是基于HTTP协议的,用于在本机( ...
- kubernetes(k8s)集群安全机制RBAC
1.基本概念 RBAC(Role-Based Access Control,基于角色的访问控制)在k8s v1.5中引入,在v1.6版本时升级为Beta版本,并成为kubeadm安装方式下的默认选项, ...
- elasticsearch集群安全重启节点
es2.x 关闭集群的动态分片:(动态分片开启状态如果节点宕机了,会导致集群重新分配数据进行数据转移,会导致节点直接大量传输数据)curl -XPUT 'http://192.168.248.193: ...
随机推荐
- select、poll、epoll之间的区别
select.poll.epoll之间的区别总结[整理] select,poll,epoll都是IO多路复用的机制.I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就 ...
- mmap DMA【转】
转自:http://blog.csdn.net/lihaoweiv/article/details/6275241 第 13 章 mmap 和 DMA 本章将深入探讨 Linux 内存管理部分,并强 ...
- Oracle PL/SQL之LOOP循环控制语句
在PL/SQL中可以使用LOOP语句对数据进行循环处理,利用该语句可以循环执行指定的语句序列.常用的LOOP循环语句包含3种形式:基本的LOOP.WHILE...LOOP和FOR...LOOP. LO ...
- “wsimport -keep ”生成客户端报错“Use of SOAP Encoding is not supported.”
本来想用 “wsimport -keep ” 生成客户端,结果报错“Use of SOAP Encoding is not supported.” 应该是缺jar包, 闲麻烦就发现了百度经验上的 这个 ...
- 转Class.forName()用法详解
主要功能 Class.forName(xxx.xx.xx)返回的是一个类 Class.forName(xxx.xx.xx)的作用是要求JVM查找并加载指定的类, 也就是说JVM会执行该类的静态代码段 ...
- 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 ...
- linux命令:du 命令
Linux du命令也是查看使用空间的,但是与df命令不同的是Linux du命令是对文件和目录磁盘使用的空间的查看,还是和df命令有一些区别的. 1.命令格式: du [选项][文件] 2.命令功能 ...
- ACM题目————棋盘问题
Description 在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别.要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子 ...
- 【转】MYSQL入门学习之十:视图的基本操作
转载地址:http://www.2cto.com/database/201212/176775.html 一.视图的基本介绍 www.2cto.com 视图是虚拟的表.与包含数据 ...
- Axure一点2
4:找茬:先安装图片,在不同的地方安上动态面板(命名为左一),在动态面板上双击,添加动态,进去动态后添加矩形改色,设置透明度为0,回到主页面.并设为隐藏,复制到另一面(命名为右一).在右一上添加图像热 ...