系统中两种单点登录实现方式:
1、 直接登录单点登录认证系统。
2、 登录业务系统,如用户此时为首次登录,则跳转得到单点登录认证界面。如用户已通过认证,则直接登录到业务系统。
【补充说明】
单点登录认证服务器地址:http://192.168.61.124:8080/SSOAuth_v1.0.0/
业务系统服务器地址:http://192.168.61.134:8080/sepmis_v1.3.0/
直接登录单点登录认证系统流程:
1、 打开单点登录认证服务器登录页面,输入用户名密码。
2、 如果通过认证,此时页面跳转到系统主界面main.jsp。这个时候在加载main.jsp页面同时,页面后端通过ajax中jsonp方式,向单点登录认证服务器后台Action发送hello请求。需要传递参数,当前用户代码。
main.jsp页面核心代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <% UserSession userSession = (UserSession)session.getAttribute(AppConst.USER_SESSION_ID); String userCode = ""; String userName = ""; if(null != userSession){ userCode = userSession.getUserCode(); userName = userSession.getUserType(); } %> <script type="text/javascript"> $.ajax({ type: "get", contentType: "application/json", url: "http://192.168.61.124:8080/SSOAuth_v1.0.0/hello.do?usercode=<%=userCode%>&callback=?", crossDomain: true, success: function(data) { $.each(data.msg, function () { $.getJSON(this); }); } }); </script> |
3、 后台接受hello请求,通过传递的参数用户代码,在SysRealuser表中判断用户所能够访问到的业务系统对象。拿到业务系统对象中url参数,组织各个业务系统setCookie Url。然后通过json异步传回前台。
Url格式例如:(红色字体处为变量)
http://192.168.61.134:8080/sepmis_v1.3.0/setCookie.do?userCode=xxx&&callback=?
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | /** * 当用户成功通过单点登录系统认证后,用户进入系统操作界面,同时页面异步发送此action请求 * 获取创建json对象,对象内容为页面使用ajax异步访问各个业务系统的 setcookie action方法 * @return result */ public String hello(){ try { List<SysInfo> tmp = getUserSysRealSys(); UserSession userSession = AuthorityUtil.getSysUserSession(); JSONObject jsonObject = new JSONObject(); JSONArray jsonArray = new JSONArray(); if(tmp!=null && tmp.size()>0){ for(int i=0; i<tmp.size(); i++){ jsonArray.put(tmp.get(i).getSysUrl() + "/setCookie.do?userCode="+userSession.getUserCode()+"&callback=?"); } } jsonObject.put("msg", jsonArray); response.setContentType("application/json"); response.setCharacterEncoding("UTF-8"); response.setHeader("Cache-Control", "no-cache"); response.getWriter().write(jsonObject.toString()); response.getWriter().flush(); //释放资源 tmp = null; userSession = null; //打印日志 String info = LoggerUtil.getInfoMsg("AJAX异步访问,用户权限范围内各业务系统的 setCookie方法"); log.info(info); return null; } catch (IOException ie) { this.exceptionMessage.setError(ie.toString()); this.exceptionMessage.setClassName(this.getClass().getName()); this.exceptionMessage.setMessage("AJAX异步访问出现IO异常!"); log.error(ie.toString()); return ERROR; } catch (Exception ex) { this.exceptionMessage.setError(ex.toString()); this.exceptionMessage.setClassName(this.getClass().getName()); this.exceptionMessage.setMessage("AJAX异步访问出现异常!"); log.error(ex.toString()); return ERROR; } } |
4、 前台main.jsp页面这时拿到传回的URL参数,此时执行Success代码段中的函数
1 2 3 4 | success: function(data) { $.each(data.msg, function () { $.getJSON(this); }); |
此时function中参数data就是传回的URL参数,然后遍历执行Jquery中getJSON方法。此时通过AJAX技术异步又向各个业务系统发送setCookie请求,此操作可以视为一种透明的登录操作,用户是不能够感知到。
5、 业务系统接受setCookie action请求。
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | public String setCookie(){ try{ // - 查找用户岗位集合 StringBuilder hql = new StringBuilder(300); userCode = request.getParameter("userCode"); hql.append("FROM SysUser WHERE userCode ='").append(userCode).append("'"); List<SysUser> sysUserList = sysUserService.findByHql(hql.toString()); if(sysUserList.size() > 0){ Cookie cookie = new Cookie("sso", userCode); cookie.setPath("/"); //设置cookie的有效期,单位是秒; //如果不使用这个方法或者参数为负数的话,当浏览器关闭的时候,这个cookie就失效了 //cookie一年内有效60*60*24*365 cookie.setMaxAge(60*60); response.addCookie(cookie); } }catch(Exception ex){ //返回异常信息 this.exceptionMessage.setError(ex.toString()); this.exceptionMessage.setClassName(this.getClass().getName()); this.exceptionMessage.setMessage("用户登录出现异常!"); log.info(ex.toString()); return ERROR; } return null; } |
业务系统通过setCookie方法,将此用户在业务系统中Cookie存储到用户本地。
6、 输入业务系统连接。
业务系统同后台通过filter过滤所有请求,获取用户在当前服务器域下Cookie值,进行校验,通过校验进入系统,未通过校验返回单点登录认证页面。
SSOFilter.java核心代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | /** * servlet filter 过滤方法 * @param ServletRequest * @param ServletResponse * @param FilterChain * @throws IOException,ServletException */ public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { // 用户本地cookie值 String cookieValue = ""; HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; String url = request.getRequestURL().toString(); // 返回结果 String result = null; // 根据上下文根截取URL String[] urlArray = url.split(request.getContextPath()); //如果是业务service地址,跳转到业务系统单点登录action if (urlArray.length == 2 && ("/").equals(urlArray[1])) { response.sendRedirect(url+"loginSSO.do"); return; }else { for(int i=0; i<urlArray.length; i++){ if("/setCookie.do".equals(urlArray[i])){ result = "setCookie"; break; } } } // 检查HTTP请求的head是否有需要的cookie Cookie[] diskCookies = request.getCookies(); if (diskCookies != null) { log.info("【权限系统-用户认证过滤器】找到COOKIES"); for (int i = 0; i < diskCookies.length; i++) { cookieValue = diskCookies[i].getValue(); log.info("【权限系统-用户认证过滤器】COOKIES中找到用户标识:" + diskCookies[i].getName() + "=" + cookieValue); if (SSO_AUTH.equals(diskCookies[i].getName())) { result = SSOService(cookieValue); if ("success".equals(result)) { log.info("【通过单点登录服务器认证】"); } else { log.info("【未通过单点登录服务器认证】COOKIES认证失败"); response.sendRedirect(ssoLoginPage); } } else if ("sso".equals(diskCookies[i].getName())) { HttpSession session = request.getSession(); session.setAttribute("sso", cookieValue); result = "success"; } } } else { log.info("【权限系统-用户认证过滤器】未找到COOKIES"); } // COOKIES效验,转到单点登录认证页面 if (result.equals("failed")) { log.info("【权限系统-用户认证过滤器】COOKIES认证失败"); response.sendRedirect(ssoLoginPage); // 效验成功并退出服务 } else if (result.equals("logout")) { log.info("【权限系统-用户认证过滤器】退出登录"); //logoutService(cookieValue); response.sendRedirect(ssoLoginPage); // 效验成功 } else { log.info("【权限系统-用户认证过滤器】认证成功"); Throwable problem = null; try { chain.doFilter(req, res); } catch (Throwable t) { problem = t; t.printStackTrace(); } if (problem != null) { if (problem instanceof ServletException) { throw ((ServletException) problem); } if (problem instanceof IOException) { throw ((IOException) problem); } sendProcessingError(problem, res); } } } /** * 使用httpclient调用身份认证服务(SSOAuth)校验cookie * * @param cookievalue * @return String * @throws IOException */ private String SSOService(String cookievalue) throws IOException { String result = "failed"; String authAction = "/sso/authcookie.do?cookieName="; String tmp = this.ssoServiceURL + authAction + cookievalue; // 通过http协议来调用身份认证服务(SSOAuth)使用httpclient实现 HttpClient httpclient = new HttpClient(); GetMethod httpget = new GetMethod(tmp); httpget.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler()); try { // 执行getMethod int statusCode = httpclient.executeMethod(httpget); if (statusCode != HttpStatus.SC_OK) { log.error("HttpClient Method failed: " + httpget.getStatusLine()); } // 获取返回内容 result = httpget.getResponseHeader("result").getValue(); } catch (HttpException e) { // 发生致命的异常,可能是协议不对或者返回的内容有问题 log.error("Please check your HttpClient address!"); log.error(e.toString()); } catch (IOException e) { // 发生网络异常 log.error(e.toString()); } finally { httpget.releaseConnection(); } log.info("【权限系统-用户认证过滤器】校验返回result=" + result); return result; } |
执行通过业务系统fileter将,根据不同的请求进行不同的操作。获取业务系统Cookie后同构使用HttpClient类方法调用单点认证中的authcookie方法,验证用户Cookie的合法性。
RealSysLoginAction.java类authcookie方法代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | /** * 验证用户本地cookie值,如果通过验证则登录到业务系统总,如果没有通过验证, * 则跳转到单点登录系统认证页面。 * @return result */ public String authcookie(){ try{ String info = null; SysUser tmpUser = sysUserService.getSysUserById(getCookieName()); if( tmpUser == null){ response.setHeader("result", "failed"); info = LoggerUtil.getInfoMsg("未通过验证用户本地cookie值。"); }else{ response.setHeader("result", "success"); info = LoggerUtil.getInfoMsg("通过验证用户本地cookie值。"); } //释放资源 tmpUser = null; //打印日志 log.info(info); return null; }catch(Exception ex){ this.exceptionMessage.setError(ex.toString()); this.exceptionMessage.setClassName(this.getClass().getName()); this.exceptionMessage.setMessage("登录业务系统出现异常!"); log.error(ex.toString()); return ERROR; } } |
此种登录方法要修改原有业务系统中的登录action方法。
业务系统LoginSSOAction.java类,loginSSO登录方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | public String loginSSO(){ try { //获取认证系统传过来的用户名密码 String sysUserCode = ActionContext.getContext().getSession().get("sso").toString(); log.info("【权限系统】获取用户登录信息:[sysUserCode="+sysUserCode+"]"); SysUser sysUser_ = this.loginService.findByPk(sysUserCode); //判断输入用户名是否正确 if (sysUser_ != null) { //判断输入密码是否正确 // - 查找用户岗位集合 StringBuilder hql = new StringBuilder(300); hql.append("SELECT sg.sysRole FROM SysUserGroup su, SysGroupRole sg WHERE su.id.userCode = '").append(sysUserCode) .append("' AND su.id.groupCode = sg.id.groupCode AND sg.sysRole.roleType = '").append(AppConst.ROLE_TYPE_01).append("'"); List<SysRole> rolesList = this.sysRoleService.findSysRoleList(hql.toString()); // - 将功能性的岗位添加至集合,并排除不同岗位中的相同功能 // - 查找用户所有功能集合 Map<String, SysRole> funcPowersMap = new HashMap<String, SysRole>(); for (SysRole role : rolesList) { funcPowersMap.put(role.getRoleCode(), role); } // - 组织UserSession UserSession userSession = new UserSession(); userSession.setUserCode(sysUserCode); userSession.setUserName(sysUser_.getUserName()); userSession.setComCode(sysUser_.getSysCompany().getComCode()); userSession.setComCname(sysUser_.getSysCompany().getComCname()); userSession.setAreaCode(sysUser_.getSysCompany().getSysArea().getAreaCode()); userSession.setComType(sysUser_.getSysCompany().getComType()); userSession.setComLevel(sysUser_.getSysCompany().getComLevel()); userSession.setFuncPowers(funcPowersMap); //用户信息放入session ActionContext.getContext().getSession().put(AppConst.USER_SESSION_ID, userSession); //释放map引用 funcPowersMap = null; //打印日志 StringBuffer infoBuffer = new StringBuffer(); infoBuffer.append("【用户登录】 机构代码: "); infoBuffer.append(userSession.getComCode()); infoBuffer.append(" 用户代码: "); infoBuffer.append(userSession.getUserCode()); infoBuffer.append(" ("); infoBuffer.append(userSession.getUserName()); infoBuffer.append(") 登录时间: "); infoBuffer.append(new DateTime(DateTime.current(), DateTime.YEAR_TO_SECOND)); infoBuffer.append(" 登录成功!"); log.info(infoBuffer.toString()); return SUCCESS; } else { this.exceptionMessage.setClassName(this.getClass().getName()); this.exceptionMessage.setMessage("密码不正确!"); log.info("密码不正确!"); return ERROR; } } catch (Exception e) { //返回异常信息 this.exceptionMessage.setError(e.toString()); this.exceptionMessage.setClassName(this.getClass().getName()); this.exceptionMessage.setMessage("用户登录出现异常!"); log.info(e.toString()); return ERROR; } } |
业务系统总filter 在web.xml文件中配置方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <!-- 实现单点登录Fileter start--> <filter> <filter-name>SSOFilter</filter-name> <filter-class>SSO.SSOFilter</filter-class> <init-param> <param-name>cookieName</param-name> <param-value>hzpCookies</param-value> </init-param> <init-param> <param-name>ssoServiceURL</param-name> <param-value>http://192.168.61.124:8080/SSOAuth_v1.0.0/</param-value> </init-param> <init-param> <param-name>ssoLoginPage</param-name> <param-value>http://192.168.61.124:8080/SSOAuth_v1.0.0/login.jsp</param-value> </init-param> </filter> <filter-mapping> <filter-name>SSOFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 实现单点登录Fileter end--> |
未通过认证,登录业务系统,跳转到单点登录认证。
1、 输入业务系统服务地址
2、 系统自动跳转到单点登录服务器,下边过程同上“直接登录单点登录认证系统流程”
其原理是通过filter实现类中dofilter方法进行过滤,判断用没有Cookie,则进行跳转到单点登录认证界面。
过滤代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | // 检查HTTP请求的head是否有需要的cookie Cookie[] diskCookies = request.getCookies(); if (diskCookies != null) { log.info("【权限系统-用户认证过滤器】找到COOKIES"); for (int i = 0; i < diskCookies.length; i++) { cookieValue = diskCookies[i].getValue(); log.info("【权限系统-用户认证过滤器】COOKIES中找到用户标识:" + diskCookies[i].getName() + "=" + cookieValue); if (SSO_AUTH.equals(diskCookies[i].getName())) { result = SSOService(cookieValue); if ("success".equals(result)) { log.info("【通过单点登录服务器认证】"); } else { log.info("【未通过单点登录服务器认证】COOKIES认证失败"); response.sendRedirect(ssoLoginPage); } } else if ("sso".equals(diskCookies[i].getName())) { HttpSession session = request.getSession(); session.setAttribute("sso", cookieValue); result = "success"; } } } else { log.info("【权限系统-用户认证过滤器】未找到COOKIES"); } // COOKIES效验,转到单点登录认证页面 if (result.equals("failed")) { log.info("【权限系统-用户认证过滤器】COOKIES认证失败"); response.sendRedirect(ssoLoginPage); // 效验成功并退出服务 } else if (result.equals("logout")) { log.info("【权限系统-用户认证过滤器】退出登录"); //logoutService(cookieValue); response.sendRedirect(ssoLoginPage); // 效验成功 } else { log.info("【权限系统-用户认证过滤器】认证成功"); Throwable problem = null; try { chain.doFilter(req, res); } catch (Throwable t) { problem = t; t.printStackTrace(); } if (problem != null) { if (problem instanceof ServletException) { throw ((ServletException) problem); } if (problem instanceof IOException) { throw ((IOException) problem); } sendProcessingError(problem, res); } } |