Passport & Passport-jwt 实现安全验证
目次Passport
认证请求
计谋
验证回调
会话
中间件
禁止 Sessions
自界说回调
passport-jwt
安装
利用
配置计谋
认证请求
在请求中包含JWT
Passport
Passport 是 NodeJS 的认证中间件。他的唯一设计目的是:验证请求。书写模块化的、封装代码是一种美德,所以 Passport 将除了验证请求之外的功能都分发给应用程序来实现。关注点分离使代码可以或许更加整齐、可维护,同时也使 Passport 可以或许极易集成到应用中。
现在 Web 程序,认证有多种形式。传统的,用户通过用户名、密码登录。随着交际网络利用上升,利用 OAuth 的单点登录,例如 Facebook 或者 Twitter 已经成为了一种盛行的认证方式。暴露一个 API 的服务通常需要基于 token 的证书来保护访问。
Passport 认识到每个应用有本身独特的认证需求。认证机制,也被成为计谋,被打包成单独的模块。应用可以或许选择接纳的计谋,无需创建不需要的依靠。
不管认证的复杂性,代码可以或许稳定的复杂。
安装
$ npm install passport 认证请求
Passport 提供了一个 authenticate() 函数,它作为路由中间件用于认证请求。
认证请求就像调用 passport.authenticate() 和指定接纳哪种计谋一样简朴。authenticate() 的函数签名是标准的 Connect 中间件,所以可以或许方便的作为路由中间件在 Express 应用中利用。
app.post("/login", passport.authenticate("jwt"), function (req, res) {
// 如果这个函数被调用,说明认证成功。
// `req.user` 包含认证的 user
res.redirect("/user", +req.user.username);
}); 默认情况下,如果认证失败,Passport 将返回 401 Unauthorized,后续其他的路由处理器将不会执行。如果认证乐成,下个处理器将调用,req.user 属性将设置为认证 user。
注意:在路由利用计谋时,肯定要预先配置。继续阅读详细配置 todo 章节。
计谋
Passport 利用被称为计谋的东西来认证请求。计谋从验证用户名、密码,利用 OAuth 委托认证或者利用 OpenID 连合认证。
在让 Passport 认证请求前,应用利用的某个计谋(或者某些计谋)必须要先配置。
计谋和他们的配置通过 use() 函数提供。例如,下面的例子利用passport-jwt 来进行tokne认证。
// 引入JWT策略和请求提取工具以及用户模型
const JwtStrategy = require("passport-jwt").Strategy,
ExtractJwt = require("passport-jwt").ExtractJwt,
UserService = require("../services/UserService");
// 初始化配置对象
opts = {};
// 配置从请求头中提取JWT的方法
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
// 配置JWT的密钥
opts.secretOrKey = "贾公子";
// 配置passport使用JWT策略
module.exports = (passport) => {
passport.use(
new JwtStrategy(opts, async (jwt_payload, done) => {
// 尝试根据JWT载荷中的用户ID查找用户
try {
let user = await UserService.findById(jwt_payload._id);
if (!user) return done(null, false);
return done(null, user);
} catch (error) {
console.log(error);
}
})
);
};
有480多个计谋。你可以在 passportjs.org 找到你想要的计谋。
验证回调
这个例子引入了一个重要的概念。计谋需要一个称为回调的东西。验证回调的目的是找到拥有一套凭证的用户。
当 Passport 认证请求时,它分析请求中的凭证。然后将凭证作为调用回调函数的参数,这个例子中就是 token。如果凭证有用,回调函数将调用 done 将已认证的用户的信息传入 Passport
return done(null, user); 如果验证失败(本例中,比如token错误),done 函数应该传入 false 而不是用户信息来表明认证失败
return done(null, false); 可以提供额外的消息来表明失败原因。这对于展示即时消息,来提示用户再次实验很有用。
return done(null, false, { message: '密码错误' });
最后,当验证凭证时发生非常(例如,数据库服务不可用),在通例的 Node 操作中 done 应该被调用来传入错误信息。
return done(err);
注意,对于区分两种可以或许发生失败的情况是很重要的。后者是服务端非常,这种情况下 err 被设置为非 null 的值。在服务器正常运行时,认证失败也是很天然的情况。确认 err 包含 null,利用最后一个参数通报详细信息。
验证回调通过委派的方式使 Passport 可以无需数据库支持。应用可以本身决定如何存储用户信息,没有验证层强加的任何假设。
会话
Passport 将维护持久的登录会话。为了使持久会话工作,认证用户必须序列化到会话,并且在后续请求时反序列化。
Passport 不会对你如何存储用户纪录施加任何限制。相反,你提供给 Passport 的函数实现了必要的序列化和反序列化逻辑。在典范的应用中,这就像序列化用户 ID,以及在反序列化时通过 ID 查找用户一样简朴。
在通例的 web 应用中,用于认证用户的凭证仅在登录请求时被发送。如果认证乐成,一个session 将被建立和保持,通过设置在浏览器中的 cookie。
每个随后的请求将不再包含凭证,但是会有唯一的 cookie 来确认 session。为了支持登录 session,Passport 将序列化和反序列化 user 实例经过 session。
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function (err, user) {
done(err, user);
});
}); 这个例子中,仅 user ID 被序列化到 session 中,从而保持存储在 session 中的数据量较小。当后续请求到达时,这个 ID 将用来找到存储在 req.user 中的 user。
序列化和反序列化逻辑由应用来提供,允许应用选择一个符合的数据库和(或者)对象mapper,无需认证层强加。
中间件
要在 Express 或 Connect 基础的应用中利用 Passport,需要配置必需的 passport.initialize() 中间件。如果你的应用利用持久登录会话(推荐,但不必需),则还必须利用 passport.session() 中间件。
var app = express();
app.use(require('serve-static')(__dirname + '/../../public'));
app.use(require('cookie-parser')());
app.use(require('body-parser').urlencoded({ extended: true }));
app.use(require('express-session')({ secret: 'keyboard cat', resave: true, saveUninitialized: true }));
app.use(passport.initialize());
app.use(passport.session()); 注意,开启 session 支持完满是可选的,尽管建议将其用于大多数应用中。如果开启,确认在 passport.session() 前利用 session(),从而确保登录 session 可以或许按精确的顺序存储。
禁止 Sessions
乐成授权后,Passport 将建立一个持久的登录 session。对于用户通过浏览器访问 web 应用的场景这是有用的。然后,其他情况下,不需要 session 支持。例如,API 服务器通常需要每个请求携带凭证。这种情况下,session 支持可以或许通过设置 session 选项为 false 来安全的禁用。
app.get('/api/users/me',
passport.authenticate('basic', { session: false }),
function(req, res) {
res.json({id: req.user.id, username: req.user.username});
}
)
自界说回调
如果内部选项不充足处理认证请求,自界说回调可以或许让应用处理乐成和失败的情况。
app.get("/login", function (req, res, next) {
passport.authenticate("local", function (err, user, info) {
if (err) {
return next(err);
}
if (!user) {
return res.redirect("/login");
}
req.logIn(user, function (err) {
if (err) {
return next(err);
}
return res.redirect("/users" + user.username);
});
})(req, res, next);
}); 这个例子中,注意 authenticate() 是在路由处理器中被调用,而不是作为路由中间件。这通过闭包给了 req和 res 对象回调权限。
如果认证失败,user 将被设置为 false。如果发生非常,err 将被设置。一个可选的 info 参数将传入,包罗计谋验证回调所提供的附加的详细信息。
这个回调可以或许利用提供的参数处理预期的认证结果。注意,当利用自界说回调时,需要应用来建立 session(通过调用 req.login()) 和发送响应。
passport-jwt
一个用于通过JSON Web Token进行身份验证的Passport计谋。
这个模块允许您利用JSON Web Token对端点进行身份验证。它旨在用于保护没有会话的RESTful端点。
安装
npm install passport-jwt 利用
配置计谋
JWT身份验证计谋的构建如下:
new JwtStrategy(options, verify) options是一个包含控制如何从请求中提取或验证令牌的选项的对象。
[*]secretOrKey是一个包含用于验证令牌签名的密钥(对称)或PEM编码的公钥(非对称)的字符串或缓冲区。除非提供了secretOrKeyProvider,否则必须提供。
[*]secretOrKeyProvider是一个回调函数,格式为function secretOrKeyProvider(request, rawJwtToken, done),应该调用done并传入给定密钥和请求组合的密钥或PEM编码的公钥(非对称)。done担当参数格式为function done(err, secret)。注意,实现者需要解码rawJwtToken。除非提供了secretOrKey,否则必须提供。
[*]jwtFromRequest(必须)函数,担当请求作为唯一参数,并返回JWT字符串或_null_。有关更多详细信息,请参阅从请求中提取JWT。
[*]issuer:如果界说了,将验证令牌的发行者(iss)是否与此值匹配。
[*]audience:如果界说了,将验证令牌的受众(aud)是否与此值匹配。
[*]algorithms:允许的算法名称列表。例如,"HS256","HS384""HS256","HS384"。
[*]ignoreExpiration:如果为true,则不验证令牌的逾期时间。
[*]passReqToCallback:如果为true,则将请求通报给验证回调。即verify(request, jwt_payload, done_callback)。
[*]jsonWebTokenOptions:passport-jwt利用jsonwebtoken验证令牌。在这里传入任何其他选项的对象。(例如maxAge)
verify是一个函数,参数为verify(jwt_payload, done)
[*]jwt_payload是一个包含解码JWT有用负载的对象。
[*]done是passport错误优先回调,担当参数done(error, user, info)
一个示例配置,它从带有'bearer'方案的http授权头读取JWT:
var JwtStrategy = require('passport-jwt').Strategy,
ExtractJwt = require('passport-jwt').ExtractJwt;
var opts = {}
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = 'secret';
opts.issuer = 'accounts.examplesoft.com';
opts.audience = 'yoursite.net';
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
User.findOne({id: jwt_payload.sub}, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
return done(null, user);
} else {
return done(null, false);
// 或者您可以创建一个新账户
}
});
})); JWT可能包含在请求中的多种方式。为了尽可能灵活,JWT通过用户提供的回调函数从请求中分析,该回调函数作为jwtFromRequest参数通报。从现在起,这个回调函数被称为提取器,担当请求对象作为参数,并返回编码的JWT字符串或_null_。
passport-jwt.ExtractJwt提供了很多提取器工厂函数。这些工厂函数返回一个配置有给定参数的新提取器。
[*]fromHeader(header_name)创建一个新的提取器,它在给定的http头中查找JWT。
[*]fromBodyField(field_name)创建一个新的提取器,它在给定的正笔墨段中查找JWT。您必须配置了正文分析器才气利用此方法。
[*]fromUrlQueryParameter(param_name)创建一个新的提取器,它在给定的URL查询参数中查找JWT。
[*]fromAuthHeaderWithScheme(auth_scheme)创建一个新的提取器,它在授权头中查找JWT,渴望方案与auth_scheme匹配。
[*]fromAuthHeaderAsBearerToken()创建一个新的提取器,它在带有'bearer'方案的授权头中查找JWT。
[*]fromExtractors()利用提供的提取器数组创建一个新的提取器。按顺序实验每个提取器,直到一个返回令牌。
如果提供的提取器不符合您的需求,您可以轻松地提供本身的回调。例如,如果您利用cookie-parser中间件并希望从cookie中提取JWT,可以利用以下函数作为jwtFromRequest选项的参数:
var cookieExtractor = function(req) {
var token = null;
if (req && req.cookies) {
token = req.cookies['jwt'];
}
return token;
};
// ...
opts.jwtFromRequest = cookieExtractor; 认证请求
利用passport.authenticate()指定'JWT'作为计谋。
app.post('/profile', passport.authenticate('jwt', { session: false }),
function(req, res) {
res.send(req.user.profile);
}
); 在请求中包含JWT
在请求中包含JWT的方法完全取决于您选择的提取器函数。例如,如果您利用fromAuthHeaderAsBearerToken提取器,则会在请求中包含一个Authorization头,并将方案设置为bearer。例如:
Authorization: bearer JSON_WEB_TOKEN_STRING.....
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]