Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/dwyl/hapi-auth-jwt2
:lock: Secure Hapi.js authentication plugin using JSON Web Tokens (JWT) in Headers, URL or Cookies
https://github.com/dwyl/hapi-auth-jwt2
Last synced: 5 days ago
JSON representation
:lock: Secure Hapi.js authentication plugin using JSON Web Tokens (JWT) in Headers, URL or Cookies
- Host: GitHub
- URL: https://github.com/dwyl/hapi-auth-jwt2
- Owner: dwyl
- License: isc
- Created: 2015-02-24T15:40:11.000Z (over 9 years ago)
- Default Branch: main
- Last Pushed: 2024-03-29T16:52:04.000Z (7 months ago)
- Last Synced: 2024-04-07T00:02:03.404Z (7 months ago)
- Language: JavaScript
- Homepage:
- Size: 1020 KB
- Stars: 800
- Watchers: 37
- Forks: 124
- Open Issues: 20
-
Metadata Files:
- Readme: README-zh_CN.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
Awesome Lists containing this project
README
[![Known Vulnerabilities](https://snyk.io/test/github/dwyl/hapi-auth-jwt2/badge.svg?targetFile=package.json&style=flat-square)](https://snyk.io/test/github/dwyl/hapi-auth-jwt2?targetFile=package.json)
[![Build Status](https://img.shields.io/travis/dwyl/hapi-auth-jwt2/master.svg?style=flat-square)](https://travis-ci.org/dwyl/hapi-auth-jwt2)
[![codecov.io](https://img.shields.io/codecov/c/github/dwyl/hapi-auth-jwt2/master.svg?style=flat-square)](http://codecov.io/github/dwyl/hapi-auth-jwt2?branch=master)
[![Inline docs](http://inch-ci.org/github/dwyl/hapi-auth-jwt2.svg?branch=master&style=flat-square)](http://inch-ci.org/github/dwyl/hapi-auth-jwt2)
[![HAPI 19.1.0](http://img.shields.io/badge/hapi-19.1.0-brightgreen.svg?style=flat-square "Latest Hapi.js")](http://hapijs.com)
[![Node.js Version](https://img.shields.io/node/v/hapi-auth-jwt2.svg?style=flat-square "Node.js 10 & 12 and io.js latest both supported")](http://nodejs.org/download/)
[![Dependencies Status](https://david-dm.org/dwyl/hapi-auth-jwt2/status.svg?style=flat-square)](https://david-dm.org/dwyl/hapi-auth-jwt2)
[![devDependencies Status](https://david-dm.org/dwyl/hapi-auth-jwt2/dev-status.svg?style=flat-square)](https://david-dm.org/dwyl/hapi-auth-jwt2?type=dev)
[![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat-square)](https://github.com/dwyl/hapi-auth-jwt2/issues)
[![HitCount](http://hits.dwyl.io/dwyl/hapi-auth-jwt2.svg)](http://hits.dwyl.io/dwyl/hapi-auth-jwt2)
[![npm package version](https://img.shields.io/npm/v/hapi-auth-jwt2.svg?style=flat-square)](https://www.npmjs.com/package/hapi-auth-jwt2)
# Hapi Auth using JSON Web Tokens (JWT)使用**JSON Web Tokens**的[**Hapi.js**](http://hapijs.com/)应用程序身份验证方案/插件。
![hapi-auth-jwt2-diagram-verify](https://cloud.githubusercontent.com/assets/194400/11937081/00f9b4bc-a80a-11e5-9f71-a7e05e92f1ae.png)
一个可以让你在你的[Hapi.js](http://hapijs.com/)网站应用中使用`JSON Web Tokens(JWTs)`的Node.js包(Hapi组件)。
如果你对`JWTs`基本不了解,我们编写了一个介绍文章来解释该概念和好处:[https://github.com/dwyl/learn-json-web-tokens](https://github.com/dwyl/learn-json-web-tokens)
如果你(或者你团队中的伙伴)不熟悉**Hapi.js**,我们这里有一个快速指南[https://github.com/dwyl/learn-hapi](https://github.com/dwyl/learn-hapi)
其他语言的文档:
- [English](./README.md)
## 使用(Usage)
我们尽可能使该组件对用户(开发者)友好,但是如果有什么不明白的地方,请在Github上以issue的形式提交问题: [https://github.com/dwyl/hapi-auth-jwt2/issues](https://github.com/dwyl/hapi-auth-jwt2/issues)
### NPM安装
```sh
npm install hapi-auth-jwt2 --save
```### 示例
基础示例会对你开始使用有所帮助:
```javascript
const Hapi = require('@hapi/hapi');const people = { // 模拟我们的用户数据库
1: {
id: 1,
name: 'Jen Jones'
}
};// 编写你自己的验证函数
const validate = async function (decoded, request, h) {// 判断该用户是否正确
if (!people[decoded.id]) {
return { isValid: false };
}
else {
return { isValid: true };
}
};const init = async () => {
const server = new Hapi.server({ port: 8000 });
// 在这里引入我们的包 ↓↓, 例如, require('hapi-auth-jwt2')
await server.register(require('../lib'));
server.auth.strategy('jwt', 'jwt',
{ key: 'NeverShareYourSecret', // 不要告诉别人你的secret key
validate // 上面定义的验证函数
});server.auth.default('jwt');
server.route([
{
method: "GET", path: "/", config: { auth: false },
handler: function(request, h) {
return {text: 'Token not required'};
}
},
{
method: 'GET', path: '/restricted', config: { auth: 'jwt' },
handler: function(request, h) {
const response = h.response({text: 'You used a Token!'});
response.header("Authorization", request.headers.authorization);
return response;
}
}
]);
await server.start();
return server;
}
init().then(server => {
console.log('Server running at:', server.info.uri);
})
.catch(err => {
console.log(err);
});```
### *快速示例*
打开你的控制台,克隆以下仓库:
```sh
git clone https://github.com/dwyl/hapi-auth-jwt2.git && cd hapi-auth-jwt2
```运行服务:
```sh
npm install && node example/server.js
```现在(*在另一个终端窗口*)使用`cURL`去访问这两个路径:
```sh
curl -v http://localhost:8000/
```#### 需要令牌(Token Required)
试着*不带Token*去访问`/restricted`内容(会出现**401 error**)
```sh
curl -v http://localhost:8000/restricted
```或者在你的浏览器中访问: http://localhost:8000/restricted。
(*所有请求都会被禁止并且返回一个`401 Unauthorized`错误*)
现在使用如下格式去访问:
`curl -H "Authorization: " http://localhost:8000/restricted`这里有个*有效的*Token你可以使用(*复制-粘贴*该命令):
```sh
curl -v -H "Authorization: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwibmFtZSI6IkFudGhvbnkgVmFsaWQgVXNlciIsImlhdCI6MTQyNTQ3MzUzNX0.KA68l60mjiC8EXaC2odnjFwdIDxE__iDu5RwLdN1F2A" \
http://localhost:8000/restricted
```或者在你的浏览器中访问该url(*在url中传入这个token*):
http://localhost:8000/restricted?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwibmFtZSI6IkFudGhvbnkgVmFsaWQgVXNlciIsImlhdCI6MTQyNTQ3MzUzNX0.KA68l60mjiC8EXaC2odnjFwdIDxE__iDu5RwLdN1F2A
这样就可以了。
现在编写你自己的`验证函数(validate)`在允许访问者继续操作之前执行检查并**解码**Token。
## 文档(Documentation)
- `key` - (***必须*** - *除非你有一个`自定义认证`函数*)这个秘钥(或一系列潜在的秘钥)用于检查token的签名 ***或者*** 一个**秘钥查找函数**和签名`async function(decoded)`例如:
- `decoded` - 从客户端发来的 ***解码*** 但未 ***验证*** 的JWT。
- 返回一个对象 `{ key, extraInfo }` 包含:
- `key` - 秘钥(或者一系列需要尝试的秘钥)
- `extraInfo` - (***可选***) 任何你想要在`验证函数(validate)`中访问使用的额外信息。
- 当秘钥查找失败时抛出一个Boom错误。参考[示例](https://github.com/dwyl/hapi-auth-jwt2/blob/master/test/dynamic_key_server.js)和[相关测试](https://github.com/dwyl/hapi-auth-jwt2/blob/master/test/dynamic_key.test.js)。
- `validate` - (***必须***) 该函数`async function(decoded, request, h)`会与签名在Token被解码后运行一次。
- `decoded` - (***必须***) 是收到请求的JWT解码和验证之后的参数。
- `request` - (***必须***) 是从客户端收到的原始 ***请求***。
- `h` - (***必须***) 响应工具集。
- 返回一个对象 `{ isValid, credentials, response }`
- `isValid` - 如果JWT验证通过,为`true`,否则就为`false`。
- `credentials` - (***可选***) 要设置替代凭证,而不是`解码后的数据`。
- `response` - (***可选***) 如果提供,将立即用作接管响应。
- `errorMessage` - (***可选*** *默认为* `'Invalid credentials'`) - 如果令牌无效,则会向Boom发出错误消息 (作为`errorContext.message`传递给`errorFunc`)### *可选* 参数
- `verifyOptions` - (***可选*** *默认为none*) 定义tokens如何被[jsonwebtoken](https://github.com/auth0/node-jsonwebtoken#jwtverifytoken-secretorpublickey-options-callback)库验证。
- `ignoreExpiration` - 忽略过期token
- `audience` - 对[*audience*](http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html#audDef)不强制执行
- `issuer` - 不需要发行人(issuer)被验证
- `algorithms` - 允许的算法列表
- `responseFunc` - (***可选***) 在写入响应头或payload中之前调用的函数,使用身份验证头装饰响应:
- `request` - 请求对象。
- `h` - 响应工具集。
- `errorFunc` - (***可选*** *默认情况下抛出请求错误*)当错误被抛出时,调用该函数。它提供了一个允许主机可以自定义错误信息的扩展点。传入的对象遵循以下范式:
- `errorContext` - 请求对象。
- `errorContext.errorType` - ***必须*** 调用`Boom`方法。(例如,未认证)
- `errorContext.message` - ***必须*** 调用`Boom`方法时传入的`message`
- `errorContext.schema` - 调用`Boom`方法时传入的`schema`
- `errorContext.attributes` - 调用`Boom`方法时传入的`attributes`
- 该函数返回了上述所有字段修改后的`errorContext`
- `request` - 请求对象。
- `h` - 响应工具集。
- `urlKey` - (***可选*** *默认 `'token'`*) - 如果你更愿意通过url来传递你得token,直接添加一个url参数`token`到你的请求中或者用一个`urlKey`设置的自定义参数。设置`urlKey`为`false`或`''`禁用该url参数。
- `cookieKey` - (***可选*** *默认 `'token'`*) - 如果你更愿意设置你自己的cookie键名或者你得项目已经有一个键名为`token`的cookie了,你可以通过`options.cookieKey='yourkeyhere'`设置为你的cookie设置一个自定义的键名。
- `headerKey` - (***可选*** *默认 `'authorization'`*) - 从http头读取token的键名小写形式。如果想要禁止从(请求)头读取token,设置该值为`false`或`''`。
- `payloadKey` - (***可选*** *默认 `'token'`*) - 从http的POST请求体中读取token的键名小写形式。如果想要禁止从http的POST请求体中读取token,设置该值为`false`或`''`。请注意,除非`attemptToExtractTokenInPayload`为`false`,否则这不能防止认证失败。
- `tokenType` - (***可选*** *默认为空*) - 允许自定义token类型。例如:`Authorization: 12345678`。
- `complete` - (***可选*** *默认为`false`*) - 设为`true`获取完整的token(`decoded.header`, `decoded.payload` 和 `decoded.signature`)作为`verify`回调函数的`decoded`的参数。
- `headless` - (***可选*** *默认为空*) - 设置为一个`对象`,其中包含JWT令牌头部内容,应将其添加到收到的无头的JWT令牌中。带有头部的令牌仍可在激活此选项的情况下使用。例如:`{ alg: 'HS256', typ: 'JWT' }`
- `attemptToExtractTokenInPayload` - (***可选*** *默认为`false`*) - 设为`true`让`authenticate`方法遇到含有`payload`(POST请求体),从`payload`中提取令牌
- `customExtractionFunc` - (***可选***) 调用该函数以执行JWT的自定义提取,其中:
- `request` - 请求对象。### 有用的特性
+ 从请求中提取的*解码的*JWT(token)会在`request`对象以`request.auth.token`提供,以供之后你在请求的生命周期中使用。该特性被@mcortesi在[hapi-auth-jwt2/issues/123](https://github.com/dwyl/hapi-auth-jwt2/issues/123)提及
### 理解请求流
最简单的,这是使用`hapi-auth-jwt2`通过Hapi应用的请求流:
![hapi auth request flow](https://cloud.githubusercontent.com/assets/443149/11938155/a5fa9554-a7cd-11e5-92b1-01efd6841ded.png)
### verifyOptions让您定义如何验证令牌(*可选*)
示例:
```js
server.auth.strategy('jwt', 'jwt', true,
{ key: 'NeverShareYourSecret', // 不要告诉别人你的secret key
validate: validate, // 上面定义的验证函数
verifyOptions: {
ignoreExpiration: true, // 过期token不会抛出错误
algorithms: [ 'HS256' ] // 指定你的安全算法
}
});
```更多请阅读:[jsonwebtoken verify options]( https://github.com/auth0/node-jsonwebtoken#jwtverifytoken-secretorpublickey-options-callback)
### 指定签名算法(*可选,但是强烈建议使用*)
阅读[security reasons](https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/),它建议您指定在对令牌进行签名时使用的允许算法。
```js
server.auth.strategy('jwt', 'jwt', true,
{ key: 'YourSuperLongKeyHere', // 不要告诉别人你的secret key
validate: validate, // 上面定义的验证函数
verifyOptions: { algorithms: [ 'HS256' ] } // 只允许HS256算法
});
```如果你更愿意*不*简单地使用这些verifyOptions中的任何一个,那么在您的应用中注册插件时,请勿进行设置;它们都是可选的。
该特性在[issues/29](https://github.com/dwyl/hapi-auth-jwt2/issues/29)中被要求到。
### 使用base64编码秘钥
某些认证服务(例如Auth0)提供base64加密的秘钥。若要判断你的认证服务是否是这些服务之一,请尝试在[http://jwt.io/](http://jwt.io/)上的验证器上尝试使用base64编码秘钥选项。
如果你的秘钥是base64编码的,为了让`JWT2`使用它,您需要将其转换为`Buffer`。以下是一个使用示例:
```js
server.auth.strategy('jwt', 'jwt', true,
{ key: Buffer.from('', 'base64'), // 不要告诉别人你的secret key
validate: validate, // 上面定义的验证函数
verifyOptions: { algorithms: [ 'HS256' ] } // 只允许HS256算法
});
```### 认证模式
该插件在路由上支持[认证模式](http://hapijs.com/api#route-options)。
- `required` - 每个请求都需要带上JWT
- `optional` - 如果没有提供JWT,请求将会将`request.auth.isAuthenticated`设为`false`并且`request.auth.credentials`会设置为空
- `try` - 与`optional`相似,但是无效的JWT可以将`request.auth.isAuthenticated`设置为`false`并且`request.auth.credentials`提供错误的认证。### 有关键和键查找功能的其他说明
- 查找秘钥的选项已添加,以支持"multi-tenant(多用户)"环境。一个使用场景是那些为其客户贴上API服务标签且无法使用共享密钥的公司。如果键查找功能需要使用令牌头中的字段(例如[x5t header](http://self-issued.info/docs/draft-jones-json-web-token-01.html#ReservedHeaderParameterName)),设置选项`completeToken`为`true`。
- 你可能想在回调中传递回`extraInfo`的原因是,你可能需要执行数据库调用来获取密钥,该密钥也可能返回有用的用户数据。这可以节省你再次调用`validate`。
- 键查找功能返回的键或值也可以是要尝试的键数组。将会依次尝试秘钥,直到其中一个密钥成功验证签名为止。仅当所有密钥均无法验证时,请求才会被认定为未经授权。如果你要支持多个有效密钥(例如,当客户端切换到新密钥时继续接受已弃用的密钥),这将非常有用。
```js
server.auth.strategy('jwt', 'jwt', true,
{ key: [ 'PrimareSecretKey', 'DeprecatedKeyStillAcceptableForNow' ],
validate: validate,
verifyOptions: { algorithms: [ 'HS256' ] }
});
```## URL(URI) Token
好些个用户请求在请求的URL中传入JSNOWebTokens的能力:
[dwyl/hapi-auth-jwt2/issues/**19**](https://github.com/dwyl/hapi-auth-jwt2/issues/19)
### 使用
按照上面的描述安装你的hapi.js服务端(*在urls中使用JWT令牌无需特殊的安装*)
```sh
https://yoursite.co/path?token=your.jsonwebtoken.here
```你将需要生成/提供一个有效的令牌以供这个正常使用。
```js
const JWT = require('jsonwebtoken');
const obj = { id:123,"name":"Charlie" }; // 你想要加密的object或者信息
const token = JWT.sign(obj, secret);
const url = "/path?token="+token;
```> 如果我想要*禁用*这个在URL中传递JWT的功能呢?
> 设置`urlKey`为`false`或者`''`。(由@bitcloud: [issue #146](https://github.com/dwyl/hapi-auth-jwt2/pull/146)添加)## 生成你的秘钥
@skota在[dwyl/hapi-auth-jwt2/issues/**48**](https://github.com/dwyl/hapi-auth-jwt2/issues/48)中提出"***怎样生成秘钥***"的疑问。
这里有*一些*选项去生成秘钥。
最*简单*的方法就是在你的终端运行Node的`crypto`哈希算法:
```js
node -e "console.log(require('crypto').randomBytes(256).toString('base64'));"
```然后拷贝该base64格式的结果作为你的JWT秘钥。
## 想要在验证*之后*访问JWT令牌?
[@mcortesi](https://github.com/mcortesi)在[dwyl/hapi-auth-jwt2/issues/**123**](https://github.com/dwyl/hapi-auth-jwt2/issues/123)中提出想要访问用于身份认证的*原始*JWT令牌。
你可以使用`request.auth.token`属性在处理程序中或请求生命周期内的任何其他函数中访问提取的JWT令牌。
*注意*这个是 ***加密的令牌***,它仅仅在你想要用该用户的令牌请求别的服务器时比较有用。
*解密*的token,可以通过`request.auth.credentials`访问
## 想要发送你的JWT/在Cookie中存储你的JWT?
[@benjaminlees](https://github.com/benjaminlees)在[dwyl/hapi-auth-jwt2/issues/**55**](https://github.com/dwyl/hapi-auth-jwt2/issues/55)中要求以cookies的形式发送/接口令牌
因此,我们添加了*可选地*将令牌发送/存储在Cookie中的功能以此来简化构建您的*网络应用程序*。
要在你的应用程序中启用cookie支持,你需要做的就是添加几行代码:
### Cookie选项
首先设置选项你想要应用到cookie:
```js
const cookie_options = {
ttl: 365 * 24 * 60 * 60 * 1000, // 从今天开始一年后过期
encoding: 'none', // 我们已经使用JWT进行编码
isSecure: true, // 温暖且模糊的感觉
isHttpOnly: true, // 防止客户变更
clearInvalid: false, // 移除无效cookies
strictHeader: true // 不允许违反RFC-6265
}
```### 在你的`reply`中设置cookie
然后,在你的认证控制器中:
```js
reply({text: 'You have been authenticated!'})
.header("Authorization", token) // JWT的令牌在哪
.state("token", token, cookie_options) // 通过选项设置cookie
```*详细的*示例请看:[https://github.com/nelsonic/hapi-auth-jwt2-cookie-example](https://github.com/nelsonic/hapi-auth-jwt2-cookie-example)
#### 背景阅读(*Cookie*)
+ 维基百科有个很好的介绍:[https://en.wikipedia.org/wiki/HTTP_cookie](https://en.wikipedia.org/wiki/HTTP_cookie)
+ Cookie的解释(由Nicholas C. Zakas - JavaScript über-master)[http://www.nczonline.net/blog/2009/05/05/http-cookies-explained/](http://www.nczonline.net/blog/2009/05/05/http-cookies-explained/)
+ 非官方的cookie FAQ:[http://www.cookiecentral.com/faq/](http://www.cookiecentral.com/faq/)
+ HTTP状态管理机制(很长但是完整):[http://tools.ietf.org/html/rfc6265](http://tools.ietf.org/html/rfc6265)- - -
## FAQ
### 我是否*需要*在我的项目中包含`jsonwebtoken`?
**Q**:我是否必须将**jsonwebtoken**引入我的项目中(鉴于**hapi-auth-jwt2**已经包含了它)?在[hapi-auth-jwt2/issues/32](https://github.com/dwyl/hapi-auth-jwt2/issues/32)有问到。
**A**:是的,如果你想要在你的应用中 ***加密(sign)*** JWT 你需要*手动地*使用NPM命令`npm install jsonwebtoken --save`安装**jsonwebtoken** Node模块。
即便**hapi-auth-jwt2**已经在**依赖**中包含了它,但是你的应用无法知道在**node_modules**依赖树中的哪里可以找到它。
除非你在引入它的时候使用**相对定位**,例如:
```js
const JWT = require('./node_modules/hapi-auth-jwt2/node_modules/jsonwebtoken');
```我们*建议*在你的**package.json**文件中 ***明确的*** 将其作为你项目的一个**依赖项**。
### ***自定义验证***?
我们能否提供一个 ***自定义验证*** 函数来替代使用**JWT.verify**方法?该问题由[Marcus Stong](https://github.com/stongo)和[Kevin Stewart](https://github.com/kdstew)分别在[issue #120](https://github.com/dwyl/hapi-auth-jwt2/issues/120)和[issue #130](https://github.com/dwyl/hapi-auth-jwt2/issues/130)中提出。
**Q**:该模块是否支持自定义验证函数或者禁用验证?
**A**:是的,它*现在支持了*!(*请看下面的“高级用法”*)包含`verify`可以使你对传入JWT的验证的*完全控制*。
### 我能将`hapi-auth-jwt2`与[`glue`](https://github.com/hapijs/glue)一起使用么?
一些用户询问我们该组件是否与Hapi的“Server Composer”[`glue`](https://github.com/hapijs/glue)兼容
答案是 ***当然***!这里有一个示例如何去做,请看[@avanslaars](https://github.com/avanslaars)的代码示例:[https://github.com/dwyl/hapi-auth-jwt2/issues/151#issuecomment-218321212](https://github.com/dwyl/hapi-auth-jwt2/issues/151#issuecomment-218321212)
### 我怎样*废除*一个*存续的令牌*?
由[@SanderElias](https://github.com/SanderElias)在[hapi-auth-jwt2/issues/126](https://github.com/dwyl/hapi-auth-jwt2/issues/126)问及
我们将基于JWT的回话存储在Redis数据存储区中,并在`验证(validate)`(*验证函数*)期间查找给定JWT的会话(`jti`),请看:[https://github.com/dwyl/hapi-auth-jwt2-example/blob/791b0d3906d4deb256daf23fcf8f5021905abe9e/index.js#L25](https://github.com/dwyl/hapi-auth-jwt2-example/blob/791b0d3906d4deb256daf23fcf8f5021905abe9e/index.js#L25)
这意味着我们可以在Redis中使会话无效,然后拒绝使用“旧的”或无效JWT的请求。请看:[https://github.com/dwyl/hapi-auth-jwt2-example/blob/791b0d3906d4deb256daf23fcf8f5021905abe9e/index.js#L25](https://github.com/dwyl/hapi-auth-jwt2-example/blob/791b0d3906d4deb256daf23fcf8f5021905abe9e/index.js#L25)
### 我怎样才能使*所有路由*都用上JWT验证?
[@abeninskibede](https://github.com/abeninskibede)在[hapi-auth-jwt2/issues/149](https://github.com/dwyl/hapi-auth-jwt2/issues/149)中问到怎样使所有路由都用上JWT验证
我们倾向于设置'jwt'默认策略为*所有*路由都开启`hapi-auth-jwt2`(故所有接口都会是`required`)因为在我们的应用中*大部分*接口都要求人/用户是被认证的。例如:
```js
server.auth.strategy('jwt', 'jwt', {
...
});
server.auth.default('jwt'); // 所有路由都会要求JWT认证
```当你想要一个特殊的路由的JWT认证为 ***不必须*** 时,你只需要简单的设置`config: { auth: false }`。例如:
```js
server.route({
method: 'GET',
path: '/login',
handler: login_handler, // 显示登录/注册表单/页面
options: { auth: false } // 不需要人们登录才能看到登录页面!
});
```*理解*所有关于Hapi认证最好的地方就是文档:[http://hapijs.com/tutorials/auth#setting-a-default-strategy](http://hapijs.com/tutorials/auth#setting-a-default-strategy)
但是如果你有什么文档解答不了的问题,请随意提[issue](https://github.com/dwyl/hapi-auth-jwt2/issues)
### 当一个令牌已经过期时如何*重定向*?
@traducer 和 @goncalvesr2 在[hapi-auth-jwt2/issues/161](https://github.com/dwyl/hapi-auth-jwt2/issues/161)和[hapi-auth-jwt2/issues/148](https://github.com/dwyl/hapi-auth-jwt2/issues/148)中分别有问到当认证失败后如何重定向的问题。
如果认证失败了,`hapi-error`(https://github.com/dwyl/hapi-error)可以让你*轻松地*重定向到任何你定义的url上(换句话说:`statusCode 401`)
请看[https://github.com/dwyl/hapi-error#redirectredirecting-to-another-endpoint](https://github.com/dwyl/hapi-error#redirectredirecting-to-another-endpoint)(*代码示例*)
### 如何更改我的令牌并重新存储它而不会变得未经身份验证?
举个例子:
如果最初添加到你的`/`接口初始化的`request.auth.credentials`对象为:
```js
{
userId: 1,
permission: 'ADMIN'
}
```然后你想要修改用户的权限为`SUPER_ADMIN`。
检索作为标记添加到`/`的初始会话对象
```js
const session = request.auth.credentials;
```修改该对象
```js
session.permission = 'SUPER_ADMIN';
```再次加密一个JWT令牌
```js
const token = JWT.sign(session, process.env.JWT_SECRET);
```像往常一样回复,同时将令牌重新添加到原始接口`/`中。
```js
reply().state('token', token, { path: '/' }).redirect('/wherever');
```## *我如何在禁用JS的情况下支持用户*
当JWT太大而无法传递查询字符串时,如果支持禁用JavaScript的用户,就会出现问题。
在禁用JS的情况下,无法使用从OAuth提供程序到消费服务的重定向将令牌添加到标头中。
云提供商将对URI长度施加限制
OAuth服务可能并不总是位于受保护服务的同级子域上,从而无法使用安全Cookie
在这种情况下,如果用户禁用了JS,传递令牌的唯一方法是使用HTML表单(该令牌在隐藏的字段中)和带有说明的按钮,则提示用户按此按钮;如果JS被启用了,某些JS则将自动提交表单
为了配置`hapi-auth-jwt`以支持这种情况,你需要调整以下样本配置
```js
server.auth.strategy('jwt', 'jwt', {
key: 'NeverShareYourSecret',
// 定义你自己的验证函数
// useful/custom with the decodedToken before reply(ing)
validate: (decoded, request) => true,
verifyOptions: { algorithms: [ 'HS256' ] }, // 只允许HS256加密算法
attemptToExtractTokenInPayload: true,
// 使用yar作为session缓存存储tokens, 详见: https://github.com/hapijs/yar
customExtractionFunc: request => {
if (request.auth && request.auth.token) {
request.yar.set('token', request.auth.token)
return request.auth.token;
}
const token = request.yar.get('token');
if (token) {
return token;
}
}
});
```上面的配置仍将运行请求头,Cookie,查询字符串参数和自定义提取的常规令牌提取尝试。但是,如果没有成功提取的令牌,它将尝试从POST请求正文中提取一个令牌
由于HAPI请求的身份验证阶段将在分析POST主体之前应用范围保护,你还需要定义在没有应用范围的情况下处理JWT的路由,否则当您将范围全局应用为您的一部分时,带有JWT有效负载的POST请求将失败应用
```js
server.route([
{
method: 'POST',
path: '/',
handler: (request, response) => response.redirect('/home'),
config: {
auth: {
strategies: ['jwt'],
payload: 'required'
}
}
}
]);
```当将JWT发布到从验证阶段到有效负载验证阶段的故障转移时,此路由将提取JWT,将其存储在YAR会话缓存中,并使用标准302响应将用户重定向到`/home`路径。 当`/home`的处理程序受JWT保护时,在认证策略中定义的`customExtractionFunc`将从用户会话缓存中读取JWT并将其用于身份验证
## *高级/替代*使用 => 使用你自己的`验证(verify)`
虽然*多数*使用`hapi-auth-jwt2`的人会选择*更简单的*用例(*使用一个* ***验证函数*** *参见上面的用法——在验证JWT有效负载后对其进行验证*)。但是其他人可能需要对`验证(verify)`步骤进行更多控制。
`hapi-auth-jwt2`的[*internals*](https://github.com/dwyl/hapi-auth-jwt2/blob/eb9fff9fc384fde07ec2a1d2f0da520be902da2c/l˜/index.js#L58)使用`jsonwebtoken.verify`方法去 ***验证*** JWT是否使用 `JWT_SECRET`(*秘钥*)加密。
如果您更愿意指定自己的验证逻辑,而不要使用`验证(verify)`方法,最简单的方法就是在初始化该插件时定义一个`verify`方法进行代替。
- `verify` - (***可选***) 在令牌被解码时将会执行一次验证的函数(*而不是`validate`*)`async function(decoded, request)`:
- `decoded` - (***必须***) 从请求中获取的解码的但是 ***未验证***的数据
- `request` - (***必须***) 从客户端接收到的原始的 ***请求***
- 返回一个对象 `{ isValid, credentials }`:
- `isValid` - 如果JWT验证通过为`true`,否则为`false`
- `credentials` - (***可选***) 用来替代`decoded`的凭证这种方法的优点是它使人们可以编写一个自定义验证功能或*完全*绕过`JWT.verify`。
获取更多详情,请看:[https://github.com/dwyl/hapi-auth-jwt2/issues/120](https://github.com/dwyl/hapi-auth-jwt2/issues/120)中关于使用场景的讨论
> ***Note***:*没人会要求 ***同时*** 使用`validate` 和 `verify`*.
这不应该是*必须的*,因为使用`verify`你可以合并你自己的逻辑。
### 兼容性
**`10.x.x`** 版本的 **`hapi-auth-jwt2`** 是一个 ***可选的升级***,因为它包含了突破性的(破坏性的)修改。
一些该插件的用户要求提供`artifacts`,以使身份验证成功后返回的是`Object`而不是`String`。
遗憾的是,这是一个具有破坏性的修改,因此只能放在新的主要版本中。
仅支持 **Node.js 12+** 的 **`9.x.x`** 版本的 **`hapi-auth-jwt2`** 与 **`19.x.x`版本的Hapi.js** 兼容。
`9.0.0`的 **`hapi-auth-jwt2`** 较之 `v8.8.1`版本没有*任何*代码改动(*故不需要更新使用此插件的代码*)。
我们认为应该谨慎地向人们表明[Hapi.js(*核心框架*)放弃了对Node.js 10的支持](https://github.com/dwyl/hapi-auth-jwt2/issues/338#issuecomment-583716612),同时人们也应该将该插件视为不再支持旧版本的Node。
`8.x.x`版本的`hapi-auth-jwt2`与`17.x.x` - `19.x.x`版本的Hapi.js兼容
`7.x.x`版本的`hapi-auth-jwt2`与`16.x.x` `15.x.x` `14.x.x` `13.x.x` `12.x.x` `11.x.x` `10.x.x` `9.x.x` `8.x.x` 版本的Hapi.js兼容,`17.x.x`版本的Hapi.js是一次重大的重写,这就是为什么`8.x.x`版本的`hapi-auth-jwt2`插件不向后兼容的原因!
但是为了安全和性能,我们*建议*使用[*最新版本*](https://github.com/hapijs/hapi/)的Hapi。
> *如果你有问题,或者需要帮助的地方* ***请提交一个issue/问题到Github***:[https://github.com/dwyl/hapi-auth-jwt2/issues](https://github.com/dwyl/hapi-auth-jwt2/issues)
### 生产环境的实例?
#### 使用PostgreSQL?
请看:[https://github.com/dwyl/hapi-login-example-postgres](https://github.com/dwyl/hapi-login-example-postgres)
#### 使用Redis
使用Redis在每个经过身份验证的请求上存储会话数据是*完美的选择*。
如果你不熟悉Redis或团队中的某人需要复习,请看[https://github.com/dwyl/learn-redis](https://github.com/dwyl/learn-redis)
***代码*** 和测试在[https://github.com/dwyl/hapi-auth-jwt2-example](https://github.com/dwyl/hapi-auth-jwt2-example)。如果有不清楚的地方欢迎随时提问!
一个更真实的例子是由[@manonthemat](https://github.com/manonthemat)维护的,请看[hapi-auth-jwt2/issues/9](https://github.com/dwyl/hapi-auth-jwt2/issues/9)
### 真实案例?
如果你想要看一下关于该插件在一个 ***生产环境***中的网页应用(API)的 ***真实案例***,请看[https://github.com/dwyl/time/tree/master/api/lib](https://github.com/dwyl/time/tree/master/api/lib)
+ **app.js** ***注册*** 了该 **hapi-auth-jwt2插件**:
[app.js#L13](https://github.com/dwyl/time/blob/0a5ec8711840528a4960c388825fb883fabddd76/app.js#L13)+ 告诉app.js在哪找到我们的**验证**函数:
[app.js#L21](https://github.com/dwyl/time/blob/0a5ec8711840528a4960c388825fb883fabddd76/app.js#L21)+ **验证**函数(我们怎样查验JWT是否有效):
[api/lib/auth_jwt_validate.js](https://github.com/dwyl/time/blob/0a5ec8711840528a4960c388825fb883fabddd76/api/lib/auth_jwt_validate.js)
在我们的ElasticSearch数据库中查找该人的会话,如果[***找到*** (有效)会话记录并且未结束](https://github.com/dwyl/time/blob/0a5ec8711840528a4960c388825fb883fabddd76/api/lib/auth_jwt_validate.js#L12)我们就允许该用户浏览限制的内容。+ **加密你的JWT**:在你的应用中,你需要一个方法去 *加密* JWT(并且将其存入数据库中,如果那是您*验证*会话的方式的话),我们的是:[api/lib/auth_jwt_sign.js](https://github.com/dwyl/time/blob/0a5ec8711840528a4960c388825fb883fabddd76/api/lib/auth_jwt_sign.js#L18)
如果你有 ***任何问题*** 关于这个,请提交issue/提问到Github:
[https://github.com/dwyl/hapi-auth-jwt2/issues](https://github.com/dwyl/hapi-auth-jwt2/issues)
(*我们将在这里帮助你开始你的 **Hapi**(Happy)之旅! *)## 贡献[![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/dwyl/hapi-auth-jwt2/issues)
如果你发现有待改进的地方,请提交一个issue到:[https://github.com/dwyl/hapi-auth-jwt2/issues](https://github.com/dwyl/hapi-auth-jwt2/issues)总有dwyl团队的一员会*一直*在线,所以我们会在几小时内给你答复。
## 动机
当开发[***Time***](https://github.com/dwyl/time)时,我们想确保我们的应用(和API)能够*尽可能的* ***易用***。
这导致我们使用JSON Web Token进行无状态身份验证。我们针对那些*可能*解决我们问题的*现存的*模块做了一个*广泛的*[调查](https://www.npmjs.com/search?q=hapi+auth+jwt);他们大多在NPM上:![NPM搜索hapi+jwt关键字](http://i.imgur.com/xIj3Xpa.png)
但是它们总是 ***太复杂了***,只有匮乏的文档和*用处不大*(没有真实案例)的“例子”。
并且,没有一个*现存的*模块对**验证方法(validate)**暴露了**request**对象,我们认为这样会更方便一些。
所以我们决定根据这些issue写一个我们自己的模块。
*不要相信我们,做自己的功课,然后决定自己喜欢哪个模块。*
## 为什么选择hapi-auth-jwt2?
我们想选的名字被选了。
我们认为我们的模块是“***新的,简化且积极维护的版本***”## 有用的链接
+ 更多有关于jsonwebtokens(JWTs)请看我们的详细概述:
[https://github.com/dwyl/learn-json-web-tokens](https://github.com/dwyl/learn-json-web-tokens)+ 保护Hapi客户端会话:
[https://blog.liftsecurity.io/2014/11/26/securing-hapi-client-side-sessions](https://blog.liftsecurity.io/2014/11/26/securing-hapi-client-side-sessions)### Hapi.js认证
我从以下项目中借鉴了代码:
+ [http://hapijs.com/tutorials/auth](http://hapijs.com/tutorials/auth)
+ [https://github.com/hapijs/hapi-auth-basic](https://github.com/hapijs/hapi-auth-basic)
+ [https://github.com/hapijs/hapi-auth-cookie](https://github.com/hapijs/hapi-auth-cookie)
+ [https://github.com/hapijs/hapi-auth-hawk](https://github.com/hapijs/hapi-auth-hawk)
+ [https://github.com/ryanfitz/hapi-auth-jwt](https://github.com/ryanfitz/hapi-auth-jwt)
(Ryan起了个好头——然而,当我们准备提交一个[pull request](https://github.com/ryanfitz/hapi-auth-jwt/pull/27)去增加它的(*安全*),却被*忽略*了*好几周*……一个[依赖](https://david-dm.org/ryanfitz/hapi-auth-jwt)中的*认证*组件[***忽略安全更新***](https://github.com/ryanfitz/hapi-auth-jwt/issues/26))这对我们来说是 ***不行的***,***安全很重要!***如果你在 ***hapi-auth-jwt2*** 中发现任何问题,请创建一个issue
[https://github.com/dwyl/hapi-auth-jwt2/issues](https://github.com/dwyl/hapi-auth-jwt2/issues)
以便我们可以尽我们可能快地*处理*它!*显然*,`某些`用户喜欢它……:
[![https://nodei.co/npm/hapi-auth-jwt2.png?downloads=true&downloadRank=true&stars=true](https://nodei.co/npm/hapi-auth-jwt2.png?downloads=true&downloadRank=true&stars=true)](https://www.npmjs.com/package/hapi-auth-jwt2)