由于一直在弄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<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,系统同步登陆就搞定了!