我的单点登录和退出思路
参考:
- 【实现】单点登录原理与简单实现 以及单点登录的三种实现方式
- 【解决问题】Java根据Session Id获取Session对象
- 【解决问题】解决session共享后,session监听销毁事件失效
- 【解决问题】使用spring-session 根据sessionId 删除session
- 【完整客户端代码mysso-cli】https://gitee.com/tongyongliang/mysso-cli.git
- 【完整服务代码mysso】https://gitee.com/tongyongliang/mysso.git
一、单点登录思路
思路:①判断业务系统是否登录,如果已登录就直接返回内容,否则判断认证中心是否登录过。
②如果认证中心已登录告诉业务系统创建session再返回受限内容,否则返回到登录界面。
1. 如何判断业务系统登录过呢?
思路:业务系统判断是否存在session
实现代码及解释:此处session中的SSO_CLIENT_ISLOGINED属性是登录后设置的值,如果没登录就不会有这个属性。
//判断是否登录 private boolean isLogined(HttpServletRequest request){ String SSO_CLIENT_ISLOGINED = (String) request.getSession().getAttribute("SSO_CLIENT_ISLOGINED"); if("true".equals(SSO_CLIENT_ISLOGINED)) return true; return false; }
2. 如何判断认证中心登录过呢?
思路:在业务系统没有登录的情况下,此时业务系统没有任何数据能够验证用户在认证中心登陆过,但是浏览器有登录认证中心的cookie可以证明登录过认证中心;因此让浏览器带着cookie去访问认证中心去判断是否登录比较合适。
实现代码及解释:如果未登录重定向至认证中心/validateLogin.do地址,认证中心收到该请求判断是否登录,未登录重定向到登录地址。
//业务系统的代码 //4.未登录重定向至向认证中心,认证中心验证是否登录 else if(!isLogined(req)){ String validateUrl = ssoHost+VALIDATE_LOGIN_URL+"?requrl="+ java.net.URLEncoder.encode(requestURL, "utf-8")+"&redirecturl="+URLEncoder.encode(loginUrl, "utf-8"); res.sendRedirect(validateUrl); logger.info("SsoFilter:未登录重定向至向认证中心,认证中心验证是否登录,requrl为用户请求的地址用于sso-server重定向(认证中心已登录),redirecturl为系统登录界面地址用于sso-server重定向(认证中心未登录登录)"); logger.info("SsoFilter:认证地址:"+validateUrl); }
3. 如果认证中心已登录,认证中心怎么告诉业务系统呢?
思路:认证中心给业务系统一个"取件码",5分钟之内赶紧到认证中心去取走你的"快递"(这里的快递指的是用户信息)
实现代码及解释:认证中心重定向至业务系统并携带"取件码",业务系统的过滤器发现有"取件码"便会向认证中心请求"快递"
/** * 认证中心发送取件码 * @Date 2021/04/27 16:26 * @Comment 验证是否登录 * browser client->sso server * 已登录:重定向至业务系统并附带token * 未登录:跳转至业务系统登录界面 */ @RequestMapping("/validateLogin") public void validateLogin(HttpSession session, HttpServletResponse response,String requrl,String redirecturl) throws IOException { User userSessionBean = null; if(session!=null){ userSessionBean = (User) session.getAttribute("userSessionBean"); } if(userSessionBean!=null){ String token = UUID.randomUUID().toString(); jedisUtil.set("sso-server:validateToken:"+token,userSessionBean.getUserid(),60); jedisUtil.set("sso-server:validateTokenSessionId:"+token,session.getId(),60); Map para = new HashMap();para.put("ssotoken",token); RedirectUtils.sendRedirect(response,requrl,para); }else{ Map para = new HashMap();para.put("requrl",requrl); RedirectUtils.sendRedirect(response,redirecturl,para); } return; }
//3.有待验证token else if(hasSsoToken(req)){ //有sso-server重定向过来的token 进行验证,验证通过执行登录 logger.info("SsoFilter:有sso-server重定向过来的token,进行验证,验证通过执行登录"); boolean isValid = this.checkToken(req,res); if(isValid){ logger.info("SsoFilter:sso-server验证通过"); filterChain.doFilter(servletRequest, servletResponse); }else{ logger.info("SsoFilter:sso-server未验证通过,跳转至登录界面"); res.sendRedirect(loginUrl); } } private boolean hasSsoToken(HttpServletRequest request){ String ssotoken = request.getParameter("ssotoken"); if(ssotoken!=null&&!"".equals(ssotoken)) return true; return false; }
//校验token private boolean checkToken(HttpServletRequest request,HttpServletResponse response) throws UnsupportedEncodingException { String ssotoken = request.getParameter("ssotoken"); Map para = new HashMap<>(); para.put("ssotoken",ssotoken); String res = HttpUtils.sendPost(ssoHost+SsoConstants.VALIDATE_TOKEN_URL+"?ssotoken="+ssotoken+"&remoteurl="+ java.net.URLEncoder.encode(request.getRequestURL().toString(),"UTF-8")+"×tamp="+System.currentTimeMillis(),new HashMap()); if(res!=null&&!"".equals(res)){ JSONObject json = JSONObject.parseObject(res); String status = (String) json.get("status"); String userid = (String) json.get("userid"); if("success".equals(status)&&userid!=null&&!"".equals(userid)){//认证中心返回token有效,业务系统执行创建session try { String sessionid = loginRunService.execute(request,response,userid); if(sessionid!=null&&!"".equals(sessionid)){ tokenSessionidMap.put(ssotoken,sessionid);//用户退出登录使用 sessionidTokenMap.put(sessionid,ssotoken);//用户退出登录使用 request.getSession(true).setAttribute("SSO_CLIENT_ISLOGINED","true"); return true; } } catch (ServletException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } return false; }
二、单点退出思路
思路:认证中心退出登录时所有的业务系统都要退出登录,因此在认证中心的session过期和删除时向"已单点登录成功过的系统"发送退出登录请求较为合适
注意实际业务中退出登录有以下几种情况
①业务系统退出登录时向认证中心发送退出登录请求;
②用户请求认证中心主动退出登录
③认证中心session过期时
1.如何监听session过期删除操作?
参考:解决session共享后,session监听销毁事件失效
三、实现单点登录和单点退出的完整流程图