项目地址: grading-system
基于session的认证和基于token的认证的方式已经被广泛使用。在session认证中,服务端会存储一份用户登录信息,这份登录信息会在响应时传递给浏览器并保存为Cookie,在下次请求时,会带上这份登录信息,这样就能识别请求来自哪个用户。
在基于session的认证中,每个用户都要生成一份session,这份session通常保存在内存中,随着用户量的增加,服务端的开销会增大,而且对分布式应用不是很友好。
在token认证中,服务端不需要保留用户认证信息。当用户登录时,服务器验证用户信息后会返回一个token,这个token存储在客户端,并且在每次请求的请求头中都带上这个token,这样服务端验证token后就可以返回数据。
JWT(JSON Web Token)是一个开放标准(RFC 7519),它定义了一种紧凑且独立的方式,可以在各方之间作为JSON对象安全地传输信息。 此信息可以通过数字签名进行验证和信任。特别适用于分布式站点的单点登录(SSO)场景。
JWT 是什么,为何要使用 JWT?
JWT 是 JSON Web Tokens 的简称,对于这个问题最精简的回答是,JWT 具有简便、紧凑、安全的特点,具体来看:
简便:只要用户登陆后,使用 JWT 认证仅需要添加一个 http header 认证信息,这可以用一个函数简单实现,我们会在后面的例子中看到这一点。
紧凑:JWT token 是一个 base 64 编码的字符串,包含若干头部信息及一些必要的数据,非常简单。签名后的 JWT 字符串通常不超过 200 字节。
安全:JWT 可以使用 RSA 或 HMAC 加密算法进行加密,确保 token 有效且防止篡改。
总之你可以有一种安全有效的方式来认证用户,并且对所有 api 调用都进行认证,而不需要解析复杂的数据结构或者实现自己的加密算法。
JWT的构成
JWT由 . 分隔的三个部分组成,它们是:
头部(Header)
荷载(Playload)
签名(Signature)
也就是说,JWT只是一个具有以下格式的字符串:
header.payload.signature
头部
头部通常由两部分组成:令牌的类型(即JWT)以及正在使用的散列算法,例如HMAC SHA256或RSA。
header.payload.signature
然后,对这个JSON进行Base64编码,形成JWT的第一部分。
ewogICJhbGciOiAiSFMyNTYiLAogICJ0eXAiOiAiSldUIgp9
荷载
JWT的第二部分是荷载,其中包含声明。 声明是关于实体(通常是用户)和其他数据的声明。声明有三种:注册的声明、公开的声明和私有的声明。
JWT规范定义了七个在标准中注册的声明名称,它们是:
iss: JWT签发者
sub: JWT所面向的用户
aud:接收JWT的一方
exp:JWT的过期时间,这个过期时间必须要大于签发时间
nbf:定义在什么时间之前,该JWT都是不可用的.
iat: JWT的签发时间
jti: JWT的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
对于特定情况,可以使用公共的声明名称。 这些包括:
auth_time:身份验证发生的时间
acr:认证上下文类的引用
nonce:用于将客户端会话与ID Token关联的值
最后,还有私有的声明名称,可以使用它们来传达与身份相关的信息,例如姓名或部门。
由于公共和私人的声明未注册,请注意避免名称冲突。
比如,我们定义一个palyload:
{ "sub": "1234567890", "name": "tc9011", "admin": true, "exp": 1441594722 }
然后将其进行base64加密,得到JWT的第二部分:
ewogICJzdWIiOiAiMTIzNDU2Nzg5MCIsCiAgIm5hbWUiOiAidGM5MDExIiwKICAiYWRtaW4iOiB0cnVlLAogICJleHAiOiAxNDQxNTk0NzIyCn0=
签名
签名由base64编码后的头、base64编码后的荷载和secret组成。
例如,将上面的两个编码后的字符串都用句号 . 连接在一起(头部在前),就形成了:
ewogICJhbGciOiAiSFMyNTYiLAogICJ0eXAiOiAiSldUIgp9.ewogICJzdWIiOiAiMTIzNDU2Nzg5MCIsCiAgIm5hbWUiOiAidGM5MDExIiwKICAiYWRtaW4iOiB0cnVlLAogICJleHAiOiAxNDQxNTk0NzIyCn0=
然后,将上面拼接完的字符串用secret作为秘钥进行HS256加密。
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
使用JWT
一般在会在请求头中加入 Authorization ,并加上 Bearer 进行标注:
fetch('api/v1/user/1', { headers: { 'Authorization': 'Bearer ' + token } })
服务端会验证token,如果验证通过就会返回相应的资源。