Passport.js 是专为 Node.js 设计的轻量级身份验证中间件。
- 灵活性极高:支持大量不同类型的身份验证策略,包含传统的本地用户名与密码验证,以及第三方登录(如 GitHub、Google、Facebook 等基于 OAuth 或 OpenID 协议的登录方式)。这使得开发者可以根据项目的实际需求,轻松选择合适的验证方式,无论是面向传统用户的系统,还是希望集成第三方登录来简化用户注册流程的应用都能适用。
- 良好的集成性:可轻松与各种流行的 Web 框架集成,像 Express、Koa 等。它如同一个可插拔的组件,开发者能在框架搭建好的基础上,无缝接入 Passport.js 来添加身份验证功能,不会对现有框架的核心逻辑造成太大干扰 。
- 模块化设计:通过不同的策略模块实现各种认证方式。这意味着如果项目只需要本地认证,那么只需引入
passport-local
模块;若还想支持 GitHub 登录,单独引入passport-github
模块即可,使得代码结构清晰,可维护性强。
准备工作
首先需要安装passport库,本文使用koa框架来进行演示,可以使用koa-passport代替passport
$ pnpm install koa koa-passport
$ pnpm install express passport
这里我们使用 sqlite
来做数据持久化,先安装一下驱动,然后添加sequelize
来通过ORM读写数据
$ pnpm install sqlite3 sequelize
来创建一个User Model
import { Model, DataTypes } from 'sequelize';
import bcrypt from 'bcrypt';
import sequelize from '../db';
class User extends Model {
public id!: number;
public username!: string;
public password!: string;
public async comparePassword(password: string): Promise<boolean> {
return bcrypt.compare(password, this.password);
}
}
User.init({
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
username: {
type: new DataTypes.STRING(128),
allowNull: false,
unique: true,
},
password: {
type: new DataTypes.STRING(128),
allowNull: false,
},
}, {
tableName: 'users',
sequelize,
hooks: {
beforeCreate: async (user: any) => {
if (user.password) {
const salt = await bcrypt.genSalt(10);
user.password = await bcrypt.hash(user.password, salt);
}
},
beforeUpdate: async (user: any) => {
if (user.changed('password')) {
const salt = await bcrypt.genSalt(10);
user.password = await bcrypt.hash(user.password, salt);
}
},
},
});
export default User;
创建数据库连接
import { Sequelize } from 'sequelize';
const sequelize = new Sequelize({
dialect: 'sqlite',
storage: './database.sqlite'
});
export default sequelize;
local 策略
安装自己所需的验证策略,先使用local策略来试一下
$ pnpm install passport-local
给passport添加local策略,顺便添加一下用户信息的序列化和反序列化
import passport from 'koa-passport';
import { Strategy as LocalStrategy } from 'passport-local';
import User from '../models/User';
passport.serializeUser((user: any, done) => {
done(null, user.id);
});
passport.deserializeUser(async (id: number, done) => {
try {
const user = await User.findByPk(id);
done(null, user);
} catch (err) {
done(err);
}
});
passport.use(new LocalStrategy({ usernameField: 'username' }, async (username, password, done) => {
try {
const user = await User.findOne({ where: { username } });
if (!user) {
return done(null, false, { message: 'Incorrect username.' });
}
const isMatch = await user.comparePassword(password);
if (!isMatch) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, user);
} catch (err) {
return done(err);
}
}));
现在我们接触到了pasport的策略配置API
- use:添加要使用的策略。
- serializeUser:序列化用户信息,将序列化的用户信息保存在sesion中,用于鉴权时使用。
- deserializeUser:反序列化用户信息,根据序列化的数据将用户信息写入请求上下文。
上面的代码已经为passport添加了local策略,下面我们来创建路由,并使用passport中间件进鉴权处理。
安装koa路由中间件
$ pnpm install @koa/router
添加路由声明如下
import Router from '@koa/router';
import passport from 'koa-passport';
const router = new Router();
router.get('/login', async (ctx) => {
if (ctx.isAuthenticated()) {
return ctx.redirect('/profile');
}
await ctx.render('login');
});
router.get('/register', async (ctx) => {
await ctx.render('register');
});
router.post('/register', async (ctx) => {
const { username, password } = ctx.request.body as any;
try {
const user = await User.create({ username, password });
await ctx.login(user);
ctx.redirect('/profile');
} catch (error) {
ctx.status = 400;
ctx.body = { message: 'Registration failed', error };
}
});
router.post('/login', passport.authenticate('local', {
successRedirect: '/profile',
failureRedirect: '/login',
failWithError: true
}));
router.post('/logout', (ctx) => {
ctx.logout();
ctx.redirect('/login');
});
router.get('/profile', async (ctx) => {
if (ctx.isAuthenticated()) {
await ctx.render('profile', { user: ctx.state.user });
} else {
ctx.redirect('/login');
}
});
export default router;
(页面使用ejs模板引擎进行渲染,这里就不贴ejs源文件代码了,查看demo仓库)
这里我们接触了passport的校验API
- isAuthenticated:判读该用户是否在session数据中。
- authenticate:验证用户身份,根据指定的策略进行鉴权。
此时我们通过浏览器访问刚才的几个页面可以看到效果如下:
到此最基本的基于local
策略的passport
鉴权已经实现了。
OAuth2策略(Github为例)
首先需要安装github策略
$ pnpm install passport-github
然后添加 github
策略到passport中
import passport from 'koa-passport';
import { Strategy as GitHubStrategy } from 'passport-github2';
import dotenv from 'dotenv';
import User from '../models/User';
dotenv.config();
passport.use(new GitHubStrategy({
clientID: process.env.GITHUB_CLIENT_ID as string,
clientSecret: process.env.GITHUB_CLIENT_SECRET as string,
callbackURL: 'http://localhost:3000/auth/github/callback'
}, async (accessToken: any, refreshToken: any, profile: any, done: any) => {
try {
const [user, created] = await User.findOrCreate({
where: { username: profile.username },
defaults: { password: '' } // No password for OAuth users
});
return done(null, user);
} catch (err) {
return done(err);
}
}));
🔔:注意不要泄漏自己的clentId
和clentSecret
,这里我通过env配置文件添加到环境变量中了。
然后为github策略添加路由
import Router from '@koa/router';
import passport from 'koa-passport';
import dotenv from 'dotenv';
import User from '../models/User';
dotenv.config();
const router = new Router();
router.get('/auth/github', passport.authenticate('github', { scope: ['user:email'] }));
router.get('/auth/github/callback', passport.authenticate('github', { failureRedirect: '/login' }), (ctx) => {
ctx.redirect('/profile');
});
export default router;
此时我们点击Login with GitHub
即可跳转至Github的授权页面
同意授权之后就可以回到刚才的profile
页面
PassportJS 的接入方式就是如此的简单。
留一个小作业:在官方策略库中还有更多的鉴权策略,可以自行尝试一下感兴趣的策略,来验证一下是否掌握了Passport的接入方式。