【开源项目】单点登录框架XXL-SSO源码解析

单点登录框架XXL-SSO源码解析

项目介绍

XXL-SSO 是一个分布式单点登录框架。只需要登录一次就可以访问所有相互信任的应用系统。 拥有"轻量级、分布式、跨域、Cookie+Token均支持、Web+APP均支持"等特性。现已开放源代码,开箱即用。

项目地址

https://gitee.com/xuxueli0323/xxl-sso

源码解析

  1. 访问SSO-Client,http://localhost:8081//xxl-sso-web-sample-springboot,会重定向到SSO-Server的登录页面。访问客户端需要经过XxlSsoWebFilter,第一次访问获取到的xxlUser信息为空,会跳转到SSO-Server的登录页面。
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;
		//...

        // valid login user, cookie + redirect
        XxlSsoUser xxlUser = SsoWebLoginHelper.loginCheck(req, res);

        // valid login fail
        if (xxlUser == null) {

            String header = req.getHeader("content-type");
            boolean isJson=  header!=null && header.contains("json");
            if (isJson) {

                // json msg
                res.setContentType("application/json;charset=utf-8");
                res.getWriter().println("{\"code\":"+Conf.SSO_LOGIN_FAIL_RESULT.getCode()+", \"msg\":\""+ Conf.SSO_LOGIN_FAIL_RESULT.getMsg() +"\"}");
                return;
            } else {

                // total link
                String link = req.getRequestURL().toString();

                // redirect logout
                String loginPageUrl = ssoServer.concat(Conf.SSO_LOGIN)
                        + "?" + Conf.REDIRECT_URL + "=" + link;

                res.sendRedirect(loginPageUrl);
                return;
            }

        }
		//...
    }

SsoWebLoginHelper.loginCheck(req, res);,从cookie中获取数据,获取不到用户信息;再根据参数获取paramSessionId,也是获取不到的,所以第一次获取到的XxlSsoUser为空。

    public static XxlSsoUser loginCheck(HttpServletRequest request, HttpServletResponse response){
		
        String cookieSessionId = CookieUtil.getValue(request, Conf.SSO_SESSIONID);

        // cookie user
        XxlSsoUser xxlUser = SsoTokenLoginHelper.loginCheck(cookieSessionId);
        if (xxlUser != null) {
            return xxlUser;
        }

        // redirect user

        // remove old cookie
        SsoWebLoginHelper.removeSessionIdByCookie(request, response);

        // set new cookie
        String paramSessionId = request.getParameter(Conf.SSO_SESSIONID);
        xxlUser = SsoTokenLoginHelper.loginCheck(paramSessionId);
        if (xxlUser != null) {
            CookieUtil.set(response, Conf.SSO_SESSIONID, paramSessionId, false);    // expire when browser close (client cookie)
            return xxlUser;
        }

        return null;
    }
  1. 重定向到授权服务器,http://xxlssoserver.com:8080/xxl-sso-server/login?redirect_url=http://localhost:8081/xxl-sso-web-sample-springboot/,进行服务端登录,WebController#doLogin
    @RequestMapping(doLogin)
    public String doLogin(HttpServletRequest request,
                        HttpServletResponse response,
                        RedirectAttributes redirectAttributes,
                        String username,
                        String password,
                        String ifRemember) {

        boolean ifRem = (ifRemember!=null&&on.equals(ifRemember))truefalse;

         valid login
        ReturnTUserInfo result = userService.findUser(username, password);
        if (result.getCode() != ReturnT.SUCCESS_CODE) {
            redirectAttributes.addAttribute(errorMsg, result.getMsg());

            redirectAttributes.addAttribute(Conf.REDIRECT_URL, request.getParameter(Conf.REDIRECT_URL));
            return redirectlogin;
        }

         1、make xxl-sso user
        XxlSsoUser xxlUser = new XxlSsoUser();
        xxlUser.setUserid(String.valueOf(result.getData().getUserid()));
        xxlUser.setUsername(result.getData().getUsername());
        xxlUser.setVersion(UUID.randomUUID().toString().replaceAll(-, ));
        xxlUser.setExpireMinute(SsoLoginStore.getRedisExpireMinute());
        xxlUser.setExpireFreshTime(System.currentTimeMillis());


         2、make session id
        String sessionId = SsoSessionIdHelper.makeSessionId(xxlUser);

         3、login, store storeKey + cookie sessionId
        SsoWebLoginHelper.login(response, sessionId, xxlUser, ifRem);

         4return, redirect sessionId
        String redirectUrl = request.getParameter(Conf.REDIRECT_URL);
        if (redirectUrl!=null && redirectUrl.trim().length()0) {
            String redirectUrlFinal = redirectUrl +  + Conf.SSO_SESSIONID + = + sessionId;
            return redirect + redirectUrlFinal;
        } else {
            return redirect;
        }

    }

主要生成了sessionId,String sessionId = xxlSsoUser.getUserid().concat("_").concat(xxlSsoUser.getVersion());

SsoWebLoginHelper.login(response, sessionId, xxlUser, ifRem);主要做了保存用户数据到redis中,存放cookie。Cookie保存在private static final String COOKIE_PATH = "/";,保证了所有域名都可以获取到cookie。

    public static void login(HttpServletResponse response,
                             String sessionId,
                             XxlSsoUser xxlUser,
                             boolean ifRemember) {

        String storeKey = SsoSessionIdHelper.parseStoreKey(sessionId);
        if (storeKey == null) {
            throw new RuntimeException("parseStoreKey Fail, sessionId:" + sessionId);
        }

        SsoLoginStore.put(storeKey, xxlUser);
        CookieUtil.set(response, Conf.SSO_SESSIONID, sessionId, ifRemember);
    }

最后跳转到SSO-Client,且带有参数sessionId。

  1. 当再次访问SSO-Client,cookie和参数中都有sessionId。根据sessionId从Redis中获取对象XxlSsoUser。
    【开源项目】单点登录框架XXL-SSO源码解析_第1张图片

  2. 用户退出。会清空cookie,跳转到SSO-Server的退出页面。

        if (logoutPath!=null
                && logoutPath.trim().length()>0
                && logoutPath.equals(servletPath)) {

            // remove cookie
            SsoWebLoginHelper.removeSessionIdByCookie(req, res);

            // redirect logout
            String logoutPageUrl = ssoServer.concat(Conf.SSO_LOGOUT);
            res.sendRedirect(logoutPageUrl);

            return;
        }

SSO-Server的退出接口,核心逻辑就是清除cookie和redis缓存数据。

    @RequestMapping(Conf.SSO_LOGOUT)
    public String logout(HttpServletRequest request, HttpServletResponse response, RedirectAttributes redirectAttributes) {

        // logout
        SsoWebLoginHelper.logout(request, response);

        redirectAttributes.addAttribute(Conf.REDIRECT_URL, request.getParameter(Conf.REDIRECT_URL));
        return "redirect:/login";
    }

项目总结

  1. 客户端的校验是根据XxlSsoWebFilter,如果cookie和参数中都没有,则需要重定向到SSO-Server。当SSO-Server登录成功之后,cookie和参数都有sessionId,且redis缓存中也存在xxlSsoUser,则校验通过。
  2. 其他的客户端的登录是因为也有XxlSsoWebFilter拦截,会通过cookie来获取用户id,然后从redis中获取用户信息,验证通过。

你可能感兴趣的:(讲解开源项目,源码解析,开源,java,前端)