最近用shiro,遇到很多坑,边学边用,现在记下来。文采不够,直接上代码
1.坐标
1 <dependency> 2 <groupId>org.apache.shiro</groupId> 3 <artifactId>shiro-web</artifactId> 4 <version>${shiro.version}</version> 5 </dependency> 6 <dependency> 7 <groupId>org.apache.shiro</groupId> 8 <artifactId>shiro-core</artifactId> 9 <version>${shiro.version}</version> 10 </dependency> 11 <dependency> 12 <groupId>org.apache.shiro</groupId> 13 <artifactId>shiro-spring</artifactId> 14 <version>${shiro.version}</version> 15 </dependency> 16 <dependency> 17 <groupId>org.apache.shiro</groupId> 18 <artifactId>shiro-ehcache</artifactId> 19 <version>${shiro.version}</version> 20 </dependency> 21 <!-- ehcache --> 22 <dependency> 23 <groupId>net.sf.ehcache</groupId> 24 <artifactId>ehcache-core</artifactId> 25 <version>2.6.6</version> 26 </dependency>
jar包版本 <shiro.version>1.4.0</shiro.version>
2.web.xml
<filter> <filter-name>shiroFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <param-name>targetFilterLifecycle</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>shiroFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
这里要注意的是filter-name 必须更filter bean的名字一样
3.applicationcontext-shiro.xml
1 <!-- 自定义realm --> 2 <bean id="comprehensiveRealm" class="com.comprehensive.shiro.ComprehensiveRealm"> 3 <property name="authorizationCacheName" value="comprehensive"></property> 4 <property name="authenticationCachingEnabled" value="true" /> 5 <property name="cachingEnabled" value="true" /> 6 </bean> 7 8 <!-- 安全管理器 --> 9 <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> 10 <property name="cacheManager" ref="redisCacheManager" /> 11 <property name="realm" ref="comprehensiveRealm" /> 12 <!-- <property name="sessionMode" value="http" /> --> 13 <property name="sessionManager" ref="defaultWebSessionManager" /> 14 </bean> 15 16 17 <!-- 初始化ehCacheManager缓存,用于缓存权限 --> 18 <bean id="ehCacheManagerFactoryBean" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"> 19 <property name="configLocation" value="classpath:/spring/ehcache.xml" /> 20 </bean> 21 <bean id="ehCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> 22 <property name="cacheManager" ref="ehCacheManagerFactoryBean"></property> 23 </bean> 24 25 <!-- 自定义redis缓存,用于缓存权限 --> 26 <bean id="redisCacheManager" class="com.comprehensive.shiro.cache.RedisCacheManager"> 27 <property name="compCacheManager" ref="compRedisCacheManager"></property> 28 </bean> 29 30 <!-- 自定义redisSessionDao --> 31 <bean id="redisSessionDao" class="com.comprehensive.shiro.session.RedisSessionDao"> 32 <property name="compCacheManager" ref="compRedisCacheManager" /> 33 </bean> 34 35 <!-- session管理器 --> 36 <bean id="defaultWebSessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager"> 37 <property name="sessionDAO" ref="redisSessionDao" /> 38 <!-- sessionIdCookie的实现,用于重写覆盖容器默认的JSESSIONID --> 39 <property name="sessionIdCookie" ref="sessionIdCookie" /> 40 <!-- 设置全局会话超时时间,默认30分钟(1800000) --> 41 <property name="globalSessionTimeout" value="1800000" /> 42 <!-- 是否在会话过期后会调用SessionDAO的delete方法删除会话 默认true --> 43 <property name="deleteInvalidSessions" value="true" /> 44 <!-- 会话验证器调度时间 --> 45 <property name="sessionValidationInterval" value="1800000" /> 46 <!-- 定时检查失效的session --> 47 <property name="sessionValidationSchedulerEnabled" value="true" /> 48 </bean> 49 50 <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie"> 51 <!-- cookie的name,对应的默认是 JSESSIONID --> 52 <constructor-arg name="name" value="comprehensive.session.id" /> 53 <!-- jsessionId的path为 / 用于多个系统共享jsessionId --> 54 <property name="path" value="/" /> 55 <property name="httpOnly" value="true" /> 56 </bean> 57 58 <!-- 管理shiro bean的生命周期 --> 59 <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" /> 60 61 <!-- shiro bean --> 62 <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> 63 <property name="securityManager" ref="securityManager" /> 64 <property name="loginUrl" value="/system/login" /> 65 <!-- <property name="successUrl" value="/aa" /> --> 66 <property name="unauthorizedUrl" value="/system/unauthorized" /> 67 <property name="filters"> 68 <util:map> 69 <entry key="logout" value-ref="logoutFilter"></entry> 70 </util:map> 71 </property> 72 <property name="filterChainDefinitions"> 73 <value> 74 /js/** = anon 75 /css/** = anon 76 /images/** = anon 77 /system/list* = roles[admin] 78 /system/aa = perms[admin1qqq] 79 /system/logout = logout 80 /system/** = authc 81 </value> 82 </property> 83 </bean> 84 85 <!-- logout过滤器 --> 86 <bean id="logoutFilter" class="org.apache.shiro.web.filter.authc.LogoutFilter"> 87 <property name="redirectUrl" value="/system/login"></property> 88 </bean>
这里面要注意的是:cacheManager我配置了两个,一个是ehCacheManager实现,一个是redisCacheManager实现,用的时候配置一个就可以了。
4.shiro中SessionDAO这个类可以管理session的创建,获取,删除,实际用的时候可以继承AbstractSessionDAO。
1 public class RedisSessionDao extends AbstractSessionDAO { 2 private Logger logger = LoggerFactory.getLogger(LoggerEnum.OPERATION_LOG.toString()); 3 4 private final String REDIS_SHIRO_SESSION = "comprehensive-shiro-session:"; 5 6 private int expire = 60 * 30; 7 8 private CompCacheManager compCacheManager; 9 10 public CompCacheManager getCompCacheManager() { 11 return compCacheManager; 12 } 13 14 public void setCompCacheManager(CompCacheManager compCacheManager) { 15 this.compCacheManager = compCacheManager; 16 } 17 18 public void setExpire(int expire) { 19 this.expire = expire; 20 } 21 22 @Override 23 public void update(Session session) throws UnknownSessionException { 24 if (session == null) { 25 logger.error("shiro update session: {session is null} "); 26 return; 27 } 28 logger.debug("shiro update session: {sessionid} " + session.getId()); 29 this.saveSession(session); 30 } 31 32 @Override 33 public void delete(Session session) { 34 if (session == null || session.getId() == null) { 35 logger.error("session or session id is null"); 36 return; 37 } 38 logger.debug("shiro delete session: {sessionid} " + session.getId()); 39 compCacheManager.delObj(getKey(session.getId())); 40 } 41 42 @Override 43 public Collection<Session> getActiveSessions() { 44 logger.debug("shiro getActiveSessions session"); 45 Set<Session> sessions = new HashSet<>(); 46 Set<byte[]> keys = compCacheManager.keysObj(REDIS_SHIRO_SESSION + "*"); 47 if (keys != null && keys.size() > 0) { 48 for (byte[] bs : keys) { 49 Session session = (Session) SerializaUtil.unserializable(bs); 50 sessions.add(session); 51 } 52 } 53 return sessions; 54 } 55 56 @Override 57 protected Serializable doCreate(Session session) { 58 logger.debug("shiro doCreate session"); 59 Serializable sessionId = this.generateSessionId(session); 60 this.assignSessionId(session, sessionId); 61 this.saveSession(session); 62 return sessionId; 63 } 64 65 @Override 66 protected Session doReadSession(Serializable sessionId) { 67 if (sessionId == null) { 68 logger.error("session id is null"); 69 return null; 70 } 71 logger.debug("shiro doReadSession session:{sessionId} " + sessionId); 72 return (Session) compCacheManager.getObj(getKey(sessionId)); 73 } 74 75 private String getKey(Serializable sessionId) { 76 return this.REDIS_SHIRO_SESSION + sessionId; 77 } 78 79 private void saveSession(Session session) { 80 logger.debug("shiro saveSession session"); 81 if (session == null || session.getId() == null) { 82 logger.error("session or session id is null"); 83 return; 84 } 85 session.setTimeout(expire * 1000); 86 compCacheManager.setObj(getKey(session.getId()), session, expire); 87 } 88 }
5.shiro中对权限的缓存,默认是ehcache,要用的话,只需要配置就可以,下面代码,是用redis来缓存权限
写两个类分别实现shiro的cache接口和继承shiro的CacheManager类
1 @SuppressWarnings("unchecked") 2 public class RedisCache<K, V> implements Cache<K, V> { 3 private Logger logger = LoggerFactory.getLogger(RedisCache.class); 4 5 private final String REDIS_SHIRO_CACHE = "comprehensive-shiro-cache:"; 6 7 private int expire = 60 * 30; 8 9 private CompCacheManager compCacheManager; 10 11 private String name; 12 13 public RedisCache(CompCacheManager compCacheManager, String name) { 14 super(); 15 this.compCacheManager = compCacheManager; 16 this.name = name; 17 } 18 19 public String getName() { 20 if (name == null) 21 return ""; 22 return name; 23 } 24 25 public void setName(String name) { 26 this.name = name; 27 } 28 29 @Override 30 public V get(K key) throws CacheException { 31 try { 32 if (key == null) { 33 return null; 34 } else { 35 byte[] value = compCacheManager.getObj(getByteKey(key)); 36 return (V) SerializaUtil.unserializable(value); 37 } 38 } catch (Throwable t) { 39 throw new CacheException(t); 40 } 41 } 42 43 @Override 44 public V put(K key, V value) throws CacheException { 45 try { 46 V v = get(key); 47 compCacheManager.setObj(getByteKey(key), SerializaUtil.serializable(value), expire); 48 return v; 49 } catch (Throwable t) { 50 throw new CacheException(t); 51 } 52 } 53 54 @Override 55 public V remove(K key) throws CacheException { 56 try { 57 V v = get(key); 58 compCacheManager.delObj(getByteKey(key)); 59 return v; 60 } catch (Throwable t) { 61 throw new CacheException(t); 62 } 63 } 64 65 @Override 66 public void clear() throws CacheException { 67 try { 68 String preKey = this.REDIS_SHIRO_CACHE + "*"; 69 compCacheManager.delAll(preKey); 70 } catch (Throwable t) { 71 throw new CacheException(t); 72 } 73 } 74 75 @Override 76 public int size() { 77 if (keys() == null) { 78 return 0; 79 } 80 return keys().size(); 81 } 82 83 @Override 84 public Set<K> keys() { 85 try { 86 Set<byte[]> keys = compCacheManager.keysObj(this.REDIS_SHIRO_CACHE + "*"); 87 if (CollectionUtils.isEmpty(keys)) { 88 return Collections.emptySet(); 89 } else { 90 Set<K> newKeys = new HashSet<K>(keys.size()); 91 for (byte[] key : keys) { 92 Object o = SerializaUtil.unserializable(key); 93 newKeys.add((K) o); 94 } 95 return newKeys; 96 } 97 } catch (Throwable t) { 98 throw new CacheException(t); 99 } 100 } 101 102 @Override 103 public Collection<V> values() { 104 try { 105 Set<byte[]> keys = compCacheManager.keysObj(this.REDIS_SHIRO_CACHE + "*"); 106 if (!CollectionUtils.isEmpty(keys)) { 107 List<V> values = new ArrayList<V>(keys.size()); 108 for (byte[] key : keys) { 109 V value = get((K) key); 110 if (value != null) { 111 values.add(value); 112 } 113 } 114 return Collections.unmodifiableList(values); 115 } else { 116 return Collections.emptyList(); 117 } 118 } catch (Throwable t) { 119 throw new CacheException(t); 120 } 121 } 122 123 private byte[] getByteKey(K key) { 124 if (key instanceof String) { 125 String preKey = this.REDIS_SHIRO_CACHE + getName() + ":" + key; 126 return preKey.getBytes(); 127 } else { 128 return SerializaUtil.serializable(key); 129 } 130 } 131 132 }
1 @SuppressWarnings("rawtypes") 2 public class RedisCacheManager implements CacheManager { 3 private CompCacheManager compCacheManager; 4 5 public void setCompCacheManager(CompCacheManager compCacheManager) { 6 this.compCacheManager = compCacheManager; 7 } 8 9 private final ConcurrentMap<String, Cache> caches; 10 11 public RedisCacheManager() { 12 caches = new ConcurrentHashMap<String, Cache>(); 13 } 14 15 @SuppressWarnings("unchecked") 16 @Override 17 public <k, v> Cache<k, v> getCache(String name) { 18 Cache cache = caches.get(name); 19 if (cache == null) { 20 cache = new RedisCache<>(compCacheManager, name); 21 caches.put(name, cache); 22 } 23 return cache; 24 } 25 }
附加两篇文章:
shiro的认证过程源码分析:https://blog.csdn.net/wn084/article/details/79554486
shiro的授权过程源码分析:https://blog.csdn.net/wn084/article/details/79563571
代码拉取地址:https://gitee.com/ye_wei/shiro.git