shiro redis 集群 session无法登陆,单机却可以

有庙无僧风扫地 2016-09-14 10:39:41
登陆时2个tomcat打印的sessionid是一样的,可是redis保存没有sessionid。跟踪了一下代码刚开始进入doReadSession方法查询redis无数据后就报错了 org.apache.shiro.session.UnknownSessionException: There is no session with id【xxxxx】然后跳转到doCreate方法生成新的sessionid。单机是先进sava sessionId方法 在进doReadSession可以正常登录,集群是先进doReadSession方法导致获取sessionId和保存的id不一样。
我的配置
applicationContext-shiro.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd" default-lazy-init="false">
<!-- 用户服务接口 -->

<!-- web.xml中shiro的filter对应的bean -->
<!-- Shiro 的Web过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<!-- loginUrl认证提交地址,如果没有认证将会请求此地址进行认证,请求此地址将由formAuthenticationFilter进行表单认证 -->
<property name="loginUrl" value="/login.do" />
<!-- 认证成功统一跳转到first.action,建议不配置,shiro认证成功自动到上一个请求路径 -->
<property name="successUrl" value="/successLogin.do"/>
<!-- 通过unauthorizedUrl指定没有权限操作时跳转页面-->
<property name="unauthorizedUrl" value="/refuse.do" />

<!-- 过虑器链定义,从上向下顺序执行,一般将/**放在最下边 -->
<property name="filterChainDefinitions">
<value>
/**/images/** = anon
/**/script/** = anon
/**/style/** = anon
/logins.do = anon
<!-- 请求 logout.action地址,shiro去清除session
/logout.do = logout -->
<!-- /** = authc 所有url都必须认证通过才可以访问 query delete update add-->
/** = authc
<!-- /** = anon所有url都可以匿名访问 -->
</value>
</property>
</bean>
<!-- securityManager安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="customRealm" />
<!-- 注入session管理器 -->
<property name="sessionManager" ref="sessionManager" />
<!-- cacheManager -->
<property name="cacheManager" ref="cacheManager" />
</bean>
<!-- realm -->
<bean id="customRealm" class="com.zefu.portal.login.controller.CustomRealm">
<!-- 将凭证匹配器设置到realm中,realm按照凭证匹配器的要求进行散列 -->
<property name="userService" ref="userService"/>
<property name="credentialsMatcher" ref="credentialsMatcher"/>
</bean>
<!-- 凭证匹配器 -->
<bean id="credentialsMatcher"
class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<property name="hashAlgorithmName" value="md5" />
<property name="hashIterations" value="1" />
</bean>
<!-- 缓存管理器 -->
<bean id="eacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManagerConfigFile" value="classpath:shiro-ehcache.xml"/>
</bean>
<!-- Shiro生命周期处理器-->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<!-- shiro redisManager -->
<bean id="redisManager" class="com.zefu.portal.login.controller.RedisManager">
<property name="host" value="192.168.0.31"/>
<property name="port" value="6379"/>
<property name="expire" value="1800"/>
</bean>
<!-- 会话DAO -->
<bean id="redisSessionDAO" class="com.zefu.portal.login.controller.RedisSessionDAO">
<property name="redisManager" ref="redisManager" />
</bean>
<!-- 会话管理器 -->
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<property name="sessionDAO" ref="redisSessionDAO" />
<property name="globalSessionTimeout" value="1800000" />
<property name="deleteInvalidSessions" value="true" />
<property name="sessionValidationSchedulerEnabled" value="false" />
</bean>
<!-- 缓存管理器 -->
<bean id="cacheManager" class="com.zefu.portal.login.controller.RedisCacheManager">
<property name="redisManager" ref="redisManager" />
</bean>
</beans>


package com.zefu.portal.login.controller;

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.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class RedisSessionDAO extends AbstractSessionDAO {//EnterpriseCacheSessionDAO 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_redis_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);
logger.debug("======saveSession页面传入sessionId [" + session.getId() + "]=======");
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()));
System.out.println("redisManager======delete:"+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;
}
logger.debug("======doReadSessionue页面传入sessionId [" + sessionId + "]=======");
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;
}


}
...全文
309 2 打赏 收藏 转发到动态 举报
写回复
用AI写文章
2 条回复
切换为时间正序
请发表友善的回复…
发表回复
goupengabc 2016-11-23
  • 打赏
  • 举报
回复
楼主,你有shiro实现集群共享session和cache的源码吗?
  • 打赏
  • 举报
回复

package com.zefu.portal.login.controller;
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.util.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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_redis_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{
    		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);
		}

	}

	@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);
	        }
	}

	@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);
        }
	}

}

package com.zefu.portal.login.controller;
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;
	}

}

package com.zefu.portal.login.controller;

import java.util.ArrayList;
import java.util.List;



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.UnknownAccountException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

import com.zefu.portal.bean.system.Permission;
import com.zefu.portal.bean.system.User;
import com.zefu.portal.services.menu.MenuService;
import com.zefu.portal.services.permission.UserService;

public class CustomRealm extends AuthorizingRealm {
	

	private UserService userService;
 
	private MenuService menuService;

	// 设置realm的名称
	@Override
	public void setName(String name) {
		super.setName("customRealm");
	}

	//realm的认证方法,从数据库查询用户信息
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken token) throws AuthenticationException {	
		// token是用户输入的用户名和密码 
		// 第一步从token中取出用户名
		String name = (String) token.getPrincipal();
		// 第二步:根据用户输入的userCode从数据库查询
		User sysUser = null;
    	try {
			sysUser = userService.findUserInfo(name);
		} catch (Exception e1) {
			e1.printStackTrace();
		}
		// 如果查询不到返回null
		if(sysUser==null){
			throw new UnknownAccountException();
	
		}
		// 从数据库查询到密码
		String password = sysUser.getPassword();
		//盐
		String salt = sysUser.getUsername();

		//将activeUser设置simpleAuthenticationInfo
		SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
				sysUser, password, ByteSource.Util.bytes(salt),this.getName());
		return simpleAuthenticationInfo;
	}
	
	

	// 用于授权
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(
			PrincipalCollection principals) {
		//从 principals获取主身份信息
		//将getPrimaryPrincipal方法返回值转为真实身份类型(在上边的doGetAuthenticationInfo认证通过填充到SimpleAuthenticationInfo中身份类型),
		User user =  (User) principals.getPrimaryPrincipal();
		//根据身份信息获取权限信息
		//从数据库获取到权限数据
		List<Permission> permissionList = null;
		try {
			permissionList = userService.getPermissions(user.getRole().getRoleId());
		} catch (Exception e) {
			e.printStackTrace();
		}
		//单独定一个集合对象 
		List<String> permissions = new ArrayList<String>();
		if(permissionList!=null){
			for(Permission sysPermission:permissionList){
				if(sysPermission!=null){
				  //将数据库中的权限标签 符放入集合
				  permissions.add(sysPermission.getElementId());
				}
			}
		}
		
		//查到权限数据,返回授权信息(要包括 上边的permissions)
		SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
		//将上边查询到授权信息填充到simpleAuthorizationInfo对象中
		simpleAuthorizationInfo.addStringPermissions(permissions);

		return simpleAuthorizationInfo;
	}
	
	//清除缓存
	public void clearCached() {
		PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals();
		super.clearCache(principals);
	}

	public UserService getUserService() {
		return userService;
	}

	public void setUserService(UserService userService) {
		this.userService = userService;
	}

	public MenuService getMenuService() {
		return menuService;
	}

	public void setMenuService(MenuService menuService) {
		this.menuService = menuService;
	}



}

67,513

社区成员

发帖
与我相关
我的任务
社区描述
J2EE只是Java企业应用。我们需要一个跨J2SE/WEB/EJB的微容器,保护我们的业务核心组件(中间件),以延续它的生命力,而不是依赖J2SE/J2EE版本。
社区管理员
  • Java EE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧