Java面试-单点登录

转载自:什么是单点登录

    我们为何需要单点登录系统

 

我们为什么需要单点登录

SSO,Single Sign On,也就是单点登录,保证一个账户在多个系统上实现单一用户的登录

现在随着网站的壮大,很多服务会进行拆分,会做SOA服务,会使用dubbo做微服务,或者简单的小型分布式,

这样在服务与服务之间,或者系统与系统之间都是通过HTTP或者restful来进行通信的,

在以往的单系统应用中,我们都是把user存入session中的,需要用到的时候随时取,如果取不到就跳转到登录注册页面,非常简单的原理

但是在现如今的分布式应用中,如何保证session同步呢?

比如订单服务是在 order.jd.com

购物车服务在 cart.jd.com

那么这2个二级域名下的用户信息如何实现同步呢?

解决方案:

  1. tomcat有一个session同步方案,就是一个传播机制,打个比方有A B C 3台tomcat,这3台tomcat的user信息都在session中并且保持一致,如果其中一台的user信息变化了,那么就会传播至另外两台,则实现同步,这样做没问题,但是仅仅只是在做tomcat集群的时候tomcat很少的时候会用,一旦集群增大,有100台,那么就互相传播吧,传播是需要性能损耗的,那么整个网站的性能就会被拉低,你的网站在你的网络风暴中就会晕死

  2. nginx 非粘性session,说穿了就是一个session绑定传播,起初user的session在tomcatA上,tomcatA宕机了,那么session会把所有的信息传播到tomcatB,以此实现session共享,但是这也有个问题,就是传播的时候需要等待,快的时候1分钟左右,慢的时候要5分钟,用户的耐性有限,所以也不能这么做

  3. 自己研发一套session高性能共享系统,我见过有这么做的公司,但是需要时间人力成本,所以不建议,如果你是BAT,随意~

  4. SSO解决方案,目前比较流行的方案,自行开发一套单点登录系统,比如就使用 sso.jd.com,可以在这个二级域名下进行登录和注册,登录和注册都是以restful形式,为了可以同时提供给cms以及手机端的支持,登陆后把用户的相关信息存入redis,redis设置好过期时间,key为一个token,使用uuid,uuid生成后需要存入cookie中,失效时间随意定,但是再登录后需要重置redis和cookie中的失效时间

京东的实现:

sso的登录系统:

sso的注册系统(京东是两套都分开了,这样布个集群怎么也得至少4台嘛)

首页:

商品(商品详情应该都是生成的静态页面):

交易系统:

这些都实现了sso,在soa各个系统中user可以随意走

拦截器配置:

在需要user信息的时候肯定需要使用到拦截器,如果获取不到user信息,那么就跳转到登录页面,但是需要注意的是需要把原页面作为redirectUrl暂时保存,登陆成功后需要跳转

获取user的时候就是从cookie中读取token,调用sso服务从redis中查询用户信息,如果有则继续,没有则登录

淘宝的二级域名:

 

单点登录需要解决的问题

1. Session不共享问题

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

不共享会发生什么?A系统登陆的用户,无法在B系统上直接使用。

解决系统之间的 Session 不共享问题有以下几种方案:

  • Tomcat集群Session全局复制(集群内每个tomcat的session完全同步)【会影响集群的性能呢,不建议】
  • 根据请求的IP进行Hash映射到对应的机器上(这就相当于请求的IP一直会访问同一个服务器)【如果服务器宕机了,会丢失了一大部分Session的数据,不建议】
  • 把Session数据放在Redis中(使用Redis模拟Session)【建议】
    -- 比如上篇提到的spring-session框架:https://www.cnblogs.com/niceyoo/p/11258392.html

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

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

  • SSO系统生成一个token,并将用户信息存到Redis中,并设置过期时间
  • 其他系统请求SSO系统进行登录,得到SSO返回的token,写到Cookie中
  • 每次请求时,Cookie都会带上,拦截器得到token,判断是否已经登录

至此,其实我们会发现其实就两个变化:

    • 将登陆功能抽取为一个系统(SSO),其他系统请求SSO进行登录
    • 本来将用户信息存到Session,现在将用户信息存到Redis

 

 

2. Cookie跨域问题

上面我们解决了Session不能共享的问题,但其实还有另一个问题。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的domain。
  3. 将Token保存在SessionStroage中(不依赖Cookie就没有跨域的问题了)

到这里,我们已经可以实现单点登录了。

2.1 CAS原理

说到单点登录,就肯定会见到这个名词:CAS (Central Authentication Service),下面说说CAS是怎么搞的。

如果已经将登录单独抽取成系统出来,我们还能这样玩。现在我们有两个系统,分别是www.java3y.com和www.java4y.com,一个SSO www.sso.com

首先,用户想要访问系统A www.java3y.com 受限的资源(比如说购物车功能,购物车功能需要登录后才能访问),系统A www.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和用户已经是登录状态了。

此时,用户想要访问系统B www.java4y.com 受限的资源(比如说订单功能,订单功能需要登录后才能访问),系统B www.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认证中心就类似一个中转站。

 

 

原文链接:https://www.cnblogs.com/leechenxiang/p/5475728.html

posted @ 2020-07-24 09:08  垫底研究生小莫  阅读(639)  评论(0编辑  收藏  举报