分布式会话和单点登录

1 分布式会话
a 概念
在Web项目开发中,会话管理是一个很重要的部分,用于存储与用户相关的数据。通常是由符合session规范的容器来负责存储管理,也就是一旦容器关闭,重启会导致会话失效。因此打造一个高可用性的系统,必须将session管理从容器中独立出来。

b 解决方案
使用容器扩展(容器插件)来实现

    基于Tomcat的tomcat-redis-session-manager,基于Jetty的jetty-session-redis

    [缺点]

    (1)过于依赖容器,一旦容器升级或者更换意味着又得从新来过。

    (2)并且代码不在项目中,对开发者来说维护也是个问题。

自己写一套会话管理的工具类

    包括Session管理和Cookie管理,在需要使用会话的时候都从自己的工具类中获取,而工具类后端存储可以放到Redis中。

    [特点]

            灵活性最大,但开发需要一些额外的时间。

            系统中存在两套Session方案,很容易弄错而导致取不到数据。

使用spring-session框架的会话管理工具

    替换了Servlet那一套会话管理,既不依赖容器,又不需要改动代码,并且是用了spring-data-redis那一套连接池,可以说是最完美的解决方案。

c 为什么使用SpringSession
Spring Session为企业级Java应用的session管理带来了革新,使得以下的功能更加容易实现:

将session所保存的状态卸载到特定的外部session存储中,如Redis或Apache Geode中,它们能够以独立于应用服务器的方式提供高质量的集群。

当用户使用WebSocket发送请求的时候,能够保持HttpSession处于活跃状态。

在非Web请求的处理代码中,能够访问session数据,比如在JMS消息的处理代码中。

支持每个浏览器上使用多个session,从而能够很容易地构建更加丰富的终端用户体验。

控制session id如何在客户端和服务器之间进行交换,这样的话就能很容易地编写Restful API,因为它可以从HTTP 头信息中获取session id,而不必再依赖于cookie。

d Spring Session分布式会话解决方案实现
(1)pom.xml引入依赖


    org.springframework.session
    spring-session-data-redis


    org.springframework.boot
    spring-boot-starter-data-redis


(2)配置redis参数

redis

#session存储类型
spring.session.store-type=redis
spring.redis.database=0
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=123456
spring.redis.pool.max-active=8
spring.redis.pool.max-wait=-1
spring.redis.pool.max-idle=8
spring.redis.pool.min-idle=0
spring.redis.timeout=3000

(3)简单的用户登录实现

@RequestMapping(value="login",method=RequestMethod.POST)
public Result login(String username,String password,HttpServletRequest request,HttpServletResponse response) throws Exception {
        SysUser user = userService.getUser(username);
        if(user==null) {
            return Result.error("用户不存在");
        }else {
            if(user.getPassword().equals(password)) {
                request.getSession().setAttribute("user", user);
                return Result.ok();
            }else {
                return Result.error("密码错误");
            }
        }
}

(4)配置代理实现,基于 Nginx

server {
        listen       80;
        server_name  blog.52itstyle.vip;
        location / {
            proxy_pass http://192.168.1.2:8080; 
        }
        location /cart {
             proxy_pass http://192.168.1.3:8080$request_uri;
        }
       location /order {
             proxy_pass http://192.168.1.4:8080$request_uri;
        }
  }

(5)配置成功后登录系统,在redis中查询用户信息

127.0.0.1:6379> keys *

  1. "spring:session:expirations:1562577660000"
  2. "spring:session:sessions:1076c2bd-95b1-4f23-abd4-ab3780e32f6f"
  3. "spring:session:sessions:expires:1076c2bd-95b1-4f23-abd4-ab3780e32f6f"
    2 单点登录SSO
    在多个应用系统中,只需要登录一次,就可以访问其他相互信任的应用系统。

图中有4个系统,分别是Application1、Application2、Application3、和SSO。Application1、Application2、Application3没有登录模块,而SSO只有登录模块,没有其他的业务模块,当Application1、Application2、Application3需要登录时,将跳到SSO系统,SSO系统完成登录,其他的应用系统也就随之登录了。这完全符合我们对单点登录(SSO)的定义。

a 同域下的单点登录(巧用了Cookie顶域的特性)
一个企业一般情况下只有一个域名,通过二级域名区分不同的系统。比如我们有个域名叫做:a.com,同时有两个业务系统分别为:app1.a.com和app2.a.com。我们要做单点登录(SSO),需要一个登录系统,叫做:sso.a.com。

我们只要在sso.a.com登录,app1.a.com和app2.a.com就也登录了。通过上面的登陆认证机制,我们可以知道,在sso.a.com中登录了,其实是在sso.a.com的服务端的session中记录了登录状态,同时在浏览器端(Browser)的sso.a.com下写入了Cookie。那么我们怎么才能让app1.a.com和app2.a.com登录呢?这里有两个问题:

Cookie是不能跨域的,我们Cookie的domain属性是sso.a.com,在给app1.a.com和app2.a.com发送请求是带不上的。
sso、app1和app2是不同的应用,它们的session存在自己的应用内,是不共享的。

那么我们如何解决这两个问题呢?针对第一个问题,sso登录以后,可以将Cookie的域设置为顶域,即.a.com,这样所有子域的系统都可以访问到顶域的Cookie。我们在设置Cookie时,只能设置顶域和自己的域,不能设置其他的域。比如:我们不能在自己的系统中给baidu.com的域设置Cookie。

Cookie的问题解决了,我们再来看看session的问题。我们在sso系统登录了,这时再访问app1,Cookie也带到了app1的服务端(Server),app1的服务端怎么找到这个Cookie对应的Session呢?这里就要把3个系统的Session共享,如图所示。共享Session的解决方案有很多,例如:Spring-Session。这样第2个问题也解决了。

同域下的单点登录就实现了,但这还不是真正的单点登录。

b 不同域下的单点登录
CAS流程---单点登录的标准流程

重要的是理解:

SSO服务端和SSO客户端直接是通过授权以后发放Token的形式来访问受保护的资源
相对于浏览器来说,业务系统是服务端,相对于SSO服务端来说,业务系统是客户端
浏览器和业务系统之间通过会话正常访问
不是每次浏览器请求都要去SSO服务端去验证,只要浏览器和它所访问的服务端的会话有效它就可以正常访问
登录与注销流程图
登录
相比单体应用登录,实现单点登录需要一个独立的认证中心,负责接受用户信息的验证请求,其他系统不提供登录入口,只接受认证中心的间接授权。间接授权通过令牌实现,认证中心验证用户的登录信息正确后,创建授权令牌,在接下来的跳转过程中,授权令牌作为参数发送给各个子系统,子系统拿到令牌后将再次回传给认证中心认证,校验授权令牌完成后,将用户的会话回传给子系统,实现登录。具体流程如下图:

流程分析:

用户访问MTV系统的受保护资源,MTV系统发现用户未登录,跳转至SSO认证中心,并将自己的地址作为参数
SSO认证中心发现用户未登录,跳转至登录页面
用户输入用户名密码提交登录申请
SSO认证中心校验用户信息,创建用户与SSO认证中心之间的全局会话,全局令牌(和用户全局会话ID绑定,代表用户在SSO登录偶)和临时令牌(用于验证子系统身份)
SSO认证中心携带令牌跳转MTV系统最初的请求地址
MTV系统拿到令牌,去SSO认证中心校验令牌是否有效
SSO认证中心校验令牌,回传用户全局会话
MTV系统保存用户的全局会话
用户浏览器显示登录成功
再次登录流程图:

流程分析:

用户访问MUSIC系统的受保护资源
MUSIC系统发现用户未登录,跳转至SSO认证中心,并将自己的地址作为参数
SSO认证中心发现用户已登录,跳转回MUSIC系统的地址,并附上令牌
MUSIC系统拿到令牌,去SSO认证中心校验令牌是否有效
SSO认证中心校验令牌,返回用户全局会话
MUSIC系统保存用户全局会话
用户浏览器显示登录成功
注销
注销流程图:

流程分析:

用户向MTV系统发起注销请求
MTV系统根据用户与建立的会话id拿到令牌,向SSO认证中心发起注销请求
SSO认证中心校验令牌有效,销毁全局会话
SSO认证中心引导用户至登录页面

————————————————
版权声明:本文为CSDN博主「高秉文」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/g759780748/article/details/119397560

你可能感兴趣的:(分布式会话和单点登录)