利用redis同步登陆实现session共享

阅读更多

     由于一直在弄oa系统,涉及与公司的其它系统整合;

    今天要弄的是用redis做同步登陆,即在oa里登陆成功后即可在其它系统实现一键登陆。

   oa是用shiro登陆的,shiro里也提供了一个redis的同步session机制,不过在测试时发现,不能用,同一个请求都会产生不同的sessionid,应该是shiro底层问题,在读取sessionid时由于某些原因总是为空,于是就时不时产生一个新的sessionid,这样就没办法实现同步了,同步需要只使用一个sessionid.

   既然不用shiro的,那么就要自己来实现,就得做个filter,拦截在系统的最前面,即在shiro的filter的前面,

这里是SSOFilter.

   本来想存session到redis的,后来想到公司还有其它语言的系统,有.net的,这可能会对他们造成读取困难,那就直接以sessionid为key,userName为value存到redis里吧。

 下面上源码吧!

  RedisManager

 

package sy.sso;

import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import sy.action.InitAction;

public class RedisManager {
	private final Logger logger = LoggerFactory.getLogger(this.getClass());
	/*private String host = "172.16.6.3";*/
	private String host = "127.0.0.1";

	private int port = 6379;
	private String dbindex = "0";

	private String password = "123456";
	// 0 - never expire
	private int expire = 30;
	private int timeout = 2000;

	private JedisPool jedisPool = null;

	public RedisManager() {
          init();
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getDbindex() {
		return dbindex;
	}

	public void setDbindex(String dbindex) {
		this.dbindex = dbindex;
	}

	/**
	 * 初始化方法
	 */
	public void init() {
		if (jedisPool == null) {
			//jedisPool = new JedisPool(new JedisPoolConfig(), host, port, timeout, password, Integer.parseInt(dbindex));
			jedisPool = new JedisPool(new JedisPoolConfig(), host, port, timeout, null, Integer.parseInt(dbindex));

		}
	}

	/**
	 * get value from redis
	 *
	 * @param key
	 * @return
	 */
	public byte[] get(byte[] key) {
		logger.debug("getkey:" + new String(key));
		byte[] value = null;
		Jedis jedis = jedisPool.getResource();
		try {
			 jedis.auth(password);
			value = jedis.get(key);

		} finally {
			jedisPool.returnResource(jedis);
		}
		return value;
	}

	/**
	 * 返回指定hash的field数量
	 *
	 * @param key
	 * @return
	 */
	public Long hlen(String key) {
		Long value = null;
		Jedis jedis = jedisPool.getResource();
		try {
			 jedis.auth(password);
			value = jedis.hlen(key);
		} finally {
			jedisPool.returnResource(jedis);
		}
		return value;
	}

	/**
	 * 获取指定的hash field
	 *
	 * @param key
	 * @return
	 */
	public String hget(String key, String value) {
		Jedis jedis = jedisPool.getResource();
		try {
			jedis.auth(password);
			value = jedis.hget(key, value);
		} finally {
			jedisPool.returnResource(jedis);
		}
		return value;
	}

	/**
	 * 设置hash field为指定值,如果key不存在,则先创建
	 *
	 * @param key
	 * @return
	 */
	public Long hset(String key, String value1, String value2) {
		Long value = null;
		Jedis jedis = jedisPool.getResource();
		try {
			jedis.auth(password);
			value = jedis.hset(key, value1, value2);
		} finally {
			jedisPool.returnResource(jedis);
		}
		return value;
	}

	/**
	 * 添加一个string元素到,key对应的set集合中,成功返回1,如果元素以及在集合中返回0,key对应的set不存在返回错误
	 *
	 * @param key
	 * @return
	 */
	public Long sadd(String key, String value1) {
		Long value = null;
		Jedis jedis = jedisPool.getResource();
		try {
			jedis.auth(password);
			value = jedis.sadd(key, value1);
		} finally {
			jedisPool.returnResource(jedis);
		}
		return value;
	}

	/**
	 * 为key指定过期时间,单位是秒。返回1成功,0表示key已经设置过过期时间或者不存在
	 *
	 * @param key
	 * @return
	 */
	public Long expire(String key, int value1) {
		Long value = null;
		Jedis jedis = jedisPool.getResource();
		try {
			jedis.auth(password);
			value = jedis.expire(key, value1);
		} finally {
			jedisPool.returnResource(jedis);
		}
		return value;
	}

	/**
	 * 判断member是否在set中
	 *
	 * @param key
	 * @return
	 */
	public Boolean sismember(String key, String value1) {
		Boolean value = null;
		Jedis jedis = jedisPool.getResource();
		try {
			jedis.auth(password);
			value = jedis.sismember(key, value1);
		} finally {
			jedisPool.returnResource(jedis);
		}
		return value;
	}

	/**
	 * 从key对应set中移除给定元素,成功返回1,如果member在集合中不
	 *
	 * @param key
	 * @return
	 */
	public Long srem(String key, String value1) {
		Long value = null;
		Jedis jedis = jedisPool.getResource();
		try {
			jedis.auth(password);
			value = jedis.srem(key, value1);
		} 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.auth(password);
			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.auth(password);
			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.auth(password);
			jedis.del(key);
		} finally {
			jedisPool.returnResource(jedis);
		}
	}

	/**
	 * flush
	 */
	public void flushDB() {
		Jedis jedis = jedisPool.getResource();
		try {
			jedis.auth(password);
			jedis.flushDB();
		} finally {
			jedisPool.returnResource(jedis);
		}
	}

	/**
	 * size
	 */
	public Long dbSize() {
		Long dbSize = 0L;
		Jedis jedis = jedisPool.getResource();
		try {
			jedis.auth(password);
			dbSize = jedis.dbSize();
		} finally {
			jedisPool.returnResource(jedis);
		}
		return dbSize;
	}

	/**
	 * keys
	 *
	 * @param regex
	 * @return
	 */
	public Set keys(String pattern) {
		Set keys = null;
		Jedis jedis = jedisPool.getResource();
		try {
			jedis.auth(password);
			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;
	}

	public static void main(String[] args) {
		// Jedis jedis = new Jedis("192.168.126.89", 6379);
		// jedis.auth("123456");
		RedisManager manager = new RedisManager();
		manager.setHost("192.168.126.89");

		manager.init();
		for (int i = 0; i < 100000; i++) {
			// BoardItem item = new BoardItem(i+"", "clientId"+i, i, 8, 0);
			String item = i + "|" + "clientId" + i;
			manager.zadd("test", i, item);
		}
		// jedis.set("aa", "记录了");
		// System.out.println(jedis.get("aa"));
		System.out.println(manager.zrevrange("test", 0, 100));
		System.out.println(manager.zrange("test", 0, 100));

	}

	/**
	 * 有序SET 添加
	 *
	 * @param key
	 * @param score
	 * @param member
	 * @return
	 */
	public Long zadd(String key, double score, String member) {
		Long value = null;
		Jedis jedis = jedisPool.getResource();
		try {
			jedis.auth(password);
			value = jedis.zadd(key, score, member);
		} finally {
			jedisPool.returnResource(jedis);
		}
		return value;
	}

	public Long zrem(String key, String member) {
		Long value = null;
		Jedis jedis = jedisPool.getResource();
		try {
			jedis.auth(password);
			value = jedis.zrem(key, member);
		} finally {
			jedisPool.returnResource(jedis);
		}
		return value;
	}

	public Set zrevrange(String key, long start, long end) {
		Set value = null;
		Jedis jedis = jedisPool.getResource();
		try {
			jedis.auth(password);
			value = jedis.zrevrange(key, start, end);
		} finally {
			jedisPool.returnResource(jedis);
		}
		return value;
	}

	public Set zrange(String key, long start, long end) {
		Set value = null;
		Jedis jedis = jedisPool.getResource();
		try {
			jedis.auth(password);
			value = jedis.zrange(key, start, end);
		} finally {
			jedisPool.returnResource(jedis);
		}
		return value;
	}

	public Long zrank(String key, String member) {
		Long value = null;
		Jedis jedis = jedisPool.getResource();
		try {
			jedis.auth(password);
			value = jedis.zrank(key, member);
		} finally {
			jedisPool.returnResource(jedis);
		}
		return value;
	}

	public Long zrevrank(String key, String member) {
		Long value = null;
		Jedis jedis = jedisPool.getResource();
		try {
			jedis.auth(password);
			value = jedis.zrevrank(key, member);
		} finally {
			jedisPool.returnResource(jedis);
		}
		return value;
	}

	public Long zcard(String key) {
		Long value = null;
		Jedis jedis = jedisPool.getResource();
		try {
			jedis.auth(password);
			value = jedis.zcard(key);
		} finally {
			jedisPool.returnResource(jedis);
		}
		return value;
	}

	public Set zrangeWithScores(String key, long start, long end) {
		Set value = null;
		Jedis jedis = jedisPool.getResource();
		try {
			jedis.auth(password);
			value = jedis.zrangeWithScores(key, start, end);
		} finally {
			jedisPool.returnResource(jedis);
		}
		return value;
	}

	public Set zrevrangeWithScores(String key, long start, long end) {
		Set value = null;
		Jedis jedis = jedisPool.getResource();
		try {
			jedis.auth(password);
			value = jedis.zrevrangeWithScores(key, start, end);
		} finally {
			jedisPool.returnResource(jedis);
		}
		return value;
	}

	public Set zrevrangeByScore(String key, double max, double min, int offset, int limit) {
		Set value = null;
		Jedis jedis = jedisPool.getResource();
		try {
			jedis.auth(password);
			value = jedis.zrevrangeByScore(key, max, min, offset, limit);
		} finally {
			jedisPool.returnResource(jedis);
		}
		return value;
	}
}

 RedisDAO

 

 

package sy.sso;

import java.io.Serializable;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import javax.servlet.http.HttpSession;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import sy.util.base.SerializeUtils;

public class RedisDAO   {

	private static Logger logger = LoggerFactory.getLogger(RedisDAO.class);
	/**
	 * shiro-redis的session对象前缀
	 */
	private final String REDIS_SESSION_PRE = "redis_session:";

	private RedisManager redisManager;
    private int timeOut=1800000;//默认30分钟

	public void update(String sessionid,String userName)  {
		this.save(sessionid,userName);
	}

	/**
	 * save session
	 *
	 * @param session
	 * @throws UnknownHttpSessionException
	 */
	private void save(String sessionid,String userName)  {
		if (userName == null) {
			logger.error("userName or userName id is null");
			return;
		}

		byte[] key = getByteKey(sessionid);
		byte[] value = SerializeUtils.serialize(userName);


		int expire =timeOut/1000;

		this.redisManager.set(key, value, expire);
	}


	public void delete(String sessionid) {
		if (sessionid == null) {
			logger.error("userName or userName id is null");
			return;
		}
		redisManager.del(this.getByteKey(sessionid));

	}


	public Collection getActives() {
		Set userNames = new HashSet();

		Set keys = redisManager.keys(this.REDIS_SESSION_PRE + "*");
		if (keys != null && keys.size() > 0) {
			for (byte[] key : keys) {
				String s = (String) SerializeUtils.deserialize(redisManager.get(key));
				userNames.add(s);
			}
		}

		return userNames;
	}




	public String doRead(Serializable sessionId) {
		if (sessionId == null) {
			logger.error("userName id is null");
			return null;
		}

		String s = (String) SerializeUtils.deserialize(redisManager.get(this.getByteKey(sessionId)));

		return s;
	}

	/**
	 * 获得byte[]型的key
	 *
	 * @param key
	 * @return
	 */
	private byte[] getByteKey(Serializable sessionid) {
		String preKey = this.REDIS_SESSION_PRE + sessionid;
		return preKey.getBytes();
	}

	public RedisManager getRedisManager() {
		return redisManager;
	}

	public void setRedisManager(RedisManager redisManager) {
		this.redisManager = redisManager;

		/**
		 * 初始化redisManager
		 */
		this.redisManager.init();
	}

	public int getTimeOut() {
		return timeOut;
	}

	public void setTimeOut(int timeOut) {
		this.timeOut = timeOut;
	}


}

 SSOFilter 这个类里一些装载的类如用户的那些按实际的来,只是提供模式,这里写的是本系统的,如要拿来用,请自行修改.

 

 

package sy.sso;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.Subject.Builder;
import org.apache.shiro.util.ThreadContext;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.subject.WebSubject;
import org.hibernate.Hibernate;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import sy.model.base.frame.SessionInfo;
import sy.model.base.frame.Syorganization;
import sy.model.base.frame.Syrole;
import sy.model.base.frame.Syuser;
import sy.model.dtom.Tuser;
import sy.service.base.frame.SyuserServiceI;
import sy.service.dtom.business.TuserServiceI;
import sy.util.base.ConfigUtil;
import sy.util.base.HqlFilter;
import sy.util.base.IpUtil;

/**
 * 用于redis同步登陆
 *
 * @author miraclerz
 *
 */
public class SSOFilter implements Filter {

	private static final Logger logger = Logger.getLogger(SSOFilter.class);

	private  RedisDAO redisDAO;
	private SyuserServiceI syuserServiceI;
	private TuserServiceI tuserServiceI;
	private DefaultWebSecurityManager securityManager;

	public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
		HttpServletRequest request = (HttpServletRequest) req;
		HttpServletResponse response = (HttpServletResponse) res;


		SessionInfo sessionInfo = null;
		if(request.getSession().getAttribute(ConfigUtil.getSessionInfoName())!=null)
		{
		sessionInfo=(SessionInfo)request.getSession().getAttribute(ConfigUtil.getSessionInfoName());
		}

		String requestURI = request.getRequestURI();
         //取得url里的JSESSIONID
    	String JSESSIONID = StringUtils.substringAfterLast(requestURI, "JSESSIONID=");

        if(request.getSession().getAttribute("JSESSIONID")!=null)
        {//如果session里的JSESSIONID不为空,表示已经登陆了,JSESSIONID就用这个了
        	JSESSIONID=(String) request.getSession().getAttribute("JSESSIONID");
        }

		 String  userName=null;
		if(sessionInfo==null&&JSESSIONID!=null&&!"".equals(JSESSIONID))
		{//如果没登陆且JSESSIONID不为空,即url地址里有JSESSIONID
          userName=redisDAO.doRead(JSESSIONID);
          logger.info(userName+":同步登陆");
		}

     	if(sessionInfo==null&&userName!=null)
		{
		HqlFilter hqlFilter = new HqlFilter();
		hqlFilter.addFilter("QUERY_t#loginname_S_EQ", userName);
 		Syuser user = syuserServiceI.getByFilter(hqlFilter);

 		HqlFilter hqlFiltert = new HqlFilter();
		hqlFiltert.addFilter("QUERY_t#username_S_EQ", userName);
 		Tuser tuser = tuserServiceI.getByFilter(hqlFiltert);

		if (user != null&&tuser!=null) {
		    sessionInfo = new SessionInfo();
			Hibernate.initialize(user.getSyroles());
			Hibernate.initialize(user.getSyorganizations());
			Hibernate.initialize(user.getSyresources());
			for (Syrole role : user.getSyroles()) {
				Hibernate.initialize(role.getSyresources());
			}
			for (Syorganization organization : user.getSyorganizations()) {
				Hibernate.initialize(organization.getSyresources());
			}

			user.setIp(IpUtil.getIpAddr(request));
			sessionInfo.setUser(user);


			//同步登陆shiro
		    SecurityUtils.setSecurityManager(securityManager);//
		    Builder builder = new WebSubject.Builder(request,response);
		    builder.authenticated(true);
		    Subject subject= builder.buildSubject();
		    //设置用户的session(如果不是shiro,就直接是普通的在这里设置session就行了)
		    subject.getSession().setAttribute(ConfigUtil.getSessionInfoName(), sessionInfo);
		    //在session里保存登陆时的sessionid,这个sessionid会存到redis里去,本系统也会一直用这个作同步
		    subject.getSession().setAttribute("JSESSIONID", JSESSIONID);
		    ThreadContext.bind(subject);//线程变量中绑定一个已通过验证的Subject对象
		}
		}
       if(sessionInfo!=null)
       {
    	   redisDAO.update(JSESSIONID,sessionInfo.getUser().getLoginname());
	       System.out.println("同步session啦=>"+JSESSIONID);
       }
		chain.doFilter(request, response);
	}

		public void init(FilterConfig filterConfig) throws ServletException {
		//这些是因为filter无法直接自动装载spring里的bean,于是用下面的方法也取得bean
		ServletContext context = filterConfig.getServletContext();
        ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(context);
        redisDAO = (RedisDAO) ctx.getBean("redisDAO");//直接以bean名称来取
        securityManager = (DefaultWebSecurityManager) ctx.getBean("securityManager");
        syuserServiceI=(SyuserServiceI)ctx.getBean("syuserServiceImpl");
        tuserServiceI=(TuserServiceI)ctx.getBean("tuserServiceImpl");

      /*  String[] syuserServices=ctx.getBeanNamesForType(SyuserServiceI.class);//取得所有这个接口的实现类的bean名(以接口装载的不知道bean名是啥)
        syuserServiceI = (SyuserServiceI)ctx.getBean(syuserServices[0]);//取第一个实现类名
        logger.info("实现类名:"+syuserServices[0]);
        String[] tuserServices=ctx.getBeanNamesForType(TuserServiceI.class);
        tuserServiceI = (TuserServiceI)ctx.getBean(tuserServices[0]);
*/
	}

	public void destroy() {
	}
}

 

  在spring 的配置文件里要加上redis的配置

   
      
		
		    
				
				
				
				
			
		
	

 在web.xml里加上(shiro的filter的前面)


  
    ssofilter
    sy.sso.SSOFilter
  
  
    ssofilter
    /*.sy
  
  
    ssofilter
    *.jsp
  

   其它系统同步时也一样是这样,配置一个ssoFilter,在这个filter里先判断是否已经登陆,如果已经登陆就直接跳过不理了,如果没登陆就判断是否地址上带有JSESSIONID,如果有就取出来,去redis里看有没有这个值,如果没有就忽略跳过,如果有就取出用户名,用这个用户名去自己的系统里把用户信息取出来,然后设置到session里就完成同步了.

在oa里还要在session的listener里对session的创建和销毁里要同步设置redis信息;

/**
	 * 向session里增加属性时调用(用户成功登陆后会调用)
	 */
	public void attributeAdded(HttpSessionBindingEvent evt) {

		String name = evt.getName();
		logger.debug("向session存入属性:" + name);
		if (ConfigUtil.getSessionInfoName().equals(name)) {// 如果存入的属性是sessionInfo的话
			HttpSession session = evt.getSession();
			SessionInfo sessionInfo = (SessionInfo) session.getAttribute(name);
			if (sessionInfo != null) {
				// System.out.println(sessionInfo.getUser().getName() + "登录了");
				//SyonlineServiceI syonlineService = (SyonlineServiceI) ctx.getBean("syonlineServiceImpl");
				Syonline online = new Syonline();
				online.setType("1");// 登录
				online.setLoginname(sessionInfo.getUser().getLoginname());
				online.setIp(sessionInfo.getUser().getIp());
			 	syonlineService.save(online);
                //登陆成功后把信息存到redis
				session.setAttribute("JSESSIONID", evt.getSession().getId());
				redisDAO.update(evt.getSession().getId(),sessionInfo.getUser().getLoginname());

			}


		}
	}

 

/**
	 * session销毁(用户退出系统时会调用)
	 */
	public void sessionDestroyed(HttpSessionEvent evt) {
		HttpSession session = evt.getSession();
		if (session != null) {
			logger.debug("session销毁:" + session.getId());
			SessionInfo sessionInfo = (SessionInfo) session.getAttribute(ConfigUtil.getSessionInfoName());
			if (sessionInfo != null) {
				// System.out.println(sessionInfo.getUser().getName() + "注销了");
			//	SyonlineServiceI syonlineService = (SyonlineServiceI) ctx.getBean("syonlineServiceImpl");
				Syonline online = new Syonline();
				online.setType("0");// 注销
				online.setLoginname(sessionInfo.getUser().getLoginname());
				online.setIp(sessionInfo.getUser().getIp());
				syonlineService.save(online);

				//用户退出后把用户信息从redis里删除
				Object JSESSIONID=session.getAttribute("JSESSIONID");
				if(JSESSIONID!=null)
				{
					redisDAO.delete((String) JSESSIONID);
				}
			}
		}
	}

 

 

 

 

   跳转的地址类似这样写:

">另一个系统go

 

  OK,系统同步登陆就搞定了!

 

 

你可能感兴趣的:(技术,redis,session共享)