之前写过用html写的页面,相关链接:nodeJs入门——注册及登录
基于之前的目录,改变view里面的页面文件
html 代码
node-demo/
|
+- controllers/ <-- Controller
| |
| +- login.js <-- 登录注册处理
|
+- views/ <-- html模板文件
| |
| +- head.pug <-- 头部css引入
| |
| +- header.pug <-- js引入
| |
| +- layout.pug <-- 公共布局页面
| |
| +- login.pug <-- 登录页面
| |
| +- signUp.pug <-- 注册页面
| |
| +- index.pug <-- 登陆完成主页
|
+- public/ <-- 静态资源文件
|
+- models <-- models
| |
| +- db.js <-- 数据库连接池设置
| |
| +- dao_mysql.js <-- 数据库通用操作方法
| |
| +- query.js <-- 数据库查询
|
+- router <-- 路由文件
| |
| +- router.js <-- 路由配置
|
+- app.js <-- 入口文件
|
+- package.json <-- 项目描述文件
|
+- node_modules/ <-- npm安装的所有依赖包
首先,安装pug依赖包: npm install pug --save
然后将之前的页面改写为pug模板引擎
head.pug
javascript 代码
link(rel="stylesheet" href= CommonLibs + '/css/bootstrap.css')
link(rel="stylesheet" href= CommonLibs + '/css/font-awesome.css')
link(rel="stylesheet" href= CommonLibs + '/css/style.css')
header.pug
javascript 代码
script(src= CommonLibs + '/js/jquery.js')
script(src= CommonLibs + '/js/popper.min.js')
script(src= CommonLibs + '/js/bootstrap.min.js')
script(src= CommonLibs + '/js/jQuery.md5.js')
layout.pug
javascript 代码
doctype
html
head
meta(charset='UTF-8')
meta(http-equiv='X-UA-Compatible',content='IE=Edge,chrome=1')
title #{title}
block head
include head
body
.containers
nav.navbar.navbar-expand-md.navbar-dark.bg-dark
a.navbar-brand(href='#')
i(class="fa fa-fort-awesome",aria-hidden="true")
| Ghost Castle
button(
class="navbar-toggler"
type="button"
data-toggle="collapse"
data-target="#navbarNavAltMarkup"
aria-controls="navbarNavAltMarkup"
aria-expanded="false"
aria-label="Toggle navigation"
)
span.navbar-toggler-icon
div(class="collapse navbar-collapse justify-content-end" id="navbarNavAltMarkup")
.navbar-nav
block opration-content
block content
block header
include header
login.pug
javascript 代码
extends layout
block prepend head
link(rel="shortcut icon" href= CommonLibs + "/icons/glyph/si-glyph-castle.svg")
block opration-content
a(class="nav-item nav-link active" href="#") Login
a(class="nav-item nav-link" href="/register") Sign Up
block content
.login.row.justify-content-md-center
.col-md-8
.card.border-secondary
.card-header Login
.card-body.text-secondary
form(id="login" onsubmit="return false;")
.form-group.row
lable(for="name" class="col-sm-4 text-right") Name
.col-sm-6
input(
type="text"
name="name"
class="form-control"
id="name"
placeholder="Name"
required
)
.form-group.row
lable(for="password" class="col-sm-4 text-right") Password
.col-sm-6
input(
type="password"
name="password"
class="form-control"
id="password"
placeholder="Password"
required
)
.form-group.row
lable.col-sm-4.text-right 验证码
#captcha.col-sm-6
p#wait.show 正在加载验证码......
.form-group.row
.col-sm-8.ml-sm-auto
label.custom-control.custom-checkbox.mb-2.mr-sm-2.mb-sm-0
input(type="checkbox" class="custom-control-input")
span.custom-control-indicator
span.custom-control-description Remember me
.row
.col-sm-8.ml-sm-auto
button(type="submit" class="btn btn-dark" id="loginBtn")
i(class="fa fa-sign-in" aria-hidden="true")
| LOGIN
append header
script(src= CommonLibs + '/js/gt.js')
script.
function login(captchaObj) {
var result = captchaObj.getValidate();
if (!result) {
return alert('请完成验证');
}
var name = $('input[name=name]').val();
var pwd = $('input[name=password]').val();
$.ajax({
url: 'gt/validate-slide',
type: 'POST',
dataType: 'json',
data: {
name: name,
password: $.md5(pwd),
geetest_challenge: result.geetest_challenge,
geetest_validate: result.geetest_validate,
geetest_seccode: result.geetest_seccode
},
success: function (data) {
if (data.result) {
alert('登录成功');
window.location.href = '/';
} else {
alert(data.errorMsg);
captchaObj.reset();
}
}
});
return false;
}
var handler = function (captchaObj) {
captchaObj.appendTo('#captcha');
captchaObj.onReady(function () {
$("#wait").hide();
});
$('#loginBtn').click(function () {
login(captchaObj);
});
// 更多接口说明请参见:http://docs.geetest.com/install/client/web-front/
window.gt = captchaObj;
};
$.ajax({
url: "gt/register-slide?t=" + (new Date()).getTime(), // 加随机数防止缓存
type: "get",
dataType: "json",
success: function (data) {
// 调用 initGeetest 进行初始化
// 参数1:配置参数
// 参数2:回调,回调的第一个参数验证码对象,之后可以使用它调用相应的接口
initGeetest({
// 以下 4 个配置参数为必须,不能缺少
gt: data.gt,
challenge: data.challenge,
offline: !data.success, // 表示用户后台检测极验服务器是否宕机
new_captcha: data.new_captcha, // 用于宕机时表示是新验证码的宕机
product: "float", // 产品形式,包括:float,popup
width: "100%"
// 更多配置参数说明请参见:http://docs.geetest.com/install/client/web-front/
}, handler);
}
});
signUp.pug
javascript 代码
extends layout
block opration-content
a(class="nav-item nav-link" href="/login") Login
a(class="nav-item nav-link active" href="#") Sign Up
block content
.login.row.justify-content-md-center
.col-md-8
.card.border-secondary
.card-header Register
.card-body.text-secondary
form(onsubmit="return false;")
.form-group.row
lable(for="name" class="col-sm-4 text-right") Name
.col-sm-6
input(
type="text"
name="name"
class="form-control"
id="name"
placeholder="Name"
required
)
.form-group.row
lable(for="sex" class="col-sm-4 text-right") Sex
.col-sm-6
label.custom-control.custom-radio
input(
id="radioStacked1"
name="sex"
type="radio"
value="0"
class="custom-control-input"
required
)
span.custom-control-indicator
span.custom-control-description Male
label.custom-control.custom-radio
input(
id="radioStacked2"
name="sex"
type="radio"
value="1"
class="custom-control-input"
required
)
span.custom-control-indicator
span.custom-control-description Female
.form-group.row
lable(for="address" class="col-sm-4 text-right") Address
.col-sm-6
input(
type="text"
name="address"
class="form-control"
id="address"
placeholder="Address"
required
)
.form-group.row
lable(for="password" class="col-sm-4 text-right") Password
.col-sm-6
input(
type="password"
name="password"
class="form-control"
id="password"
placeholder="Password"
required
)
.form-group.row
lable(for="password_confirmation" class="col-sm-4 text-right") Confirm Password
.col-sm-6
input(
type="password"
name="password_confirmation"
class="form-control"
id="password_confirmation"
placeholder="Confirm Password"
required
)
.form-group.row
lable.col-sm-4.text-right 验证码
#captcha.col-sm-6
p#wait.show 正在加载验证码......
.row
.col-sm-8.ml-sm-auto
button(type="submit" class="btn btn-dark" id="registerBtn")
i(class="fa fa-sign-in" aria-hidden="true")
| Sign Up
append header
script(src= CommonLibs + '/js/gt.js')
script.
function register(captchaObj) {
var result = captchaObj.getValidate();
if (!result) {
return alert('请完成验证');
}
var name = $('input[name=name]').val();
var pwd = $('input[name=password]').val();
var cpwd = $('input[name=password_confirmation]').val();
var sex = $('input[name=sex]:checked').val();
var address = $('input[name=address]').val();
var udate = new Date();
if (name == '' || pwd == '' || cpwd == '' || sex == '' || address == '') {
return false;
}
udate = udate.Format('yyyy-MM-dd hh:mm:ss');
if ($.md5(pwd) != $.md5(cpwd)) {
alert('The confirmation password is not identical.');
return false;
}
var param = {
uname: name,
upwd: $.md5(pwd),
usex: sex,
uaddress: address,
udate: udate
};
$.ajax({
type: 'post',
url: '/ghost/register',
data: param,
dataType: 'json',
success: function (res) {
alert('Register Success');
window.location.href = '/login';
},
error: function (err) {
console.log(err);
captchaObj.reset();
}
});
return false;
}
var handler = function (captchaObj) {
captchaObj.appendTo('#captcha');
captchaObj.onReady(function () {
$("#wait").hide();
});
$('#registerBtn').click(function () {
register(captchaObj);
});
// 更多接口说明请参见:http://docs.geetest.com/install/client/web-front/
window.gt = captchaObj;
};
$.ajax({
url: "gt/register-slide?t=" + (new Date()).getTime(), // 加随机数防止缓存
type: "get",
dataType: "json",
success: function (data) {
// 调用 initGeetest 进行初始化
// 参数1:配置参数
// 参数2:回调,回调的第一个参数验证码对象,之后可以使用它调用相应的接口
initGeetest({
// 以下 4 个配置参数为必须,不能缺少
gt: data.gt,
challenge: data.challenge,
offline: !data.success, // 表示用户后台检测极验服务器是否宕机
new_captcha: data.new_captcha, // 用于宕机时表示是新验证码的宕机
product: "float", // 产品形式,包括:float,popup
width: "100%"
// 更多配置参数说明请参见:http://docs.geetest.com/install/client/web-front/
}, handler);
}
});
// 对Date的扩展,将 Date 转化为指定格式的String
// 月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符,
// 年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字)
// 例子:
// (new Date()).Format("yyyy-MM-dd hh:mm:ss.S") ==> 2006-07-02 08:09:04.423
// (new Date()).Format("yyyy-M-d h:m:s.S") ==> 2006-7-2 8:9:4.18
Date.prototype.Format = function (fmt) { //author: meizz
var o = {
"M+": this.getMonth() + 1, //月份
"d+": this.getDate(), //日
"h+": this.getHours(), //小时
"m+": this.getMinutes(), //分
"s+": this.getSeconds(), //秒
"q+": Math.floor((this.getMonth() + 3) / 3), //季度
"S": this.getMilliseconds() //毫秒
};
if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
for (var k in o)
if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
return fmt;
}
index.pug
javascript 代码
extends layout
block opration-content
a(class="nav-item nav-link" href="#") Holle,#{name}
a(class="nav-item nav-link" href="/login") Login out
block content
.jumbotron
h1.display-3 Holle world!
p.lead This is a simple hero unit, a simple jumbotron-style component for calling extra attention to featured content or information.
hr.my-4
p It uses utility classes for typography and spacing to space content out within the larger container.
p.lead
a(class="btn btn-primary btn-lg",href="#",role="button") Learn more
页面文件写好之后,修改app.js入口文件:
javascript 代码
var express = require('express');
var bodyParser = require('body-parser'); //用于处理 JSON, Raw, Text 和 URL 编码的数据
var app = express();
var router = require('./router/router');
var session = require('express-session');
//设置session配置
app.use(session({
resave: false, //重新保存
saveUninitialized: true, //
secret: 'ghost castle', //通过设置的 secret 字符串,来计算 hash 值并放在 cookie 中,使产生的 signedCookie 防篡改。
cookie:{ maxAge: 1000*60*60} //失效时间
}));
app.use(bodyParser.json()); //解析json数据
app.use(bodyParser.urlencoded({ extended: true })); //解析form表单提交上来的数据
app.set('view engine', 'pug'); // 配置模板引擎
app.use(express.static('public'));
app.use('/', router);
var server = app.listen(3001, function() {
var host = server.address().address;
var port = server.address().port;
console.log("http://%s:%s", host, port);
});
最后修改路由配置文件router.js
javascript 代码
const express = require('express');
const router = express.Router();
const Geetest = require('gt3-sdk'); //sdk
//极验配置
const captcha = new Geetest({
geetest_id: '极验id',
geetest_key: '极验key'
});
const user = require('../controllers/user');
const login = require('../controllers/login');
const Render = (path, title) => ((req, res) => res.render(path, {title}))
//注册登录
router.post('/ghost/login', login.login);
router.post('/ghost/register', login.register);
//验证码
router.get('/gt/register-slide', function(req, res) {
captcha.register(null, function (err, data) {
if (err) {
console.log(err);
return;
}
if (!data.success) {
// 进入 fallback,如果一直进入此模式,请检查服务器到极验服务器是否可访问
// 可以通过修改 hosts 把极验服务器 api.geetest.com 指到不可访问的地址
// 为以防万一,你可以选择以下两种方式之一:
// 1. 继续使用极验提供的failback备用方案
req.session.fallback = true;
res.send(data);
// 2. 使用自己提供的备用方案
// todo
} else {
// 正常模式
req.session.fallback = false;
res.send(data);
}
});
});
router.post('/gt/validate-slide', function(req, res) {
captcha.validate(req.session.fallback, {
geetest_challenge: req.body.geetest_challenge,
geetest_validate: req.body.geetest_validate,
geetest_seccode: req.body.geetest_seccode
}, function (err, success) {
if (err) {
// 网络错误
res.send(err);
} else if (!success) {
// 二次验证失败
res.send({"result" : false,"errorMsg": "登录失败,请完成验证"});
} else {
login.login(req, res);
//res.send({"status" : "success"});
}
});
});
router.post('/get_user_info', user.getUserInfo);
router.post('/add_user_info', user.addUserInfo);
router.post('/update_user_info', user.updateUserInfo);
router.post('/delete_user_info', user.deleteUserInfo);
router.post('/export_user_info', user.exportUser); //导出excel
// 总路由
router.use(function(req, res, next) {
global.CommonLibs = '';
const url = req.originalUrl;
if (url != "/login" && url != "/register" && !req.session.user) {
return res.redirect("/login");
}
//next的作用是将请求转发,这个必须有,如果没有,请求到这就挂起了。
next();
});
// /list_user 页面 post 请求
router.get('/list_user', function(req, res) {
res.sendfile('views/userList.html');
});
router.get('/login', Render('login', 'Login'));
router.get('/register', Render('signUp', 'Register'));
router.get('/', function(req, res){
const user = req.session.user[0];
res.render('index', {title: 'Home', name: user.uname});
});
module.exports = router;
运行效果如下(添加了极验的验证码校验):




github地址:github.com/ShanaFang00…