前言

前端登陆的四种方式介绍以及应用

关键词:Cookie/Session、Token、SSO单点登录、OAuth第三方登陆

验证登陆

  • Cookie/Session
  • Token
  • SSO 单点登陆
  • OAuth第三方登陆

cookie session

登陆流程:首次登陆

  1. 用户访问a.com/pageA并输入密码登陆
  2. 服务器验证密码无误后,会创建Session ID并将其保存起来
  3. 服务端响应这个请求,并通过Set-Cookie将Session ID写入Cookie中

再次登陆:

  1. 用户访问a.com/pageB自动带上第一次登陆写入的Cookie
  2. 服务端对比Cookie中的SessionID和保存在服务端的SessionID是否一致
  3. 一致则成功
  • 服务器需要对接大量的客户端,导致服务器压力大。
  • 如果服务器是一个集群,为了同步登陆态,就需要将SessionID同步到每一台机器上,无形中增加了服务器的维护成本。
  • 由于SessionID存放在Cookie中所以无法避免CSRF攻击

token登陆

为了解决cookie+session暴露出来的问题,我们可以使用token的登陆方式

token是服务端生成的一串字符串,以作为客户端请求的一个令牌。当第一次登陆完成后,服务器会生成一个token并返回给客户端,客户端后续访问只要带上这个token就可以完成身份验证。

token机制实现流程

首次登陆:

  1. 用户输入账号密码,并点击登录。
  2. 服务器端验证账号密码无误,创建 Token。
  3. 服务器端将 Token 返回给客户端,由客户端自由保存。

后续页面访问时:

  1. 用户访问 a.com/pageB 时,带上第一次登录时获取的 Token。
  2. 服务器端验证 Token ,有效则身份验证成功。

Token 机制的特点

根据上面的案例,我们可以分析出Token的优缺点:

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

Token的生成方式

最常见的Token生成方式是使用JWT,他是一种简洁的,自包含的方法用于通信双方之间以JSON对象的形式安全的传递信息。

上文中我们说到,使用Token后,服务器端并不会存储Token,那怎么判断客户端的token是否合法有效?

答案其实就在Token字符串中,其实Token并不是一串杂乱无章的字符串,而是通过多种算法拼接组合而成的字符串。如下:

JWT算法主要分成了三个部分

  • header 头信息
  • payload 消息体
  • signature 签名

header部分指定了JWT使用的签名算法:

1
header = '{"alg":"HS256","typ":"JWT"}'   // `HS256` 表示使用了 HMAC-SHA256 来生成签名。

payload部分指明了JWT的使用意图

1
payload = '{"loggedInAs":"admin","iat":1422779638}'     //iat 表示令牌生成的时间

signature部分为JWT的签名 主要为了让JWT不能随意更改,签名的方法分为两个步骤

  1. 输入base64url编码的header部分,base64url编码的payload部分,输出unsignedToken
  2. 输入服务器端私钥,unsignedToken,输出signature签名
1
2
3
4
5
6
7
const base64Header = encodeBase64(header)
const base64Payload = encodeBase64(payload)
const unsignedToken = `${base64Header}.${base64Payload}`
const key = '服务器私钥'

signature = HMAC(key, unsignedToken)

最后Token计算如下:

1
2
3
4
5
const base64Header = encodeBase64(header)
const base64Payload = encodeBase64(payload)
const base64Signature = encodeBase64(signature)

token = `${base64Header}.${base64Payload}.${base64Signature}`

服务器在判断token的时候

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const [base64Header, base64Payload, base64Signature] = token.split('.')

const signature1 = decodeBase64(base64Signature)
const unsignedToken = `${base64Header}.${base64Payload}`
const signature2 = HMAC('服务器私钥', unsignedToken)

if(signature1 === signature2) {
return '签名验证成功,token 没有被篡改'
}

const payload = decodeBase64(base64Payload)
if(new Date() - payload.iat < 'token 有效期'){
return 'token 有效'
}

有了 Token 之后,登录方式已经变得非常高效,接下来我们介绍另外两种登录方式。

SSO单点登陆

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

SSO机制实现流程

分为同域和不同域。

同域的单点登陆

比如现在有一个域名叫zlinni.cn,然后我的其他页面分别为:

1
2
3
4
a.zlinni.cn
b.zlinni.cn
c.zlinni.cn
login.zlinni.cn//sso登陆系统

那么此时我们只要满足在login中登陆,然后abc三个都登陆就完成了sso

实现的方式就是利用了cookie的path和domain,我们设置domain为.zlinni.cn,然后path为/就能实现所有的域名都共享login的cookie,就实现了单点登陆。

不同域的单点登陆

用户首次访问需要在认证中心登陆:

  1. 用户先访问a.com下面的pageA页面,然后系统发现它未登陆,给它重定向到sso认证中心
  2. 在sso中注册,提交登陆
  3. sso验证账号密码有效,重定向到a.com并带上授权码ticket,再将sso的登录态写入cookie
  4. 此时a就拿着ticket去sso中验证是否有效。
  5. 在a的服务器中拿着ticket向sso验证确认真实性
  6. 验证成功后,服务器将写入两个信息到cookie,一个是ticket作为a的登录态,一个是sso的token记录sso的登录态

抛出疑问:为什么要用ticket?这个问题我们后面再讲

然后登陆完成访问a下面的其他页面

此时我们的cookie中携带了ticket,只要到服务端验证是否在有效的时间内即可。

访问b页面的时候(这个b页面是认证中心下面的不同产品,如果要做例子的话,可以认为a是新浪微博,b是新浪博客)

此时由于我们的b是不同源的页面,所以cookie中是没有ticket的(!!!!非常重要),但是它重定向到sso中,sso中我们的cookie是存在登录态的,所以此时不需要再注册等操作,验证token之后,直接给b下发ticket即可。b再去验证ticket真实性即可完成登陆。

为什么需要Ticket

ticket的存在是为了解决不同域携带的cookie不一样的问题

以上过程就解释了我们要用ticket的理由,因为a和b不是同源页面所以不会携带同样的ticket,但是sso中拥有登录态token所以我们会下发ticket到b中验证。

SSO单点登陆退出

单点登陆使得多个产品共享登录态,但是怎么做到在一个产品中退出登陆,其他的产品也退出呢?

这里其实要做的事情很简单,比如说退出这个bcom的登陆:

  1. 首先我们要清除掉b中的ticket
  2. 认证中心sso中清除对应的cookie
  3. sso遍历所有对应cookie下发了ticket的产品并删除。

总结sso

sso就是类似于Nginx+ticket+token的组合.其中,我们利用它的重定向规则,可以将token存在sso的cookie中,将ticket返回到具体的页面存在cookie中,然后再认证完成登陆

OAuth第三方登陆

由于sso实际上还是比较麻烦的,所以我们可以利用大厂提供的第三方登陆系统实现登陆

我们不关心这个完成授权的过程,但是要知道用户点击到返回登陆的这个过程发生了什么

  1. 首先用户在acom点击用户登陆,跳转到OAuth,并带上回调地址acom
  2. 用户二维码验证之后,微信返回临时票据code
  3. a带着code和自己申请的appid、appsecret,向微信的服务器申请token,验证成功微信下发token
  4. 有了token之后,a就可以向微信服务器获取用户的头像和昵称等信息。并将用户的登录态写入cookie以便下一次登陆。