关于多系统单点登录,这并非陌生概念。然而在企业应用系统的建设过程中,多个应用系统一般是在不同时期开发完成的,各应用系统由于功能侧重、设计方案和开发技术有所不同,也就形成了各自独立的用户库和用户认证体系。这也是在实施单点登录改造时,面临的最大问题,而其中尤以大量存在的“三无系统”成为单点登录改造实施的最大拦路虎。
关于单点登录技术,最近在研究,单点登录包含好几个方面,接入前后端分离的单点登录系统,这个相对比较简单些,选择的鉴权验证机制也比较多,因为大多数前后端分离的系统是基于token验证的,不存在跨域问题,但是如果要接入第三方的老系统的话,如果老系统有人在维护,可以改代码的话也比较简单,但是如果是三无老旧系统的话就比较麻烦了。
所谓“三无系统”是指企业中没有提供单点登录对接接口,没有源码可以修改、过保,没有厂家支撑的存量系统。企业中的“三无系统”使用这种方式进行单点登录集成,在落地上存在较大的难题,首先是实施成本高,需要调动原厂修改程序,费用高;其次是实施复杂,涉及开发者较多,实施周期长;再之扩展性差,跟系统厂家、应用的平台、环境有关,有些系统不能进行单点登录改造。
那么对于这些三无系统我们是如何处理的呢?为此我也是进行了大量的验证,找解决方案,因为我是做后端开发的,首先想到的是从java后端出发,首先肯定的是模拟登录请求,将模拟登陆请求的sessionid传入到response中,然后通过后端重定向到三无系统登录后的页面,其中需要注意的是cookie设置domain解决跨域问题,按照这个思路我写了如下接口代码。
@GET @Path("/formLogin") @ApiOperation(value = "auth模拟登陆") public void formLogin(@Context HttpServletRequest request, @Context HttpServletResponse response) { String CookieValue = ""; String loginUrl = "http://zsyz.dev.jpsycn.com/admin/check.do"; String dataUrl = "http://zsyz.dev.jpsycn.com/admin/index.do"; String userName = request.getParameter("userName"); String password = request.getParameter("password"); System.out.println("userName "+userName+" password "+password); HttpClient httpClient = new HttpClient(); httpClient.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, "utf-8"); PostMethod postMethod = new PostMethod(loginUrl); NameValuePair[] postData = {new NameValuePair("userName", userName), new NameValuePair("passWord", password)}; postMethod.setRequestBody(postData); try { httpClient.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY); int statusCode=httpClient.executeMethod(postMethod); System.out.println("code==============="+statusCode); httpClient.executeMethod(postMethod); System.out.println("httpclient的cookie "+httpClient.getState().getCookies()); String JSESSIONID = ""; org.apache.commons.httpclient.Cookie[] cookies = httpClient.getState().getCookies(); for (int i = 0; i < cookies.length; i++) { CookieValue = CookieValue+cookies[i]+";"; if(i==0){ String sp[] = cookies[i].toString().split("="); JSESSIONID = sp[1]; } } if(statusCode==200) {//重定向到新的URL CookieValue+=" Domain=jpsycn.com;"; System.out.println("模拟登录成功 "+CookieValue); GetMethod getMethod = new GetMethod(dataUrl); getMethod.setRequestHeader("Cookie", CookieValue); postMethod.setRequestHeader("Host", "zsyz.dev.jpsycn.com"); postMethod.setRequestHeader("Referer", "http://zsyz.dev.jpsycn.com/admin/login.do"); postMethod.setRequestHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36"); httpClient.executeMethod(getMethod); String result = getMethod.getResponseBodyAsString(); Cookie cookie = new CookieBuilder() .setName("JSESSIONID") .setValue(JSESSIONID) .setDomain("jpsycn.com") .setPath("/") .setExpireSeconds((int)(60)) .build(); response.addCookie(cookie); System.out.println("登陆成功的cookie "+CookieValue+" header "+response.getHeader("Set-Cookie")); response.sendRedirect("http://zsyz.dev.jpsycn.com/admin/index.do"); }else{ System.out.println("登录失败"); } } catch (Exception e) { e.printStackTrace(); } }
这种思路实现的代码虽然能模拟登录成功,但是因为并不是token验证机制,每次重定向后的页面cookie中的sessionid都会变化,所以这种方案并不可行。
接下来我们来从前端着手看看能不能实现,前端实现的话也分带验证码不带,我们之前看了不少公司的产品,他们有个插件可以获取到form表单登录的用户名,密码,带验证码的他们目前还做不到,但是我做到了,一会分享代码。
先来看看不带验证码的,以http://zsyz.dev.jpsycn.com/admin/login.do郑州市招商引资平台来说。
模拟登录了一下获取登录了一下
发现他是form表单登录的,这时我们使用网上说的密码代填方式,说白了就是表单登录,还不能是ajaxForm这种形式的表单提交,这样会出现跨域问题,但是这种提交会返回json的验证数据,我们怎么接收呢?我是采用了ifream的形式展示在页面上,当时实际上应该隐藏着的,前端页面我部署在自己的121.36.107.248服务器上,如果直接通过ip访问会出现跨域问题,这时我就会想到配子域名的方式,于是我在host文件中配置了一下,用一个和这个平台二级域名相同的域名映射到我的服务器ip上,实际上这个操作应该nginx来实现,但想到要购买域名,所以就先改host文件。
通过http://zsyz2.dev.jpsycn.com/index.html访问我自己的前端页面
接下来我们来看看有验证码的系统如何模拟登录,我们以http://218.28.223.243:9000/zzjyw/loginController.do?login
我们要考虑的问题是验证码和接口调用是在218的域下,前端页面部署在121的域下,就是要处理这俩问题才能解决跨域问题,于是我在host文件下配置了映射。
然后验证码也是通过aaa的域名访问,接口也是aaa,前端页面部署在121用bbb访问,这就可以解决跨域问题。
下边我附上前端的主要代码,想要全部代码或者有什么疑问可以关注公众号“蛋皮皮”进行咨询。
<iframe id="frameFile" name="frameFile" style="display:text"></iframe> <form action = "http://www.aaa.baixing.com:9000/zzjyw/loginController.do?checkuser" method="post" id="formP" target="frameFile"> <input type="text" id="userName" name="userName" value="admin"/> <input type="text" id="password" name="password" value="3edc#EDC"/> <input type="text" id="randCode" name="randCode" /> <input type="text" id="orgId" name="orgId" value=""/> <input type="langCode" id="langCode" name="langCode" value="zh-cn"/> <input type="button" id="btn" value="表单登录" /> <img src="http://www.aaa.baixing.com:9000/zzjyw/randCodeImage"/> </form>
点击事件
var oForm = document.querySelector('#formP'); var oBtn = document.querySelector('#btn'); oBtn.onclick = function(){ alert("111"); oForm.submit(); var int=self.setInterval(function(){ // 这个方法是说在延迟两秒后执行大括号里的方法 window.location.href="http://www.aaa.baixing.com:9000/zzjyw/loginController.do?login"; },2000) //这里2000代表两秒 }
喜欢请关注“蛋皮皮”公众号