Java使用reids,以及redis与shiro集成

什么是redis:redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。(我也是百度的,个人理解reids就是个比较轻量级的key——value模式的 数据库,这个数据库的存储性能很好,可以用来代替缓存实现的很多功能。而且这个redis数据库是第三方独立的服务,可以用在负载均衡情况下多个服务器,多个web容器之间公用数据的缓存。) 要使用reids,首先可以到官网reids的jar包和redis。 然后把redis的jar包导入到项目中。 \ 我用的版本是2.1.0. 系统是32位的,所以我解压了32位的redis服务。 \
打开以后使用里面的redis-servier。exe来启动redis服务。启动后的结果如下图所示 \


?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
package org.calonlan.security.component;
 
import java.util.Iterator;
import java.util.Set;
 
import org.springframework.beans.factory.annotation.Value;
 
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
 
/**
  * @author Administrator
  *         redismanager主要用来给用户提供一个设计完备的,通过jedis的jar包来管理redis内存数据库的各种方法
  */
public class RedisManager {
 
     // ip和port属性都定义在了properties文件中,这里通过spring的注解方式来直接使用
     @Value ( "${redis.ip}" )
     private String host;
     @Value ( "${redis.port}" )
     private int port;
 
     // 设置为0的话就是永远都不会过期
     private int expire = 0 ;
 
     // 定义一个管理池,所有的redisManager共同使用。
     private static JedisPool jedisPool = null ;
 
     public RedisManager() {
     }
 
     /**
      *
      * 初始化方法,在这个方法中通过host和port来初始化jedispool。
      *
      * */
 
     public void init() {
         if ( null == host || 0 == port) {
             System.out.println( "请初始化redis配置文件" );
             throw new NullPointerException( "找不到redis配置" );
         }
         if (jedisPool == null ) {
             jedisPool = new JedisPool( new JedisPoolConfig(), host, port);
         }
     }
 
     /**
      * get value from redis
      *
      * @param key
      * @return
      */
     public byte [] get( byte [] key) {
         byte [] value = null ;
         Jedis jedis = jedisPool.getResource();
         try {
             value = jedis.get(key);
         } finally {
             jedisPool.returnResource(jedis);
         }
         return value;
     }
 
     /**
      * get value from redis
      *
      * @param key
      * @return
      */
     public String get(String key) {
         String value = null ;
         Jedis jedis = jedisPool.getResource();
         try {
             value = jedis.get(key);
         } finally {
             jedisPool.returnResource(jedis);
         }
         return value;
     }
 
     /**
      * set
      *
      * @param key
      * @param value
      * @return
      */
     public byte [] set( byte [] key, byte [] value) {
         Jedis jedis = jedisPool.getResource();
         try {
             jedis.set(key, value);
             if ( this .expire != 0 ) {
                 jedis.expire(key, this .expire);
             }
         } finally {
             jedisPool.returnResource(jedis);
         }
         return value;
     }
 
     /**
      * set
      *
      * @param key
      * @param value
      * @return
      */
     public String set(String key, String value) {
         Jedis jedis = jedisPool.getResource();
         try {
             jedis.set(key, value);
             if ( this .expire != 0 ) {
                 jedis.expire(key, this .expire);
             }
         } finally {
             jedisPool.returnResource(jedis);
         }
         return value;
     }
 
     /**
      * set
      *
      * @param key
      * @param value
      * @param expire
      * @return
      */
     public byte [] set( byte [] key, byte [] value, int expire) {
         Jedis jedis = jedisPool.getResource();
         try {
             jedis.set(key, value);
             if (expire != 0 ) {
                 jedis.expire(key, expire);
             }
         } finally {
             jedisPool.returnResource(jedis);
         }
         return value;
     }
 
     /**
      * set
      *
      * @param key
      * @param value
      * @param expire
      * @return
      */
     public String set(String key, String value, int expire) {
         Jedis jedis = jedisPool.getResource();
         try {
             jedis.set(key, value);
             if (expire != 0 ) {
                 jedis.expire(key, expire);
             }
         } finally {
             jedisPool.returnResource(jedis);
         }
         return value;
     }
 
     /**
      * del
      *
      * @param key
      */
     public void del( byte [] key) {
         Jedis jedis = jedisPool.getResource();
         try {
             jedis.del(key);
         } finally {
             jedisPool.returnResource(jedis);
         }
     }
 
     /**
      * del
      *
      * @param key
      */
     public void del(String key) {
         Jedis jedis = jedisPool.getResource();
         try {
             jedis.del(key);
         } finally {
             jedisPool.returnResource(jedis);
         }
     }
 
     /**
      * flush
      */
     public void flushDB() {
         Jedis jedis = jedisPool.getResource();
         try {
             jedis.flushDB();
         } finally {
             jedisPool.returnResource(jedis);
         }
     }
 
     /**
      * size
      */
     public Long dbSize() {
         Long dbSize = 0L;
         Jedis jedis = jedisPool.getResource();
         try {
             dbSize = jedis.dbSize();
         } finally {
             jedisPool.returnResource(jedis);
         }
         return dbSize;
     }
 
     /**
      * keys
      *
      * @param regex
      * @return
      */
     public Set< byte []> keys(String pattern) {
         Set< byte []> keys = null ;
         Jedis jedis = jedisPool.getResource();
         try {
             keys = jedis.keys(pattern.getBytes());
         } finally {
             jedisPool.returnResource(jedis);
         }
         return keys;
     }
 
     public void dels(String pattern) {
         Set< byte []> keys = null ;
         Jedis jedis = jedisPool.getResource();
         try {
             keys = jedis.keys(pattern.getBytes());
             Iterator< byte []> ito = keys.iterator();
             while (ito.hasNext()) {
                 jedis.del(ito.next());
             }
         } finally {
             jedisPool.returnResource(jedis);
         }
     }
 
     public String getHost() {
         return host;
     }
 
     public void setHost(String host) {
         this .host = host;
     }
 
     public int getPort() {
         return port;
     }
 
     public void setPort( int port) {
         this .port = port;
     }
 
     public int getExpire() {
         return expire;
     }
 
     public void setExpire( int expire) {
         this .expire = expire;
     }
}
</ byte []></ byte []></ byte []></ byte []>


这里的redisManager是通过spring来进行管理的,所以直接使用了@value来读取properties文件中的属性。properties文件的内容如下:
?
1
2
<span style= "white-space:pre" >  </span>redis.ip= 127.0 . 0.1
<span style= "white-space:pre" >  </span>     redis.port= 6379


ip定义为本机的ip,端口是redis默认的6379端口。
?
1
2
3
4
5
6
7
8
<bean class = "org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" id= "propertyConfigurer" >
     <property name= "locations" >
         <list>
             <value>classpath:/config/jdbc.properties</value>
             <value>classpath:/config/redis.properties</value>
         </list>
     </property>
</bean>
在spring中就是在这个地方把reids服务的配置文件redis.properties文件加载到spring中管理的。

这样redis服务就集成到了项目中,具体的使用,通过对shiro默认缓存和session数据的缓存的保存过程来展示。
项目中遇到的问题是这样的,在同时使用一个机器使用多个web容器或者多个机器同时来为一个项目进行负载均衡时,shiro的默认缓存和session数据无法在多个web容器或者多个机器之间进行同步。为了达到多web容器或多机器负载均衡的目的,我们修改了shiro的默认session管理器和缓存管理器来使shiro通过redis来管理session和缓存。这样多个web容器或机器之间的数据就可以互通共用了。 首先是shiro的配置文件,我用的是spring集成的方式。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
         http: //www.springframework.org/schema/util
         http: //www.springframework.org/schema/util/spring-util-3.0.xsd
         http: //www.springframework.org/schema/beans
         http: //www.springframework.org/schema/beans/spring-beans-3.0.xsd
         http: //www.springframework.org/schema/context
         http: //www.springframework.org/schema/context/spring-context-3.0.xsd
         http: //www.springframework.org/schema/tx
         http: //www.springframework.org/schema/tx/spring-tx-3.0.xsd
         http: //www.springframework.org/schema/jee
         http: //www.springframework.org/schema/jee/spring-jee-3.0.xsd
         http: //www.springframework.org/schema/mvc
         http: //www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
         http: //www.springframework.org/schema/aop
         http: //www.springframework.org/schema/aop/spring-aop-3.0.xsd">
 
     <!-- 缓存管理器 -->
     <bean class = "org.calonlan.security.spring.SpringCacheManagerWrapper" id= "cacheManager" >
         <property name= "cacheManager" ref= "springCacheManager" >
     </property></bean>
 
     <!-- 凭证匹配器 -->
     <bean class = "org.calonlan.security.credentials.RetryLimitHashedCredentialsMatcher" id= "credentialsMatcher" >
         <constructor-arg ref= "cacheManager" >
         <property name= "hashAlgorithmName" value= "md5" >
         <property name= "hashIterations" value= "2" >
         <property name= "storedCredentialsHexEncoded" value= "true" >
     </property></property></property></constructor-arg></bean>
 
     <!-- Realm实现 -->
     <bean class = "org.calonlan.security.realm.UserRealm" id= "userRealm" >
         <property name= "credentialsMatcher" ref= "credentialsMatcher" >
         <property name= "cachingEnabled" value= "true" >
         <!--<property name= "authenticationCachingEnabled" value= "true" /> -->
         <!--<property name= "authenticationCacheName" value= "authenticationCache" /> -->
         <!--<property name= "authorizationCachingEnabled" value= "true" /> -->
         <!--<property name= "authorizationCacheName" value= "authorizationCache" /> -->
     </property></property></bean>
 
     <!-- 会话ID生成器 -->
     <bean class = "org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator" id= "sessionIdGenerator" >
 
     <!-- 会话Cookie模板 -->
     <bean class = "org.apache.shiro.web.servlet.SimpleCookie" id= "sessionIdCookie" >
         <constructor-arg value= "sid" >
         <property name= "httpOnly" value= "true" >
         <property name= "maxAge" value= "-1" >
     </property></property></constructor-arg></bean>
 
     <bean class = "org.apache.shiro.web.servlet.SimpleCookie" id= "rememberMeCookie" >
         <constructor-arg value= "rememberMe" >
         <property name= "httpOnly" value= "true" >
         <property name= "maxAge" value= "2592000" ><!-- 30 天 -->
     </property></property></constructor-arg></bean>
 
     <!-- rememberMe管理器 -->
     <bean class = "org.apache.shiro.web.mgt.CookieRememberMeManager" id= "rememberMeManager" >
         <!-- rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度( 128 256 512 位) -->
         <property name= "cipherKey" value= "#{T(org.apache.shiro.codec.Base64).decode('4AvVhmFLUs0KTA3Kprsdag==')}" >
         <property name= "cookie" ref= "rememberMeCookie" >
     </property></property></bean>
 
     <!-- 会话DAO -->
     <bean class = "org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO" id= "sessionDAO" >
         <property name= "activeSessionsCacheName" value= "shiro-activeSessionCache" >
         <property name= "sessionIdGenerator" ref= "sessionIdGenerator" >
     </property></property></bean>
 
     <!-- 会话验证调度器 -->
     <bean class = "org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler" id= "sessionValidationScheduler" >
         <property name= "sessionValidationInterval" value= "1800000" >
         <property name= "sessionManager" ref= "sessionManager" >
     </property></property></bean>
 
     <!-- 会话管理器 -->
     <bean class = "org.apache.shiro.web.session.mgt.DefaultWebSessionManager" id= "sessionManager" >
         <property name= "globalSessionTimeout" value= "1800000" >
         <property name= "deleteInvalidSessions" value= "true" >
         <property name= "sessionValidationSchedulerEnabled" value= "true" >
         <property name= "sessionValidationScheduler" ref= "sessionValidationScheduler" >
         <property name= "sessionIdCookieEnabled" property= "" ref= "<span style=" value= "true" >
         <property name= "sessionIdCookie" ref= "sessionIdCookie" >
     </property></property></property></property></property></property></bean>
 
     <span style= "color:#ff0000;" ><bean class = "org.calonlan.security.component.CustomShiroSessionDao" id= "customShiroSessionDAO" >
         <property name= "shiroSessionRepository" ref= "jedisShiroSessionRepository" > //自己定义的sessiondao
     </property></bean></span>
 
<span style= "color:#ff0000;" >   <bean class = "org.calonlan.security.component.JedisShiroSessionRepository" id= "jedisShiroSessionRepository" >
         <property name= "redisManager" ref= "redisManager" ></property>  
     </bean></span>
     <span style= "color:#ff0000;" ><bean class = "org.calonlan.security.component.RedisManager" id= "redisManager" ></bean> //注册上面实现的redisManager到spring中</span>
 
     <span style= "background-color: rgb(255, 0, 0);" ><bean class = "org.calonlan.security.component.JedisShiroCacheManager" id= "jedisShiroCacheManager" >
         <property name= "redisManager" ref= "redisManager" ></property>
     </bean></span><span style= "background-color: rgb(255, 255, 255);" >
</span>
<span style= "color:#ff0000;" >   <bean class = "org.calonlan.security.component.CustomShiroCacheManager" id= "customShiroCacheManager" >
         <property name= "shrioCacheManager" ref= "jedisShiroCacheManager" ></property>
     </bean></span>
     <!-- 安全管理器 -->
     <bean class = "org.apache.shiro.web.mgt.DefaultWebSecurityManager" id= "securityManager" >
         <property name= "realm" ref= "userRealm" >
         <property name= "sessionManager" ref= "sessionManager" >
         <property name= "rememberMeManager" property= "" ref= "rememberMeManager" >
     </property></property></property></bean>
 
     <!-- 相当于调用SecurityUtils.setSecurityManager(securityManager) -->
     <bean class = "org.springframework.beans.factory.config.MethodInvokingFactoryBean" >
         <property name= "staticMethod" value= "org.apache.shiro.SecurityUtils.setSecurityManager" >
         <property name= "arguments" ref= "securityManager" >
     </property></property></bean>
 
     <!-- 基于Form表单的身份验证过滤器 -->
     <bean class = "org.apache.shiro.web.filter.authc.FormAuthenticationFilter" id= "formAuthenticationFilter" >
         <property name= "usernameParam" value= "username" >
         <property name= "passwordParam" value= "password" >
         <property name= "rememberMeParam" value= "rememberMe" >
         <property name= "loginUrl" value= "/login" >
     </property></property></property></property></bean>
 
     <bean class = "org.calonlan.security.web.shiro.filter.SysUserFilter" id= "sysUserFilter" >
 
     <!-- Shiro的Web过滤器 -->
     <bean class = "org.apache.shiro.spring.web.ShiroFilterFactoryBean" id= "shiroFilter" >
         <property name= "securityManager" ref= "securityManager" >
         <!-- 逻辑上正确,不起作用 -->
         <property name= "loginUrl" value= "/login" >
         <property name= "successUrl" value= "/admin/index" >
         <property name= "filters" >
             <util:map>
                 <entry key= "authc" value-ref= "formAuthenticationFilter" >
                 <entry key= "sysUser" value-ref= "sysUserFilter" >
             </entry></entry></util:map>
         </property>
         <property name= "filterChainDefinitions" >
             <value>
                 /img/** =anon
                 /ueditor/jsp/upload/** =anon
                 /login = authc
                 /authenticated = authc
                 /css/** = anon
                 /common/** = anon
                 /js/** = anon
                 /admin/** = user,sysUser
                 //*=anon
             </value>
         </property>
     </property></property></property></bean>
 
     <!-- Shiro生命周期处理器 -->
     <bean class = "org.apache.shiro.spring.LifecycleBeanPostProcessor" id= "lifecycleBeanPostProcessor" >
</bean></bean></bean></beans>



主要看上图的红色部分:其中customShiroSessionDAO、jedisShiroSessionRepository用来处理session;customShiroCacheManager、jedisShiroCacheManager用来处理缓存。
他们的源代码如下:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package org.calonlan.security.component;
 
import java.io.Serializable;
import java.util.Collection;
 
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
 
public class CustomShiroSessionDao extends AbstractSessionDAO {
 
     private ShiroSessionRepository shiroSessionRepository;
 
     public ShiroSessionRepository getShiroSessionRepository() {
         return shiroSessionRepository;
     }
 
     public void setShiroSessionRepository(
             ShiroSessionRepository shiroSessionRepository) {
         this .shiroSessionRepository = shiroSessionRepository;
     }
 
     @Override
     public void delete(Session session) {
         if (session == null ) {
             System.out.println( "错误" );
             return ;
         }
         Serializable id = session.getId();
         if (id != null )
             getShiroSessionRepository().deleteSession(id);
 
     }
 
     @Override
     public Collection<session> getActiveSessions() {
         return getShiroSessionRepository().getAllSessions();
     }
 
     @Override
     public void update(Session session) throws UnknownSessionException {
         getShiroSessionRepository().saveSession(session);
     }
 
     @Override
     protected Serializable doCreate(Session session) {
         Serializable sessionId = this .generateSessionId(session);
         this .assignSessionId(session, sessionId);
         getShiroSessionRepository().saveSession(session);
         return sessionId;
     }
 
     @Override
     protected Session doReadSession(Serializable sessionId) {
         return getShiroSessionRepository().getSession(sessionId);
     }
 
}
</session>


这里我们是继承了shiro的AbstractSessionDAO,然后仿照shiro的模式,给他一个shiroSessionRepository,来进行详细的session存储操作。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package org.calonlan.security.component;
 
import java.io.Serializable;
import java.util.Collection;
 
import org.apache.shiro.session.Session;
 
public interface ShiroSessionRepository {
 
     void saveSession(Session session);
 
     void deleteSession(Serializable sessionId);
 
     Session getSession(Serializable sessionId);
 
     Collection<session> getAllSessions();
}</session>

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package org.calonlan.security.component;
 
import java.io.Serializable;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
 
import org.apache.shiro.session.Session;
 
public class JedisShiroSessionRepository implements ShiroSessionRepository {
 
     /**
      *
      * redis session key 前缀
      *
      * */
     private final String REDIS_SHIRO_SESSION = "shiro-session" ;
 
     private RedisManager redisManager;
 
     @Override
     public void saveSession(Session session) {
         redisManager.init();
         if (session == null || session.getId() == null ) {
             System.out.println( "session 或者 session ID 为空" );
         }
         byte [] key = SerializeUtils.serialize(getRedisSessionKey(session
                 .getId()));
         byte [] value = SerializeUtils.serialize(session);
 
         Long timeOut = session.getTimeout() / 1000 ;
         redisManager.set(key, value, Integer.parseInt(timeOut.toString()));
 
     }
 
     @Override
     public void deleteSession(Serializable sessionId) {
         redisManager.init();
         if (sessionId == null ) {
             System.out.println( "id为空" );
         }
         redisManager.del(SerializeUtils
                 .serialize(getRedisSessionKey(sessionId)));
 
     }
 
     @Override
     public Session getSession(Serializable sessionId) {
         redisManager.init();
         if ( null == sessionId) {
             System.out.println( "id为空" );
             return null ;
         }
         Session session = null ;
         byte [] value = redisManager.get(SerializeUtils
                 .serialize(getRedisSessionKey(sessionId)));
         if ( null == value)
             return null ;
         session = (Session) SerializeUtils.deserialize(value);
         return session;
     }
 
     @Override
     public Collection<session> getAllSessions() {
         redisManager.init();
         Set<session> sessions = new HashSet<session>();
         Set< byte []> byteKeys = redisManager
                 .keys( this .REDIS_SHIRO_SESSION + "*" );
         if (byteKeys != null && byteKeys.size() > 0 ) {
             for ( byte [] bs : byteKeys) {
                 Session s = (Session) SerializeUtils.deserialize(redisManager
                         .get(bs));
                 sessions.add(s);
             }
         }
         return sessions;
     }
 
     /**
      * 获取redis中的session key
      *
      * @param sessionId
      * @return
      */
     private String getRedisSessionKey(Serializable sessionId) {
         return this .REDIS_SHIRO_SESSION + sessionId;
     }
 
     public RedisManager getRedisManager() {
         return redisManager;
     }
 
     public void setRedisManager(RedisManager redisManager) {
         this .redisManager = redisManager;
     }
 
     public JedisShiroSessionRepository() {
 
     }
 
     // public static void main(String[] args) {
     // Jedis jj = new Jedis("localhost");
     // //jj.set("key2", "232323231=========");
     // String ss = jj.get("key1");
     // System.out.println(jj.get("key2"));
     // System.out.println(ss);
     // }
}
</ byte []></session></session></session>

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package org.calonlan.security.component;
 
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.util.Destroyable;
 
public class CustomShiroCacheManager implements CacheManager, Destroyable {
 
     private ShiroCacheManager shrioCacheManager;
 
     public ShiroCacheManager getShrioCacheManager() {
         return shrioCacheManager;
     }
 
     public void setShrioCacheManager(ShiroCacheManager shrioCacheManager) {
         this .shrioCacheManager = shrioCacheManager;
     }
 
     @Override
     public void destroy() throws Exception {
         getShrioCacheManager().destroy();
     }
 
     @Override
     public <k, v= "" > Cache<k, v= "" > getCache(String name) throws CacheException {
         return getShrioCacheManager().getCache(name);
     }
 
}
</k,></k,>


?
1
2
3
4
5
6
7
8
9
10
package org.calonlan.security.component;
 
import org.apache.shiro.cache.Cache;
 
public interface ShiroCacheManager {
     <k, v= "" > Cache<k, v= "" > getCache(String name); 
       
     void destroy(); 
}
</k,></k,>


?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package org.calonlan.security.component;
 
import org.apache.shiro.cache.Cache;
 
public class JedisShiroCacheManager implements ShiroCacheManager {
 
     private RedisManager redisManager;
 
     public RedisManager getRedisManager() {
         return redisManager;
     }
 
     public void setRedisManager(RedisManager redisManager) {
         this .redisManager = redisManager;
     }
 
     @Override
     public <k, v= "" > Cache<k, v= "" > getCache(String name) {
         return new JedisShiroCache<k, v= "" >(redisManager, name);
     }
 
     @Override
     public void destroy() {
         redisManager.init();
         redisManager.flushDB();
     }
 
}
</k,></k,></k,>


具体的使用就如如上代码所示,为什么要弄一个什么ShiroCacheManager和ShiroSessionRepository接口呢,我想各位大侠也能知道的。有了这个两个接口,我们就可以通过这个两个接口来实现更多形式的shiro的session和缓存的管理了。

在很多地方使用到了SerializeUtils,它的作用就是把对象转化为byte数组,或把byte数组转化为对象。源代码如下:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package org.calonlan.security.component;
 
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
 
public class SerializeUtils {
 
     public static byte [] serialize(Object o) {
         ByteArrayOutputStream out = new ByteArrayOutputStream();
         try {
             ObjectOutputStream outo = new ObjectOutputStream(out);
             outo.writeObject(o);
         } catch (IOException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         }
 
         return out.toByteArray();
     }
 
     public static Object deserialize( byte [] b) {
         ObjectInputStream oin;
         try {
             oin = new ObjectInputStream( new ByteArrayInputStream(b));
             try {
                 return oin.readObject();
             } catch (ClassNotFoundException e) {
                 // TODO Auto-generated catch block
                 e.printStackTrace();
                 return null ;
             }
         } catch (IOException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
             return null ;
         }
 
     }
}


然后项目的运行结果如下: \
从图中可以看到,我登录后,session信息已经保存到了redis数据库中。多个tomcat做负载均衡的测试结果也很良好,但是我这个笔记本没有安装那种环境,所以无法测试。这都是个人一点小东西,各位大神勿喷。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本项目详细介绍请看:http://www.sojson.com/shiro (强烈推荐) Demo已经部署到线上,地址是http://shiro.itboy.net, 管理员帐号:admin,密码:sojson.com 如果密码错误,请用sojson。 PS:你可以注册自己的帐号,然后用管理员赋权限给你自己的帐号,但是,每20分钟会把数据初始化一次。建议自己下载源码,让Demo跑起来,然后跑的更快,有问题加群解决。 声明: 本人提供这个Shiro + SpringMvc + Mybatis + Redis 的Demo 本着学习的态度,如果有欠缺和不足的地方,给予指正,并且多多包涵。 “去其糟粕取其精华”。如果觉得写的好的地方就给个赞,写的不好的地方,也请多多包涵。 使用过程: 1.创建数据库。 创建语句 :tables.sql 2.插入初始化数据 插入初始化数据:init.data.sql 3.运行。 管理员帐号:admin 密码:sojson ps:定时任务的sql会把密码改变为sojson.com 新版本说明:http://www.sojson.com/blog/164.html 和 http://www.sojson.com/blog/165.html 主要解决是之前说的问题:Shiro 教程,关于最近反应的相关异常问题,解决方法合集。 项目在本页面的附件中提取。 一、Cache配置修改。 配置文件(spring-cache.xml )中已经修改为如下配置: <!-- redis 配置,也可以把配置挪到properties配置文件中,再读取 --> <!-- 这种 arguments 构造的方式,之前配置有缺点。 这里之前的配置有问题,因为参数类型不一致,有时候jar和环境的问题,导致参数根据index对应,会处理问题, 理论上加另一个 name,就可以解决,现在把name 和type都加上,更保险。 --> 二、登录获取上一个URL地址报错。 当没有获取到退出前的request ,为null 的时候会报错。在(UserLoginController.java )135行处有所修改。 /** * shiro 获取登录之前的地址 * 之前0.1版本这个没判断空。 */ SavedRequest savedRequest = WebUtils.getSavedRequest(request); String url = null ; if(null != savedRequest){ url = savedRequest.getRequestUrl(); } /** * 我们平常用的获取上一个请求的方式,在Session不一致的情况下是获取不到的 * String url = (String) request.getAttribute(WebUtils.FORWARD_REQUEST_URI_ATTRIBUTE); */ 三、删除了配置文件中的cookie写入域的问题。 在配置文件里(spring-shiro.xml )中的配置有所修改。 <!-- 会话Cookie模板 --> <!--cookie的name,我故意取名叫xxxxbaidu --> <!--cookie的有效时间 --> <!-- 配置存储Session Cookie的domain为 一级域名 --> 上面配置是去掉了 Session 的存储Key 的作用域,之前设置的.itboy.net ,是写到当前域名的 一级域名 下,这样就可以做到N 个 二级域名 下,三级、四级....下 Session 都是共享的。 <!-- 用户信息记住我功能的相关配置 --> <!-- 配置存储rememberMe Cookie的domain为 一级域名 --> <!-- 30天时间,记住我30天 --> 记住我登录的信息配置。和上面配置是一样的道理,可以在相同 一级域名 下的所有域名都可以获取到登录的信息。 四、简单实现了单个帐号只能在一处登录。 我们在其他的系统中可以看到,单个帐号只允许一人使用,在A处登录了,B处再登录,那A处就被踢出了。如下图所示。 但是此功能不是很完美,当A处被踢出后,再重新登录,这时候B处反应有点慢,具体我还没看,因为是之前加的功能,现在凌晨了,下次我有空再瞧瞧,同学你也可以看看,解决了和我说一声,我把功能修复。 五、修复功能(BUG) 1.修复权限添加功能BUG。 之前功能有问题,每当添加一个权限的时候,默认都给角色为“管理员”的角色默认添加当前新添加的权限。这样达到管理员的权限永远是最大的。由于代码有BUG ,导致所有权限删除了。现已修复。 2.修复项目只能部署到Root目录下的问题。 问题描述:之前项目只能部署到Root 下才能正常运行,目前已经修复,可以带项目路径进行访问了,之前只能这样访问,http://localhost:8080 而不能http://localhost:8080/shiro.demo/ 访问,目前是可以了。 解决方案:在 FreeMarkerViewExtend.java 33行处 增加了BasePath ,通过BasePath 来控制请求目录,在 Freemarker 中可以自由使用,而 JSP 中是直接在 JSP 中获取BasePath 使用。 解决后遗症:因为我们的权限是通过URL 来控制的,那么增加了项目的目录,导致权限不能正确的判断,再加上我们的项目名称(目录)可以自定义,导致更不好判断。 后遗症解决方案:PermissionFilter.java 50行处 解决了这个问题,详情请看代码和注释,其实就是replace 了一下。 HttpServletRequest httpRequest = ((HttpServletRequest)request); /** * 此处是改版后,为了兼容项目不需要部署到root下,也可以正常运行,但是权限没设置目前必须到root 的URI, * 原因:如果你把这个项目叫 ShiroDemo,那么路径就是 /ShiroDemo/xxxx.shtml ,那另外一个人使用,又叫Shiro_Demo,那么就要这么控制/Shiro_Demo/xxxx.shtml * 理解了吗? * 所以这里替换了一下,使用根目录开始的URI */ String uri = httpRequest.getRequestURI();//获取URI String basePath = httpRequest.getContextPath();//获取basePath if(null != uri && uri.startsWith(basePath)){ uri = uri.replace(basePath, ""); } 3.项目启动的时候报错,关于JNDI的错误提示。 其实也不是错,但是看着不舒服,所以还得解决这个问题。解决这个问题需要在web.xml 中的开始部位加入以下代码。 spring.profiles.active dev spring.profiles.default dev spring.liveBeansView.mbeanDomain dev 4.项目Maven打包问题。 打包的时候,不同版本的 Eclipse 还有IDEA 会有打包打不进去Mapper.xml 文件,这个时候要加如下代码(群里同学提供的)。 src/main/java **/*.properties **/*.xml false 在 标签内加入即可,如果还是不能解决,那么请你加群(改名后)说明你的问题,有人会回答你。 5.Tomcat7以上在访问JSP页面的时候,提示JSTL错误。 这个错误是因为Tomcat7 中没有 JSTL 的jar包,现在已经在项目pom.xml 中增加了如下 jar 的引入管理。 javax.servlet jstl 1.2 javax.servlet jsp-api 2.0 provided 如果还是不能解决问题,请在官方群(群号:259217951)内搜索“jstl” 如图下载依赖包。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值