我正在处理的应用程序要求所有身份验证代码在路由声明之后放置,而不是通过中间件将路由分成公共和私有路由,并在回调内部调用passport.authenticate进行登录方法(代码A),我需要在配置护照之前加载所有路由(代码B)。
我修改了这个例子:https://github.com/rkusa/koa-passport-example 帮助解释我想要完成的任务。上面的 auth.js 的精简版本如下:
const passport = require('koa-passport')
const fetchUser = (() => {
// This is an example! Use password hashing in your
const user = { id: 1, username: 'test', password: 'test' }
return async function() {
return user
}
})()
passport.serializeUser(function(user, done) {
console.log("serializing")
done(null, user.id)
})
passport.deserializeUser(async function(id, done) {
console.log("DEserializing")
try {
const user = await fetchUser()
done(null, user)
} catch(err) {
done(err)
}
})
const LocalStrategy = require('passport-local').Strategy
passport.use(new LocalStrategy(function(username, password, done) {
console.log("asked for authentication")
fetchUser()
.then(user => {
if (username === user.username && password === user.password) {
done(null, user)
} else {
done(null, false)
}
})
.catch(err => done(err))
}))
通过拆分公共路线和私人路线工作的 server.js (代码A)的剥离版本如下:
const Koa = require('koa')
const app = new Koa()
// trust proxy
app.proxy = true
// sessions
const session = require('koa-session')
app.keys = ['your-session-secret']
app.use(session({}, app))
// body parser
const bodyParser = require('koa-bodyparser')
app.use(bodyParser())
// authentication
require('./auth')
const passport = require('koa-passport')
app.use(passport.initialize())
app.use(passport.session())
// routes
const fs = require('fs')
const route = require('koa-route')
//public routes
app.use(route.get('/login', (ctx) => {
ctx.body = "LOGIN";
}));
app.use(route.post('/login',
passport.authenticate('local', {
successRedirect: '/home',
failureRedirect: '/login'
})
))
app.use(route.get('/logout', function(ctx) {
ctx.logout()
ctx.redirect('/login')
}))
// Require authentication for now
app.use(function(ctx, next) {
if (ctx.isAuthenticated()) {
console.log("authenticated user")
return next()
} else {
console.log("unauthenticated user")
ctx.redirect('/login')
}
})
//private route
app.use(route.get('/home', function(ctx) {
ctx.body = "home"
}))
// start server
const port = process.env.PORT || 3000
app.listen(port, () => console.log('Server listening on', port))
需要的是上述功能,但是在路由全部在身份验证之前声明,然后我们稍后捕获身份验证请求。我写了另一个 server.js (代码B)尝试这样做:
const Koa = require('koa')
const app = new Koa()
// trust proxy
app.proxy = true
// sessions
const session = require('koa-session')
app.keys = ['your-session-secret']
app.use(session({}, app))
// body parser
const bodyParser = require('koa-bodyparser')
app.use(bodyParser())
// routes
const fs = require('fs')
const route = require('koa-route')
app.use(route.get('/login', (ctx) => {
ctx.body = "LOGIN";
}));
app.use(route.post('/login', async (ctx, next) => {
ctx.id = 0;
console.log(); console.log("login");
await next();
}))
app.use(route.get('/home', async (ctx, next) => {
ctx.id = 1;
console.log(); console.log("home")
await next();
ctx.body = "home";
}))
app.use(route.get('/logout', async (ctx) => {
ctx.logout();
ctx.redirect('/login')
}))
const port = process.env.PORT || 3000
app.listen(port, () => console.log('Server listening on', port))
// authentication
require('./auth')
const passport = require('koa-passport')
app.use(passport.initialize())
app.use(passport.session());
auth = async (ctx, next) => {
console.log("Check for authentication request")
if (ctx.id === 0 && !ctx.isAuthenticated()) {
//here I want to actually authenticate if request if for authentication. Previously this code would have been in the callback of the public route i.e. the post method for login
passport.authenticate('local', {
successRedirect: '/home',
failureRedirect: '/login'
});
} else {
//middleware blocking access to other middlewares. Previously this would have been placed inside a non -async function and placed before all protected routes
if (ctx.isAuthenticated()) {
console.log("authenticated user")
return next()
} else {
console.log("unauthenticated user")
ctx.redirect('/login')
}
}
}
app.use(auth)
//app.use(MW1)
//app.use(MW2)
在每个路由回调中,我向ctx添加一个id属性,其中ctx.id = 0表示请求是一个身份验证发布请求。 问题:不会调用passport.authenticate。
然后我添加一个中间件来检查请求的其余部分是否经过身份验证(与代码A相同)。这部分有效,即我的私有路由/ home重定向到/ login,因为用户未经过身份验证。
我遇到的另一个问题是发送注销请求会给出错误“ctx.logout不是函数”。我想这与passport.authenticate根本没有被调用的事实有关。
我使用Postman发送POST和GET而不是表单。
POST localhost:3000 / login?username = test& password = test
获取localhost:3000 / home
获取localhost:3000 / logout