前段时间项目要用到权限控制的相关模块,经过讨论决定采用Apache下面的Shiro开源框架进行身份校验与权限控制,因项目需部署在集群环境下,所以需要分布式的支持,故配置了Redis作为权限数据的存储,这里简单的记录下相关的配置
applicationContext-shiro.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c" xmlns:util="http://www.springframework.org/schema/util" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd"> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" /> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="authenticator" ref="authenticator" /> <property name="sessionManager" ref="sessionManager" /> <!-- <property name="sessionMode" value="http"/> --> <property name="cacheManager" ref="redisCacheManager" /> <property name="realms"> <list> <ref local="customRealm" /> </list> </property> </bean> <bean class="com.bbk.security.realm.CustomRealm" id="customRealm"> <property name="credentialsMatcher" ref="hashedCredentialsMatcher" /> </bean> <bean id="redisCacheManager" class="com.bbk.security.cache.RedisCacheManager"><!-- 自定义cacheManager --> <property name="redisManager" ref="redisManager" /> </bean> <bean id="redisCache" class="com.bbk.security.cache.RedisCache"><!-- 自定义cacheManager --> <constructor-arg ref="redisManager"></constructor-arg> </bean> <bean id="roleOR" class="com.bbk.filter.CustomAuthorizationFilter" /> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <!-- 调用我们配置的权限管理器 --> <property name="securityManager" ref="securityManager" /> </bean> <bean id="authenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator" /> <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager"> <property name="sessionDAO" ref="redisShiroSessionDAO" /> <property name="globalSessionTimeout" value="${shiro.session.timeout}" /><!-- 会话过期时间,在配置文件里面配置 --> <property name="sessionValidationInterval" value="3000000" /> <property name="sessionValidationSchedulerEnabled" value="true" /> </bean> <bean id="redisShiroSessionDAO" class="com.bbk.security.cache.RedisSessionDAO"> <property name="redisManager" ref="redisManager" /> </bean> <bean id="redisManager" class="com.bbk.security.cache.RedisManager"></bean> <bean id="hashedCredentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"><!-- 密钥的算法 --> <property name="hashAlgorithmName" value="MD5" /> <property name="storedCredentialsHexEncoded" value="true" /> <property name="hashIterations" value="1" /> </bean> <!-- copy to 'spring-mvc.xml' --> <!-- <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor" /> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager" /> </bean> --> </beans>
RedisCache
package com.bbk.security.cache;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bbk.common.SerializeUtils;
public class RedisCache<K, V> implements Cache<K, V> {
private Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* The wrapped Jedis instance.
*/
private RedisManager cache;
/**
* The Redis key prefix for the sessions
*/
private String keyPrefix = "shiro_session:";
/**
* Returns the Redis session keys
* prefix.
* @return The prefix
*/
public String getKeyPrefix() {
return keyPrefix;
}
/**
* Sets the Redis sessions key
* prefix.
* @param keyPrefix The prefix
*/
public void setKeyPrefix(String keyPrefix) {
this.keyPrefix = keyPrefix;
}
/**
* 通过一个JedisManager实例构造RedisCache
*/
public RedisCache(RedisManager cache) {
if (cache == null) {
throw new IllegalArgumentException("Cache argument cannot be null.");
}
this.cache = cache;
}
/**
* Constructs a cache instance with the specified
* Redis manager and using a custom key prefix.
* @param cache The cache manager instance
* @param prefix The Redis key prefix
*/
public RedisCache(RedisManager cache, String prefix) {
this(cache);
// set the prefix
this.keyPrefix = prefix;
}
/**
* 获得byte[]型的key
* @param key
* @return
*/
private byte[] getByteKey(K key) {
if (key instanceof String) {
String preKey = this.keyPrefix + key;
return preKey.getBytes();
} else if(key instanceof PrincipalCollection){
String preKey = this.keyPrefix + key.toString();
return preKey.getBytes();
}else{
return SerializeUtils.serialize(key);
}
}
@Override
public V get(K key) throws CacheException {
logger.debug("根据key从Redis中获取对象 key [" + key + "]");
try {
if (key == null) {
return null;
} else {
byte[] rawValue = cache.get(getByteKey(key));
@SuppressWarnings("unchecked") V value = (V) SerializeUtils.deserialize(rawValue);
return value;
}
} catch (Throwable t) {
throw new CacheException(t);
}
}
public String getStr(String key) throws CacheException {
logger.debug("根据key从Redis中获取对象 key [" + key + "]");
try {
if (key == null) {
return null;
} else {
return cache.get(key);
}
} catch (Throwable t) {
throw new CacheException(t);
}
}
@Override
public V put(K key, V value) throws CacheException {
logger.debug("根据key从存储 key [" + key + "]");
try {
cache.set(getByteKey(key), SerializeUtils.serialize(value));
return value;
} catch (Throwable t) {
throw new CacheException(t);
}
}
public String putStr(String key, String value) throws CacheException {
logger.debug("根据key从存储 key [" + key + "]");
try {
cache.set(key, value);
return value;
} catch (Throwable t) {
throw new CacheException(t);
}
}
public String put(String key,String value, int expire) throws CacheException {
logger.debug("根据key从存储 key [" + key + "]");
try {
cache.set(key, value, expire);
return value;
} catch (Throwable t) {
throw new CacheException(t);
}
}
public String removeString(String key) throws CacheException {
logger.debug("从redis中删除 key [" + key + "]");
try {
String previous = cache.get(key);
cache.del(key);
return previous;
} catch (Throwable t) {
throw new CacheException(t);
}
}
@Override
public V remove(K key) throws CacheException {
logger.debug("从redis中删除 key [" + key + "]");
try {
V previous = get(key);
cache.del(getByteKey(key));
return previous;
} catch (Throwable t) {
throw new CacheException(t);
}
}
@Override
public void clear() throws CacheException {
logger.debug("从redis中删除所有元素");
try {
cache.flushDB();
} catch (Throwable t) {
throw new CacheException(t);
}
}
@Override
public int size() {
try {
Long longSize = new Long(cache.dbSize());
return longSize.intValue();
} catch (Throwable t) {
throw new CacheException(t);
}
}
@SuppressWarnings("unchecked")
@Override
public Set<K> keys() {
try {
Set<byte[]> keys = cache.keys(this.keyPrefix + "*");
if (CollectionUtils.isEmpty(keys)) {
return Collections.emptySet();
} else {
Set<K> newKeys = new HashSet<K>();
for (byte[] key : keys) {
newKeys.add((K) key);
}
return newKeys;
}
} catch (Throwable t) {
throw new CacheException(t);
}
}
@Override
public Collection<V> values() {
try {
Set<byte[]> keys = cache.keys(this.keyPrefix + "*");
if (!CollectionUtils.isEmpty(keys)) {
List<V> values = new ArrayList<V>(keys.size());
for (byte[] key : keys) {
@SuppressWarnings("unchecked") V value = get((K) key);
if (value != null) {
values.add(value);
}
}
return Collections.unmodifiableList(values);
} else {
return Collections.emptyList();
}
} catch (Throwable t) {
throw new CacheException(t);
}
}
}
Realm
package com.bbk.security.realm;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import org.apache.log4j.Logger;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.CacheManagerAware;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import com.bbk.bean.Module;
import com.bbk.bean.Role;
import com.bbk.bean.User;
import com.bbk.bean.enums.EnumStatus;
import com.bbk.common.Constants;
import com.bbk.common.ObjectUtils;
import com.bbk.common.exception.UserInputException;
import com.bbk.security.cache.RedisCache;
import com.bbk.service.IBaseService;
import com.bbk.service.IModuleService;
import com.bbk.service.IRoleService;
import com.bbk.service.IUserService;
import fmath.a.a.c;
public class CustomRealm extends AuthorizingRealm implements Realm, InitializingBean {
@Autowired
private IRoleService roleService;
@Autowired
private IModuleService moduleService;
@Autowired
private IUserService userService;
@Autowired
private RedisCache<String, Object> redisCache;
@Autowired
private IBaseService baseService;
private Logger logger = Logger.getLogger(this.getClass());
public CustomRealm() {
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//logger.info("授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
String username = (String) super.getAvailablePrincipal(principals);
User user = userService.findUserByUsername(username);
List<Role> roles = roleService.queryRolesByUserId(user);
StringBuilder sb = new StringBuilder();
for (Role role : roles) {
info.addRole(role.getCode());
sb.append(role.getId());
sb.append(",");
}
if (sb.length() > 0) {
List<Module> perms = moduleService.findPermNamesByUser(sb.substring(0, sb.length() - 1));
for (Module module : perms) {
if (ObjectUtils.isNotEmpty(module.getCode())) {
info.addStringPermission(module.getCode());
}
}
redisCache.put(Constants.getUserModuleCacheKey(user.getUserName()), perms);
}
//cacheManager.getCache("").clear();
redisCache.put(Constants.getUserRolesCacheKey(user.getUserName()), roles);
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
SimpleAuthenticationInfo info = null;
UsernamePasswordToken upt = (UsernamePasswordToken) token;
User user = userService.findUserByUsername(upt.getUsername());
if (user == null) {
throw new AuthenticationException("用户名不存在!");
}
if (user.getStatus() < EnumStatus.NORMAL.getStatus()) {
throw new UserInputException("您的帐号号已被限制登录,如需帮助请联系管理员!");
}
redisCache.put(Constants.getUserSessionKey(user.getUserName()), user);
info = new SimpleAuthenticationInfo(user.getUserName(), user.getUserPsw(), getName());
return info;
}
/**
* 更新用户授权信息缓存.
*/
public void clearCachedAuthorizationInfo(String principal) {
SimplePrincipalCollection principals = new SimplePrincipalCollection(principal, getName());
super.clearCachedAuthorizationInfo(principals);
super.clearCache(principals);
super.clearCachedAuthenticationInfo(principals);
redisCache.remove(Constants.getUserModuleCacheKey(principal));
redisCache.remove(Constants.getUserRolesCacheKey(principal));
}
/**
* 清除所有用户授权信息缓存.
*/
public void clearAllCachedAuthorizationInfo() {
redisCache.clear();
/*Cache<Object, AuthenticationInfo> cache = getAuthenticationCache();
if (null != cache) {
for (Object key : cache.keys()) {
cache.remove(key);
}
}*/
}
@Override
public void afterPropertiesSet() throws Exception {
}
}
RedisCacheManager
package com.bbk.security.cache;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.cache.CacheManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RedisCacheManager implements CacheManager {
private static final Logger logger = LoggerFactory.getLogger(RedisCacheManager.class);
// fast lookup by name map
private final ConcurrentMap<String, Cache> caches = new ConcurrentHashMap<String, Cache>();
private RedisManager redisManager;
/**
* The Redis key prefix for caches
*/
private String keyPrefix = "shiro_redis_cache:";
/**
* Returns the Redis session keys
* prefix.
* @return The prefix
*/
public String getKeyPrefix() {
return keyPrefix;
}
/**
* Sets the Redis sessions key
* prefix.
* @param keyPrefix The prefix
*/
public void setKeyPrefix(String keyPrefix) {
this.keyPrefix = keyPrefix;
}
@Override
public <K, V> Cache<K, V> getCache(String name) throws CacheException {
logger.debug("获取名称为: " + name + " 的RedisCache实例");
Cache c = caches.get(name);
if (c == null) {
// initialize the Redis manager instance
redisManager.init();
// create a new cache instance
c = new RedisCache<K, V>(redisManager, keyPrefix);
// add it to the cache collection
caches.put(name, c);
}
return c;
}
public RedisManager getRedisManager() {
return redisManager;
}
public void setRedisManager(RedisManager redisManager) {
this.redisManager = redisManager;
}
}
RedisSessionDAO
package com.bbk.security.cache;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bbk.common.SerializeUtils;
/**
* @author Jon Chiang
* @project video
* @create_date 2014-5-6 下午5:35:07
*/
public class RedisSessionDAO extends AbstractSessionDAO {
private static Logger logger = LoggerFactory.getLogger(RedisSessionDAO.class);
/**
* shiro-redis的session对象前缀
*/
private RedisManager redisManager;
/**
* The Redis key prefix for the sessions
*/
private String keyPrefix = "shiro_session:";
@Override
public void update(Session session) throws UnknownSessionException {
this.saveSession(session);
}
/**
* save session
* @param session
* @throws UnknownSessionException
*/
private void saveSession(Session session) throws UnknownSessionException{
if(session == null || session.getId() == null){
logger.error("session or session id is null");
return;
}
byte[] key = getByteKey(session.getId());
byte[] value = SerializeUtils.serialize(session);
session.setTimeout(redisManager.getExpire()*1000);
this.redisManager.set(key, value, redisManager.getExpire());
}
@Override
public void delete(Session session) {
if(session == null || session.getId() == null){
logger.error("session or session id is null");
return;
}
redisManager.del(this.getByteKey(session.getId()));
}
@Override
public Collection<Session> getActiveSessions() {
Set<Session> sessions = new HashSet<Session>();
Set<byte[]> keys = redisManager.keys(this.keyPrefix + "*");
if(keys != null && keys.size()>0){
for(byte[] key:keys){
Session s = (Session)SerializeUtils.deserialize(redisManager.get(key));
sessions.add(s);
}
}
return sessions;
}
@Override
protected Serializable doCreate(Session session) {
Serializable sessionId = this.generateSessionId(session);
this.assignSessionId(session, sessionId);
this.saveSession(session);
return sessionId;
}
@Override
protected Session doReadSession(Serializable sessionId) {
if(sessionId == null){
logger.error("session id is null");
return null;
}
Session s = (Session)SerializeUtils.deserialize(redisManager.get(this.getByteKey(sessionId)));
return s;
}
/**
* 获得byte[]型的key
* @param key
* @return
*/
private byte[] getByteKey(Serializable sessionId){
String preKey = this.keyPrefix + sessionId;
return preKey.getBytes();
}
public RedisManager getRedisManager() {
return redisManager;
}
public void setRedisManager(RedisManager redisManager) {
this.redisManager = redisManager;
/**
* 初始化redisManager
*/
this.redisManager.init();
}
/**
* Returns the Redis session keys
* prefix.
* @return The prefix
*/
public String getKeyPrefix() {
return keyPrefix;
}
/**
* Sets the Redis sessions key
* prefix.
* @param keyPrefix The prefix
*/
public void setKeyPrefix(String keyPrefix) {
this.keyPrefix = keyPrefix;
}
}
RedisManager
package com.bbk.security.cache;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import com.bbk.common.JedisUtil;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public class RedisManager {
private static Logger logger = LoggerFactory.getLogger(RedisSessionDAO.class);
@Value("${redis.ip}")
private String host;
@Value("${redis.port}")
private int port;
// 0 - never expire
private int expire = 0;
private static JedisPool jedisPool = null;
public RedisManager() {
}
/**
* 初始化方法
*/
public void init(){
if(null == host || 0 == port){
logger.error("请初始化redis配置文件!");
throw new NullPointerException("找不到redis配置");
}
if(jedisPool == null){
//jedisPool = JedisUtil.getJedisPool();
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 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;
}
}