身份验证
身份验证是大多数 Web 应用程序的重要组成部分。因此 Midway 封装了目前 Nodejs 中最流行的 Passport 库。
相关信息:
| web 支持情况 | |
|---|---|
| @midwayjs/koa | ✅ |
| @midwayjs/faas | ✅ |
| @midwayjs/web | ✅ |
| @midwayjs/express | ✅ |
从 v3.4.0 开始 Midway 自行维护 passport,将不再需要引入社区包和类型包。
一些概念
passport 是社区使用较多的身份验证库,通过称为策略的可扩展插件进行身份验证请求。
它本身包含几个部分:
- 1、验证的策略,比如 jwt 验证,github 验证,oauth 验证等,passport 最为丰富的也是这块
- 2、执行策略之后,中间件的逻辑处理和配置,比如成功或者失败后的跳转,报错等
安装依赖
安装 npm i @midwayjs/passport 和相关策略依赖。
## 必选
$ npm i @midwayjs/passport@3 --save
## 可选
## 下面安装本地策略
$ npm i passport-local --save
$ npm i @types/passport-local --save-dev
## 下面安装 Github 策略
$ npm i passport-github --save
## 下面安装 Jwt 策略
$ npm i passport-jwt --save
或者在 package.json 中增加如下依赖后,重新安装。
{
"dependencies": {
"@midwayjs/passport": "^3.0.0",
// 本地策略
"passport-local": "^1.0.0"
// Jwt 策略
"passport-jwt": "^4.0.0",
// Github 策略
"passport-github": "^1.1.0",
// ...
},
"devDependencies": {
// 本地策略
"@types/passport-local": "^1.0.34",
// Jwt 策略
"@types/passport-jwt": "^3.0.6",
// Github 策略
"@types/passport-github": "^1.1.7",
// ...
}
}
启用组件
首先启用组件。
// src/configuration.ts
import { join } from 'path';
import { ILifeCycle } from '@midwayjs/core';
import { Configuration } from '@midwayjs/core';
import * as passport from '@midwayjs/passport';
@Configuration({
imports: [
// ...
passport,
],
importConfigs: [join(__dirname, './config')],
})
export class MainConfiguration implements ILifeCycle {}
策略示例
这里我们以使用本地认证策略,和 Jwt 策略作为演示。
示例:本地策略
我们以 passport-local 来介绍 Passport 策略在 Midway 中如何使用, passport-local 的官方文档示例如下,通过 passport.use 加载一个策略,策略的验证逻辑是一个 verify 方法,包含 callback 参数,其余的策略的参数都在构造器中。
passport.use(
// 初始化一个策略
new LocalStrategy({
usernameField: 'username',
passwordField: 'password',
passReqToCallback: true,
session: false
},
function verify(username, password, done) {
User.findOne({ username: username }, function (err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false); }
if (!user.verifyPassword(password)) { return done(null, false); }
return done(null, user);
});
}
)
);
Midway 对此进行了改造,通过 @CustomStrategy 和 PassportStrategy 类继承一个 Passport 现有策略。
异步的 validate 方法代替原有的 verify 方法,validate 方法返回验证后的用户结果,方法的参数和原有对应的策略一致。
在 Midway 中编写的效果如下:
// src/strategy/local.strategy.ts
import { CustomStrategy, PassportStrategy } from '@midwayjs/passport';
import { Strategy, IStrategyOptions } from 'passport-local';
import { Repository } from 'typeorm';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { UserEntity } from './user';
import * as bcrypt from 'bcrypt';
@CustomStrategy()
export class LocalStrategy extends PassportStrategy(Strategy) {
@InjectEntityModel(UserEntity)
userModel: Repository<UserEntity>;
// 策略的验证
async validate(username, password) {
const user = await this.userModel.findOneBy({ username });
if (!user) {
throw new Error('用户不存在 ' + username);
}
if (!await bcrypt.compare(password, user.password)) {
throw new Error('密码错误 ' + username);
}
return user;
}
// 当前策略的构造器参数
getStrategyOptions(): IStrategyOptions {
return {
usernameField: 'username',
passwordField: 'password',
passReqToCallback: true,
session: false
};
}
}
注意:validate 方法是社区策略 verify 的 Promise 化替代方法,你无需在最后传递 callback 参数。
在 passport-local 的官方文档中,实现完策略后,需要作为中间件加载到业务中,比如:
app.post('/login/password', passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/login'
}));
这里的 local 是 passport-local 内部的名字。
在 Midway 中,也需要将上述实现的 LocalStrategy 通过中间件加载。