【Shiro学习之七】shiro缓存
apahce shiro:1.6.0
Shiro 提供了类似于 Spring 的 Cache 抽象,即 Shiro 本身不实现 Cache,但是对 Cache 进行了又抽象,方便更换不同的底层 Cache 实现。
一、相关组件
1、Cache接口
2、CacheManager接口
3、CacheManagerAware接口用于注入CacheManager
Shiro内部相应的组件DefaultSecurityManager会自动检测相应的对象(如Realm)是否实现了CacheManagerAware并自动注入相应的CacheManager。
二、Realm 缓存
Shiro 提供了 CachingRealm,其实现了 CacheManagerAware 接口,提供了缓存的一些基础实现;另外 AuthenticatingRealm 及 AuthorizingRealm 分别提供了对 AuthenticationInfo和AuthorizationInfo 信息的缓存。
当用户修改密码或者修改权限后需要将缓存中的 AuthenticationInfo和AuthorizationInfo清除掉。
package com.github.zhangkaitao.shiro.chapter11.realm; import com.github.zhangkaitao.shiro.chapter11.entity.User; import com.github.zhangkaitao.shiro.chapter11.service.UserService; import com.github.zhangkaitao.shiro.chapter11.service.UserServiceImpl; import org.apache.shiro.authc.*; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.util.ByteSource; /** * <p>User: Zhang Kaitao * <p>Date: 14-1-28 * <p>Version: 1.0 */ public class UserRealm extends AuthorizingRealm { private UserService userService = new UserServiceImpl(); @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { String username = (String)principals.getPrimaryPrincipal(); SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); authorizationInfo.setRoles(userService.findRoles(username)); authorizationInfo.setStringPermissions(userService.findPermissions(username)); return authorizationInfo; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String username = (String)token.getPrincipal(); User user = userService.findByUsername(username); if(user == null) { throw new UnknownAccountException();//没找到帐号 } if(Boolean.TRUE.equals(user.getLocked())) { throw new LockedAccountException(); //帐号锁定 } //交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配,如果觉得人家的不好可以自定义实现 SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo( user.getUsername(), //用户名 user.getPassword(), //密码 ByteSource.Util.bytes(user.getCredentialsSalt()),//salt=username+salt getName() //realm name ); return authenticationInfo; } @Override public void clearCachedAuthorizationInfo(PrincipalCollection principals) { super.clearCachedAuthorizationInfo(principals); } @Override public void clearCachedAuthenticationInfo(PrincipalCollection principals) { super.clearCachedAuthenticationInfo(principals); } @Override public void clearCache(PrincipalCollection principals) { super.clearCache(principals); } public void clearAllCachedAuthorizationInfo() { getAuthorizationCache().clear(); } public void clearAllCachedAuthenticationInfo() { getAuthenticationCache().clear(); } public void clearAllCache() { clearAllCachedAuthenticationInfo(); clearAllCachedAuthorizationInfo(); } }
在某些清空下这种方式可能不是最好的选择,可以考虑直接废弃 Shiro 的缓存,然后自己通过如 AOP 机制实现自己的缓存;可以参考:
https://github.com/zhangkaitao/es/tree/master/web/src/main/java/com/sishuok/es/extra/aop
另外如果和 Spring 集成时可以考虑直接使用 Spring 的 Cache 抽象,可以考虑使用SpringCacheManagerWrapper, 其对 Spring Cache 进行了包装, 转换为 Shiro 的 CacheManager实现:
https://github.com/zhangkaitao/es/blob/master/web/src/main/java/org/apache/shiro/cache/spring/SpringCacheManagerWrapper.java
三、Session 缓存
securityManager实现了SessionsSecurityManager,其会自动判断 SessionManager是否实现了CacheManagerAware 接口,如果实现了会把 CacheManager 设置给它。然后sessionManager会判断相应的sessionDAO(如继承自 CachingSessionDAO)是否实现了CacheManagerAware, 如果实现了会把 CacheManager 设置给它。
package com.github.zhangkaitao.shiro.chapter11.session.dao; import com.github.zhangkaitao.shiro.chapter11.JdbcTemplateUtils; import com.github.zhangkaitao.shiro.chapter11.SerializableUtils; import org.apache.shiro.session.Session; import org.apache.shiro.session.mgt.ValidatingSession; import org.apache.shiro.session.mgt.eis.CachingSessionDAO; import org.springframework.jdbc.core.JdbcTemplate; import java.io.Serializable; import java.util.List; /** * <p>User: Zhang Kaitao * <p>Date: 14-2-8 * <p>Version: 1.0 */ public class MySessionDAO extends CachingSessionDAO { private JdbcTemplate jdbcTemplate = JdbcTemplateUtils.jdbcTemplate(); @Override protected Serializable doCreate(Session session) { Serializable sessionId = generateSessionId(session); assignSessionId(session, sessionId); String sql = "insert into sessions(id, session) values(?,?)"; jdbcTemplate.update(sql, sessionId, SerializableUtils.serialize(session)); return session.getId(); } @Override protected void doUpdate(Session session) { if(session instanceof ValidatingSession && !((ValidatingSession)session).isValid()) { return; //如果会话过期/停止 没必要再更新了 } String sql = "update sessions set session=? where id=?"; jdbcTemplate.update(sql, SerializableUtils.serialize(session), session.getId()); } @Override protected void doDelete(Session session) { String sql = "delete from sessions where id=?"; jdbcTemplate.update(sql, session.getId()); } @Override protected Session doReadSession(Serializable sessionId) { String sql = "select session from sessions where id=?"; List<String> sessionStrList = jdbcTemplate.queryForList(sql, String.class, sessionId); if(sessionStrList.size() == 0) return null; return SerializableUtils.deserialize(sessionStrList.get(0)); } }