Koa系列-Authentication:koa-passport
2019-04-22
字数统计:25k 字
阅读时长 ≈ 22 分钟
Koa的Passport中间件。
首先,阻止了 passport
的补丁,将 __monkeypatchNode
定义为一个没有作用的函数,因为这个是对 Express
和 connect
做的补丁,但 koa
不需要。
1 2 3 const connect = require ('passport/lib/framework/connect' )connect.__monkeypatchNode = function ( ) {}
然后自定义了 Framework/koa
,让 password
的框架为 koa
。
然后定义类 KoaPassport
继承 Passport
。
作者重新自定义了 initialize
和 authenticate
中间件,用于搭配 koa
的使用。
1 2 3 4 5 6 7 8 9 10 11 function promisify (expressMiddleware ) { return function (req, res ) { return new Promise (function (resolve, reject ) { expressMiddleware (req, res, function (err, result ) { if (err) reject (err) else resolve (result) }) }) } }
函数 initialize
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 function initialize (passport ) { const middleware = promisify (_initialize (passport)) return function passportInitialize (ctx, next ) { const userProperty = passport._userProperty || 'user' if (!ctx.req .hasOwnProperty (userProperty)) { Object .defineProperty (ctx.req , userProperty, { enumerable : true , get : function ( ) { return ctx.state [userProperty] }, set : function (val ) { ctx.state [userProperty] = val } }) } const req = createReqMock (ctx, userProperty) const login = req.login ctx.login = ctx.logIn = function (user, options ) { return new Promise ((resolve, reject ) => { login.call (req, user, options, err => { if (err) reject (err) else resolve () }) }) } ctx.logout = ctx.logOut = req.logout .bind (req) ctx.isAuthenticated = req.isAuthenticated .bind (req) ctx.isUnauthenticated = req.isUnauthenticated .bind (req) return middleware (req, ctx).then (function ( ) { return next () }) } }
函数 createReqMock
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 exports .create = function (ctx, userProperty ) { const req = Object .create (ctx.request , properties) Object .defineProperty (req, userProperty, { enumerable : true , get : function ( ) { return ctx.state [userProperty] }, set : function (val ) { ctx.state [userProperty] = val } }) Object .defineProperty (req, 'ctx' , { enumerable : true , get : function ( ) { return ctx } }) req.login = IncomingMessageExt .logIn req.logIn = IncomingMessageExt .logIn req.logout = IncomingMessageExt .logOut req.logOut = IncomingMessageExt .logOut req.isAuthenticated = IncomingMessageExt .isAuthenticated req.isUnauthenticated = IncomingMessageExt .isUnauthenticated return req }
函数 authorize
1 2 3 4 5 6 function authorize (passport, name, options, callback ) { options = options || {} options.assignProperty = 'account' return authenticate (passport, name, options, callback) }
函数 authenticate
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 function authenticate (passport, name, options, callback ) { if (typeof options === 'function' ) { callback = options options = {} } options = options || {} if (callback) { const _callback = callback callback = function (err, user, info, status ) { try { Promise .resolve (_callback (err, user, info, status)) .then (() => callback.resolve (false )) .catch (err => callback.reject (err)) } catch (err) { callback.reject (err) } } } const middleware = promisify (_authenticate (passport, name, options, callback)) return function passportAuthenticate (ctx, next ) { const p = new Promise ((resolve, reject ) => { const req = createReqMock (ctx, options.assignProperty || passport._userProperty || 'user' ) function setBodyAndResolve (content ) { if (content) ctx.body = content resolve (false ) } const res = { redirect : function (url ) { ctx.redirect (url) resolve (false ) }, setHeader : ctx.set .bind (ctx), end : setBodyAndResolve, send : setBodyAndResolve, set statusCode (status ) { ctx.status = status }, get statusCode () { return ctx.status } } req.res = res if (callback) { callback.resolve = resolve callback.reject = reject } middleware (req, res).then (resolve, reject) }) return p.then (cont => { if (cont !== false ) { return next () } }) } }