介绍
在本文中,制作一个简单的应用程序来演示如何在Express.js中处理身份验证。
项目设置
首先,创建一个名为simple-web-app
的新文件夹,使用终端,导航到该文件夹,并创建一个框架Node.js项目:
$ npm init
现在,我们也可以安装Express:
$ npm install --save express
继续安装express-handlebars
:
$ npm install --save express-handlebars
我们还将使用另外两个Express中间件包(body-parser
和cookie-parser
)来解析HTTP请求主体,并解析所需的Cookies以进行身份验证:
$ npm install --save body-parser cookie-parser
实现
我们要构建的应用程序将包含一个只有登录用户才能访问的"受保护"页面,否则,他们被重定向到主页-提示他们登录或注册。
首先,导入我们之前安装的库:
const express = require('express');
const exphbs = require('express-handlebars');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
使用node的原生crypto
模块进行密码哈希,并生成验证令牌-本文稍后将详细介绍。
接下来,创建一个简单的Express应用程序,并配置我们导入的中间件,以及Handlebars引擎:
const app = express();
// To support URL-encoded bodies
app.use(bodyParser.urlencoded({ extended: true }));
// To parse cookies from the HTTP Request
app.use(cookieParser());
app.engine('hbs', exphbs({
extname: '.hbs'
}));
app.set('view engine', 'hbs');
// Our requests hadlers will be implemented here...
app.listen(3000);
默认情况下,在Handlebars中,模板扩展应该是.handlebars
,现在创建几个模板文件:
layouts
文件夹在view
文件夹中将保存你的主布局,这将为其他模板提供基本的HTML。
创建main.hbs
,主包装页面:
Document
{{{body}}}
其他模板将呈现此模板的内部{{{body}}}
标记,我们在这个布局中导入了HTML样板文件以及Bootstrap所需的CSS和JS文件。
完成主包装之后,创建home.hbs
页面,提示用户登录或注册:
Simple Authentication App
Login
Register
然后,创建一个指向路径root path (/
)的请求处理程序来呈现主模板。
app.get('/', function (req, res) {
res.render('home');
});
启动应用程序,并导航到http://localhost:3000
:
帐户注册
有关帐户的信息通过registration.hbs
页面收集:
{{#if message}}
{{message}}
{{/if}}
First Name
Last Name
Email address
Password
Confirm Password
Login
密码和确认密码并将操作设置为/register
route,
创建一个请求句柄,以便在用户访问http://localhost:3000/register
时呈现注册模板:
app.get('/register', (req, res) => {
res.render('register');
});
const crypto = require('crypto');
const getHashedPassword = (password) => {
const sha256 = crypto.createHash('sha256');
const hash = sha256.update(password).digest('base64');
return hash;
}
当用户提交注册表单时,POST
请求被发送到/register
路径。
由于每个服务器重新启动都将重新初始化数组,因此硬编码一个用户,以便测试每次初始化:
const users = [
// This user is added to the array to avoid creating a new user on each restart
{
firstName: 'John',
lastName: 'Doe',
email: '[email protected]',
// This is the SHA256 hash for value of `password`
password: 'XohImNooBHFR0OVvjcYpJ3NgPQ1qq73WKhHvch0VQtg='
}
];
app.post('/register', (req, res) => {
const { email, firstName, lastName, password, confirmPassword } = req.body;
// Check if the password and confirm password fields match
if (password === confirmPassword) {
// Check if user with the same email is also registered
if (users.find(user => user.email === email)) {
res.render('register', {
message: 'User already registered.',
messageClass: 'alert-danger'
});
return;
}
const hashedPassword = getHashedPassword(password);
// Store user into the database if you are using one
users.push({
firstName,
lastName,
email,
password: hashedPassword
});
res.render('login', {
message: 'Registration Complete. Please login to continue.',
messageClass: 'alert-success'
});
} else {
res.render('register', {
message: 'Password does not match.',
messageClass: 'alert-danger'
});
}
});
已接收的email
,firstName
,lastName
,password
和密码已验证过。
如果每个验证都成功,我们对密码进行散列,并将信息存储在数组内部,并将用户重定向到登录页面,否则,用错误消息重新呈现注册页面。
现在,访问/register
端点以验证它是否正常工作:
登录帐号
通过注销,我们可以实现登录功能,首先创建login.hbs
页面:
{{#if message}}
{{message}}
{{/if}}
Email address
Password
Login
然后,为该请求创建一个处理程序:
app.get('/login', (req, res) => {
res.render('login');
});
当用户提交表单时,此表单将向POST
请求发送到/login
,不过,我们要做的另一件事是为登录发送身份验证令牌,此令牌将用于标识用户,每次发送HTTP请求时,此令牌将作为cookie发送:
const generateAuthToken = () => {
return crypto.randomBytes(30).toString('hex');
}
使用helper方法,我们可以为登录页面创建一个请求处理程序:
// This will hold the users and authToken related to users
const authTokens = {};
app.post('/login', (req, res) => {
const { email, password } = req.body;
const hashedPassword = getHashedPassword(password);
const user = users.find(u => {
return u.email === email && hashedPassword === u.password
});
if (user) {
const authToken = generateAuthToken();
// Store authentication token
authTokens[authToken] = user;
// Setting the auth token in cookies
res.cookie('AuthToken', authToken);
// Redirect user to the protected page
res.redirect('/protected');
} else {
res.render('login', {
message: 'Invalid username or password',
messageClass: 'alert-danger'
});
}
});
击中/login
端点,会看到: