快速认识JWT

根据维基百科的定义,JSON WEB TokenJWT,读作 [/dʒɒt/]),是一种基于JSON的、用于在网络上声明某种主张的令牌(token)。JWT通常由三部分组成: 头信息(header), 消息体(payload)和签名(signature)。

第一部分头信息指定了该JWT使用的签名算法,HS256 表示使用了 HMAC-SHA256 来生成签名:

header = '{"alg":"HS256","typ":"JWT"}'

第二部分消息体包含了JWT的载荷信息:

payload = '{"loggedInAs":"admin","iat":1422779638}'

第三部分未签名的令牌由base64编码的头信息和消息体拼接而成(使用".“分隔)

var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);

签名则通过私有的key计算而成,secret是私钥字符串,保管好:

var signature = HMACSHA256(encodedString, 'secret'); 

最后得到token如下:

token = encodedString + '.' + signature

# token看起来像这样: 
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dnZWRJbkFzIjoiYWRtaW4iLCJpYXQiOjE0MjI3Nzk2Mzh9.
gzSraSYS8EXBxLN_oWnFSRgCzcmJmMjLiuyu5CSpyHI 

一般是在请求头里加入Authorization,并加上Bearer标注:

fetch('api/user/1', {
  headers: {
    'Authorization': 'Bearer ' + token
  }
})

注意事项

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

Bearer认证

HTTP提供了一套标准的身份验证框架:服务器可以用来针对客户端的请求发送质询(challenge),客户端根据质询提供身份验证凭证。质询与应答的工作流程如下:服务器端向客户端返回401(Unauthorized,未授权)状态码,并在WWW-Authenticate头中添加如何进行验证的信息,其中至少包含有一种质询方式。然后客户端可以在请求中添加Authorization头进行验证,其Value为身份验证的凭证信息。

在HTTP标准验证方案中,我们比较熟悉的是"Basic"和"Digest”,前者将用户名密码使用BASE64编码后作为验证凭证,后者是Basic的升级版,更加安全,因为Basic是明文传输密码信息,而Digest是加密后传输。在前文介绍的Cookie认证属于Form认证,并不属于HTTP标准验证。

本文要介绍的Bearer验证也属于HTTP协议标准验证,它随着OAuth协议而开始流行。

Bearer验证中的凭证称为BEARER_TOKEN,或者是access_token,它的颁发和验证完全由我们自己的应用程序来控制,而不依赖于系统和Web服务器。

那么使用Bearer验证有什么好处呢?

  • CORS: cookies+CORS 并不能跨域。而Bearer验证在任何域名下都可以使用HTTP header头部来传输用户信息。
  • 对移动端友好: 当你在一个原生平台(iOS, Android, WindowsPhone等)时,使用Cookie验证并不是一个好主意,因为你得和Cookie容器打交道,而使用Bearer验证则简单的多。
  • CSRF: 因为Bearer验证不再依赖于cookies, 也就避免了跨站请求攻击。
  • 标准:在Cookie认证中,用户未登录时,返回一个302到登录页面,这在非浏览器情况下很难处理,而Bearer验证则返回的是标准的401 challenge

PS:在使用在Bearer认证时,通常还需与刷新Token配合来使用,因为JwtToken的验证是无需经过STS的,而当用户执行了退出,修改密码等操作时,是无法使该Token失效的。所以,通常会给access_token设置一个较短的有效期(JwtBearer认证默认会验证有效期,通过notBeforeexpires来验证),当access_token过期后,可以在用户无感知的情况下,使用refresh_token自动从STS重新获取access_token,但这就不属于Bearer认证的范畴了,在后续介绍IdentityServer时再来详细介绍一下。

快速认识OAuth

OAuth是一个开放标准,提供了一种简单和标准的安全授权方法,允许用户无需将某个网站的用户名密码提供给第三方应用就可以让该第三方应用访问该用户在某网站上的某些特定信息(如简单的个人信息)。这份标准历史上有两个,而且不兼容,现在都采用OAuth2标准。

OAuth2协议处理流程

     +--------+                               +---------------+
     |        |--(A)- Authorization Request ->|   Resource    |
     |        |                               |     Owner     |
     |        |<-(B)-- Authorization Grant ---|               |
     |        |                               +---------------+
     |        |
     |        |                               +---------------+
     |        |--(C)-- Authorization Grant -->| Authorization |
     | Client |                               |     Server    |
     |        |<-(D)----- Access Token -------|               |
     |        |                               +---------------+
     |        |
     |        |                               +---------------+
     |        |--(E)----- Access Token ------>|    Resource   |
     |        |                               |     Server    |
     |        |<-(F)--- Protected Resource ---|               |
     +--------+                               +---------------+
  • (A)用户打开客户端以后,客户端要求用户给予授权。
  • (B)用户同意给予客户端授权。
  • (C)客户端使用上一步获得的授权,向认证服务器申请令牌。
  • (D)认证服务器对客户端进行认证以后,确认无误,同意发放令牌。
  • (E)客户端使用令牌,向资源服务器申请获取资源。
  • (F)资源服务器确认令牌无误,同意向客户端开放资源。

客户端的授权模式

客户端必须得到用户的授权(authorization grant),才能获得令牌(access token),而这是解决问题的关键。OAuth 2.0定义了四种授权方式。

1)授权码模式(authorization code) 这种模式是最安全的OAuth2的授权模式。设计了auth code,通过这个code再获取token,最后通过token获取资源。支持refresh token。

image-20210223133144815

应用场景: 各大应用内的qq,微信,微博登录等。比如某应用内的qq登录,过程如下: a.用户点击qq登录,会先跳转到qq登录页面,这时请求已经跳转到qq服务器了,然后用户输入账号或者扫码登录,这时所有请求都在qq服务器完成。 b.用户正确登录后,qq服务器返回用户的code给第三方应用,然后第三方应用再使用code去授权服务器请求获取token。(这一步用户不可见) c.第三方应用获取到token后,再使用token获取用户的qq名称,头像等信息。

优缺点: 优点:用户可以控制自身的一些权限是否给第三方,第三方只能获取到用户临时产生的一个访问的code,安全性。 缺点:认证过程繁琐。

2)隐式授权码模式/简单模式(implicit) 和授权码模式类似,只不过少了获取code的步骤,是直接获取令牌token的,适用于公开的浏览器单页应用,令牌直接从授权服务器返回,不支持刷新令牌,且没有code安全保证,令牌容易因为被拦截窃听而泄露。 不支持refresh token

3)密码模式(resource owner password credentials) 这种模式是最不推荐的,因为client可能存了用户密码 这种模式主要用来做遗留项目升级为oauth2的适配方案 当然如果client是自家的应用,也是可以 支持refresh token

4)客户端模式(client credentials) 这种模式直接根据client的id和密钥即可获取token,无需用户参与 这种模式比较合适消费api的后端服务,比如拉取一组用户信息等 不支持refresh token,主要是没有必要

总结

其实JWT和OAuth2是两个有区别的概念。

JWT是用户首次访问系统,系统返回一个token,以后每次访问只要带上这个token,服务器就知道是那个用户上来了。这是一个用来取代cookie保持会话状态的技术。

OAuth2则是一种身份统一认证的机制,用户只需要拥有一个"认证中心"(通常是大型权威机构)账号密码,就可以用这份账号密码登录某个第三方系统,都不需要在第三方系统中留存自己的账号密码,而且还可以让三方系统访问到用户留存在 “认证中心” 上的基本信息。这个过程中用到了token的概念,但这个token和JWT中的token包含的内容主体不一样。

JWT和OAuth2在实际应用中都很广泛,只不过应用场景不一样。有时候还结合使用。

参考阅读:

https://blog.csdn.net/summer_fish/article/details/134271013

(完)