├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── _config.yml
├── checkstyle.xml
├── docs
├── README.md
└── _config.yml
├── pom.xml
└── src
├── integration-test
└── java
│ ├── org
│ └── crazycake
│ │ └── shiro
│ │ ├── RedisSessionDAOIntegrationTest.java
│ │ └── integration
│ │ ├── RedisCacheTest.java
│ │ ├── RedisManagerTest.java
│ │ └── fixture
│ │ ├── TestFixture.java
│ │ └── model
│ │ ├── FakeAuth.java
│ │ ├── FakeSession.java
│ │ └── UserInfo.java
│ └── shiro-standalone.ini
├── main
└── java
│ └── org
│ └── crazycake
│ └── shiro
│ ├── IRedisManager.java
│ ├── LettuceRedisClusterManager.java
│ ├── LettuceRedisManager.java
│ ├── LettuceRedisSentinelManager.java
│ ├── RedisCache.java
│ ├── RedisCacheManager.java
│ ├── RedisClusterManager.java
│ ├── RedisManager.java
│ ├── RedisSentinelManager.java
│ ├── RedisSessionDAO.java
│ ├── common
│ ├── AbstractLettuceRedisManager.java
│ ├── SessionInMemory.java
│ └── WorkAloneRedisManager.java
│ ├── exception
│ ├── CacheManagerPrincipalIdNotAssignedException.java
│ ├── PoolException.java
│ ├── PrincipalIdNullException.java
│ ├── PrincipalInstanceException.java
│ └── SerializationException.java
│ └── serializer
│ ├── MultiClassLoaderObjectInputStream.java
│ ├── ObjectSerializer.java
│ ├── RedisSerializer.java
│ └── StringSerializer.java
└── test
└── java
└── org
└── crazycake
└── shiro
├── RedisCacheManagerTest.java
├── RedisCacheTest.java
├── RedisClusterManagerTest.java
└── RedisSessionDAOTest.java
/.gitignore:
--------------------------------------------------------------------------------
1 | .settings
2 | .classpath
3 | .project
4 | target
5 | .idea
6 | shiro-redis.iml
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 | services:
3 | - redis-server
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 xi yang
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | shiro-redis
2 | =============
3 |
4 | ## Introduction
5 |
6 | shiro only provide the support of ehcache and concurrentHashMap. Here is an implement of redis cache can be used by shiro. Hope it will help you!
7 |
8 | ## Documentation
9 |
10 | Official documentation [is located here](http://alexxiyang.github.io/shiro-redis/).
11 |
12 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-merlot
--------------------------------------------------------------------------------
/checkstyle.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
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 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | shiro-redis
2 | =============
3 |
4 | [](https://travis-ci.org/alexxiyang/shiro-redis)
5 | [](https://maven-badges.herokuapp.com/maven-central/org.crazycake/shiro-redis)
6 |
7 | shiro only provide the support of ehcache and concurrentHashMap. Here is an implement of redis cache can be used by shiro. Hope it will help you!
8 |
9 | # Download
10 |
11 | You use either of the following 2 ways to include `shiro-redis` into your project
12 | * use `git clone https://github.com/alexxiyang/shiro-redis.git` to clone project to your local workspace and build jar file by your self
13 | * add maven dependency
14 |
15 | ```xml
16 |
17 | org.crazycake
18 | shiro-redis
19 | 3.3.1
20 |
21 | ```
22 |
23 | > **Note:**\
24 | > 3.3.0 is compiled in java11 by mistake.
25 | > Please use 3.3.1 which is compiled in java8
26 |
27 | ## shiro-core/jedis Version Comparison Charts
28 |
29 | | shiro-redis | shiro | jedis |
30 | | :----------------:| :-------: | :-------: |
31 | | 3.2.3 | 1.3.2 | 2.9.0 |
32 | | 3.3.0 (java11) | 1.6.0 | 3.3.0 |
33 | | 3.3.1 (java8) | 1.6.0 | 3.3.0 |
34 |
35 | # Before use
36 | Here is the first thing you need to know. Shiro-redis needs an id field to identify your authorization object in Redis. So please make sure your principal class has a field which you can get unique id of this object. Please setting this id field name by `cacheManager.principalIdFieldName = `
37 |
38 | For example:
39 |
40 | If you create `SimpleAuthenticationInfo` like this:
41 | ```java
42 | @Override
43 | protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
44 | UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken)token;
45 | UserInfo userInfo = new UserInfo();
46 | userInfo.setUsername(usernamePasswordToken.getUsername());
47 | return new SimpleAuthenticationInfo(userInfo, "123456", getName());
48 | }
49 | ```
50 |
51 | Then the `userInfo` object is your principal object. You need to make sure `UserInfo` has an unique field for Redis to identify it. Take `userId` as an example:
52 | ```java
53 | public class UserInfo implements Serializable{
54 |
55 | private Integer userId
56 |
57 | private String username;
58 |
59 | public String getUsername() {
60 | return username;
61 | }
62 |
63 | public void setUsername(String username) {
64 | this.username = username;
65 | }
66 |
67 | public Integer getUserId() {
68 | return this.userId;
69 | }
70 | }
71 | ```
72 |
73 | Put userId as the value of `cacheManager.principalIdFieldName`, like this:
74 | ```properties
75 | cacheManager.principalIdFieldName = userId
76 | ```
77 |
78 | If you're using Spring, the configuration should be
79 | ```xml
80 |
81 | ```
82 |
83 | Then `shiro-redis` will call `userInfo.getUserId()` to get the id for saving Redis object.
84 |
85 | # How to configure ?
86 |
87 | You can configure `shiro-redis` either in `shiro.ini` or in `spring-*.xml`
88 |
89 | ## shiro.ini
90 | Here is the configuration example for shiro.ini.
91 |
92 | ### Redis Standalone
93 | If you are running Redis in Standalone mode
94 |
95 | ```properties
96 | [main]
97 | #====================================
98 | # shiro-redis configuration [start]
99 | #====================================
100 |
101 | #===================================
102 | # Redis Manager [start]
103 | #===================================
104 |
105 | # Create redisManager
106 | redisManager = org.crazycake.shiro.RedisManager
107 |
108 | # Redis host. If you don't specify host the default value is 127.0.0.1:6379
109 | redisManager.host = 127.0.0.1:6379
110 |
111 | #===================================
112 | # Redis Manager [end]
113 | #===================================
114 |
115 | #=========================================
116 | # Redis session DAO [start]
117 | #=========================================
118 |
119 | # Create redisSessionDAO
120 | redisSessionDAO = org.crazycake.shiro.RedisSessionDAO
121 |
122 | # Use redisManager as cache manager
123 | redisSessionDAO.redisManager = $redisManager
124 |
125 | sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager
126 |
127 | sessionManager.sessionDAO = $redisSessionDAO
128 |
129 | securityManager.sessionManager = $sessionManager
130 |
131 | #=========================================
132 | # Redis session DAO [end]
133 | #=========================================
134 |
135 | #==========================================
136 | # Redis cache manager [start]
137 | #==========================================
138 |
139 | # Create cacheManager
140 | cacheManager = org.crazycake.shiro.RedisCacheManager
141 |
142 | # Principal id field name. The field which you can get unique id to identify this principal.
143 | # For example, if you use UserInfo as Principal class, the id field maybe `id`, `userId`, `email`, etc.
144 | # Remember to add getter to this id field. For example, `getId()`, `getUserId()`, `getEmail()`, etc.
145 | # Default value is id, that means your principal object must has a method called `getId()`
146 | cacheManager.principalIdFieldName = id
147 |
148 | # Use redisManager as cache manager
149 | cacheManager.redisManager = $redisManager
150 |
151 | securityManager.cacheManager = $cacheManager
152 |
153 | #==========================================
154 | # Redis cache manager [end]
155 | #==========================================
156 |
157 | #=================================
158 | # shiro-redis configuration [end]
159 | #=================================
160 | ```
161 |
162 | For complete configurable options list, check [Configurable Options](#configurable-options).
163 |
164 | Here is a [tutorial project](https://github.com/alexxiyang/shiro-redis-tutorial) for you to understand how to configure `shiro-redis` in `shiro.ini`.
165 |
166 | ### Redis Sentinel
167 | if you're using Redis Sentinel, please replace the `redisManager` configuration of the standalone version into the following:
168 | ```properties
169 | #===================================
170 | # Redis Manager [start]
171 | #===================================
172 |
173 | # Create redisManager
174 | redisManager = org.crazycake.shiro.RedisSentinelManager
175 |
176 | # Sentinel host. If you don't specify host the default value is 127.0.0.1:26379,127.0.0.1:26380,127.0.0.1:26381
177 | redisManager.host = 127.0.0.1:26379,127.0.0.1:26380,127.0.0.1:26381
178 |
179 | # Sentinel master name
180 | redisManager.masterName = mymaster
181 |
182 | #===================================
183 | # Redis Manager [end]
184 | #===================================
185 | ```
186 |
187 | For complete configurable options list, check [Configurable Options](#configurable-options).
188 |
189 | ### Redis Cluster
190 | If you're using redis cluster, please replace the `redisManager` configuration of the standalone version into the following:
191 |
192 | ```properties
193 | #===================================
194 | # Redis Manager [start]
195 | #===================================
196 |
197 | # Create redisManager
198 | redisManager = org.crazycake.shiro.RedisClusterManager
199 |
200 | # Redis host and port list
201 | redisManager.host = 192.168.21.3:7000,192.168.21.3:7001,192.168.21.3:7002,192.168.21.3:7003,192.168.21.3:7004,192.168.21.3:7005
202 |
203 | #===================================
204 | # Redis Manager [end]
205 | #===================================
206 | ```
207 |
208 | For complete configurable options list, check [Configurable Options](#configurable-options).
209 |
210 | ## Spring
211 | If you are using Spring
212 |
213 | ### Redis Standalone
214 | If you are running Redis in Standalone mode
215 |
216 | ```xml
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 | For complete configurable options list, check [Configurable Options](#configurable-options).
253 |
254 | Here is a [tutorial project](https://github.com/alexxiyang/shiro-redis-spring-tutorial) for you to understand how to configure `shiro-redis` in spring configuration file.
255 |
256 | ### Redis Sentinel
257 | If you use redis sentinel, please replace the `redisManager` configuration of the standalone version into the following:
258 | ```xml
259 |
260 |
261 |
262 |
263 |
264 |
265 | ```
266 |
267 | For complete configurable options list, check [Configurable Options](#configurable-options).
268 |
269 | ### Redis Cluster
270 | If you use redis cluster, please replace the `redisManager` configuration of the standalone version into the following:
271 | ```xml
272 |
273 |
274 |
275 |
276 |
277 | ```
278 |
279 | For complete configurable options list, check [Configurable Options](#configurable-options).
280 |
281 | ## Serializer
282 | Since redis only accept `byte[]`, there comes a serializer problem.
283 | Shiro-redis is using `StringSerializer` as key serializer and `ObjectSerializer` as value serializer.
284 | You can use your own custom serializer, as long as this custom serializer implements `org.crazycake.shiro.serializer.RedisSerializer`
285 |
286 | For example, we can change the charset of keySerializer like this
287 | ```properties
288 | # If you want change charset of keySerializer or use your own custom serializer, you need to define serializer first
289 | #
290 | # cacheManagerKeySerializer = org.crazycake.shiro.serializer.StringSerializer
291 |
292 | # Supported encodings refer to https://docs.oracle.com/javase/8/docs/technotes/guides/intl/encoding.doc.html
293 | # UTF-8, UTF-16, UTF-32, ISO-8859-1, GBK, Big5, etc
294 | #
295 | # cacheManagerKeySerializer.charset = UTF-8
296 |
297 | # cacheManager.keySerializer = $cacheManagerKeySerializer
298 | ```
299 |
300 | These 4 options that you can replace them with your cutom serializers:
301 | - cacheManager.keySerializer
302 | - cacheManager.valueSerializer
303 | - redisSessionDAO.keySerializer
304 | - redisSessionDAO.valueSerializer
305 |
306 | ## Configurable Options
307 | Here are all the available options you can use in `shiro-redis` configuration file.
308 |
309 | ### RedisManager
310 |
311 | | Title | Default | Description |
312 | | :------------------| :------------------- | :---------------------------|
313 | | host | `127.0.0.1:6379` | Redis host. If you don't specify host the default value is `127.0.0.1:6379`. If you run redis in sentinel mode or cluster mode, separate host names with comma, like `127.0.0.1:26379,127.0.0.1:26380,127.0.0.1:26381` |
314 | | masterName | `mymaster` | **Only used for sentinel mode**
The master node of Redis sentinel mode |
315 | | timeout | `2000` | Redis connect timeout. Timeout for jedis try to connect to redis server(In milliseconds) |
316 | | soTimeout | `2000` | **Only used for sentinel mode or cluster mode**
The timeout for jedis try to read data from redis server |
317 | | maxAttempts | `3` | **Only used for cluster mode**
Max attempts to connect to server |
318 | | password | | Redis password |
319 | | database | `0` | Redis database. Default value is 0 |
320 | | jedisPoolConfig | `new redis.clients.jedis.JedisPoolConfig()` | JedisPoolConfig. You can create your own JedisPoolConfig instance and set attributes as you wish
Most of time, you don't need to set jedisPoolConfig
Here is an example.
`jedisPoolConfig = redis.clients.jedis.JedisPoolConfig`
`jedisPoolConfig.testWhileIdle = false`
`redisManager.jedisPoolConfig = jedisPoolConfig` |
321 | | count | `100` | Scan count. Shiro-redis use Scan to get keys, so you can define the number of elements returned at every iteration. |
322 | | jedisPool | `null` | **Only used for sentinel mode or single mode**
You can create your own JedisPool instance and set attributes as you wish |
323 |
324 | ### RedisSessionDAO
325 |
326 | | Title | Default | Description |
327 | | :------------------| :------------------- | :---------------------------|
328 | | redisManager | | RedisManager which you just configured above (Required) |
329 | | expire | `-2` | Redis cache key/value expire time. The expire time is in second.
Special values:
`-1`: no expire
`-2`: the same timeout with session
Default value: `-2`
**Note**: Make sure expire time is longer than session timeout. |
330 | | keyPrefix | `shiro:session:` | Custom your redis key prefix for session management
**Note**: Remember to add colon at the end of prefix. |
331 | | sessionInMemoryTimeout | `1000` | When we do signin, `doReadSession(sessionId)` will be called by shiro about 10 times. So shiro-redis save Session in ThreadLocal to remit this problem. sessionInMemoryTimeout is expiration of Session in ThreadLocal.
Most of time, you don't need to change it. |
332 | | sessionInMemoryEnabled | `true` | Whether or not enable temporary save session in ThreadLocal |
333 | | keySerializer | `org.crazycake.shiro.serializer.StringSerializer` | The key serializer of cache manager
You can change the implement of key serializer or the encoding of StringSerializer.
Supported encodings refer to [Supported Encodings](https://docs.oracle.com/javase/8/docs/technotes/guides/intl/encoding.doc.html). Such as `UTF-8`, `UTF-16`, `UTF-32`, `ISO-8859-1`, `GBK`, `Big5`, etc
For more detail, check [Serializer](#serializer) |
334 | | valueSerializer | `org.crazycake.shiro.serializer.ObjectSerializer` | The value serializer of cache manager
You can change the implement of value serializer
For more detail, check [Serializer](#serializer) |
335 |
336 | ### CacheManager
337 |
338 | | Title | Default | Description |
339 | | :--------------------| :------------------- | :---------------------------|
340 | | redisManager | | RedisManager which you just configured above (Required) |
341 | | principalIdFieldName | `id` | Principal id field name. The field which you can get unique id to identify this principal.
For example, if you use UserInfo as Principal class, the id field maybe `id`, `userId`, `email`, etc.
Remember to add getter to this id field. For example, `getId()`, `getUserId(`), `getEmail()`, etc.
Default value is `id`, that means your principal object must has a method called `getId()` |
342 | | expire | `1800` | Redis cache key/value expire time.
The expire time is in second. |
343 | | keyPrefix | `shiro:cache:` | Custom your redis key prefix for cache management
**Note**: Remember to add colon at the end of prefix. |
344 | | keySerializer | `org.crazycake.shiro.serializer.StringSerializer` | The key serializer of cache manager
You can change the implement of key serializer or the encoding of StringSerializer.
Supported encodings refer to [Supported Encodings](https://docs.oracle.com/javase/8/docs/technotes/guides/intl/encoding.doc.html). Such as `UTF-8`, `UTF-16`, `UTF-32`, `ISO-8859-1`, `GBK`, `Big5`, etc
For more detail, check [Serializer](#serializer) |
345 | | valueSerializer | `org.crazycake.shiro.serializer.ObjectSerializer` | The value serializer of cache manager
You can change the implement of value serializer
For more detail, check [Serializer](#serializer) |
346 |
347 | # Spring boot starter
348 |
349 | Using `Spring-Boot` integration is the easiest way to integrate `shiro-redis` into a Spring-base application.
350 |
351 | > Note: `shiro-redis-spring-boot-starter` version `3.2.1` is based on `shiro-spring-boot-web-starter` version `1.4.0-RC2`
352 |
353 | First include the `shiro-redis` Spring boot starter dependency in you application classpath
354 |
355 | ```xml
356 |
357 | org.crazycake
358 | shiro-redis-spring-boot-starter
359 | 3.3.1
360 |
361 | ```
362 |
363 | The next step depends on whether you've created your own `SessionManager` or `SessionsSecurityManager`.
364 | ## If you haven't created your own `SessionManager` or `SessionsSecurityManager`
365 | If you don't have your own `SessionManager` or `SessionsSecurityManager` in your configuration, `shiro-redis-spring-boot-starter` will create `RedisSessionDAO` and `RedisCacheManager` for you. Then inject them into `SessionManager` and `SessionsSecurityManager` automatically.
366 | So, You are all set. Enjoy it!
367 |
368 | ## If you have created your own `SessionManager` or `SessionsSecurityManager`
369 | If you have created your own `SessionManager` or `SessionsSecurityManager` like this:
370 | ```java
371 | @Bean
372 | public SessionsSecurityManager securityManager(List realms) {
373 | DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(realms);
374 |
375 | // other stuff...
376 |
377 | return securityManager;
378 | }
379 | ```
380 |
381 | Then inject `redisSessionDAO` and `redisCacheManager` which created by `shiro-redis-spring-boot-starter` already
382 | ```java
383 | @Autowired
384 | RedisSessionDAO redisSessionDAO;
385 |
386 | @Autowired
387 | RedisCacheManager redisCacheManager;
388 | ```
389 |
390 | Inject them into your own `SessionManager` and `SessionsSecurityManager`
391 |
392 | ```java
393 | @Bean
394 | public SessionManager sessionManager() {
395 | DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
396 |
397 | // inject redisSessionDAO
398 | sessionManager.setSessionDAO(redisSessionDAO);
399 |
400 | // other stuff...
401 |
402 | return sessionManager;
403 | }
404 |
405 | @Bean
406 | public SessionsSecurityManager securityManager(List realms, SessionManager sessionManager) {
407 | DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(realms);
408 |
409 | //inject sessionManager
410 | securityManager.setSessionManager(sessionManager);
411 |
412 | // inject redisCacheManager
413 | securityManager.setCacheManager(redisCacheManager);
414 |
415 | // other stuff...
416 |
417 | return securityManager;
418 | }
419 | ```
420 |
421 | For full example, see [shiro-redis-spring-boot-tutorial](https://github.com/alexxiyang/shiro-redis-spring-boot-tutorial)
422 |
423 | ### Configuration Properties
424 | Here are all available options you can use in Spring-boot starter configuration
425 |
426 | | Title | Default | Description |
427 | | :--------------------------------------------------| :------------------- | :---------------------------|
428 | | shiro-redis.enabled | `true` | Enables shiro-redis’s Spring module |
429 | | shiro-redis.redis-manager.deploy-mode | `standalone` | Redis deploy mode. Options: `standalone`, `sentinel`, 'cluster' |
430 | | shiro-redis.redis-manager.host | `127.0.0.1:6379` | Redis host. If you don't specify host the default value is `127.0.0.1:6379`. If you run redis in sentinel mode or cluster mode, separate host names with comma, like `127.0.0.1:26379,127.0.0.1:26380,127.0.0.1:26381` |
431 | | shiro-redis.redis-manager.master-name | `mymaster` | **Only used for sentinel mode**
The master node of Redis sentinel mode |
432 | | shiro-redis.redis-manager.timeout | `2000` | Redis connect timeout. Timeout for jedis try to connect to redis server(In milliseconds) |
433 | | shiro-redis.redis-manager.so-timeout | `2000` | **Only used for sentinel mode or cluster mode**
The timeout for jedis try to read data from redis server |
434 | | shiro-redis.redis-manager.max-attempts | `3` | **Only used for cluster mode**
Max attempts to connect to server |
435 | | shiro-redis.redis-manager.password | | Redis password |
436 | | shiro-redis.redis-manager.database | `0` | Redis database. Default value is 0 |
437 | | shiro-redis.redis-manager.count | `100` | Scan count. Shiro-redis use Scan to get keys, so you can define the number of elements returned at every iteration. |
438 | | shiro-redis.session-dao.expire | `-2` | Redis cache key/value expire time. The expire time is in second.
Special values:
`-1`: no expire
`-2`: the same timeout with session
Default value: `-2`
**Note**: Make sure expire time is longer than session timeout. |
439 | | shiro-redis.session-dao.key-prefix | `shiro:session:` | Custom your redis key prefix for session management
**Note**: Remember to add colon at the end of prefix. |
440 | | shiro-redis.session-dao.session-in-memory-timeout | `1000` | When we do signin, `doReadSession(sessionId)` will be called by shiro about 10 times. So shiro-redis save Session in ThreadLocal to remit this problem. sessionInMemoryTimeout is expiration of Session in ThreadLocal.
Most of time, you don't need to change it. |
441 | | shiro-redis.session-dao.session-in-memory-enabled | `true` | Whether or not enable temporary save session in ThreadLocal |
442 | | shiro-redis.cache-manager.principal-id-field-name | `id` | Principal id field name. The field which you can get unique id to identify this principal.
For example, if you use UserInfo as Principal class, the id field maybe `id`, `userId`, `email`, etc.
Remember to add getter to this id field. For example, `getId()`, `getUserId(`), `getEmail()`, etc.
Default value is `id`, that means your principal object must has a method called `getId()` |
443 | | shiro-redis.cache-manager.expire | `1800` | Redis cache key/value expire time.
The expire time is in second. |
444 | | shiro-redis.cache-manager.key-prefix | `shiro:cache:` | Custom your redis key prefix for cache management
**Note**: Remember to add colon at the end of prefix. |
445 |
446 |
447 | ## Working with `spring-boot-devtools`
448 | If you are using `shiro-redis` with `spring-boot-devtools`. Please add this line to `resources/META-INF/spring-devtools.properties` (Create it if there is no this file):
449 | ```ini
450 | restart.include.shiro-redis=/shiro-[\\w-\\.]+jar
451 | ```
452 |
453 | # If you found any bugs
454 |
455 | Please create the issue
456 |
457 | 可以用中文
458 |
--------------------------------------------------------------------------------
/docs/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-merlot
2 | show_downloads: true
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 | org.crazycake
6 | shiro-redis
7 | 3.3.2
8 | jar
9 |
10 | shiro-redis
11 | shiro only provide the support of ehcache and concurrentHashMap. Here is an implement of redis cache can be used by shiro. Hope it will help you!
12 | https://github.com/alexxiyang/shiro-redis
13 |
14 |
15 | UTF-8
16 |
17 |
18 |
19 |
20 | The Apache Software License, Version 2.0
21 | http://www.apache.org/licenses/LICENSE-2.0.txt
22 | repo
23 |
24 |
25 |
26 |
27 |
28 | redis.clients
29 | jedis
30 | 3.6.0
31 | true
32 |
33 |
34 | io.lettuce
35 | lettuce-core
36 | 6.2.3.RELEASE
37 | true
38 |
39 |
40 |
41 | org.slf4j
42 | slf4j-api
43 | 1.7.30
44 |
45 |
46 | org.apache.shiro
47 | shiro-core
48 | 1.11.0
49 |
50 |
51 |
52 |
53 | org.junit.jupiter
54 | junit-jupiter-api
55 | 5.6.2
56 | test
57 |
58 |
59 | org.slf4j
60 | slf4j-simple
61 | 1.7.30
62 | test
63 |
64 |
65 | commons-logging
66 | commons-logging
67 | 1.2
68 | test
69 |
70 |
71 | org.mockito
72 | mockito-core
73 | 3.5.7
74 | test
75 |
76 |
77 | com.github.javafaker
78 | javafaker
79 | 1.0.2
80 | test
81 |
82 |
83 | org.hamcrest
84 | hamcrest
85 | 2.2
86 | test
87 |
88 |
89 |
90 |
91 |
92 | alexxiyang
93 | Alex Yang
94 | alexxiyang@gmail.com
95 | GMT-7
96 | https://github.com/alexxiyang
97 |
98 |
99 |
100 |
101 |
102 | scm:git:https://github.com/alexxiyang/shiro-redis.git
103 | scm:git:https://github.com/alexxiyang/shiro-redis.git
104 | https://github.com/alexxiyang/shiro-redis.git
105 |
106 |
107 |
108 | ossrh
109 | https://oss.sonatype.org/content/repositories/snapshots
110 |
111 |
112 | ossrh
113 | https://oss.sonatype.org/service/local/staging/deploy/maven2/
114 |
115 |
116 |
117 | shiro-redis
118 |
119 |
120 | maven-compiler-plugin
121 | 3.8.0
122 |
123 | 1.8
124 | 1.8
125 | UTF-8
126 | -nowarn
127 |
128 |
129 |
130 | org.apache.maven.plugins
131 | maven-checkstyle-plugin
132 | 3.1.0
133 |
134 |
135 | checkstyle
136 | validate
137 |
138 | check
139 |
140 |
141 | true
142 | true
143 | checkstyle.xml
144 |
145 |
146 |
147 |
148 |
149 | org.apache.maven.plugins
150 | maven-surefire-plugin
151 | 2.22.0
152 |
153 |
154 |
155 | org.junit.platform
156 | junit-platform-surefire-provider
157 | 1.3.2
158 |
159 |
160 | org.junit.jupiter
161 | junit-jupiter-engine
162 | 5.6.2
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 | release-sign-artifacts
172 |
173 |
174 | release
175 | true
176 |
177 |
178 |
179 |
180 | D688E942
181 | alexxiyang
182 |
183 |
184 |
185 |
186 |
187 | org.sonatype.plugins
188 | nexus-staging-maven-plugin
189 | 1.6.8
190 | true
191 |
192 | ossrh
193 | https://oss.sonatype.org/
194 | true
195 |
196 |
197 |
198 | org.apache.maven.plugins
199 | maven-source-plugin
200 | 3.2.1
201 |
202 |
203 | attach-sources
204 |
205 | jar-no-fork
206 |
207 |
208 |
209 |
210 |
211 | org.apache.maven.plugins
212 | maven-javadoc-plugin
213 | 3.2.0
214 |
215 |
216 | attach-javadocs
217 |
218 | jar
219 |
220 |
221 |
222 |
223 |
224 | org.apache.maven.plugins
225 | maven-gpg-plugin
226 | 1.6
227 |
228 |
229 | sign-artifacts
230 | verify
231 |
232 | sign
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
--------------------------------------------------------------------------------
/src/integration-test/java/org/crazycake/shiro/RedisSessionDAOIntegrationTest.java:
--------------------------------------------------------------------------------
1 | package org.crazycake.shiro;
2 |
3 | import org.apache.shiro.session.Session;
4 | import org.apache.shiro.session.UnknownSessionException;
5 | import org.crazycake.shiro.common.SessionInMemory;
6 | import org.crazycake.shiro.exception.SerializationException;
7 | import org.crazycake.shiro.integration.fixture.model.FakeSession;
8 | import org.crazycake.shiro.serializer.ObjectSerializer;
9 | import org.crazycake.shiro.serializer.StringSerializer;
10 | import org.junit.jupiter.api.AfterEach;
11 | import org.junit.jupiter.api.Assertions;
12 | import org.junit.jupiter.api.BeforeEach;
13 | import org.junit.jupiter.api.Test;
14 |
15 | import java.io.Serializable;
16 | import java.util.Collection;
17 | import java.util.Map;
18 |
19 | import static org.crazycake.shiro.integration.fixture.TestFixture.*;
20 | import static org.hamcrest.CoreMatchers.*;
21 | import static org.hamcrest.MatcherAssert.assertThat;
22 |
23 | /**
24 | * RedisSessionDAO integration test was put under org.crazycake.shiro
25 | * is because I want to test protected method `doReadSession`
26 | */
27 | public class RedisSessionDAOIntegrationTest {
28 |
29 | private RedisSessionDAO redisSessionDAO;
30 | private FakeSession session1;
31 | private FakeSession session2;
32 | private FakeSession emptySession;
33 | private String name1;
34 | private String prefix;
35 | private StringSerializer keySerializer = new StringSerializer();
36 | private ObjectSerializer valueSerializer = new ObjectSerializer();
37 |
38 | private void blast() {
39 | blastRedis();
40 | }
41 |
42 | private void scaffold() {
43 | prefix = scaffoldPrefix();
44 | RedisManager redisManager = scaffoldStandaloneRedisManager();
45 | redisSessionDAO = scaffoldRedisSessionDAO(redisManager, prefix);
46 | session1 = scaffoldSession();
47 | session2 = scaffoldSession();
48 | emptySession = scaffoldEmptySession();
49 | name1 = scaffoldUsername();
50 | }
51 |
52 | @BeforeEach
53 | public void setUp() {
54 | blast();
55 | scaffold();
56 | }
57 |
58 | @AfterEach
59 | public void tearDown() {
60 | blast();
61 | }
62 |
63 | @Test
64 | public void testDoCreateNull() {
65 | Assertions.assertThrows(UnknownSessionException.class, () -> {
66 | redisSessionDAO.doCreate(null);
67 | });
68 | }
69 |
70 | @Test
71 | public void testDoCreate() {
72 | redisSessionDAO.doCreate(session1);
73 | Session actualSession = redisSessionDAO.doReadSession(session1.getId());
74 | assertSessionEquals(actualSession, session1);
75 | }
76 |
77 | @Test
78 | public void testDoCreateWithSessionTimeout() {
79 | doSetSessionDAOExpire(redisSessionDAO, -2);
80 | redisSessionDAO.doCreate(session2);
81 | assertEquals(getRedisTTL(prefix + session2.getId(), new StringSerializer()), 1800L);
82 | }
83 |
84 | @Test
85 | public void testUpdateNull() {
86 | Assertions.assertThrows(UnknownSessionException.class, () -> {
87 | redisSessionDAO.update(null);
88 | });
89 | }
90 |
91 | @Test
92 | public void testUpdateEmptySession() {
93 | Assertions.assertThrows(UnknownSessionException.class, () -> {
94 | redisSessionDAO.update(emptySession);
95 | });
96 | }
97 |
98 | @Test
99 | public void testUpdate() {
100 | redisSessionDAO.doCreate(session1);
101 | redisSessionDAO.doReadSession(session1.getId());
102 | doChangeSessionName(session1, name1);
103 | redisSessionDAO.update(session1);
104 | FakeSession actualSession = (FakeSession)redisSessionDAO.doReadSession(session1.getId());
105 | assertEquals(actualSession.getName(), name1);
106 | }
107 |
108 | @Test
109 | public void testUpdateWithoutSessionInMemory() {
110 | redisSessionDAO.setSessionInMemoryEnabled(false);
111 | redisSessionDAO.doCreate(session1);
112 | redisSessionDAO.doReadSession(session1.getId());
113 | doChangeSessionName(session1, name1);
114 | redisSessionDAO.update(session1);
115 | FakeSession actualSession = (FakeSession)redisSessionDAO.doReadSession(session1.getId());
116 | assertEquals(actualSession.getName(), name1);
117 | }
118 |
119 | @Test
120 | public void testDelete() {
121 | redisSessionDAO.doCreate(session1);
122 | redisSessionDAO.delete(session1);
123 | assertRedisEmpty();
124 | }
125 |
126 | @Test
127 | public void testGetActiveSessions() {
128 | redisSessionDAO.doCreate(session1);
129 | redisSessionDAO.doCreate(session2);
130 | Collection activeSessions = redisSessionDAO.getActiveSessions();
131 | assertEquals(activeSessions.size(), 2);
132 | }
133 |
134 | @Test
135 | public void testRemoveExpiredSessionInMemory() throws InterruptedException, SerializationException {
136 | redisSessionDAO.setSessionInMemoryTimeout(500L);
137 | redisSessionDAO.doCreate(session1);
138 | redisSessionDAO.doReadSession(session1.getId());
139 | Thread.sleep(1000);
140 | redisSessionDAO.doCreate(session2);
141 | redisSessionDAO.doReadSession(session2.getId());
142 | Map sessionMap = (Map) redisSessionDAO.getSessionsInThread().get();
143 | assertEquals(sessionMap.size(), 1);
144 | }
145 |
146 | @Test
147 | public void testTurnOffSessionInMemoryEnabled() throws InterruptedException, SerializationException {
148 | redisSessionDAO.setSessionInMemoryTimeout(2000L);
149 | session1.setCompany("apple");
150 | redisSessionDAO.doCreate(session1);
151 | // Load session into SessionInThread
152 | redisSessionDAO.doReadSession(session1.getId());
153 | // Directly update session in Redis
154 | session1.setCompany("google");
155 | RedisManager redisManager = scaffoldStandaloneRedisManager();
156 | String sessionRedisKey = prefix + session1.getId();
157 | redisManager.set(keySerializer.serialize(sessionRedisKey), valueSerializer.serialize(session1), 10);
158 | // Try to read session again
159 | Thread.sleep(500);
160 | FakeSession sessionFromThreadLocal = (FakeSession)redisSessionDAO.doReadSession(session1.getId());
161 | // The company should be the old value
162 | assertThat(sessionFromThreadLocal.getCompany(), is("apple"));
163 | // Turn off sessionInMemoryEnabled
164 | redisSessionDAO.setSessionInMemoryEnabled(false);
165 | // Try to read session again. It should get the version in Redis
166 | FakeSession sessionFromRedis = (FakeSession)redisSessionDAO.doReadSession(session1.getId());
167 | assertThat(sessionFromRedis.getCompany(), is("google"));
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/src/integration-test/java/org/crazycake/shiro/integration/RedisCacheTest.java:
--------------------------------------------------------------------------------
1 | package org.crazycake.shiro.integration;
2 |
3 | import com.github.javafaker.Faker;
4 | import org.apache.commons.lang3.math.NumberUtils;
5 | import org.apache.shiro.subject.PrincipalCollection;
6 | import org.apache.shiro.subject.SimplePrincipalCollection;
7 | import org.crazycake.shiro.RedisCache;
8 | import org.crazycake.shiro.RedisCacheManager;
9 | import org.crazycake.shiro.RedisManager;
10 | import org.crazycake.shiro.exception.CacheManagerPrincipalIdNotAssignedException;
11 | import org.crazycake.shiro.exception.PrincipalInstanceException;
12 | import org.crazycake.shiro.integration.fixture.model.FakeAuth;
13 | import org.crazycake.shiro.integration.fixture.model.UserInfo;
14 | import org.crazycake.shiro.serializer.ObjectSerializer;
15 | import org.crazycake.shiro.serializer.StringSerializer;
16 | import org.junit.jupiter.api.AfterEach;
17 | import org.junit.jupiter.api.Assertions;
18 | import org.junit.jupiter.api.BeforeEach;
19 | import org.junit.jupiter.api.Test;
20 |
21 | import java.util.Properties;
22 | import java.util.Set;
23 |
24 | import static org.crazycake.shiro.integration.fixture.TestFixture.*;
25 |
26 | /**
27 | * input key, value (java)
28 | * output value (java)
29 | */
30 | public class RedisCacheTest {
31 |
32 | private RedisCache redisCache;
33 | private RedisCache redisCacheWithPrincipalIdFieldName;
34 | private RedisCache redisCacheWithEmptyPrincipalIdFieldName;
35 | private RedisCache redisCacheWithStrings;
36 |
37 | private Properties properties = loadProperties("shiro-standalone.ini");
38 | private PrincipalCollection user1;
39 | private PrincipalCollection user2;
40 | private PrincipalCollection user3;
41 | private PrincipalCollection user4;
42 |
43 | private Set users1_2_3;
44 | private String prefix;
45 |
46 | private void blast() {
47 | blastRedis();
48 | }
49 |
50 | private void scaffold() {
51 | RedisManager redisManager = scaffoldStandaloneRedisManager();
52 | prefix = scaffoldPrefix();
53 | redisCache = scaffoldRedisCache(redisManager, new StringSerializer(), new ObjectSerializer(), prefix, NumberUtils.toInt(properties.getProperty("cacheManager.expire")), RedisCacheManager.DEFAULT_PRINCIPAL_ID_FIELD_NAME);
54 | redisCacheWithPrincipalIdFieldName = scaffoldRedisCache(redisManager, new StringSerializer(), new ObjectSerializer(), prefix, NumberUtils.toInt(properties.getProperty("cacheManager.expire")), properties.getProperty("cacheManager.principalIdFieldName"));
55 | redisCacheWithEmptyPrincipalIdFieldName = scaffoldRedisCache(redisManager, new StringSerializer(), new ObjectSerializer(), prefix, NumberUtils.toInt(properties.getProperty("cacheManager.expire")), "");
56 | redisCacheWithStrings = scaffoldRedisCache(redisManager, new StringSerializer(), new ObjectSerializer(), prefix, NumberUtils.toInt(properties.getProperty("cacheManager.expire")), properties.getProperty("cacheManager.principalIdFieldName"));
57 | user1 = scaffoldAuthKey(scaffoldUser());
58 | user2 = scaffoldAuthKey(scaffoldUser());
59 | user3 = scaffoldAuthKey(scaffoldUser());
60 | user4 = new SimplePrincipalCollection(Faker.instance().gameOfThrones().character(), Faker.instance().gameOfThrones().city());
61 | users1_2_3 = scaffoldKeys(user1, user2, user3);
62 | }
63 |
64 |
65 | @BeforeEach
66 | public void setUp() {
67 | blast();
68 | scaffold();
69 | }
70 |
71 | @AfterEach
72 | public void tearDown() {
73 | blast();
74 | }
75 |
76 |
77 | @Test
78 | public void testInitialize() {
79 | Assertions.assertThrows(IllegalArgumentException.class, () -> {
80 | new RedisCache(null, null, null, "abc:", 1, RedisCacheManager.DEFAULT_PRINCIPAL_ID_FIELD_NAME);
81 | });
82 |
83 | Assertions.assertThrows(IllegalArgumentException.class, () -> {
84 | new RedisCache(new RedisManager(), null, null, "abc:", 1, RedisCacheManager.DEFAULT_PRINCIPAL_ID_FIELD_NAME);
85 | });
86 |
87 | Assertions.assertThrows(IllegalArgumentException.class, () -> {
88 | new RedisCache(new RedisManager(), new StringSerializer(), null, "abc:", 1, RedisCacheManager.DEFAULT_PRINCIPAL_ID_FIELD_NAME);
89 | });
90 |
91 | RedisCache rc = new RedisCache(new RedisManager(), new StringSerializer(), new ObjectSerializer(), "abc", 1, RedisCacheManager.DEFAULT_PRINCIPAL_ID_FIELD_NAME);
92 | assertEquals(rc.getKeyPrefix(), "abc");
93 | }
94 |
95 | @Test
96 | public void testPutNull() {
97 | doPutAuth(redisCache, null);
98 | assertRedisEmpty();
99 | }
100 |
101 | @Test
102 | public void testPut() {
103 | doPutAuth(redisCache, user1);
104 | FakeAuth fakeAuth = redisCache.get(user1);
105 | assertAuthEquals(fakeAuth, turnUserToFakeAuth((UserInfo)user1.getPrimaryPrincipal()));
106 | }
107 |
108 | @Test
109 | public void testPutString() {
110 | redisCacheWithStrings.put(user4, user4.getPrimaryPrincipal().toString());
111 | String auth = redisCacheWithStrings.get(user4);
112 | assertEquals(auth, user4.getPrimaryPrincipal());
113 | }
114 |
115 | @Test
116 | public void testSize() throws InterruptedException {
117 | doPutAuth(redisCache, user1);
118 | doPutAuth(redisCache, user2);
119 | assertEquals(redisCache.size(), 2);
120 | }
121 |
122 | @Test
123 | public void testPutInvalidPrincipal() {
124 | Assertions.assertThrows(PrincipalInstanceException.class, () -> {
125 | doPutAuth(redisCacheWithPrincipalIdFieldName, user3);
126 | });
127 | }
128 |
129 | @Test
130 | public void testPutPrincipalWithEmptyIdFieldName() {
131 | Assertions.assertThrows(CacheManagerPrincipalIdNotAssignedException.class, () -> {
132 | doPutAuth(redisCacheWithEmptyPrincipalIdFieldName, user3);
133 | });
134 | }
135 |
136 | @Test
137 | public void testRemove() {
138 | doPutAuth(redisCache, user1);
139 | doRemoveAuth(redisCache, user1);
140 | assertRedisEmpty();
141 | }
142 |
143 | @Test
144 | public void testClear() {
145 | doClearAuth(redisCache);
146 | assertRedisEmpty();
147 | }
148 |
149 | @Test
150 | public void testKeys() {
151 | doPutAuth(redisCache, user1);
152 | doPutAuth(redisCache, user2);
153 | doPutAuth(redisCache, user3);
154 | Set actualKeys = doKeysAuth(redisCache);
155 | assertKeysEquals(actualKeys, turnPrincipalCollectionToString(users1_2_3, prefix));
156 | }
157 |
158 |
159 | }
160 |
--------------------------------------------------------------------------------
/src/integration-test/java/org/crazycake/shiro/integration/RedisManagerTest.java:
--------------------------------------------------------------------------------
1 | package org.crazycake.shiro.integration;
2 |
3 | import org.apache.shiro.subject.PrincipalCollection;
4 | import org.apache.shiro.subject.SimplePrincipalCollection;
5 | import org.crazycake.shiro.RedisManager;
6 | import org.crazycake.shiro.exception.SerializationException;
7 | import org.crazycake.shiro.integration.fixture.model.UserInfo;
8 | import org.crazycake.shiro.serializer.ObjectSerializer;
9 | import org.crazycake.shiro.serializer.StringSerializer;
10 | import org.junit.jupiter.api.AfterEach;
11 | import org.junit.jupiter.api.BeforeEach;
12 | import org.junit.jupiter.api.Test;
13 |
14 | import java.util.Set;
15 |
16 | import static org.crazycake.shiro.integration.fixture.TestFixture.*;
17 | import static org.hamcrest.MatcherAssert.assertThat;
18 | import static org.hamcrest.CoreMatchers.*;
19 |
20 | public class RedisManagerTest {
21 | private PrincipalCollection user1;
22 | private RedisManager redisManager;
23 | private StringSerializer keySerializer = new StringSerializer();
24 | private ObjectSerializer valueSerializer = new ObjectSerializer();
25 |
26 | private void scaffold() {
27 | redisManager = scaffoldStandaloneRedisManager();
28 | user1 = scaffoldAuthKey(scaffoldUser());
29 | }
30 |
31 | @BeforeEach
32 | public void setUp() {
33 | scaffold();
34 | }
35 |
36 | @AfterEach
37 | public void tearDown() {
38 | blastRedis();
39 | }
40 |
41 | @Test
42 | public void testSet() throws SerializationException, InterruptedException {
43 | UserInfo userInfo = (UserInfo)user1.getPrimaryPrincipal();
44 | this.redisManager.set(this.keySerializer.serialize("user:" + userInfo.getId()), this.valueSerializer.serialize(user1), 1);
45 | assertThat(this.redisManager.get(this.keySerializer.serialize("user:" + userInfo.getId())), is(this.valueSerializer.serialize(user1)));
46 | Thread.sleep(1500);
47 | byte[] aaa = this.redisManager.get(this.keySerializer.serialize("user:" + userInfo.getId()));
48 | assertThat(valueSerializer.deserialize(this.redisManager.get(this.keySerializer.serialize("user:" + userInfo.getId()))), is(nullValue()));
49 | }
50 |
51 | @Test
52 | public void testDel() throws SerializationException {
53 | UserInfo userInfo = (UserInfo)user1.getPrimaryPrincipal();
54 | this.redisManager.set(this.keySerializer.serialize("user:" + userInfo.getId()), this.valueSerializer.serialize(user1), 2);
55 | this.redisManager.del(this.keySerializer.serialize("user:" + userInfo.getId()));
56 | assertThat(this.redisManager.get(this.keySerializer.serialize("user:" + userInfo.getId())), is(nullValue()));
57 | }
58 |
59 | @Test
60 | public void testKeys() throws SerializationException {
61 | for (int i = 1; i < 121; i++) {
62 | UserInfo user = new UserInfo();
63 | user.setId(i);
64 | SimplePrincipalCollection principal = new SimplePrincipalCollection();
65 | principal.add(user, "student");
66 | this.redisManager.set(this.keySerializer.serialize("user:" + user.getId()), this.valueSerializer.serialize(principal), 10);
67 | }
68 |
69 | Set keys = this.redisManager.keys(this.keySerializer.serialize("user:*"));
70 | assertThat(keys.size(), is(120));
71 | }
72 |
73 | @Test
74 | public void testDbSize() throws SerializationException {
75 | for (int i = 1; i < 136; i++) {
76 | UserInfo user = new UserInfo();
77 | user.setId(i);
78 | SimplePrincipalCollection principal = new SimplePrincipalCollection();
79 | principal.add(user, "student");
80 | this.redisManager.set(this.keySerializer.serialize("user:" + user.getId()), this.valueSerializer.serialize(principal), 10);
81 | }
82 |
83 | Long dbSize = this.redisManager.dbSize(this.keySerializer.serialize("user:*"));
84 | assertThat(dbSize, is(135L));
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/integration-test/java/org/crazycake/shiro/integration/fixture/TestFixture.java:
--------------------------------------------------------------------------------
1 | package org.crazycake.shiro.integration.fixture;
2 |
3 | import com.github.javafaker.Faker;
4 | import org.apache.commons.lang3.math.NumberUtils;
5 | import org.apache.shiro.session.Session;
6 | import org.apache.shiro.subject.PrincipalCollection;
7 | import org.apache.shiro.subject.SimplePrincipalCollection;
8 | import org.crazycake.shiro.RedisCache;
9 | import org.crazycake.shiro.RedisManager;
10 | import org.crazycake.shiro.RedisSessionDAO;
11 | import org.crazycake.shiro.exception.SerializationException;
12 | import org.crazycake.shiro.integration.fixture.model.FakeAuth;
13 | import org.crazycake.shiro.integration.fixture.model.FakeSession;
14 | import org.crazycake.shiro.integration.fixture.model.UserInfo;
15 | import org.crazycake.shiro.serializer.RedisSerializer;
16 | import redis.clients.jedis.Jedis;
17 |
18 | import java.io.IOException;
19 | import java.io.InputStream;
20 | import java.util.HashSet;
21 | import java.util.Properties;
22 | import java.util.Set;
23 |
24 | import static org.hamcrest.CoreMatchers.is;
25 | import static org.hamcrest.CoreMatchers.notNullValue;
26 | import static org.hamcrest.MatcherAssert.assertThat;
27 | import static org.hamcrest.core.StringContains.containsString;
28 |
29 | public class TestFixture {
30 |
31 | private static Properties properties = loadProperties("shiro-standalone.ini");
32 | private static Faker faker = new Faker();
33 |
34 | // /$$ /$$ /$$
35 | // | $$ | $$ | $$
36 | // | $$$$$$$ | $$ /$$$$$$ /$$$$$$$ /$$$$$$
37 | // | $$__ $$| $$ |____ $$ /$$_____/|_ $$_/
38 | // | $$ \ $$| $$ /$$$$$$$| $$$$$$ | $$
39 | // | $$ | $$| $$ /$$__ $$ \____ $$ | $$ /$$
40 | // | $$$$$$$/| $$| $$$$$$$ /$$$$$$$/ | $$$$/
41 | // |_______/ |__/ \_______/|_______/ \___/
42 |
43 |
44 | public static void blastRedis() {
45 | Jedis jedis = doGetRedisInstance();
46 | jedis.flushAll();
47 | jedis.close();
48 | }
49 |
50 | // /$$$$$$ /$$$$$$ /$$ /$$
51 | // /$$__ $$ /$$__ $$ | $$ | $$
52 | // /$$$$$$$ /$$$$$$$ /$$$$$$ | $$ \__/| $$ \__//$$$$$$ | $$ /$$$$$$$
53 | // /$$_____/ /$$_____/ |____ $$| $$$$ | $$$$ /$$__ $$| $$ /$$__ $$
54 | //| $$$$$$ | $$ /$$$$$$$| $$_/ | $$_/ | $$ \ $$| $$| $$ | $$
55 | // \____ $$| $$ /$$__ $$| $$ | $$ | $$ | $$| $$| $$ | $$
56 | // /$$$$$$$/| $$$$$$$| $$$$$$$| $$ | $$ | $$$$$$/| $$| $$$$$$$
57 | //|_______/ \_______/ \_______/|__/ |__/ \______/ |__/ \_______/
58 |
59 | public static RedisCache scaffoldRedisCache(RedisManager redisManager, RedisSerializer keySerializer, RedisSerializer valueSerializer, String prefix, int expire, String principalIdFieldName) {
60 | return new RedisCache(redisManager, keySerializer, valueSerializer, prefix, expire, principalIdFieldName);
61 | }
62 |
63 | public static RedisSessionDAO scaffoldRedisSessionDAO(RedisManager redisManager, String prefix) {
64 | RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
65 | redisSessionDAO.setRedisManager(redisManager);
66 | redisSessionDAO.setKeyPrefix(prefix);
67 | redisSessionDAO.setExpire(NumberUtils.toInt(properties.getProperty("redisSessionDAO.expire")));
68 | return redisSessionDAO;
69 | }
70 |
71 | public static String scaffoldPrefix() {
72 | return faker.university().name().replace(" ", "_") + ":";
73 | }
74 |
75 | public static RedisManager scaffoldStandaloneRedisManager() {
76 | RedisManager redisManager = new RedisManager();
77 | redisManager.setHost(properties.getProperty("redisManager.host"));
78 | return redisManager;
79 | }
80 |
81 | public static UserInfo scaffoldUser() {
82 | UserInfo user = new UserInfo();
83 | user.setId(faker.number().randomDigitNotZero());
84 | user.setUsername(faker.name().username());
85 | user.setAge(faker.number().numberBetween(18, 60));
86 | user.setRole(faker.number().randomDigitNotZero());
87 | return user;
88 | }
89 |
90 | public static FakeSession scaffoldSession() {
91 | return new FakeSession(faker.number().randomDigitNotZero(), faker.name().username());
92 | }
93 |
94 | public static FakeSession scaffoldEmptySession() {
95 | return new FakeSession();
96 | }
97 |
98 | public static String scaffoldUsername() {
99 | return faker.name().username();
100 | }
101 |
102 | public static PrincipalCollection scaffoldAuthKey(UserInfo user) {
103 | SimplePrincipalCollection key = new SimplePrincipalCollection();
104 | key.add(user, faker.beer().name());
105 | return key;
106 | }
107 |
108 | public static Set scaffoldKeys(Object... users) {
109 | Set keys = new HashSet();
110 | for (Object user : users) {
111 | keys.add(user);
112 | }
113 | return keys;
114 | }
115 |
116 | // /$$ /$$
117 | // | $$ |__/
118 | // /$$$$$$ /$$$$$$$ /$$$$$$ /$$ /$$$$$$ /$$$$$$$ /$$$$$$$
119 | // |____ $$ /$$_____/|_ $$_/ | $$ /$$__ $$| $$__ $$ /$$_____/
120 | // /$$$$$$$| $$ | $$ | $$| $$ \ $$| $$ \ $$| $$$$$$
121 | // /$$__ $$| $$ | $$ /$$| $$| $$ | $$| $$ | $$ \____ $$
122 | // | $$$$$$$| $$$$$$$ | $$$$/| $$| $$$$$$/| $$ | $$ /$$$$$$$/
123 | // \_______/ \_______/ \___/ |__/ \______/ |__/ |__/|_______/
124 |
125 | public static void doPutAuth(RedisCache redisCache, PrincipalCollection user) {
126 | if (user == null) {
127 | redisCache.put(null, null);
128 | return;
129 | }
130 | redisCache.put(user, turnUserToFakeAuth((UserInfo)user.getPrimaryPrincipal()));
131 | }
132 |
133 | public static void doRemoveAuth(RedisCache redisCache, PrincipalCollection user) {
134 | redisCache.remove(user);
135 | }
136 |
137 | public static void doClearAuth(RedisCache redisCache) {
138 | redisCache.clear();
139 | }
140 |
141 | public static Set doKeysAuth(RedisCache redisCache) {
142 | return redisCache.keys();
143 | }
144 |
145 | public static void doSetSessionDAOExpire(RedisSessionDAO redisSessionDAO, int expire) {
146 | redisSessionDAO.setExpire(expire);
147 | }
148 |
149 | public static void doChangeSessionName(FakeSession session, String name) {
150 | session.setName(name);
151 | }
152 |
153 | private static Jedis doGetRedisInstance() {
154 | return new Jedis(properties.getProperty("redisManager.host").split(":")[0]);
155 | }
156 |
157 | // /$$
158 | // | $$
159 | // /$$$$$$ /$$$$$$$ /$$$$$$$ /$$$$$$ /$$$$$$ /$$$$$$
160 | // |____ $$ /$$_____//$$_____/ /$$__ $$ /$$__ $$|_ $$_/
161 | // /$$$$$$$| $$$$$$| $$$$$$ | $$$$$$$$| $$ \__/ | $$
162 | // /$$__ $$ \____ $$\____ $$| $$_____/| $$ | $$ /$$
163 | // | $$$$$$$ /$$$$$$$//$$$$$$$/| $$$$$$$| $$ | $$$$/
164 | // \_______/|_______/|_______/ \_______/|__/ \___/
165 |
166 | public static void assertRedisEmpty() {
167 | Jedis jedis = doGetRedisInstance();
168 | assertThat("Redis should be empty",jedis.dbSize(), is(0L));
169 | }
170 |
171 | public static void assertKeysEquals(Set actualKeys, Set expectKeys) {
172 | assertEquals(expectKeys, actualKeys);
173 | }
174 |
175 | public static void assertAuthEquals(FakeAuth actualAuth, FakeAuth expectAuth) {
176 | assertThat(actualAuth.getId(), is(expectAuth.getId()));
177 | assertThat(actualAuth.getRole(), is(expectAuth.getRole()));
178 | }
179 |
180 | public static void assertPrincipalInstanceException(Exception e) {
181 | assertThat(e, is(notNullValue()));
182 | assertThat(e.getMessage(), containsString("must has getter for field: " + properties.getProperty("cacheManager.principalIdFieldName")));
183 | }
184 |
185 | public static void assertEquals(Object actual, Object expect) {
186 | assertThat(actual,is(expect));
187 | }
188 |
189 | public static void assertSessionEquals(Session actualSession, Session expectSession) {
190 | assertThat(actualSession.getId(), is(expectSession.getId()));
191 | assertThat(((FakeSession)actualSession).getName(), is(((FakeSession)expectSession).getName()));
192 | }
193 |
194 | // /$$ /$$$$$$
195 | // | $$ /$$__ $$
196 | // /$$$$$$ /$$$$$$ /$$$$$$ /$$$$$$$ /$$$$$$$| $$ \__//$$$$$$ /$$$$$$
197 | // |_ $$_/ /$$__ $$|____ $$| $$__ $$ /$$_____/| $$$$ /$$__ $$ /$$__ $$
198 | // | $$ | $$ \__/ /$$$$$$$| $$ \ $$| $$$$$$ | $$_/ | $$$$$$$$| $$ \__/
199 | // | $$ /$$| $$ /$$__ $$| $$ | $$ \____ $$| $$ | $$_____/| $$
200 | // | $$$$/| $$ | $$$$$$$| $$ | $$ /$$$$$$$/| $$ | $$$$$$$| $$
201 | // \___/ |__/ \_______/|__/ |__/|_______/ |__/ \_______/|__/
202 | //
203 |
204 | public static Set turnPrincipalCollectionToString(Set users, String prefix) {
205 | Set keys = new HashSet();
206 | for (PrincipalCollection user : users) {
207 | keys.add(prefix + ((UserInfo)user.getPrimaryPrincipal()).getId());
208 | }
209 | return keys;
210 | }
211 |
212 | public static FakeAuth turnUserToFakeAuth(UserInfo user) {
213 | FakeAuth auth = new FakeAuth();
214 | auth.setId(user.getId());
215 | auth.setRole(user.getRole());
216 | return auth;
217 | }
218 |
219 | // /$$ /$$
220 | // | $$ | $$
221 | // /$$$$$$ /$$$$$$ /$$$$$$ | $$ /$$$$$$$
222 | // |_ $$_/ /$$__ $$ /$$__ $$| $$ /$$_____/
223 | // | $$ | $$ \ $$| $$ \ $$| $$| $$$$$$
224 | // | $$ /$$| $$ | $$| $$ | $$| $$ \____ $$
225 | // | $$$$/| $$$$$$/| $$$$$$/| $$ /$$$$$$$/
226 | // \___/ \______/ \______/ |__/|_______/
227 |
228 | public static Properties loadProperties(String propFileName) {
229 |
230 | Properties props = new Properties();
231 | InputStream inputStream = TestFixture.class.getClassLoader()
232 | .getResourceAsStream(propFileName);
233 |
234 | if (inputStream != null) {
235 | try {
236 | props.load(inputStream);
237 | } catch (IOException e) {
238 | e.printStackTrace();
239 | }
240 | }
241 |
242 | return props;
243 | }
244 |
245 | public static Long getRedisTTL(String key, RedisSerializer keySerializer) {
246 | Jedis jedis = doGetRedisInstance();
247 | Long ttl = 0L;
248 | try {
249 | ttl = jedis.ttl(keySerializer.serialize(key));
250 | } catch (SerializationException e) {
251 | e.printStackTrace();
252 | }
253 | jedis.close();
254 | return ttl;
255 | }
256 |
257 | }
258 |
--------------------------------------------------------------------------------
/src/integration-test/java/org/crazycake/shiro/integration/fixture/model/FakeAuth.java:
--------------------------------------------------------------------------------
1 | package org.crazycake.shiro.integration.fixture.model;
2 |
3 | import java.io.Serializable;
4 |
5 | public class FakeAuth implements Serializable{
6 | private Integer id;
7 | private Integer role;
8 |
9 | public FakeAuth() {}
10 |
11 | public FakeAuth(Integer id, Integer role) {
12 | this.id = id;
13 | this.role = role;
14 | }
15 |
16 | public Integer getId() {
17 | return id;
18 | }
19 |
20 | public void setId(Integer id) {
21 | this.id = id;
22 | }
23 |
24 | public Integer getRole() {
25 | return role;
26 | }
27 |
28 | public void setRole(Integer role) {
29 | this.role = role;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/integration-test/java/org/crazycake/shiro/integration/fixture/model/FakeSession.java:
--------------------------------------------------------------------------------
1 | package org.crazycake.shiro.integration.fixture.model;
2 |
3 | import org.apache.shiro.session.InvalidSessionException;
4 | import org.apache.shiro.session.Session;
5 | import org.apache.shiro.session.mgt.SimpleSession;
6 |
7 | import java.io.Serializable;
8 | import java.util.Collection;
9 | import java.util.Date;
10 |
11 | public class FakeSession extends SimpleSession implements Serializable, Session{
12 | private Integer id;
13 | private String name;
14 | private String company;
15 |
16 | public FakeSession() {}
17 |
18 | public FakeSession(Integer id, String name) {
19 | this.id = id;
20 | this.name = name;
21 | }
22 |
23 | public Integer getId() {
24 | return id;
25 | }
26 |
27 | public void setId(Integer id) {
28 | this.id = id;
29 | }
30 |
31 | public String getName() {
32 | return name;
33 | }
34 |
35 | public void setName(String name) {
36 | this.name = name;
37 | }
38 |
39 | public String getCompany() {
40 | return company;
41 | }
42 |
43 | public void setCompany(String company) {
44 | this.company = company;
45 | }
46 |
47 | @Override
48 | public Date getStartTimestamp() {
49 | return null;
50 | }
51 |
52 | @Override
53 | public Date getLastAccessTime() {
54 | return null;
55 | }
56 |
57 | @Override
58 | public void setTimeout(long l) throws InvalidSessionException {
59 |
60 | }
61 |
62 | @Override
63 | public String getHost() {
64 | return null;
65 | }
66 |
67 | @Override
68 | public void touch() throws InvalidSessionException {
69 |
70 | }
71 |
72 | @Override
73 | public void stop() throws InvalidSessionException {
74 |
75 | }
76 |
77 | @Override
78 | public Collection