JWT
JSON Web Token( JWT)是目前最流行的跨域認證解決方案。
一般的跨域認證方式
流程如下:
- 客戶端向服務器發送用戶名和密碼
- 服務器驗證通過后,在session中保存相關數據
- 服務器向客戶端返回一個session_token放到cookie中
- 客戶端每次請求都通過cookie攜帶session_token
- 服務器拿到session_token,通過這個找到以前存的用戶信息,進行判斷
上面session中存放的數據,通過數據庫或者別的持久化操作存儲,但是會存在服務器集群數據同步的問題,處理起來比較麻煩。
JWT原理
服務器拿到用戶登錄信息,驗證通過后會根據用戶信息去生成一個Token并攜帶Signature返回給用戶,服務器不存儲session的信息,交由用戶去存儲。
JWT組成
xxxxx.yyyyy.zzzzz
對應的依次如下
- Header
- Payload
- Signature
中間通過.
分隔:Header.Payload.Signature
Header
使用的是Base64URL 算法轉成字符串,解析后是個JSON對象,描述JWT的元數據
{
"alg": "HS256",
"typ": "JWT"
}
Payload
使用的是Base64URL 算法轉成字符串,解析后是個JSON對象,存放JWT需要傳輸的信息
- iss 簽發人
- exp 過期時間
- sub 主體信息
- iat 簽發時間
- aud 受眾
- nbf 生效時間
- jti 編號
注意,JWT 默認是不加密的,任何人都可以讀到,所以不要把重要信息放在這個部分。
Signature
使用你需要的加密方式(默認HMAC SHA256加密)
HMACSHA256(base64UrlEncode(Header) + "." +base64UrlEncode(Payload),secret)
JWT客戶端發送
客戶端收到服務器返回的Token,存儲在cookie、sessionStorage或者localStorage中
在需要認證的時候,將Token添加到Http請求的Header中
Header: {
Authorization: `Bearer ${Token}`
}
注意, Bearer和Token之間有個空格
在node中使用jwt
node-jsonwebtoken
jsonwebtoken是JWT的node版本 github
- 安裝
yarn add jsonwebtoken
- 生成Token
const jwt = require('jsonwebtoken')
const token = jwt.sign({
// 設置一個小時的過期時間
exp: Math.floor(Date.now() / 1000) + 3600,
// 自定義的攜帶信息
data: {
name: user.name,
createTime: user.createTime
}
}, 'demo')
- JWT驗證
jwt.verify(token, 'demo', (err, decode) => {
// decode是攜帶的信息
if (err) {
// 認證失敗
// ...
} else {
// 成功
// ...
}
})
- 路由權限驗證
由于本人使用的是koa搭建的服務端,所以下面是基于koa的操作
const koa = require('koa')
const Router = require('koa-router')
const bodyParser = require('koa-bodyparser')
const jwtKoa = require('koa-jwt')
const app = new koa()
const router = new Router()
app.use(bodyParser())
// JWT攔截
/**
* secret是服務端存儲的密鑰
* unless是不做JWT驗證的條件
* unless.method 不做驗證的請求方式 這里是所有的GET請求不去做攔截
* unless.path 不做驗證的請求路徑 這里是/login請求不去做攔截
*
*/
// 特殊路徑處理,這里的login用的是post
const specialPath = new RegExp(`^\/login`)
app.use(jwtKoa({ secret: 'demo' }).unless({ method: ['GET'], path: [specialPath] }))
router.get('/demo',(ctx, next) => {
})
router.post('/demo', (ctx, next) => {
})
router.del('/demo/:id', (ctx, next) => {
})
router.put('/demo/:id',(ctx, next) => {
})
app.use(router.routes()).use(router.allowedMethods())
jsonwebtoken 文檔
jwt.sign(payload, secretOrPrivateKey, [options, callback])
異步:如果提供回調,則使用err或JWT 調用回調。
同步:將JsonWebToken返回為字符串。
payload
payload 必須是一個object, buffer或者string。注意, exp只有當payload是object字面量時才可以設置。
如果payload不是buffer或string,它將被強制轉換為使用的字符串JSON.stringify()。
secretOrPrivateKey
secretOrPrivateKey 是包含HMAC算法的密鑰或RSA和ECDSA的PEM編碼私鑰的string或buffer。
options
options:
- algorithm:加密算法(默認值:HS256)
- expiresIn:過期時間, 以秒表示或描述時間跨度zeit / ms的字符串。如60,"2 days","10h","7d"
- notBefore:生效時間, 以秒表示或描述時間跨度zeit / ms的字符串。如:60,"2days","10h","7d"
- audience:受眾
- issuer:簽發人
- jwtid:編號
- subject:主體
- noTimestamp
- header
在expiresIn,notBefore,audience,subject,issuer沒有默認值時。也可以直接在payload中用exp,nbf,aud,sub和iss分別表示,但是你不能在這兩個地方同時設置。
生成的jwts通常會默認包含一個iat值除非指定了noTimestamp。
jwt.sign({
exp: Math.floor(Date.now() / 1000) + (60 * 60),
data: '一個小時過期'
}, 'demo')
// 或者
jwt.sign({
data: '一個小時過期'
}, 'demo', { expiresIn: '1h' });
jwt.verify(token,secretOrPublicKey,[options,callback])
用法和jwt.sign差不多,用來驗證token的合法性
jwt.decode(token [,options])
返回解碼沒有驗證簽名是否有效的payload, 用來獲取payload
參考鏈接:JSON Web Token 入門教程,
jsonwebtoken文檔, jwt.io,github: node-jsonwebtoken