单点登陆

作者: 小吴可是全村的希望 | 来源:发表于2018-05-17 11:10 被阅读0次

单系统登陆

普通的单个系统登陆流程是什么样子的呢?
用户访问系统,如果访问的是受限制的资源,比如http://localhost:8080/orderList,请求经过拦截器处理:

public class LoginInterceptor extends HandlerInterceptorAdapter{
     @Override 
     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            // 取登录标记
            HttpSession session = request.getSession();
            Object key = session.getAttribute("session-user-info");
            
            // 取请求路径,并将路径中的context path去除
            String uri = request.getRequestURI().replace(request.getContextPath(),"");
            
            //如果是登陆方法,直接不拦截
            if(uri.matches("/login") || uri.matches("/userLogin")){
                return true;
                
            }
            // 如果用户没有登录或者session过期,则转到登录页面
            if (session == null || key == null || key.toString().trim().length() == 0 || uri.matches("/")) {
                response.sendRedirect(request.getContextPath() + "/login");
                return false;
            }
            return true;
    }
}

对orderList这个uri的处理是直接跳转到登陆页面,在登陆页面用户输入用户名密码等信息后请求:http://localhost:8080/userLogin,拦截器判断是登陆请求,return true,交给下一个拦截器或者其他处理器处理,最终到达LoginController:

// 验证签名信息,验证时间戳信息,验证用户名是否存在,验证密码是否正确代码省略......
// 当验证用户名密码正确后,设置session
HttpSession session = request.getSession();
session.setMaxInactiveInterval(60 * 60 * 10);//单位为秒,此处设最长时间为1年
request.getSession().setAttribute("user-info", user);

请求返回客户端浏览器的时候,会带上一个jsessionid放在客户端的cookie中,相当于session的一个唯一标示,下次再次请求服务端的时候,会把这个jsessionid一起带上。到这里一个简单的登陆流程就完成了。

单系统登陆存在的问题

就以我所处的公司为例,公司有自己的home系统,固资系统,运营系统,CRM系统,OA系统等等,都属于公司内部系统,如此多系统,一个一个去登陆,注销非常麻烦,这时候我们希望登陆其中一个系统(比如使用公司工号登陆)不需要再次登陆其他系统也可以访问这些系统。这时候我们单系统登陆就不再适合这种应用场景了。

<font color='red'>问题1:cookie跨域问题</font>
单系统登陆其中一个核心是cookie,cookie携带会话唯一id(上文提到的jsession)维持会话状态,多个系统使用不同的域名,那么多个系统生成的jsessionid是不会在同一个域的cookie下,浏览器发送请求的时候,只能带上当前域对应的cookie里面的jsessionid

<font color='red'>问题2:不同应用服务器</font>
如果将多个应用放在同一个域下,不就能解决问题1了吗?但是,这就需要这些放在同一个域下面的应用使用的是同一种技术,同一个web服务器,否则,放在cookie中的key值可能就不是叫JSESSIONID了。同时cookie本身的安全性也不高

为了解决以上这些问题,单点登录SSO出现啦~~

SSO原理

应用系统不提供登陆验证,认证中心来对每一个应用系统进行登陆验证,验证成功,即创建一个授权令牌给个子系统,子系统拿到令牌后创建局部会话,局部会话的登陆方式同单系统的登陆方式。流程如下:

1. 用户访问系统1的受保护资源,系统1发现用户未登录,跳转至sso认证中心,并将自己的地址作为参数
2. sso认证中心发现用户未登录,将用户引导至登录页面
3. 用户输入用户名密码提交登录申请
4. sso认证中心校验用户信息,创建用户与sso认证中心之间的会话,称为全局会话,同时创建授权令牌
5. sso认证中心带着令牌跳转会最初的请求地址(系统1)
6. 系统1拿到令牌,去sso认证中心校验令牌是否有效
7. sso认证中心校验令牌,返回有效,注册系统1
8. 系统1使用该令牌创建与用户的会话,称为局部会话,返回受保护资源
10. 系统2发现用户未登录,跳转至sso认证中心,并将自己的地址作为参数
11. sso认证中心发现用户已登录,跳转回系统2的地址,并附上令牌
12. 系统2拿到令牌,去sso认证中心校验令牌是否有效
13. sso认证中心校验令牌,返回有效,注册系统2
14. 系统2使用该令牌创建与用户的局部会话,返回受保护资源

系统部署方式:
每一个子系统集群部署,并且我们会实现一个sso-client.jar,这个jar处理和sso相关的逻辑,在每一个子系统中引入这个sso-jar,认证中心单独部署为sso-server.war

伪代码(sso-client和sso-server之间的通信使用HttpClient):

<font color='red'>1. sso-client拦截未登录请求</font>
用户请求子系统的时候,我们需要对用户的请求做拦截,java中拦截用户请求有多种方式,servlet,filter,listener都可以,这里我们选用filter过滤器:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;
        HttpSession session = req.getSession();
        if (session.getAttribute("isLogin")) {
            chain.doFilter(request, response);
            return;
        }
        //跳转至sso认证中心
        res.sendRedirect("sso-server-url-with-system-url");
    }

以上代码中的session就是子系统和用户之间的局部会话,如果局部会话是存在的,那么就通过过滤器,进行下一个过滤或者拦截直至访问受限资源。局部会话不存在的话,那么就需要跳转到sso认证中心了。

<font color='red'>2. sso-server拦截未登录请求</font>
sso-server拦截方式和sso-client基本一致,如果拦截到用户没有和sso-server创建令牌,那么跳转到登陆页面

<font color='red'>3. sso-server验证用户登录信息并创建授权令牌</font>

@RequestMapping("/login")
public String login(String username, String password, HttpServletRequest req) {
    this.checkLoginInfo(username, password);
    req.getSession().setAttribute("isLogin", true); // 这里的session是全局会话
    return "success";
}

sso-server创建令牌:

String token = UUID.randomUUID().toString(); // 使用redis来创建也可以,只要不重复,不容易伪造就行了
reids.hmapset(token, "子系统注册地址list集合");
// 将token作为key,子系统的注册地址作为集合存储在redis中,后续注销的时候就知道要注销哪些子系统的session了

<font color='red'>4. sso-client取得令牌并校验</font>
在sso-client的filter中加入代码来获取sso-server返回的token并验证这个token:

// 请求附带token参数
String token = req.getParameter("token");
if (token != null) {
    // 去sso认证中心校验token
    boolean verifyResult = this.verify("sso-server-verify-url", token);
    if (!verifyResult) {
        res.sendRedirect("sso-server-url");
        return;
    }
    chain.doFilter(request, response);
}

<font color='red'>5. sso-server接收并处理校验令牌请求</font>
sso-server拿到子系统的校验请求,验证token是否存在,是否过期,如果验证成功,将token和当前验证请求一起放入redis中

jedis.lpush(token, "验证请求list");
// 将验证请求和token绑定存到redis中的目的是为了后面注销系统的时候知道要注销哪些系统

到这里,SSO的登陆原理就差不多ok了

相关文章

  • 单点登陆SSO(Single Sign On)

    Q:什么是单点登陆 在多系统应用群众登陆一个系统,便可在其他所有系统中得到授权而无需再次登陆,包括单点登陆和单点注...

  • Session和EL表达式实现登陆验证

    现在多系统的登陆都采用单点登陆了,emmmm......后期再更单点登陆的,这次由于只是个小demo,所以我们采用...

  • cas 单点登录

    CAS 单点登录简介cas单点登陆。就这一篇就够了!!!!!

  • 网络安全之反序列化漏洞复现

    0x01 Apereo cas简介 Apereo CAS 单点登陆系统是Java服务器环境下使用较为广泛的单点登陆...

  • 单点登陆

    单系统登陆 普通的单个系统登陆流程是什么样子的呢?用户访问系统,如果访问的是受限制的资源,比如http://loc...

  • 单点登陆

    一、前言 什么是SSO? SSO英文全称Single Sign On,单点登录。SSO是在多个应用系统中,用户只需...

  • 单点登陆

    1.SSO搭建(框架KISSO) 2.CAS实现单点登录SSO执行原理探究(终于明白了)

  • 单点登录

    单点登录 1.1 什么是单点登陆 [图片上传失败...(image-1c8250-1575374040883)] ...

  • 单点登录的原理

    一、单点登录的概念 1、什么是单点登陆 单点登录(Single Sign On),简称为 SSO,是目前比较流行的...

  • 跨域单点登陆的一种实现

    单点登陆需求在现代企业系统中是非常常见的一个需求,无论是ToC的系统,还是ToB的系统。 单点登陆(SSO,Si...

网友评论

    本文标题:单点登陆

    本文链接:https://www.haomeiwen.com/subject/leqbdftx.html