由来
- Cookie+Session的方式,在用户登录通过验证后,服务端将 数据加密后 通过在响应头(Header)保存到客户端浏览器的Cookie(包含sid或token)中,同时服务器保留相对应的Session(文件或DB)。用户之后发起的请求(Request)都会携带Cookie信息,服 务端需要根据Cookie寻回对应的Session,从而完成验证,确认这是之前登陆过的用户。
- API应该被设计成无状态的(Stateless)。这意味着没有登陆,注销的方法,也没有sessions,API的设计者同样也不能依赖Cookie,因为不能保证这些request是由浏览器所发出的
JWT介绍
- 分为三段,通过解码可以得到:
// 1. Headers
// 包括类别(typ)、加密算法(alg);
{
"alg": "HS256",
"typ": "JWT"
}
// 2. Claims
// 包括需要传递的用户信息;
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
// 3. Signature
// 根据alg算法与私有秘钥进行加密得到的签名字串;
// 这一段是最重要的敏感信息,只能在服务端解密;
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
SECREATE_KEY
)
在使用过程中,服务端通过用户登录验证之后,将Header+Claim信息加密后得到第三段签名,然后将三段签名合并后返回给客户端。客户端获取到token后,应该在每次向服务器请求数据时附带这个token,然后服务端验证token。
因此JWT是用来取代服务端的Session而非客户端Cookie的方案.对于客户端本地存储,不同的选择更多是出于安全性的考虑
客户端在与服务器第一次通信时,通过一些可靠信息(用户名、密码)和服务器交换取token,这个token作为客服端再次请求的权限钥匙。Token通常比密码更加长而且复杂。JWTs通常会长达150个字符。一旦获得了token,在每次调用API的时候都要附加上它。把token想象成一个安全的护照。你在一个安全的前台验证你的身份(通过你的用户名和密码),如果你成功验证了自己,你就可以取得这个。当你走进大楼的时候(试图从调用API获取资源),你会被要求验证你的护照,而不是在前台重新验证。
client
- client 头部值部分使用Bearer<token> Authorization: Bearer <token>
- .set('Authorization', 'Bearer ' + token)
encoded
jwt.sign(payload, secretOrPrivateKey, options, [callback]);
payload
Claims,即传递的用户信息,could be an object literal, buffer or string.if not,it will be coerced into a string using JSON.stringify
.
secretOrPrivateKey
is a string or buffer containing either the secret for HMAC algorithms, or the PEM encoded private key for RSA and ECDSA.字符串,或着fs.readFileSync读取的证书
options
:
-
algorithm
(default:HS256
) 加密算法 -
expiresIn
: expressed in seconds or an string describing a time span rauchg/ms. Eg:60
,"2 days"
,"10h"
,"7d"
有效期/分钟 audience
subject
issuer
noTimestamp
headers
Example
// sign with default (HMAC SHA256)
var jwt = require('jsonwebtoken');
var token = jwt.sign({ foo: 'bar' }, 'shhhhh');
// sign with RSA SHA256
var cert = fs.readFileSync('private.key'); // get private key
var token = jwt.sign({ foo: 'bar' }, cert, { algorithm: 'RS256'});
decoded
var jwt = require('express-jwt');
app.get('/protected',
jwt({secret: 'shhhhhhared-secret'}),
function(req, res) {
if (!req.user.admin) return res.sendStatus(401);
res.sendStatus(200);
});
you can make some paths unprotected as follows:
//设置路由/token 不验证
app.use(jwt({ secret: 'shhhhhhared-secret'}).unless({path: ['/token']}));
By default, the decoded token is attached to req.user but can be configured with the requestProperty option.
默认解密后的token Claim内容加到req.user
//将解密后的token加到req.auth
jwt({ secret: publicKey, requestProperty: 'auth' });
安全性
- XSS 主要原因是对用户输入信息不加过滤,导致用户 (被误导)恶意输入的Js代码在访问该网页时被执行,而Js可以读取当前网站域名下保存的Cookie信息.
对用户输入的所有信息进行过滤即可
,CDN服务也有可能引入不安全的Js脚本 - CSRF,跨站请求伪造 主要利用Cookie是按照域名存储,同时访问某域名时浏览器会自动携带该域名所保存的Cookie信息这一特征.如果执意要将JWT存储在Cookie中,服务端则需要额外验证请求来源.将JWT保存在localStorage中,即将JWT放入Request Header中的Authorization位。