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)); } }