由于一直在弄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 Setkeys(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 CollectiongetActives() { 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); } } } }
跳转的地址类似这样写:
OK,系统同步登陆就搞定了!