单点登录实现方式(推荐收藏)

前言:

由于最近业务涉及到单点登录,所以深扒单点登录知识猛补。

一、什么是单点登录?


光从字面意思理解,我想这也太简单了叭。讲个自己的糗事,在我刚接触到单点登录这一名词的时候,我曾经以为单点登录就是单用户登录,比如说我在一台手机上登录后,另一台手机再次登录就会把原先的那个给挤掉。我不光自己这样理解,还和朋友侃侃而谈单点登录,现在想想真是丢大脸了。
实际上,单点登录(SSO)是指在多个应用系统中,用户只需要登录任意一个系统,就可以访问其他的互相信任的系统。即仅给予员工用户一套单一的凭证(如账号密码),就可以使其访问多个权限内的应用系统,也就是说员工只需要输入一套用户名和密码,就可以访问OA、邮箱、HR、CRM等所有工作相关的应用系统。

二、为什么需要单点登录?

1、单点登录可以有效提升员工工作效率

一份调查研究表明,68%的企业员工每个小时都需要在数十个应用系统中进行切换。因此,如果将数十个应用系统的登录简化为一次门户登录,就可以大幅减少员工日常工作中在应用系统切换上所花费的时间,从而提升员工的工作效率。

2、单点登录为员工提供了一个应用整合门户

单点登录解决方案为企业员工提供了一个统一的应用访问门户,员工在登录门户后就可以看到授权范围内所有的应用系统,也就是说企业员工日常工作中没有必要再记忆或用书签标记多个应用系统的访问地址和登录密码。

3、单点登录有助于减少密码重置请求

企业员工在日常工作中仅需记忆一套用户名和密码,因此即使偶尔忘记也不需要求助IT来进行密码重置。

三、常见的登录实现

1.Cookie + Session 登录

HTTP 是一种无状态的协议,客户端每次发送请求时,首先要和服务器端建立一个连接,在请求完成后又会断开这个连接。这种方式可以节省传输时占用的连接资源,但同时也存在一个问题:每次请求都是独立的,服务器端无法判断本次请求和上一次请求是否来自同一个用户,进而也就无法判断用户的登录状态。

为了解决 HTTP 无状态的问题,Lou Montulli 在 1994 年的时候,推出了 Cookie。

Cookie 是服务器端发送给客户端的一段特殊信息,这些信息以文本的方式存放在客户端,客户端每次向服务器端发送请求时都会带上这些特殊信息。

有了 Cookie 之后,服务器端就能够获取到客户端传递过来的信息了,如果需要对信息进行验证,还需要通过 Session。

客户端请求服务端,服务端会为这次请求开辟一块内存空间,这个便是 Session 对象。

有了 Cookie 和 Session 之后,我们就可以进行登录认证了。

实现流程:
Cookie + Session 的登录方式是最经典的一种登录方式,现在仍然有大量的企业在使用。

用户首次登录时:

(1) 用户访问A网站的页面a,输入密码。
(2) 服务器验证账号密码。
(3) 验证成功,创建并保存SessionID。
(4) 服务器响应客户端的HTTP请求,并通过Set-Cookie头信息把SessionId写入Cookie。
*服务器端的 SessionId 可能存放在很多地方,例如:内存、文件、数据库等。

第一次登录完成之后,后续的访问就可以直接使用 Cookie 进行身份验证了:

(1) 用户访问A网站的页面b,会带上Cookie信息。
(2) 服务器验证Cookie中的SessionId和服务器上的是否一致。
(3) 一致就允许访问,不一致就踢回登录。
单点登录实现方式(推荐收藏)_第1张图片

存在的问题:

由于服务器端需要对接大量的客户端,也就需要存放大量的 SessionId,这样会导致服务器压力过大。
如果服务器端是一个集群,为了同步登录态,需要将 SessionId 同步到每一台机器上,无形中增加了服务器端维护成本。

由于 SessionId 存放在 Cookie 中,所以无法避免 CSRF 攻击。

2.Token登录

JSON Web token简称JWT, 是用于对应用程序上的用户进行身份验证的标记。也就是说, 使用 JWTS 的应用程序不再需要保存有关其用户的 cookie 或其他session数据。此特性便于可伸缩性, 同时保证应用程序的安全。在身份验证过程中, 当用户使用其凭据成功登录时, 将返回 JSON Web token, 并且必须在本地保存 (通常在本地存储中)。每当用户要访问受保护的路由或资源 (端点) 时, 用户代理(user agent)必须连同请求一起发送 JWT, 通常在授权标头中使用Bearer schema。后端服务器接收到带有 JWT 的请求时, 首先要做的是验证token。

2.1 组成

一个JWT实际上就是一个字符串,它由三部分组成,头部、载荷与签名。

头部(Header)

头部用于描述关于该JWT的最基本的信息,例如其类型以及签名所用的算法等。这也可以被表示成一个JSON对象。

{“typ”:“JWT”,“alg”:“HS256”}

在头部指明了签名算法是HS256算法。 我们进行BASE64编码(http://base64.xpcha.com/),编码后的字符串如下:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

载荷(playload)

载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分:

(1). 标准中注册的声明(建议但不强制使用)

iss: jwt签发者
sub: jwt所面向的用户
aud: 接收jwt的一方
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的.
iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token

(2) .公共的声明

公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密。

(3). 私有的声明

私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。
这个指的就是自定义的claim。比如前面那个结构举例中的admin和name都属于自定的claim。这些claim跟JWT标准规定的claim区别在于:JWT规定的claim, JWT的接收方在拿到JWT之后,都知道怎么对这些标准的claim进行验证(还不知道是否能够验证);而private claims不会验证,除非明确告诉接收方要对这些claim进行验证以及规则才行。定义一个payload:

   {"sub":"1234567890","name":"John Doe","admin":true}

然后将其进行base64加密,得到Jwt的第二部分。

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRta  W4iOnRydWV9

签证(signature)

jwt的第三部分是一个签证信息,这个签证信息由三部分组成:

header (base64后的)
payload (base64后的)
secret

这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

注意

secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。

2.2 使用场景

(1). 一次性验证

比如用户注册后需要发一封邮件让其激活账户,通常邮件中需要有一个链接,这个链接需要具备以下的特性:能够标识用户,该链接具有时效性(通常只允许几小时之内激活),不能被篡改以激活其它可能的账户……这种场景就和 jwt 的特性非常贴近,jwt 的 payload中固定的参数:iss 签发者和 exp 过期时间正是为其做准备的。

(2). restful api 的无状态认证

使用 jwt 来做 restful api 的身份认证也是值得推崇的一种使用方案。客户端和服务端共享 secret;过期时间由服务端校验,客户端定时刷新;签名信息不可被修改…spring security oauth jwt 提供了一套完整的 jwt 认证体系,以笔者的经验来看:使用 oauth2 或 jwt 来做 restful api 的认证都没有大问题,oauth2 功能更多,支持的场景更丰富,后者实现简单。

(3).使用 jwt 做单点登录+会话管理(不推荐)

实现流程:

首次登录时:

(1) 用户访问网站A的页面a,输入密码。
(2) 服务器验证账号密码,正确则生成token返回给客户端,客户端自行保存。
(3) 登录成功,允许访问。

单点登录实现方式(推荐收藏)_第2张图片

后续访问时:

(1) 用户访问网站A的页面b,请求时传递token,服务端验证token。
(2) 一致就允许访问,不一致就踢回登录。

特点:

服务器端不需要存放 Token,所以不会对服务器端造成压力,即使是服务器集群,也不需要增加维护成本。
Token 可以存放在前端任何地方,可以不用保存在 Cookie 中,提升了页面的安全性。
Token 下发之后,只要在生效时间之内,就一直有效,如果服务器端想收回此 Token 的权限,并不容易。

3.SSO 单点登录

实现流程:

单点登录指的是在公司内部搭建一个公共的认证中心,公司下的所有产品的登录都可以在认证中心里完成,一个产品在认证中心登录后,再去访问另一个产品,可以不用再次登录,即可获取登录状态。

3.1 业务系统判断当前是否为登录状态,若未登录;
  3.1.1 若为get请求,保存当前请求地址到cookie中便于登录成功后重新访问;
  3.1.2 重定向单点系统授权接口;
  
3.2 单点系统输入账号密码进行登录操作(若单点系统是已登录状态,则该步骤会自动跳过);

3.3 单点系统重定向业务回调地址redirect_uri,并会携带一个唯一code;

3.4 业务系统根据唯一code从单点系统获取当前登录用户信息;

3.5 业务系统创建session,并保存用户登录信息;

3.6 将创建的session与jti绑定,登出时需要通过jti找到对应的session做失效处理;

3.7 判断是否有缓存的请求地址,如果有重定向到缓存的地址,没有则重定向到首页;

3.8 调用需要登录授权接口时,需要异步调用单点系统的session失效时间刷新接口;

单点登录实现方式(推荐收藏)_第3张图片

登出逻辑分为两种情况。一种是本身系统点击登出按钮退出,一种是单点系统调用登出接口退出

本身系统登出

(1) 调用本身系统的登出接口;

(2) 本身系统登出成功后,重定向单点系统的登出接口;

单点登录实现方式(推荐收藏)_第4张图片

SSO 单点登录退出:

(1) 单点系统调用业务系统登出接口,并携带当前业务系统登录session唯一jti(此登出接口与自身业务系统登出接口不是同一个,需要单独编写)

(2) 业务系统登出接口通过logoutRequest获取jti参数;

(3) 根据jti找到对应的session对象进行失效或删除(登录时,会缓存jti与session对象的对应关系);

4.OAuth 第三方登录

以微信为例:

(1) 首先,a.com 的运营者需要在微信开放平台注册账号,并向微信申请使用微信登录功能。

(2) 申请成功后,得到申请的 appid、appsecret。

(3) 用户在 a.com 上选择使用微信登录。

(4) 这时会跳转微信的 OAuth 授权登录,并带上 a.com 的回调地址。

(5) 用户输入微信账号和密码,登录成功后,需要选择具体的授权范围,如:授权用户的头像、昵称等。

(6) 授权之后,微信会根据拉起 a.com?code=123 ,这时带上了一个临时票据 code。

(7) 获取 code 之后, a.com 会拿着 code 、appid、appsecret,向微信服务器申请 token,验证成功后,微信会下发一个 token。

(8) 有了 token 之后, a.com 就可以凭借 token 拿到对应的微信用户头像,用户昵称等信息了。

(9) a.com 提示用户登录成功,并将登录状态写入 Cooke,以作为后续访问的凭证。

单点登录实现方式(推荐收藏)_第5张图片

你可能感兴趣的:(网络,java,服务器)