SSO single sign-on implementation process summary record

Preface

    In the early days, when the scale of the project was small, there were not many systems in the enterprise, usually one or two. Each system has its own independent login module, so that it is not particularly troublesome for users to log in, just log in separately. However, as the scale of the enterprise continues to grow, more and more system modules follow, and each module has its own independent login, then users will have many login accounts, and they must enter the system separately. Landing, otherwise it is the most painful.
    So can it be possible to log in to one system without having to log in to other systems? Just like I clicked on the link to enter Tmall Mall after logging in on Taobao, I don’t have to log in again. There are solutions, and that is to use single sign-on solutions. With the plan, different implementation technologies will appear. The following is an open source xxl-sso framework written by Xu Xueli.
    The full English name of single sign-on is Single Sign On, or SSO for short. Its explanation is: in multiple application systems, only need to complete the login in one system, you can access other mutually trusted application systems.
    Previously, each system had a separate login authentication module. Now the login function is unified by SSO for login authentication, and other application modules have no login function.
Insert picture description here

    Xxl-sso is a distributed single sign-on framework. You only need to log in once to access all mutually trusted application systems. Let’s first introduce its features:

简洁:API直观简洁,可快速上手
轻量级:环境依赖小,部署与接入成本较低
单点登录:只需要登录一次就可以访问所有相互信任的应用系统
分布式:接入SSO认证中心的应用,支持分布式部署
HA:Server端与Client端,均支持集群部署,提高系统可用性
跨域:支持跨域应用接入SSO认证中心
Cookie+Token均支持:支持基于Cookie和基于Token两种接入方式,并均提供Sample项目
Web+APP均支持:支持Web和APP接入
实时性:系统登陆、注销状态,全部Server与Client端实时共享
CS结构:基于CS结构,包括Server”认证中心”与Client”受保护应用”
记住密码:未记住密码时,关闭浏览器则登录态失效;记住密码时,支持登录态自动延期,在自定义延期时间的基础上,原则上可以无限延期
路径排除:支持自定义多个排除路径,支持Ant表达式,用于排除SSO客户端不需要过滤的路径

xxl-sso frame structure

Project download address: https://github.com/xuxueli/xxl-sso

  • Contains three modules

Insert picture description here

xxl-sso-server:中央认证服务,支持集群
xxl-sso-core:Client端依赖,主要作用为路径的排除,哪些路径不需要登陆就可以访问的、登陆时token的认证检查等
xxl-sso-samples:单点登陆Client端接入示例项目
    xxl-sso-web-sample-springboot:基于Cookie接入方式,供用户浏览器访问,springboot版本
    xxl-sso-token-sample-springboot:基于Token接入方式,常用于无法使用Cookie的场景使用,如APP、Cookie被禁用等,springboot版本
  • Environment
    JDK: 1.7+
    Redis: 4.0+
  • Architecture diagram
    Insert picture description here
  • Related configuration file introduction
  1. xxl-sso-server certification center (SSO Server)
    configuration file location: application.properties
### xxl-sso
redis 地址: 如 "{ip}"、"{ip}:{port}"、"{redis/rediss}://xxl-sso:{password}@{ip}:{port:6379}/{db}";多地址逗号分隔
xxl.sso.redis.address=redis://127.0.0.1:6379
登录态有效期窗口,默认24H,当登录态有效期窗口过半时,自动顺延一个周期
xxl.sso.redis.expire.minite=1440

Password configuration: You can view the ShardedJedis example code in the JedisUtil class under com.xxl.sso.core.util
Insert picture description here
2. xxl-sso-web-sample-springboot (application client)
introduces the xxl-sso-core core dependency package

<dependency>
    <groupId>com.xuxueli</groupId>
    <artifactId>xxl-sso-core</artifactId>
    <version>${最新稳定版}</version>
</dependency>

Configure xxlSsoFilter
Insert picture description here
configuration file location: application.properties

### xxl-sso
### Sso server认证中心地址
xxl.sso.server=http://xxlssoserver.com:8080/xxl-sso-server
### 注销登陆path
xxl.sso.logout.path=/logout
### 路径排除path,允许设置多个,且支持Ant表达式。用于排除SSO客户端不需要过滤的路径
xxl-sso.excluded.paths=
// redis address, like "{ip}"、"{ip}:{port}"、"{redis/rediss}://xxl-sso:{password}@{ip}:{port:6379}/{db}";Multiple "," separated
xxl.sso.redis.address=redis://127.0.0.1:6379
  • Modify the hosts file to simulate the real environment
127.0.0.1 xxlssoserver.com
127.0.0.1 xxlssoclient1.com
127.0.0.1 xxlssoclient2.com

If the above content is added to the hosts file, it will not take effect. Solution:
Open the command line window:
    ipconfig /displaydns Check whether the configured dns exists
    ipconfig /flushdns refresh the dns configuration

  • Project startup
    Run the "xxl-sso-server" and "xxl-sso-web-sample-springboot" projects respectively

Corresponding access test address

1、SSO认证中心地址:
http://xxlssoserver.com:8080/xxl-sso-server
2、Client1应用地址:
http://xxlssoclient1.com:8081/xxl-sso-token-sample-springboot/
3、Client2应用地址:
http://xxlssoclient2.com:8081/xxl-sso-token-sample-springboot/
  • Source code tracking
  1. Access to the Client1 application address will be intercepted by XxlSsoWebFilter. XxlSsoWebFilter exists in the core dependency package, and the configuration in the application is as follows:
    Insert picture description here
  2. In the XxlSsoWebFilter filter, the init initialization operation is performed first, and the initialization content is the relevant information configured in the Client1 application. Then take the doFilter method to realize the login check.
    Insert picture description here
  3. The specific code of the doFilter method is as follows:
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    
    
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;

        // (1) 请求路径
        String servletPath = req.getServletPath();

        //(2) 路径排除
        if (excludedPaths!=null && excludedPaths.trim().length()>0) {
    
    
            for (String excludedPath:excludedPaths.split(",")) {
    
    
                String uriPattern = excludedPath.trim();

                // 支持ANT表达式
                if (antPathMatcher.match(uriPattern, servletPath)) {
    
    
                  
                    chain.doFilter(request, response);
                    return;
                }

            }
        }

        // (3) 登出
        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;
        }

        // (4) 登陆检查
        XxlSsoUser xxlUser = SsoWebLoginHelper.loginCheck(req, res);

        // (5) 用户信息不存在时
        if (xxlUser == null) {
    
    

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

                //(6) json消息
                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 {
    
    

                // (7) 访问源地址 http://xxlssoclient1.com:8081/xxl-sso-web-sample-springboot/
                String link = req.getRequestURL().toString();

                // (8) 重定向地址 http://xxlssoserver.com:8080/xxl-sso-server/login?redirect_url=http://xxlssoclient1.com:8081/xxl-sso-web-sample-springboot/
                String loginPageUrl = ssoServer.concat(Conf.SSO_LOGIN)
                        + "?" + Conf.REDIRECT_URL + "=" + link;

                res.sendRedirect(loginPageUrl);
                return;
            }

        }

        // ser sso user
        request.setAttribute(Conf.SSO_USER, xxlUser);


        // (9) 过滤器放行
        chain.doFilter(request, response);
        return;
    }

Numbering notes (1-9):
(1)-(3): Mainly do path elimination and user logout operations.
(4): Login user information check (key part), code tracking will be carried out below.
(5): If there is no user information processing.
(6-9): Relevant information notes, which will be used below.

  1. Next, let's look at the specific processing logic of number (4).
    Calling the loginCheck method in SsoWebLoginHelper
    Insert picture description here
    will also call the lgoinCheck method in SsoTokenLoginHelper. The code is as follows:
public static XxlSsoUser loginCheck(String  sessionId){
    
    

        // 解析key sessionId生成规则 userId+"_"+version
        String storeKey = SsoSessionIdHelper.parseStoreKey(sessionId);
        if (storeKey == null) {
    
    
            return null;
        }
		//根据key从redis中获取用户信息
        XxlSsoUser xxlUser = SsoLoginStore.get(storeKey);
        if (xxlUser != null) {
    
    
            String version = SsoSessionIdHelper.parseVersion(sessionId);
            if (xxlUser.getVersion().equals(version)) {
    
    

                // 判断时间是否过半,如果是 自动刷新
                if ((System.currentTimeMillis() - xxlUser.getExpireFreshTime()) > xxlUser.getExpireMinite()/2) {
    
    
                    xxlUser.setExpireFreshTime(System.currentTimeMillis());
                    SsoLoginStore.put(storeKey, xxlUser);
                }

                return xxlUser;
            }
        }
        return null;
    }


    /**
     * login check
     *
     * @param request
     * @return
     */
    public static XxlSsoUser loginCheck(HttpServletRequest request){
    
    
        String headerSessionId = request.getHeader(Conf.SSO_SESSIONID);
        return loginCheck(headerSessionId);
    }
  1. If (4) is finished, XxlSsoUser is empty.
    Insert picture description here
    Determine whether it is a json request. At this time, the request is not json. Redirect to (8) SSO Server project for authentication:
    authentication address: http://xxlssoserver.com:8080/xxl-sso-server/login?redirect_url=http://xxlssoclient1:8081/xxl-sso-web-sample -springboot/
    Insert picture description here
    store the redirected address in the login hidden domain
    Insert picture description here
  2. Log in
    Click to log in, request /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))?true:false;

        // 查询用户逻辑
        ReturnT<UserInfo> 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 "redirect:/login";
        }

        // 模拟用户信息
        XxlSsoUser xxlUser = new XxlSsoUser();
        xxlUser.setUserid(String.valueOf(result.getData().getUserid()));
        xxlUser.setUsername(result.getData().getUsername());
        xxlUser.setVersion(UUID.randomUUID().toString().replaceAll("-", ""));
        xxlUser.setExpireMinite(SsoLoginStore.getRedisExpireMinite());
        xxlUser.setExpireFreshTime(System.currentTimeMillis());


        // 生成sessionId,生成规则:userId+"_"+version
        String sessionId = SsoSessionIdHelper.makeSessionId(xxlUser);

        // 存储到redis和cookie中
        SsoWebLoginHelper.login(response, sessionId, xxlUser, ifRem);

        // 获取隐藏域中存放的登陆成功之后的地址
        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:/";
        }

    }

Carry the sessionId to redirect to the source request resource address: http://xxlssoclient1.com:8081/xxl-sso-web-sample-springboot/, then it will go through the XxlSsoWebFilter filter,
take the doFilter method and
Insert picture description here
call the loginCheck method to check and verify, and write Enter cookie

Insert picture description here
Go here xxlssoclient1.com successfully logged in

  • Requesting the Client2 application address
    will also go through XxlSsoWebFilter and call the SsoWebLoginHelper.loginCheck method to verify user information. Finally, it will be redirected to Sso Server.
    How does SSO Server know that client2 does not need to log in? Look at the code in the SSO Server project
 @RequestMapping(Conf.SSO_LOGIN)
    public String login(Model model, HttpServletRequest request, HttpServletResponse response) {
    
    

        // 做登陆检查,此时获取cookieSessionid是从xxlssoserver.com域名下获取的,一定存在用户信息,此前在client1系统中已经登陆过了。
        XxlSsoUser xxlUser = SsoWebLoginHelper.loginCheck(request, response);

        if (xxlUser != null) {
    
    

            // success redirect
            String redirectUrl = request.getParameter(Conf.REDIRECT_URL);
            if (redirectUrl!=null && redirectUrl.trim().length()>0) {
    
    

                String sessionId = SsoWebLoginHelper.getSessionIdByCookie(request);
                String redirectUrlFinal = redirectUrl + "?" + Conf.SSO_SESSIONID + "=" + sessionId;;
                // 重定向到client2应用地址,携带sessionId
                return "redirect:" + redirectUrlFinal;
            } else {
    
    
                return "redirect:/";
            }
        }

        model.addAttribute("errorMsg", request.getParameter("errorMsg"));
        model.addAttribute(Conf.REDIRECT_URL, request.getParameter(Conf.REDIRECT_URL));
        return "login";
    }

The redirection method is the same as that of client1, it will go through the XxlSsoWebFilter filter,
take the doFilter method,
Insert picture description here
call the loginCheck method to check and verify, and write the cookie to
Insert picture description here
this point. After the login is completed in the Client1 system, the Client2 system is accessed and the login operation is no longer performed.

Guess you like

Origin blog.csdn.net/qq_37640410/article/details/108399649