我的单点登录和退出思路

参考:

一、单点登录思路

  思路:①判断业务系统是否登录,如果已登录就直接返回内容,否则判断认证中心是否登录过。

     ②如果认证中心已登录告诉业务系统创建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")+"&timestamp="+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监听销毁事件失效

三、实现单点登录和单点退出的完整流程图



原文地址:https://www.cnblogs.com/tonghaolang/p/15194371.html