今天要弄的是用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<byte[]> keys(String pattern) {
- Set<byte[]> 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<String> zrevrange(String key, long start, long end) {
- Set<String> value = null;
- Jedis jedis = jedisPool.getResource();
- try {
- jedis.auth(password);
- value = jedis.zrevrange(key, start, end);
- } finally {
- jedisPool.returnResource(jedis);
- }
- return value;
- }
- public Set<String> zrange(String key, long start, long end) {
- Set<String> 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<redis.clients.jedis.Tuple> zrangeWithScores(String key, long start, long end) {
- Set<redis.clients.jedis.Tuple> value = null;
- Jedis jedis = jedisPool.getResource();
- try {
- jedis.auth(password);
- value = jedis.zrangeWithScores(key, start, end);
- } finally {
- jedisPool.returnResource(jedis);
- }
- return value;
- }
- public Set<redis.clients.jedis.Tuple> zrevrangeWithScores(String key, long start, long end) {
- Set<redis.clients.jedis.Tuple> value = null;
- Jedis jedis = jedisPool.getResource();
- try {
- jedis.auth(password);
- value = jedis.zrevrangeWithScores(key, start, end);
- } finally {
- jedisPool.returnResource(jedis);
- }
- return value;
- }
- public Set<String> zrevrangeByScore(String key, double max, double min, int offset, int limit) {
- Set<String> 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<String> getActives() {
- Set<String> userNames = new HashSet<String>();
- Set<byte[]> 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的配置
- <bean id="redisDAO" class="sy.sso.RedisDAO">
- <property name="timeOut" value="1800000"></property>
- <property name="redisManager" >
- <bean class="sy.sso.RedisManager">
- <!-- <property name="host" value="172.16.6.3"></property> -->
- <property name="host" value="127.0.0.1"></property>
- <property name="dbindex" value="0"></property>
- <property name="password" value="123456"></property>
- </bean>
- </property>
- </bean>
在web.xml里加上(shiro的filter的前面)
- <!--sso同步登陆filter -->
- <filter>
- <filter-name>ssofilter</filter-name>
- <filter-class>sy.sso.SSOFilter</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>ssofilter</filter-name>
- <url-pattern>/*.sy</url-pattern>
- </filter-mapping>
- <filter-mapping>
- <filter-name>ssofilter</filter-name>
- <url-pattern>*.jsp</url-pattern>
- </filter-mapping>
其它系统同步时也一样是这样,配置一个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);
- }
- }
- }
- }
跳转的地址类似这样写:
<a target="_blank" href="http://127.0.0.1:8089/oa/login.jsp;JSESSIONID=<%=request.getSession().getAttribute("JSESSIONID")%>">另一个系统go</a>
OK,系统同步登陆就搞定了!