在带有 React.js 前端的 Node.js 上使用 Passport.js 进行身份验证
Posted
技术标签:
【中文标题】在带有 React.js 前端的 Node.js 上使用 Passport.js 进行身份验证【英文标题】:Authentication using Passport.js on Node.js with React.js frontend 【发布时间】:2018-03-08 17:42:55 【问题描述】:在身份验证方面,我完全是新手。我正在尝试将 Colt Steele 使用的方法作为他的web developer's bootcamp course 的一部分来适应我的项目。
Colt 使用 Node.js + Express.js + MongoDB,在 ejs 中进行服务器端渲染。对于用户身份验证,他使用 Passport.js。
我正在使用 Node.js + Express.js + MongoDB 和 React.js 进行客户端渲染。我也使用反应路由器。我也在尝试使用 Passport 对用户进行身份验证。
我的问题是,虽然我可以成功登录,但我仍然无法访问登录墙后面的任何内容。当我向服务器发出请求以检查用户是否经过身份验证的某个路由时,答案总是错误的。
这是我的验证码。
const express = require("express"),
server = express(),
path = require("path"),
mongoose = require("mongoose"),
User = require("./models/User"),
bodyParser = require("body-parser"),
passport = require("passport"),
localStrategy = require("passport-local"),
passportLocalMongoose = require("passport-local-mongoose"),
expressSession = require("express-session");
mongoose.connect("mongodb://localhost/diary");
server.use(
bodyParser.urlencoded(
extended: true
)
);
//Auth
server.use(
expressSession(
secret: "This is pretty damn weird.",
resave: false,
saveUninitialized: false
)
);
server.use(passport.initialize());
server.use(passport.session());
passport.use(new localStrategy(User.authenticate()));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
// Auth login
server.post("/login", function(request, response)
passport.authenticate("local", function(err, user, info)
if (err)
return console.log(err);
if (!user)
return response.send(false);
request.logIn(user, function(err)
if (err)
return console.log(err);
checkLogIn(request, response);
return response.send(true);
);
)(request, response);
);
//Auth checkLogIn
function checkLogIn(request, response)
if (request.isAuthenticated())
return console.log("authenticated");
else
return console.log("not authenticated");
由于我使用的是客户端渲染,我无法使用 response.redirect 将用户重定向到任何地方。这就是我使用 response.send(true)(或 false)的原因:我使用它们作为标志来告诉 React 应用程序该做什么。
上面的代码与 Colt 的代码几乎相同:他将 expressSession 的配置保持在最低限度,并且似乎没有定义自己的本地策略或(反)序列化函数。在我发现的几个教程中,每个人似乎都这样做,但 Colt 没有,而且他的应用程序运行得很好。他也不使用任何 MongoStore,很多其他人似乎都这样做。
如您所见,我在 request.logIn 之后调用了一个 checkLogIn 函数,它只是 console.logs 用户是否登录。登录后,它在屏幕上打印 true,即登录确实成功.
此时我应该已经登录,但是如果我尝试从浏览器访问受保护的部分,我会被告知我没有登录。例如,我以假用户 Elmo 成功登录,然后尝试访问/elmo/diary。这会将我带到一个 React 组件,该组件在特定路由上查询服务器,这个:
//Find all entries of a given user.
router.get("/entries/:userURL", isLoggedIn, (request, response) =>
console.log("get entries");
User.findOne(
customURL: request.params.userURL ,
entryIDs: 1 ,
(err, data) =>
//Get the actual entries.
Entry.find( _id: $in: data.entryIDs , (err, entries) =>
if (err)
console.log(err);
else
response.send(entries);
);
);
);
如您所见,我使用了一个 isLoggedIn 中间件,如下所示:
//Auth isLoggedIn
function isLoggedIn(request, response, next)
console.log(request);
if (request.isAuthenticated())
console.log("logged in");
return next();
console.log("not logged in");
return (function()
response.send(false);
)();
所以如果我以 Elmo 身份登录,上面的路由应该会返回数据。但是,无论我是否登录,中间件都会返回 false。
我检查了我的 localhost 的 cookie,应用程序在其中运行,只有一个具有令牌外观的值 (s%3Ak0SZJ97eJkPjqjE6jC-azn7y3ztK2UMw.fYOfM9324JUIeUDGvh26swEWwMf3j3HDUmh9WubNF%2B8),所以我猜 Passport 正在创建会话并存储它在cookie中成功。
说实话,我也不确定这个系统对 Colt 是如何工作的,因为他的前端似乎没有发回任何令牌/数据供后端检查,而我(诚然有限)的理解是不是应该是这样的。
知道我做错了什么吗?
【问题讨论】:
客户端路由,你用的是react-router还是express? @Dream_Cap 反应路由器。 【参考方案1】:当您说“登录墙”时,我假设您想要访问受保护的路线/组件。我相信您需要像这样的带有 react-router 的受保护路由:
//Placed near your other react router routes, pass the logged in state to this route
<PrivateRoute path="/dashboard" component=Dashboard loggedIn=loggedIn />
然后私有路由将如下所示:
//If auth'd, redirect to the component you want, if not then redirect to login
const PrivateRoute = ( component: Component, ...rest, loggedIn ) => (
<Route
...rest
render=props => (
loggedIn ? (
<Component ...props />
) : (
<Redirect to=
pathname: '/login',
state: from: props.location ,
/>
)
)
/>
);
至于让你的路由知道你是否已登录,我认为你需要将 express 的身份验证响应存储在本地 React 组件状态或 Redux 中(我最近在我的博客项目 here 中这样做了。它将位于您的 redux 存储(状态)中,因为这样您就可以在需要时从多个视图/组件中访问它。例如,可以在多个视图中检索登录状态。
以下是路线的示例:
const AllRoutes = ( loggedIn ) => (
<BrowserRouter history=history>
<div>
<PrivateRoute path="/dashboard" component=Dashboard loggedIn=loggedIn />
//other routes
</div>
</BrowserRouter>
);
这是 Redux 部分将loggedIn 传递给allRoutes 的样子:
//import connect for connecting the component into redux's store(state)
import connect from 'react-redux';
//map redux's store to props so the component can access it (this is how loggedIn can get passed in)
const mapStateToProps = state => (
loggedIn: state.signup.loggedIn,
);
//wraps the component in Redux's store(state)
export default connect(mapStateToProps, null)(AllRoutes);
【讨论】:
登录时loggedIn的值如何传递给AllRoutes? 我更新了答案以包含更多信息 这个答案有帮助吗? 我仍在努力解决。我现在正在学习 Udemy 课程,说根本不需要涉及 react,因为浏览器会自动将身份验证 cookie 附加到每个 http 请求。事实上,我真的不需要受保护的路由,而只需要身份验证来决定是否可以向用户显示某些数据。对不起,如果我的措辞令人困惑。无论如何,感谢您的帮助!以上是关于在带有 React.js 前端的 Node.js 上使用 Passport.js 进行身份验证的主要内容,如果未能解决你的问题,请参考以下文章
如何为 api 部署 node js express js,为前端部署 react js,为管理员部署 angular
基于 React.js 和 Node.js 的SSR实现方案