0x00什么是单点登录
在一个服务登录,同时登录其他服务。
PS:代码框架为spring MVC和jquery。
0x01技术难点
跨域访问
session同步
0x02什么是跨域访问
在客户端A,访问服务器B,在B的页面1上访问服务器C,页面1跨域访问了服务器C。
0x03为什么要同步session
session是浏览器和服务器的一次回话,如果session不过期,服务器不删除session,之后对该服务器的所有的请求都是同一个session。
单点登录成功之前有两个session有登录状态,一个是A对B的session,B对C的session,A想要登录服务器C,必须把A登录B的session数据同步到A到C的session。
0x04跨域代码
在服务器B的页面post数据到服务器C,在服务器C的controller响应post,前端登录代码如下:
$.ajax({url:uri,timeout:1000,type:'post',data:{ userName : $("#userName").val(), password : $("#password").val(), langCode : "zh-cn", originIp : document.location.origin },success:function(result) { result = JSON.parse(result); if(result.success&&result.attributes.sessionId){ sucess(result.success, page + "&sessionId=" + result.attributes.sessionId); }else{ sucess(false); } }}
访问失败,通过chrome控制台网络分析请求,发现是服务器没有开启跨域,
Google之,在controller里配置以下代码允许跨域。
res.setHeader("Access-Control-Allow-Origin", 'IP/*'); res.setHeader("Access-Control-Allow-Headers", "Content-Type"); res.setHeader("Access-Control-Allow-Methods", "POST"); res.setHeader("Allow", "POST"); res.setHeader("P3P", "CP=CAO PSA OUR");
再次访问,可以返回正确的值,成功登录服务器C,把本页面跳转到服务器C的项目主页,发现并没有什么卵用,还是需要登录,服务器C获取的session的用户为空。
0x05session同步
首先在服务器C记录B登录服务器的session信息,在异步方法返回令牌,在响应方法里加入如下代码:
private static Map map = new ConcurrentHashMap(); map.put(session.getId(), session); AjaxJson j = new AjaxJson(); j.getAttributes().put("sessionId", session.getId()); return j;
在项目的首页加入如下代码,把之前的B的session数据同步给A的session:
String sessionId = request.getParameter("sessionId"); if (StringUtils.isNotBlank(sessionId) && map.containsKey(sessionId)) { HttpSession session = map.get(sessionId); HttpSession current = request.getSession(); ClientManager cm = ClientManager.getInstance(); cm.addClinet(current.getId(), cm.getClient(sessionId)); map.remove(sessionId); current.setAttribute("lang", session.getAttribute("lang")); session.setMaxInactiveInterval(1); }
至此完美解决单点登录问题,本机测试毫无压力,客户端A和服务器C均为本机,分为三台也测试通过,当我把服务器B和C放在一台时出现bug,只有第一次访问时可以访问,不管点什么都会要求登录,session又丢了。
0x06debug
分析session之后发现,session超时了。。。。。。。。
当我把服务器B和C放在一台服务器之后,并没有跨域,我在controller里写了session.setMaxInactiveInterval(1);把session超时了。。。。
解决方法一:判断服务和单点服务是否同一个IP,同一个IP就跳过;
解决方法二:session超时时间设置为10分钟;
其他的跨域方式,有时间再写吧。。。
安利两个java类:
java.util.concurrent.TimeUnit,在读这个类之前,完全不知道java的枚举还可以这样玩,[目瞪口呆]表情;
java.net.InetAddress,获取本机IP,request.getLocalAddr()只能得到127.0.0.1。