Skip to content

Commit

Permalink
- Add principalIdFieldName. Using reflection to get id of redis objec…
Browse files Browse the repository at this point in the history
…t instead of using AuthCachePrincipal
  • Loading branch information
alexxiyang committed Jun 11, 2018
1 parent d21cf4f commit 354c6bf
Show file tree
Hide file tree
Showing 15 changed files with 286 additions and 51 deletions.
25 changes: 18 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ You can choose these 2 ways to include shiro-redis into your project
> Do not use version < 3.0.0\
> **注意**\
> 请不要使用3.0.0以下版本
# Before use
Please make sure your principal class implements `org.crazycake.shiro.AuthCachePrincipal`. Because shiro-redis needs a unique key to store authorization object. You will need to implement `getAuthCacheKey()` method to provide the unique key. Best practice is using userId or userName as authCachedKey.
# Before use
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.principalId = id`

For example:

Expand All @@ -41,9 +41,11 @@ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
}
```

You need to make sure `UserInfo` implements `org.crazycake.shiro.AuthCachePrincipal`. Like this:
You need to make sure `UserInfo` has an unique field to identify it in Redis. Take userId as an example:
```java
public class UserInfo implements Serializable, AuthCachePrincipal{
public class UserInfo implements Serializable{

private Integer userId

private String username;

Expand All @@ -65,13 +67,16 @@ public class UserInfo implements Serializable, AuthCachePrincipal{
this.age = age;
}

@Override
public String getAuthCacheKey() {
return getUsername();
public Integer getUserId() {
return this.userId;
}
}
```

And put userId as `cacheManager.principalId`, like this:
```properties
cacheManager.principalId = userId
```

# How to configure ?

Expand Down Expand Up @@ -164,6 +169,11 @@ securityManager.sessionManager = $sessionManager
# Create cacheManager
cacheManager = org.crazycake.shiro.RedisCacheManager

# 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 userId, userName, email, etc. Remember to add getter to this id field. For example, getUserId(), getUserName(), getEmail(), etc.
# Default value is authCacheKey or id, that means your principal object has a method called "getAuthCacheKey()" or "getId()"
#
# cacheManager.principalIdFieldName = id

# Redis cache key/value expire time. Default value: 1800 .The expire time is in second. (Optional)
#
# cacheManager.expire = <expire>
Expand Down Expand Up @@ -300,6 +310,7 @@ spring.xml:
<property name="redisManager" ref="redisManager" />
<property name="expire" value="1800"/>
<property name="keyPrefix" value="shiro:cache:" />
<property name="principalIdFieldName" value="id" />
</bean>

<!-- securityManager -->
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/org/crazycake/shiro/AuthCachePrincipal.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.crazycake.shiro;

@Deprecated
public interface AuthCachePrincipal {

/**
Expand All @@ -11,7 +12,11 @@ public interface AuthCachePrincipal {
* authorization redis key = shiro:mycache:exampleRealm.authorizationCache:123
*
* In most cases, authCacheKey should be userId or userName
*
* @Deprecated use
*
* @return
*/
@Deprecated
String getAuthCacheKey();
}
71 changes: 53 additions & 18 deletions src/main/java/org/crazycake/shiro/RedisCache.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.CollectionUtils;
import org.crazycake.shiro.exception.PrincipalIdNullException;
import org.crazycake.shiro.exception.PrincipalInstanceException;
import org.crazycake.shiro.exception.SerializationException;
import org.crazycake.shiro.serializer.ObjectSerializer;
Expand All @@ -12,6 +13,8 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

public class RedisCache<K, V> implements Cache<K, V> {
Expand All @@ -23,12 +26,13 @@ public class RedisCache<K, V> implements Cache<K, V> {
private IRedisManager redisManager;
private String keyPrefix = "";
private int expire = 0;
private String principalIdFieldName = RedisCacheManager.DEFAULT_PRINCIPAL_ID_FIELD_NAME;

/**
* Construction
* @param redisManager
*/
public RedisCache(IRedisManager redisManager, RedisSerializer keySerializer, RedisSerializer valueSerializer, String prefix, int expire) {
public RedisCache(IRedisManager redisManager, RedisSerializer keySerializer, RedisSerializer valueSerializer, String prefix, int expire, String principalIdFieldName) {
if (redisManager == null) {
throw new IllegalArgumentException("redisManager cannot be null.");
}
Expand All @@ -47,6 +51,9 @@ public RedisCache(IRedisManager redisManager, RedisSerializer keySerializer, Red
if (expire != -1) {
this.expire = expire;
}
if (principalIdFieldName != null && !"".equals(principalIdFieldName)) {
this.principalIdFieldName = principalIdFieldName;
}
}

@Override
Expand Down Expand Up @@ -107,34 +114,54 @@ private Object getRedisCacheKey(K key) {
if (key == null) {
return null;
}
Object redisKey = key;
if (keySerializer instanceof StringSerializer) {
redisKey = getStringRedisKey(key);
return this.keyPrefix + getStringRedisKey(key);
}
if (redisKey instanceof String) {
return this.keyPrefix + (String) redisKey;
}
return redisKey;
return key;
}

private Object getStringRedisKey(K key) {
Object redisKey;
private String getStringRedisKey(K key) {
String redisKey;
if (key instanceof PrincipalCollection) {
redisKey = getRedisKeyFromPrincipalCollection((PrincipalCollection) key);
redisKey = getRedisKeyFromPrincipalIdField((PrincipalCollection) key);
} else {
redisKey = key.toString();
}
return redisKey;
}

private Object getRedisKeyFromPrincipalCollection(PrincipalCollection key) {
Object redisKey;
PrincipalCollection principalCollection = key;
if (!(principalCollection.getPrimaryPrincipal() instanceof AuthCachePrincipal)) {
throw new PrincipalInstanceException();
}
AuthCachePrincipal principal = (AuthCachePrincipal) principalCollection.getPrimaryPrincipal();
redisKey = principal.getAuthCacheKey();
private String getRedisKeyFromPrincipalIdField(PrincipalCollection key) {
String redisKey;
Object principalObject = key.getPrimaryPrincipal();
Method pincipalIdGetter = null;
Method[] methods = principalObject.getClass().getDeclaredMethods();
for (Method m:methods) {
if (RedisCacheManager.DEFAULT_PRINCIPAL_ID_FIELD_NAME.equals(this.principalIdFieldName)
&& ("getAuthCacheKey".equals(m.getName()) || "getId".equals(m.getName()))) {
pincipalIdGetter = m;
break;
}
if (m.getName().equals("get" + this.principalIdFieldName.substring(0, 1).toUpperCase() + this.principalIdFieldName.substring(1))) {
pincipalIdGetter = m;
break;
}
}
if (pincipalIdGetter == null) {
throw new PrincipalInstanceException(principalObject.getClass(), this.principalIdFieldName);
}

try {
Object idObj = pincipalIdGetter.invoke(principalObject);
if (idObj == null) {
throw new PrincipalIdNullException(principalObject.getClass(), this.principalIdFieldName);
}
redisKey = idObj.toString();
} catch (IllegalAccessException e) {
throw new PrincipalInstanceException(principalObject.getClass(), this.principalIdFieldName, e);
} catch (InvocationTargetException e) {
throw new PrincipalInstanceException(principalObject.getClass(), this.principalIdFieldName, e);
}

return redisKey;
}

Expand Down Expand Up @@ -224,4 +251,12 @@ public String getKeyPrefix() {
public void setKeyPrefix(String keyPrefix) {
this.keyPrefix = keyPrefix;
}

public String getPrincipalIdFieldName() {
return principalIdFieldName;
}

public void setPrincipalIdFieldName(String principalIdFieldName) {
this.principalIdFieldName = principalIdFieldName;
}
}
13 changes: 12 additions & 1 deletion src/main/java/org/crazycake/shiro/RedisCacheManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,17 @@ public class RedisCacheManager implements CacheManager {
public static final String DEFAULT_CACHE_KEY_PREFIX = "shiro:cache:";
private String keyPrefix = DEFAULT_CACHE_KEY_PREFIX;

public static final String DEFAULT_PRINCIPAL_ID_FIELD_NAME = "authCacheKey or id";
private String principalIdFieldName = DEFAULT_PRINCIPAL_ID_FIELD_NAME;

@Override
public <K, V> Cache<K, V> getCache(String name) throws CacheException {
logger.debug("get cache, name=" + name);

Cache cache = caches.get(name);

if (cache == null) {
cache = new RedisCache<K, V>(redisManager, keySerializer, valueSerializer, keyPrefix + name + ":", expire);
cache = new RedisCache<K, V>(redisManager, keySerializer, valueSerializer, keyPrefix + name + ":", expire, principalIdFieldName);
caches.put(name, cache);
}
return cache;
Expand Down Expand Up @@ -85,4 +88,12 @@ public int getExpire() {
public void setExpire(int expire) {
this.expire = expire;
}

public String getPrincipalIdFieldName() {
return principalIdFieldName;
}

public void setPrincipalIdFieldName(String principalIdFieldName) {
this.principalIdFieldName = principalIdFieldName;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.crazycake.shiro.exception;

public class PrincipalIdNullException extends RuntimeException {

private static final String MESSAGE = "Principal Id shouldn't be null!";

public PrincipalIdNullException(Class clazz, String idMethodName) {
super(clazz + " id field: " + idMethodName + ", value is null\n" + MESSAGE);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@

public class PrincipalInstanceException extends RuntimeException {

private static final String MESSAGE = "Principal must implement org.crazycake.shiro.AuthCachePrincipal.\n"
+ "shiro-redis will get the key for store authorization object in Redis from org.crazycake.shiro.AuthCachePrincipal\n"
+ "So please use AuthCachePrincipal to tell shiro-redis how to get the cache key\n"
+ " For example: There is a class UserInfo which implements org.crazycake.shiro.AuthCachePrincipal."
+ " You can use this class to initial SimpleAuthenticationInfo like this:\n"
+ " UserInfo userInfo = new userInfo();\n"
+ " new SimpleAuthenticationInfo(userInfo, \"123456\", \"realm1\")";
private static final String MESSAGE = "We need a field to identify this Cache Object in Redis. "
+ "So you need to defined an id field which you can get unique id to identify this principal. "
+ "For example, if you use UserInfo as Principal class, the id field maybe userId, userName, email, etc. "
+ "For example, getUserId(), getUserName(), getEmail(), etc.\n"
+ "Default value is authCacheKey or id, that means your principal object has a method called \"getAuthCacheKey()\" or \"getId()\"";

public PrincipalInstanceException() {
super(MESSAGE);
public PrincipalInstanceException(Class clazz, String idMethodName) {
super(clazz + " must has getter for field: " + idMethodName + "\n" + MESSAGE);
}

public PrincipalInstanceException(Class clazz, String idMethodName, Exception e) {
super(clazz + " must has getter for field: " + idMethodName + "\n" + MESSAGE, e);
}
}
Loading

0 comments on commit 354c6bf

Please sign in to comment.