tomcat+nginx+shiro+jfinal 实现负载均衡,session共享

1.创建OnlineSessionDao继承EnterpriseCacheSessionDAO,实现session持久化共享 

package com.jsaas.core.security;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Collection;

import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.SimpleSession;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;

import com.jfinal.log.Log;
import com.jfinal.plugin.ehcache.CacheKit;
import com.jfinal.plugin.redis.Redis;

/**
 * @ClassName: onlineSessionDao
 * @Description: session管理,获取在线用户等(session持久化,配合nginx负载均衡,实现共享session)
 * @author tuozq
 * @date 2017年11月3日 上午9:48:01
 *
 */
public class OnlineSessionDao extends EnterpriseCacheSessionDAO {

	private static final Log log = Log.getLog(OnlineSessionDao.class);
	
	//定义sessionDao缓存的前缀,可以通过 Redis.use().getJedis().keys(OnlineSessionDao.cacheNamePrefix + "*") 获取到sessionDao缓存的所有session
	public static final String cacheNamePrefix = "shiro_sessionDao_cache:";
	
	private void set(String key, Object value){
		Redis.use().set(cacheNamePrefix + key, value);
	}
	
	private Object get(String key){
		return Redis.use().get(cacheNamePrefix + key);
	}
	
	private void remove(String key){
		Redis.use().del(cacheNamePrefix + key); 
	}
	
	/**
	 * 创建session
	 */
	@Override
	public Serializable doCreate(Session session) {
		Serializable sessionId = super.doCreate(session);
		log.info("创建  Session:"+session.getHost() + ";" + session.getId());
		set(session.getId().toString(), sessionToByte(session));
		return sessionId;
	}
	
	
	/**
	 * 删除session
	 */
	@Override
	public void doDelete(Session session) {
		log.info("删除 Session:"+session.getHost() + ";" + session.getId());
		remove(session.getId().toString());
		super.doDelete(session);
	}

	/**
	 * 更新session的最后一次访问时间
	 */
	@Override
	public void doUpdate(Session session) throws UnknownSessionException {
		log.info("更新 Session:"+session.getHost() + ";" + session.getId());
		set(session.getId().toString(), sessionToByte(session));
		super.doUpdate(session);

	}

	/**
	 * 获取session
	 */
	@Override
	protected Session doReadSession(Serializable sessionId) {
		Session session = super.doReadSession(sessionId);
		if(session == null){
			byte[] bytes = (byte[]) get(sessionId.toString());
			if(bytes != null && bytes.length > 0){
				session = byteToSession(bytes);
			}
		}
		return session;
	}
	

	// 把session对象转化为byte保存到缓存中
	public byte[] sessionToByte(Session session){
		ByteArrayOutputStream bo = new ByteArrayOutputStream();
		byte[] bytes = null;
		try {
			ObjectOutputStream oo = new ObjectOutputStream(bo);
			oo.writeObject(session);
			bytes = bo.toByteArray();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return bytes;
	}

	// 把byte还原为session
	public Session byteToSession(byte[] bytes){
		ByteArrayInputStream bi = new ByteArrayInputStream(bytes);
		ObjectInputStream in;
		SimpleSession session = null;
		try {
			in = new ObjectInputStream(bi);
			session = (SimpleSession) in.readObject();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

		return session;
	}
}

2.使用redis接管shiro cacheManage

创建RedisCacheManage类实现CacheManager接口,在shiro.ini中指定cacheManager为RedisCacheManage

package com.jsaas.core.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 com.jfinal.log.Log;
import com.jfinal.plugin.redis.Redis;

/**   
* @Title: RedisCacheManage.java 
* @Package com.jsaas.core.security 
* @Description: TODO(接管shiro缓存管理) 
* @author tuozq 
* @date 2017年12月7日 上午9:26:07 
* @version V1.0   
*/
@SuppressWarnings({"rawtypes","unchecked"})
public class RedisCacheManage implements CacheManager {
	
	private static final Log log = Log.getLog(RedisCacheManage.class);
	
	
	private final ConcurrentMap<String, Cache> caches = new ConcurrentHashMap<String, Cache>();
	
	public <K, V> Cache<K, V> getCache(String name) throws CacheException {
		// TODO Auto-generated method stub
		log.info(String.format("获取redis %s 实例", name));
		if(caches.containsKey(name)){
			return caches.get(name);
		}
		RedisCache<K, V> redisCache = new RedisCache<K, V>(new RedisManage(name));
		caches.put(name, redisCache);
		return redisCache;
	}

}

 实现shiro的cache

package com.jsaas.core.security.cache;

import java.util.ArrayList;
import java.util.Collection;
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 com.jfinal.log.Log;

import redis.clients.jedis.Jedis;

/**   
* @Title: RedisCache.java 
* @Package com.jsaas.core.security.cache 
* @Description: TODO(shiro缓存cache) 
* @author tuozq 
* @date 2017年12月7日 上午9:50:19 
* @version V1.0   
*/
public class RedisCache<K, V> implements Cache<K, V> {
	
	Log log = Log.getLog(RedisCache.class);
	
	private RedisManage redisManage;
	
	public RedisCache(RedisManage redisManage){
		this.redisManage = redisManage;
	}
	
	private com.jfinal.plugin.redis.Cache getCache(){
		log.info("user cache :" + redisManage.getPrefix());
		return redisManage.getCache();
	}
	

	public void clear() throws CacheException {
		// TODO Auto-generated method stub
		getCache().getJedis().flushDB();
	}

	public V get(K key) throws CacheException {
		// TODO Auto-generated method stub
		return getCache().get(redisManage.getPrefix() + key);
	}

	@SuppressWarnings("unchecked")
	public Set<K> keys() {
		// TODO Auto-generated method stub
		Jedis jedis = getCache().getJedis();
		Set<String> keys = jedis.keys(redisManage.getPrefix() + "*");
		Set<K> ks = new HashSet<K>();
		for (String key : keys) {
			ks.add((K)key);
		}
		return ks;
	}

	public V put(K key, V value) throws CacheException {
		// TODO Auto-generated method stub
		getCache().set(redisManage.getPrefix() + key, value);
		return value;
	}

	public V remove(K key) throws CacheException {
		// TODO Auto-generated method stub
		V value = getCache().get(redisManage.getPrefix() + key);
		getCache().del(redisManage.getPrefix() + key);
		return value;
	}

	public int size() {
		// TODO Auto-generated method stub
		return keys().size();
	}

	public Collection<V> values() {
		// TODO Auto-generated method stub
		Set<K> ks = keys();
		List<V> vs = new ArrayList<V>();
		for (K k : ks) {
			vs.add(get(k));
		}
		return vs;
	}

}

shiro redisManage

package com.jsaas.core.security.cache;

import com.jfinal.plugin.redis.Redis;
import com.jsaas.common.Constant;

/**   
* @Title: RedisManage.java 
* @Package com.jsaas.core.security.cache 
* @Description: TODO(shiro redis 缓存管理) 
* @author tuozq 
* @date 2017年12月7日 上午11:28:31 
* @version V1.0   
*/
public class RedisManage {
	private com.jfinal.plugin.redis.Cache cache;
	
	//用于区分shiro不同的cache name
	private String prefix;
	
	public RedisManage(String cachename) {
		// TODO Auto-generated constructor stub
		this.prefix = cachename + ":";
	}
	
	public com.jfinal.plugin.redis.Cache getCache() {
		if(cache == null){
			//在jfinalConfig中添加redis插件  me.add(new RedisPlugin(Constant.REDIS_SHIROMANAGE_CACHE, "127.0.0.1", 6379));
			cache = Redis.use(Constant.REDIS_SHIROMANAGE_CACHE);
		}
		return cache;
	}
	
	public String getPrefix(){
		return this.prefix;
	}
}

3.nginx配置

#配置集群
	upstream jsaas_server {                                                         
		server localhost:81 weight=5;
		server localhost:82 weight=5;		
		
	}

    #gzip  on;

    server {
        listen       80;
        server_name  localhost;
		
		#配置代理服务器
		location / {
			proxy_pass http://jsaas_server;# http://localhost:80/ 请求转发到jsaas_server集群
			proxy_set_header Host  $http_host;
            proxy_set_header Cookie $http_cookie;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            client_max_body_size  100m;
            proxy_connect_timeout 1;#连接超时时间,不能设置过长,否则服务器宕机时无法快速切换服务器
			#proxy_set_header Host $host:$server_port;
		}

4.shiro.ini

#指定sessiondao
sessionDAO = com.jsaas.core.security.OnlineSessionDao
sessionDAO.activeSessionsCacheName = shiro-activeSessionCache
sessionManager.sessionDAO = $sessionDAO

#指定cacheManager
cacheManager = com.jsaas.core.security.cache.RedisCacheManage
securityManager.cacheManager = $cacheManager



###############################全部shiro配置########################################
[main]
#realm
#自定义Realm
myRealm = com.jsaas.core.security.ShiroDbRealm
securityManager.realm = $myRealm

#配置shiro的密码验证方式为盐加密   也可以通过ShiroDbRealm 中 setCredentialsMatcher方法指定自定义的密码验证方式
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
credentialsMatcher.hashAlgorithmName=SHA-1
credentialsMatcher.hashIterations=1024
credentialsMatcher.storedCredentialsHexEncoded=true
myRealm.credentialsMatcher=$credentialsMatcher

#没有登录的用户请求需要登录的页面时自动跳转到登录页面,不是必须的属性,不输入地址的话会自动寻找项目web项目的根目录下的”/login.jsp”
shiro.loginUrl = /login
#登录成功默认跳转页面,不配置则跳转至”/”。如果登陆前点击的一个需要登录的页面,则在登录自动跳转到那个需要登录的页面。不跳转到此。
shiro.successUrl = /sys/user/successUrl
#没有权限默认跳转的页面。
shiro.unauthorizedUrl = /403

#sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager
sessionManager = com.jsaas.core.security.MyWebSessionManager

#ehcache
#shiroCacheManager = org.apache.shiro.cache.ehcache.EhCacheManager
#shiroCacheManager.cacheManagerConfigFile = classpath:ehcache.xml
#securityManager.cacheManager = $shiroCacheManager

#redis cache
cacheManager = com.jsaas.core.security.cache.RedisCacheManage
securityManager.cacheManager = $cacheManager

#session
#sessionDAO = org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO

sessionDAO = com.jsaas.core.security.OnlineSessionDao
sessionDAO.activeSessionsCacheName = shiro-activeSessionCache
sessionManager.sessionDAO = $sessionDAO

securityManager.sessionManager = $sessionManager
securityManager.sessionManager.globalSessionTimeout = 360000


[urls]

/res/** = anon
/login/** = anon
/user/** = anon
/** = authc

猜你喜欢

转载自my.oschina.net/u/2276456/blog/1585202