你需要知道的前端鉴权方式

简介

在 web 开发中,鉴权是非常常用的。而且鉴权的方式也有很多种,有各自的优缺点和使用场景。这里笔者就对当下几种常用的鉴权手段进行一个整理,方便大家的学习巩固和自己的复习

HTTP Basic Authentication

这种授权方式是浏览器遵守 http 协议实现的基本授权方式,HTTP 协议进行通信的过程中,HTTP 协议定义了基本认证认证允许 HTTP 服务器对客户端进行用户身份证的方法。

认证过程:

1. 客户端向服务器请求数据,请求的内容可能是一个网页或者是一个 ajax 异步请求,此时,假设客户端尚未被验证,则客户端提供如下请求至服务器:

1
2
Get /index.html HTTP/1.0
Host:www.google.com

2. 服务器向客户端发送验证请求代码 401,(WWW-Authenticate: Basic realm=”google.com”这句话是关键,如果没有客户端不会弹出用户名和密码输入界面)服务器返回的数据大抵如下:

1
2
3
4
5
HTTP/1.0 401 Unauthorised
Server: SokEvo/1.0
WWW-Authenticate: Basic realm=”google.com”
Content-Type: text/html
Content-Length: xxx

3. 当符合 http1.0 或 1.1 规范的客户端(如 IE,FIREFOX)收到 401 返回值时,将自动弹出一个登录窗口,要求用户输入用户名和密码。

4. 用户输入用户名和密码后,将用户名及密码以 BASE64 加密方式加密(base64 不安全!),并将密文放入前一条请求信息中,则客户端发送的第一条请求信息则变成如下内容:

1
2
3
Get /index.html HTTP/1.0
Host:www.google.com
Authorization: Basic d2FuZzp3YW5n

注:d2FuZzp3YW5n 表示加密后的用户名及密码(用户名:密码 然后通过 base64 加密,加密过程是浏览器默认的行为,不需要我们人为加密,我们只需要输入用户名密码即可)

5. 服务器收到上述请求信息后,将 Authorization 字段后的用户信息取出、解密,将解密后的用户名及密码与用户数据库进行比较验证,如用户名及密码正确,服务器则根据请求,将所请求资源发送给客户端

效果:
客户端未未认证的时候,会弹出用户名密码输入框,这个时候请求时属于 pending 状态, 这个时候其实服务当用户输入用户名密码的时候客户端会再次发送带 Authentication 头的请求。

当然有登陆就有注销,我们会发现当我们认证成功后每次请求请求头都会带上 Authentication 及里面的内容,那么如何做到让这次登陆失效的?

网上查了半天,目前最有效的方式就是在注销操作的时候,专门在服务器设置一个专门的注销账号,当接收到的 Authentication 信息为注销用户名密码的时候纠就带便注销成功了,而客户端在注销操作的时候,手动的的去修改请求头重的 Authentication,将他设置未服务器默认的注销账号和密码。

通过上面的简单讲解 其实我们已经可以返现这种验证方式的缺陷加密方式简单,仅仅是 base64 加密,这种加密方式是可逆的。同时在每个请求的头上都会附带上用户名和密码信息,这样在外网是很容易被嗅探器探测到的。

总结:

正式因为这样,这种加密方式一般多被用在内部安全性要求不高的的系统上,只是相对的多,总的来说现在使用这种鉴权比较少了。如果项目需要部署在公网上,这种方式不推荐。

第二种这个方式是利用服务器端的 session(会话)和浏览器端的 cookie 来实现前后端的认证,由于 http 请求时是无状态的,服务器正常情况下是不知道当前请求之前有没有来过,这个时候我们如果要记录状态,就需要在服务器端创建一个会话(seesion),将同一个客户端的请求都维护在各自得会会话中,每当请求到达服务器端的时候,先去查一下该客户端有没有在服务器端创建 seesion,如果有则已经认证成功了,否则就没有认证。
session-cookie 认证主要分四步:

  1. 服务器在接受客户端首次访问时在服务器端创建 seesion,然后保存 seesion(我们可以将 seesion 保存在内存中,也可以保存在 redis 中,推荐使用后者),然后给这个 session 生成一个唯一的标识字符串,然后在响应头中种下这个唯一标识字符串。

  2. 签名。这一步只是对 sid 进行加密处理,服务端会根据这个 secret 密钥进行解密。(非必需步骤)

  3. 浏览器中收到请求响应的时候会解析响应头,然后将 sid 保存在本地 cookie 中,浏览器在下次 http 请求 de 请求头中会带上该域名下的 cookie 信息,

  4. 服务器在接受客户端请求时会去解析请求头 cookie 中的 sid,然后根据这个 sid 去找服务器端保存的该客户端的 session,然后判断该请求是否合法。

Token(jwt)

使用基于 Token 的身份验证方法,大概的流程是这样的:

  • 客户端使用用户名跟密码请求登录

  • 服务端收到请求,去验证用户名与密码

  • 验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端

  • 客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage 里

  • 客户端每次向服务端请求资源的时候需要带着服务端签发的 Token

  • 服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据

总的来说就是客户端在首次登陆以后,服务端再次接收 http 请求的时候,就只认 token 了,请求只要每次把 token 带上就行了,服务器端会拦截所有的请求,然后校验 token 的合法性,合法就放行,不合法就返回 401(鉴权失败)。

乍的一看好像和前面的 seesion-cookie 有点像,seesion-cookie 是通过 seesionid 来作为浏览器和服务端的链接桥梁,而 token 验证方式貌似是 token 来起到 seesionid 的角色。其实这两者差别是很大的。

  1. sessionid 他只是一个唯一标识的字符串,服务端是根据这个字符串,来查询在服务器端保持的 seesion,这里面才保存着用户的登陆状态。但是 token 本身就是一种登陆成功凭证,他是在登陆成功后根据某种规则生成的一种信息凭证,他里面本身就保存着用户的登陆状态。服务器端只需要根据定义的规则校验这个 token 是否合法就行。
  2. session-cookie 是需要 cookie 配合的,居然要 cookie,那么在 http 代理客户端的选择上就是只有浏览器了,因为只有浏览器才会去解析请求响应头里面的 cookie,然后每次请求再默认带上该域名下的 cookie。但是我们知道 http 代理客户端不只有浏览器,还有原生 APP 等等,这个时候 cookie 是不起作用的,或者浏览器端是可以禁止 cookie 的(虽然可以,但是这基本上是属于吃饱没事干的人干的事)…,但是 token 就不一样,他是登陆请求在登陆成功后再请求响应体中返回的信息,客户端在收到响应的时候,可以把他存在本地的 cookie,storage,或者内存中,然后再下一次请求的请求头重带上这个 token 就行了。简单点来说 cookie-session 机制他限制了客户端的类型,而 token 验证机制丰富了客户端类型。
  3. 时效性。session-cookie 的 sessionid 实在登陆的时候生成的而且在登出事时一直不变的,在一定程度上安全就会低,而 token 是可以在一段时间内动态改变的。
  4. 可扩展性。token 验证本身是比较灵活的,一是 token 的解决方案有许多,常用的是 JWT,二来我们可以基于 token 验证机制,专门做一个鉴权服务,用它向多个服务的请求进行统一鉴权。

下面就拿最常用的 JWT(JSON WEB TOKEN)来说:

JWT 是 Auth0 提出的通过对 JSON 进行加密签名来实现授权验证的方案,就是登陆成功后将相关信息组成 json 对象,然后对这个对象进行某中方式的加密,返回给客户端,客户端在下次请求时带上这个 token,服务端再收到请求时校验 token 合法性,其实也就是在校验请求的合法性。
JWT 对象通常由三部分构成:

  • Headers: 包括类别(typ)、加密算法(alg)
1
2
3
4
{
"alg": "HS256",
"typ": "JWT"
}
  • Claims :包括需要传递的用户信息
1
2
3
4
5
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
  • Signature: 根据 alg 算法与私有秘钥进行加密得到的签名字串, 这一段是最重要的敏感信息,只能在服务端解密;
1
2
3
4
5
HMACSHA256(  
base64UrlEncode(Headers) + "." +
base64UrlEncode(Claims),
SECREATE_KEY
)

编码之后的 JWT 看起来是这样的一串字符:

1
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

OAuth(开放授权)

OAuth(开放授权)是一个开放标准,允许用户授权第三方网站访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方网站或分享他们数据的所有内容,为了保护用户数据的安全和隐私,第三方网站访问用户数据前都需要显式的向用户征求授权。我们常见的提供 OAuth 认证服务的厂商有支付宝,QQ,微信。
OAuth 协议又有 1.0 和 2.0 两个版本。相比较 1.0 版,2.0 版整个授权验证流程更简单更安全,也是目前最主要的用户身份验证和授权方式。

假设是微信授权登录,OAuth 2.0 涉及的关键参与方有:

  • Resource Owner:资源所有者。这里指微信用户。
  • Third-party application:第三方应用。这里指个人网站。。
  • Authorization server:授权服务器。这里指微信开放平台的授权服务。
  • Resource server:资源服务器,用来存储、获取用户资源。这里指的是微信开放平台的服务器。

OAuth 的基本流程

OAuth2.0 主要包含两个关键步骤:

  1. 第三方应用取得用户的授权
  2. 第三方应用访问用户资源

如图所示,access token 的获取分为两步:

  1. 获取授权码 code,这是临时授权凭证:步骤 A、B、C、D
  2. 通过 code 交换 access token,这是正式授权凭证:步骤 E、F
    获取 access token 是重点

1、请求用户授权
第三方应用,将资源所有者导向一个特定的地址,并在地址里带上如下信息:

1
2
3
4
5
response_type:必选,请求类型。这里固定为"code"。
client_id:必选,标识第三方应用的id。很多地方也用apppid来代替。
edirect_uri:可选,授权完成后重定向的地址。当取得用户授权后,授权服务会重定向到这个地址,并在地址里带上授权码。
scope:可选,第三方请求的资源范围。比如是想获取基本信息、敏感信息等。
state:推荐,用于状态保持,可以是任意字符串。授权服务会原封不动地返回。

对于redirect_uri是可选的,大家可能会有疑惑。在实际中,redirect_uri 一般在应用后台就完成了填写和验证,因此可以是选填的。

2、用户授权返回
资源所有者,同意授权第三方应用访问受限资源后,请求返回,跳转到 redirect_uri 指定的地址。
地址中带了如下信息:

1
2
code:必选,授权码。后续步骤中,用来交换access token。
state:必选(如果授权请求中,带上了state),这里原封不动地回传。

3、请求 access token
第三方应用,向授权服务请求获取 access token。请求参数包括:

1
2
3
4
grant_type:必选,许可类型,这里固定为“authorization_code”。
code:必选,授权码。在用户授权步骤中,授权服务返回的。
redirect_uri:必选,如果在授权请求步骤中,带上了redirect_uri,那么这里也必须带上,且值相同。
client_id:必选,第三方应用id。

4、返回 access token
请求合法且授权验证通过,那么授权服务将 access token 返回给第三方应用。
关键返回字段:

1
2
3
access token:必选,访问令牌,第三方应用访问用户资源的凭证。
expires_in:推荐,access token的有效时长。
refresh token:可选,更新access token的凭证。当access token过期,可以refresh token为凭证,获取新的access token。

下图为微信统一登录的时序图:

步骤分解如下:

1、请求用户授权:步骤 2、3、4
带上 appid、redirect_uri、response_type、scope、state。其中:

1
2
3
4
5
appid:应用id,就是前面提到的client_id。
redirect_uri:授权回调的地址,在微信管理后台填写。
response_type:响应类型,固定为"code"。
scope:授权许可范围,固定为"snsapi_login"。
state:可选,授权服务回传。

2、用户授权返回:步骤 5
用户同意授权,重定向到 redirect_uri, 并返回临时票据 code。如下所示:

1
redirect_uri?code=CODE&state=STATE

3、请求 access token
应用拿到临时票据后,用临时票据去换取真实票据 access token。所需参数如下:

1
2
3
4
appid:必选,应用id。
secret:必选,应用秘钥,在微信后台生成。
code:必选,前面获取的授权码。
grant_type:必选,值固定为"authorization_code"

4、返回 access token
微信后台经过验证,确认请求合法后,将 access token 返回给第三方应用。
返回例子如下:

1
2
3
4
5
6
7
8
9
{
"access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE",
"unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"

}

除前面提到的 access_token、refresh_token、expires_in,这里还返回了 openid、unionid,这两者是用户信息,微信体系特有的。

sso(单点登录鉴权)

-------------本文结束感谢您的阅读-------------

本文标题:你需要知道的前端鉴权方式

文章作者:Water

发布时间:2021年08月27日 - 07:08

最后更新:2023年08月01日 - 06:08

原始链接:https://water.buging.cn/2021/08/27/你需要知道的前端鉴权/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

坚持原创技术分享,您的支持将鼓励我继续创作!