【分布式登录 002】单点登录SSO

一、前言

单点登录定义:英文名全称 Single Sign On(简称SSO),译为单点登录,

二、分布式架构导致了单点登录

黄金一句:分布式架构导致了单点登录,单点登录就是分布式登录,就是分布式系统中,一个系统登录,其他所有系统共享登录状态。

单体架构中,所有的功能都在同一个系统上。
在这里插入图片描述
后来,我们为了合理利用资源和降低耦合性,于是把单系统拆分成多个子系统,形成了分布式架构。
在这里插入图片描述
实践:阿里系的淘宝和天猫,很明显地我们可以知道这是两个系统,但是你在使用的时候,登录了天猫,淘宝也会自动登录。所以,单点登录就是在多个系统中,用户只需一次登录,各个系统即可感知该用户已经登录

三、单体架构的登录

3.1 单体架构登录理论

单体架构登录理论
众所周知,HTTP是无状态的协议,这意味着服务器无法确认用户的信息。于是乎,W3C就提出了:给每一个用户都发一个通行证,无论谁访问的时候都需要携带通行证,这样服务器就可以从通行证上确认用户的信息。通行证就是Cookie。
如果说Cookie是检查用户身上的”通行证“来确认用户的身份,那么Session就是通过检查服务器上的”客户明细表“来确认用户的身份的。Session相当于在服务器中建立了一份“客户明细表”。
HTTP协议是无状态的,Session不能依据HTTP连接来判断是否为同一个用户。于是乎:服务器向用户浏览器发送了一个名为JESSIONID的Cookie,它的值是Session的id值。其实Session是依据Cookie来识别是否是同一个用户。金手指:Session是存放在Cookie里面的,当然也可以存放在URL里面

3.2 单体架构登录实现思路(三个函数)

单体架构登录实现思路
第一,实现登录功能:服务端中,将用户信息保存在Session对象中,
如果在Session对象中能查到,说明已经登录
如果在Session对象中查不到,说明没登录(或者已经退出了登录)
第二,实现注销功能(退出登录):从Session中删除用户的信息
第三,实现记住我功能(关闭掉浏览器后,重新打开浏览器还能保持登录状态):配合Cookie来用(Session默认是)

金手指:解释三功能(登录、注销、记住我),记住cookie和session重要的点
登录:因为第一次访问,登陆的时候,服务器已经将Cookie发送给浏览器,第二次访问,浏览器就直接带着Cookie到服务器了,所以不用手动登陆了(但是第一次没有存放用户token的Cookie的,必须手动登陆)。
注销:删除服务端内存中的session和浏览器/磁盘中的cookie
记住我:登录后未注销的第 2-n 次登录功能(因为注销会删除内存中的session和浏览器/磁盘中的cookie,所以下一次又要重新发送cookie了)

3.3 Demo:单体架构的登录(三个函数)

单体架构的登录Demo

// 第一,用户登录功能,第一次登录功能,因为第一次访问,登陆的时候,服务器已经将Cookie发送给浏览器,第二次访问,浏览器就直接带着Cookie到服务器了,所以不用手动登陆了(但是第一次没有存放用户token的Cookie的,必须手动登陆)。
@PostMapping(value = "/user/session", produces = {
    
    "application/json;charset=UTF-8"})
public Result login(String mobileNo, String password, String inputCaptcha, HttpSession session, HttpServletResponse response) {
    
    
  
    if (WebUtils.validateCaptcha(inputCaptcha, "captcha", session)) {
    
         //判断验证码是否正确
        User user = userService.userLogin(mobileNo, password);  //根据账号密码,判断有没有该用户
        if (user != null) {
    
    
            // 第一步,将token保存在数据库中 3句:生成与用户对应的token,user设置loginToken属性,将带有logintoken的user对象写入到数据库的user表中去,注意:这个user表有一个loginToken字段,这个字段cookie里面也有,这就是自动登录一个新奇的原理
            String loginToken = WebUtils.md5(new Date().toString() + session.getId());  // 通过当前时间和sesssion得到与用户对应的loginToken
            user.setLoginToken(loginToken);   // user设置loginToken属性
            User user1 = userService.userUpload(user);    // 第一次登录,将用户带loginToken的user写入数据库中,表示这个用户已经登录
            
            // 第二,设置cookie为一个星期,表示自动登陆持续一个星期 
            session.setAttribute("user", user1);      // user1设置在session里面
            CookieUtil.addCookie(response,"loginToken",loginToken,604800);  // loginToken设置在Cookie里面
            return ResultUtil.success(user1);   // 返回指定结构体的成功
        } else {
    
     
            return ResultUtil.error(ResultEnum.LOGIN_ERROR);   // 返回指定结构体的失败
        }
    } else {
    
    
        return ResultUtil.error(ResultEnum.CAPTCHA_ERROR);   // 返回指定结构体的失败
    }
}

// 第二,用户退出功能,注销功能,删除服务端内存中的session和浏览器/磁盘中的cookie
@DeleteMapping(value = "/session", produces = {
    
    "application/json;charset=UTF-8"})
public Result logout(HttpSession session,HttpServletRequest request,HttpServletResponse response ) {
    
    
    //删除session和cookie,简单
    session.removeAttribute("user");   // session中去除掉user属性,发命令让浏览器删去磁盘中的cookie文件
    CookieUtil.clearCookie(request, response, "loginToken");
    return ResultUtil.success();
}

// 第三,拦截器;记住我,实现自动登陆功能,登录后未注销的第 2-n 次登录功能(因为注销会删除内存中的session和浏览器/磁盘中的cookie,所以下一次又要重新发送cookie了)
public class UserInterceptor implements HandlerInterceptor {
    
    
@Autowired
private UserService userService;
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
    
    
    User sessionUser = (User) request.getSession().getAttribute("user");   // 第一步,查看服务端session,如果找到,表示登录了,放行,没找到,走else继续    ps:session是保存在服务器内存中的,一次会话就没了,就是关闭浏览器页面就没有了,所以session中没有不能代表没有登录,要继续判断
    if (sessionUser != null) {
    
    
        return true;
    } else {
    
    
        String loginToken = CookieUtil.findCookieByName(request, "loginToken");  //第二步,当服务端指定session为null,查看带过来cookie,是否存在loginToken(第一次登录的时候已经给cookie设置loginToken,并设置一个星期过期时间了)
        if (StringUtils.isNotBlank(loginToken)) {
    
    
            User user = userService.findUserByLoginToken(loginToken);  //第三步,存在loginToken,拿着loginToken到数据库查询有没有该user   ps:数据库表user中有一个列loginToken,loginToken由当前时间+sessionid md5运算生成,是唯一的,用户id也是唯一的,所以loginToken和user是一对一关系,一个loginToken对应一个user,一个user对应一个loginToken
            if (user != null) {
    
       // 用户不为空,找到了
                request.getSession().setAttribute("user", user);   // 第四步,进入到这里说明没有session,但是有cookie,所以设置好session,然后直接返回为true
                return true;
            } else {
    
       // 用户为空,找不到
                CookieUtil.clearCookie(request, response, "loginToken");  // 第四步,进入到这里说明没有cookie中的loginToken指定的用户,即Cookie不匹配,直接将cookie删掉  返回false
                return false;
            }
        } else {
    
    
            //第三步,cookie中不存在loginToken,
            if (request.getRequestURI().contains("session")) {
    
      // 没有cookie,但是URL里面放着session,放行
                return true;
            }
            //没有sesssion,没有cookie凭证,要求登录,跳转到登录
            response.sendRedirect("/login.html");
            return false;
        }
    }
}
}

总结一下上面代码的思路:
第一个函数:用户登录时,验证用户的账户和密码(第一次登录功能中的 userService.userLogin(mobileNo, password); )
第一个函数五行代码:生成一个Token保存在数据库中,将用户数据保存在Session中,并将Token写到Cookie中,这个cookie里面有loginToken,这个cookie保存一星期,表示一个星期自动登录(第一个函数五句代码,前三句是写入数据库,后两句是设置session和cookie)
第二个函数,注销,直接删除session和cookie就好(session中去除掉user属性,发命令让浏览器删去磁盘中的cookie文件)
第三个函数,先验证session,然后验证cookie,以下情况:
1、session里面可以取到user属性,直接放行(另:url里面有session,放行);
2、session里面无法取到user属性,看cookie里面有没有loginToken
3、cookie里面有loginToken且合法(该loginToken可以在数据库里面找到对应user),设置好session,放行。
4、cookie里面有loginToken且不合法(该loginToken可以在数据库里面找到对应user),删除这个cookie,拒绝。
5、没有session,没有cookie,转到登录界面

四、多系统登录的问题与解决

4.1 问题1:Session不共享问题

问题:单系统登录功能主要是用Session保存用户信息来实现的,但我们清楚的是:多系统即可能有多个Tomcat,而Session是依赖当前系统的Tomcat,所以系统A的Session和系统B的Session是不共享的。
在这里插入图片描述

扫描二维码关注公众号,回复: 11909066 查看本文章

解决系统之间Session不共享问题有一下几种方案:
方案1:Tomcat集群Session全局复制(集群内每个tomcat的session完全同步)【会影响集群的性能呢,不建议】
方案2:根据请求的IP进行Hash映射到对应的机器上(这就相当于请求的IP一直会访问同一个服务器)【如果服务器宕机了,会丢失了一大部分Session的数据,不建议】
方案3:引入一个各个系统都可以访问的中间件Redis,把Session数据放在Redis中,这样所有的系统就都可以知道现在用户登录没有(使用Redis模拟Session)【建议】
共同点(单点登录的核心)
问题:单点登录的问题是session是各个系统所独自拥有的,各个系统不知道用户是否登录,无法共享用户的登录状态,
目标/切入点:目标/切入点是 “一定要让所有的系统就都可以知道现在用户登录没有”,只要能够实现这个目标/切入点,就可以作为方案,所以 Tomcat集群Session全局复制、请求的IP一直会访问同一个服务器、引入中间件Redis 都是方案。

4.2 问题2:Cookie跨域的问题

Cookie是不能跨域的
\比如说,我们请求https://www.google.com/时,浏览器会自动把google.com的Cookie带过去给google的服务器,而不会把https://www.baidu.com/的Cookie带过去给google的服务器。
这就意味着,由于域名不同,用户向系统A登录后,系统A返回给浏览器的Cookie,用户再请求系统B的时候不会将系统A的Cookie带过去。

针对Cookie存在跨域问题,有几种解决方案:
方案1:服务端将Cookie写到客户端后,客户端对Cookie进行解析,将Token解析出来,此后请求都把这个Token带上就行了
方案2:多个域名共享Cookie,具体:服务端在新建Cookie发送给客户端的时候,就设置好Cookie的domain。
方案3:将Token保存在SessionStroage中(不依赖Cookie就没有跨域的问题了)
跨域问题:就是不同域名(一级域名相同二级域名不同)的Cookie,服务端接收不到,但是Cookie不是重点,Cookie里面存放的token才是实现自动登录的重点,token存在于Cookie中,Cookie不能只能让创建cookie的服务端接收到,导致token只能让创建cookie的服务端接收到
切入点/目标:一定要让token在全网服务端都能接收到,知道达到这个目标,都是解决方案
方案1:前端第一个请求服务端,服务端通过Cookie将token给了前端之后,前端将token解析出来,前端 第2-n次请求, 前端就干脆不要再使用Cookie来传输token了,直接将token放到url请求地址后面(笔者上一家公司就是这么做的)
方案2:前端2-n次请求,还是使用Cookie传输token,但是,第一次请求,服务端在新建Cookie发送给客户端的时候,就设置好Cookie的domain,设置为全网Cookie
方案3:将Token保存在SessionStroage中(笔者没用过)

4.3 Demo:分布式架构的单点登录(三个函数)

我们可以将登录功能单独抽取出来,做成一个子系统。

SSO(登录系统)的逻辑如下:

// 登录功能(SSO系统上的登录接口  三步走,数据库中查找user,核对数据库中user密码,token,user对应关系放入redis)
@Override
public TaotaoResult login(String username, String password) throws Exception {
    
    

    TbUserExample example = new TbUserExample();
    Criteria criteria = example.createCriteria();
    criteria.andUsernameEqualTo(username);   
    List<TbUser> list = userMapper.selectByExample(example);   // 到数据库中去找参数username,如果没找到,用户不存在,如果找到了,核对密码
    if (null == list || list.isEmpty()) {
    
    
        return TaotaoResult.build(400, "用户不存在");
    }
    
    //第二步,找打了username,核对密码
    TbUser user = list.get(0);
    if (!DigestUtils.md5DigestAsHex(password.getBytes()).equals(user.getPassword())) {
    
    
        return TaotaoResult.build(400, "密码错误");
    }
    //第三步,用户名和密码正确,登录成功,但是不能就这样结束了,一定要放到中间件redis中,让 让所有的系统就都可以知道现在用户登录没有
    String token = UUID.randomUUID().toString();      //生成唯一token,用户是唯一的,token也是唯一的,所以user和token 一对一关系
    jedisCluster.set(USER_TOKEN_KEY + ":" + token, JsonUtils.objectToJson(user));  // 建立(token,user)的一对一关系,放入到全局中间件redis中
    jedisCluster.expire(USER_TOKEN_KEY + ":" + token, SESSION_EXPIRE_TIME);   //设置redis指定key(即USER_TOKEN_KEY + ":" + token)过期时间
    return TaotaoResult.ok(token);   // 返回指定结构体,返回token,为什么?其他系统的登录接口调用SSO接口,需要得到token,放到cookie里面,将cookie返回给浏览器,存入磁盘文件;第2-n登录其他系统,直接拿着带token的cookie来
}

其他子系统登录时,请求SSO(登录系统)进行登录,将返回的token写到Cookie中,下次访问时则把Cookie带上:

// 登录功能(其他系统上的登录接口)
public TaotaoResult login(String username, String password, 
        HttpServletRequest request, HttpServletResponse response) {
    
    
    //请求参数
    Map<String, String> param = new HashMap<>();
    param.put("username", username);
    param.put("password", password);
    //第一步,其他系统上的登录接口,直接转到SSO系统的登录接口,交给它去处理就好
    String stringResult = HttpClientUtil.doPost(REGISTER_USER_URL + USER_LOGIN_URL, param);
    TaotaoResult result = TaotaoResult.format(stringResult);
    //登录出错
    if (result.getStatus() != 200) {
    
    
        return result;
    }
    //第二步,登录成功后把取token信息,并写入cookie,将cookie返回给浏览器,存入磁盘文件;第2-n登录其他系统,直接拿着带token的cookie来
    String token = (String) result.getData();
    //写入cookie
    CookieUtils.setCookie(request, response, "TT_TOKEN", token);
    //返回成功
    return result;
}

其他系统的注销功能、其他系统的记住我功能,和上面一样
唯一改变,之前的单系统登录,一个登录接口;现在分布式系统单点登录,变为两个登录接口(SSO系统的登录接口 + 其他业务系统的登录接口)

对于代码的解释:
1、第一个函数(SSO系统):SSO系统的登录接口,每次生成一个token,并将用户信息存到Redis中,并设置过期时间
2、第一个函数(其他系统):其他系统请求SSO系统进行登录,得到SSO返回的token,写到Cookie中
3、第三个函数(拦截器):每次请求时,Cookie都会带上,拦截器得到token,判断是否已经登录

单系统登录到分布式系统的单点登录,本质改变:
(token,user)是实现自动登录/记住我的关键,
单体系统中
第一(同一个session中,保存登录状态,不是重点),服务端存在Session里面(session.setAttribute(“user”, user1);)(第一个函数);下次浏览器登录的时候,服务端内存的session存在(页面没有关闭,是同一个浏览器页面)就直接登录了(request.getSession().getAttribute(“user”); )(第三个函数)
第二((token,user)是实现自动登录的重点),服务端将(token,user)存入数据库( user.setLoginToken(loginToken); User user1 = userService.userUpload(user); ),返回给浏览器带有token的Cookie(第一个函数);下次浏览器直接拿着Cookie里面的token来登录,与数据库(token,user)比对(userService.findUserByLoginToken(loginToken); )(第三个函数)
分布式系统中
第一个,同一个session中,保存登录状态,没有实现了
第二个,((token,user)是实现自动登录的重点),现在将(token,user)存到Redis,返回给浏览器带有token的Cookie(第一个函数 SSO系统 + 业务系统);下次浏览器直接拿着Cookie里面的token来登录,来redis(token,user)比对(第三个函数)
小结:服务端 (token,user) ,之前存放在session,后来在redis中。

五、CAS原理(认证中心服务)

说到单点登录,就肯定会见到这个名词:CAS (Central Authentication Service),下面说说CAS是怎么搞的。
如果已经将登录单独抽取成系统出来,我们还能这样玩。

现在我们有两个系统,分别是www.java3y.com和www.java4y.com,一个SSOwww.sso.com
第一步,用户想要访问系统Awww.java3y.com受限的资源(比如说购物车功能,购物车功能需要登录后才能访问),系统Awww.java3y.com发现用户并没有登录,于是重定向到sso认证中心,并将自己的地址作为参数。请求的地址如下:
www.sso.com?service=www.java3y.com
sso认证中心发现用户未登录,将用户引导至登录页面,用户进行输入用户名和密码进行登录,用户与认证中心建立全局会话(生成一份Token,写到Cookie中,保存在浏览器上)
在这里插入图片描述

第二步,认证中心重定向回系统A,并把Token携带过去给系统A,重定向的地址如下:
www.java3y.com?token=xxxxxxx
系统A去sso认证中心验证这个Token是否正确,如果正确,则系统A和用户建立局部会话(创建Session)。到此,系统A和用户已经是登录状态了。
在这里插入图片描述

第三步,用户想要访问系统Bwww.java4y.com受限的资源(比如说订单功能,订单功能需要登录后才能访问),系统Bwww.java4y.com发现用户并没有登录,于是重定向到sso认证中心,并将自己的地址作为参数。请求的地址如下:
www.sso.com?service=www.java4y.com
注意,因为之前用户与认证中心www.sso.com已经建立了全局会话(当时已经把Cookie保存到浏览器上了),所以这次系统B重定向到认证中心www.sso.com是可以带上Cookie的
认证中心根据带过来的Cookie发现已经与用户建立了全局会话了,认证中心重定向回系统B,并把Token携带过去给系统B,重定向的地址如下:
www.java4y.com?token=xxxxxxx
接着,系统B去sso认证中心验证这个Token是否正确,如果正确,则系统B和用户建立局部会话(创建Session)。到此,系统B和用户已经是登录状态了。
在这里插入图片描述

所以,其实SSO认证中心就类似一个中转站。

六、面试金手指(要记的东西)

6.1 分布式架构导致了单点登录 + session和cookie两个不同

黄金一句:分布式架构导致了单点登录,单点登录就是分布式登录,就是分布式系统中,一个系统登录,其他所有系统共享登录状态。
实践:阿里系的淘宝和天猫,很明显地我们可以知道这是两个系统,但是你在使用的时候,登录了天猫,淘宝也会自动登录。所以,单点登录就是在多个系统中,用户只需一次登录,各个系统即可感知该用户已经登录

Session和Cookie的两个不同
1、存储位置不同:Session是存放在服务端的四种作用域之一,Cookie存放在浏览器磁盘文件上。
JSP中的四种作用域包括page、request、session和application,具体来说:
page代表与一个页面相关的对象和属性。
request代表与Web客户机发出的一个请求相关的对象和属性。一个请求可能跨越多个页面,涉及多个Web组件;需要在页面显示的临时数据可以置于此作用域。request的域对象只能是一次http请求,提交表单数据的时候request域对象的数据取不出来。
session代表与某个用户与服务器建立的一次会话相关的对象和属性。跟某个用户相关的数据应该放在用户自己的session中。
application代表与整个Web应用程序相关的对象和属性,它实质上是跨越整个Web应用程序,包括多个页面、请求和会话的一个全局作用域。ServletContext代表整个web应用,如果有几个用户浏览器同时访问,ServletContext域对象的数据会被多次覆盖掉,也就是说域对象的数据就毫无意义了。
注意,page request session application/servletContext 都是只有一个的,都是JSP的作用域
2、生命周期不同:
Session的生命周期指的是不活动的时间,如果设置session是10s,在10s内,没有访问session,session属性失效,如果在9s的时候,你访问session,就重新计数;
如果重启了tomcat,或者reload web应用,或者关机了,session也会失效,也可以通过后端程序函数让session失效,invalidate(),该方案是让session中的所有属性失效,常常用于安全退出;
如果需要让session中的某个属性失效,可以使用方法removeAttritue(),如上面的第二个注销函数 session.removeAttribute(“user”); 去掉session中的user属性,注意,一次会话就只有一个session.
cookie的生命周期是按累计时间类计算的,不管用户有没有访问过session.

6.2 单体架构的登录

6.2.1 单体架构登录理论

单体架构登录理论
众所周知,HTTP是无状态的协议,这意味着服务器无法确认用户的信息。于是乎,W3C就提出了:给每一个用户都发一个通行证,无论谁访问的时候都需要携带通行证,这样服务器就可以从通行证上确认用户的信息。通行证就是Cookie。
如果说Cookie是检查用户身上的”通行证“来确认用户的身份,那么Session就是通过检查服务器上的”客户明细表“来确认用户的身份的。Session相当于在服务器中建立了一份“客户明细表”。
HTTP协议是无状态的,Session不能依据HTTP连接来判断是否为同一个用户。于是乎:服务器向用户浏览器发送了一个名为JESSIONID的Cookie,它的值是Session的id值。其实Session是依据Cookie来识别是否是同一个用户。金手指:Session是存放在Cookie里面的,当然也可以存放在URL里面

6.2.2 单体架构登录实现思路(三个函数)

单体架构登录实现思路
第一,实现登录功能:服务端中,将用户信息保存在Session对象中,
如果在Session对象中能查到,说明已经登录
如果在Session对象中查不到,说明没登录(或者已经退出了登录)
第二,实现注销功能(退出登录):从Session中删除用户的信息
第三,实现记住我功能(关闭掉浏览器后,重新打开浏览器还能保持登录状态):配合Cookie来用(Session默认是)

金手指:解释三功能(登录、注销、记住我),记住cookie和session重要的点
登录:因为第一次访问,登陆的时候,服务器已经将Cookie发送给浏览器,第二次访问,浏览器就直接带着Cookie到服务器了,所以不用手动登陆了(但是第一次没有存放用户token的Cookie的,必须手动登陆)。
注销:删除服务端内存中的session和浏览器/磁盘中的cookie
记住我:登录后未注销的第 2-n 次登录功能(因为注销会删除内存中的session和浏览器/磁盘中的cookie,所以下一次又要重新发送cookie了)

6.2.3 Demo:单体架构的登录(三个函数)

总结一下上面代码的思路:
第一个函数:用户登录时,验证用户的账户和密码(第一次登录功能中的 userService.userLogin(mobileNo, password); )
第一个函数五行代码:生成一个Token保存在数据库中,将用户数据保存在Session中,并将Token写到Cookie中,这个cookie里面有loginToken,这个cookie保存一星期,表示一个星期自动登录(第一个函数五句代码,前三句是写入数据库,后两句是设置session和cookie)
第二个函数,注销,直

6.3 多系统登录的问题与解决

6.3.1 问题1:Session不共享问题

解决系统之间Session不共享问题有一下几种方案:
方案1:Tomcat集群Session全局复制(集群内每个tomcat的session完全同步)【会影响集群的性能呢,不建议】
方案2:根据请求的IP进行Hash映射到对应的机器上(这就相当于请求的IP一直会访问同一个服务器)【如果服务器宕机了,会丢失了一大部分Session的数据,不建议】
方案3:引入一个各个系统都可以访问的中间件Redis,把Session数据放在Redis中,这样所有的系统就都可以知道现在用户登录没有(使用Redis模拟Session)【建议】
共同点(单点登录的核心)
问题:单点登录的问题是session是各个系统所独自拥有的,各个系统不知道用户是否登录,无法共享用户的登录状态,
目标/切入点:目标/切入点是 “一定要让所有的系统就都可以知道现在用户登录没有”,只要能够实现这个目标/切入点,就可以作为方案,所以 Tomcat集群Session全局复制、请求的IP一直会访问同一个服务器、引入中间件Redis 都是方案。

6.3.2 问题2:Cookie跨域的问题

针对Cookie存在跨域问题,有几种解决方案:
方案1:服务端将Cookie写到客户端后,客户端对Cookie进行解析,将Token解析出来,此后请求都把这个Token带上就行了
方案2:多个域名共享Cookie,具体:服务端在新建Cookie发送给客户端的时候,就设置好Cookie的domain。
方案3:将Token保存在SessionStroage中(不依赖Cookie就没有跨域的问题了)
跨域问题:就是不同域名(一级域名相同二级域名不同)的Cookie,服务端接收不到,但是Cookie不是重点,Cookie里面存放的token才是实现自动登录的重点,token存在于Cookie中,Cookie不能只能让创建cookie的服务端接收到,导致token只能让创建cookie的服务端接收到
切入点/目标:一定要让token在全网服务端都能接收到,知道达到这个目标,都是解决方案
方案1:前端第一个请求服务端,服务端通过Cookie将token给了前端之后,前端将token解析出来,前端 第2-n次请求, 前端就干脆不要再使用Cookie来传输token了,直接将token放到url请求地址后面(笔者上一家公司就是这么做的)
方案2:前端2-n次请求,还是使用Cookie传输token,但是,第一次请求,服务端在新建Cookie发送给客户端的时候,就设置好Cookie的domain,设置为全网Cookie
方案3:将Token保存在SessionStroage中(笔者没用过)

6.3.3 Demo:分布式架构的单点登录(三个函数)

单系统登录到分布式系统的单点登录,本质改变:
(token,user)是实现自动登录/记住我的关键,
单体系统中
第一(同一个session中,保存登录状态,不是重点),服务端存在Session里面(session.setAttribute(“user”, user1);)(第一个函数);下次浏览器登录的时候,服务端内存的session存在(页面没有关闭,是同一个浏览器页面)就直接登录了(request.getSession().getAttribute(“user”); )(第三个函数)
第二((token,user)是实现自动登录的重点),服务端将(token,user)存入数据库( user.setLoginToken(loginToken); User user1 = userService.userUpload(user); ),返回给浏览器带有token的Cookie(第一个函数);下次浏览器直接拿着Cookie里面的token来登录,与数据库(token,user)比对(userService.findUserByLoginToken(loginToken); )(第三个函数)
分布式系统中
第一个,同一个session中,保存登录状态,没有实现了
第二个,((token,user)是实现自动登录的重点),现在将(token,user)存到Redis,返回给浏览器带有token的Cookie(第一个函数 SSO系统 + 业务系统);下次浏览器直接拿着Cookie里面的token来登录,来redis(token,user)比对(第三个函数)
小结:服务端 (token,user) ,之前存放在session,后来在redis中。

6.4 CAS原理(认证中心服务)

说到单点登录,就肯定会见到这个名词:CAS (Central Authentication Service),下面说说CAS是怎么搞的。
如果已经将登录单独抽取成系统出来,我们还能这样玩。

现在我们有两个系统,分别是www.java3y.com和www.java4y.com,一个SSOwww.sso.com
第一步,用户想要访问系统Awww.java3y.com受限的资源(比如说购物车功能,购物车功能需要登录后才能访问),系统Awww.java3y.com发现用户并没有登录,于是重定向到sso认证中心,并将自己的地址作为参数。请求的地址如下:
www.sso.com?service=www.java3y.com
sso认证中心发现用户未登录,将用户引导至登录页面,用户进行输入用户名和密码进行登录,用户与认证中心建立全局会话(生成一份Token,写到Cookie中,保存在浏览器上)
在这里插入图片描述

第二步,认证中心重定向回系统A,并把Token携带过去给系统A,重定向的地址如下:
www.java3y.com?token=xxxxxxx
系统A去sso认证中心验证这个Token是否正确,如果正确,则系统A和用户建立局部会话(创建Session)。到此,系统A和用户已经是登录状态了。
在这里插入图片描述

第三步,用户想要访问系统Bwww.java4y.com受限的资源(比如说订单功能,订单功能需要登录后才能访问),系统Bwww.java4y.com发现用户并没有登录,于是重定向到sso认证中心,并将自己的地址作为参数。请求的地址如下:
www.sso.com?service=www.java4y.com
注意,因为之前用户与认证中心www.sso.com已经建立了全局会话(当时已经把Cookie保存到浏览器上了),所以这次系统B重定向到认证中心www.sso.com是可以带上Cookie的
认证中心根据带过来的Cookie发现已经与用户建立了全局会话了,认证中心重定向回系统B,并把Token携带过去给系统B,重定向的地址如下:
www.java4y.com?token=xxxxxxx
接着,系统B去sso认证中心验证这个Token是否正确,如果正确,则系统B和用户建立局部会话(创建Session)。到此,系统B和用户已经是登录状态了。
在这里插入图片描述

所以,其实SSO认证中心就类似一个中转站。

七、小结

单点登录SSO 002,完成了。

天天打码,天天进步!!!

猜你喜欢

转载自blog.csdn.net/qq_36963950/article/details/108947217
今日推荐