简介和思路

  • 使用express作为node框架,mlab作线上数据库,postman作为调试接口工具;
  • 注册时匹配唯一邮箱,录入用户名、密码(用bcrypt加密过)、邮箱、头像(获取gravatar公认头像),保存数据库成功后返回用户信息;
  • 登录时匹配用户名和密码,匹配成功后返回Bearer token令牌;
  • 访问一个接口,校验传入的authorization是否和登录时的token匹配,匹配通过则返回接口数据,否则响应未通过认证。

github

https://github.com/MaTonna/express-manage-web

文件目录

文件目录

流程演示

注册成功后返回用户信息
用户信息
注册时邮箱重复后返回错误信息
错误信息
登录成功后返回token
token
密码错误返回错误信息
错误信息
访问一个返回用户信息的接口,token验证通过才能获取到
用户信息
token验证不通过则默认会返回验证失败
验证失败

用到的依赖和工具

  • 工具:postman 发送请求和接收响应,用于调接口
  • “bcrypt”: “^3.0.2” 对注册密码进行加密
  • “body-parser”: “^1.18.3” 对post请求的请求体进行解析
  • “express”: “^4.16.4” nodejs框架,设置中间件响应HTTP请求,定义路由执行HTTP请求动作,给HTML页面传递参数
  • “gravatar”: “^1.6.0” 全球公认头像
  • “jsonwebtoken”: “^8.3.0” 跨域认证解决方案,用于生成token令牌,服务端认定用户身份
  • “mongoose”: “^5.3.8” 对mongodb进行便捷操作的对象模型工具
  • “passport”: “^0.4.0” 用于验证登录信息,如果token通过才能访问接口
  • “passport-jwt”: “^4.0.0”

server.js

功能:配置/连接数据库,配置访问API的中间件,配置/监听端口号等等。

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
const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const passport = require('passport');
// 接口api
const users = require('./routers/api/users');
const profiles = require('./routers/api/profiles');

const app = express();

// 使用body-parser中间件
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

// 使用中间件使用users,访问/api/users时获取到users
app.use('/api/users', users);
app.use('/api/profiles', profiles);

// 链接数据库
const db = require('./config/keys').mongoURL;
mongoose
.connect(
db,
{ useNewUrlParser: true }
)
.then(() => {
console.log('connect');
})
.catch(err => {
console.log(err);
});

// 初始化passport
// passport的配置
app.use(passport.initialize());
require('./config/passport')(passport);

// 访问主页时给页面发送数据
app.get('/', (req, res) => {
res.send('hello world');
});

const port = process.env.PORT || 5000;

app.listen(port, () => {
console.log('server start');
});

users.js

功能:接口代码,配置router

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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
const express = require('express');
const router = express.Router();
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const gravatar = require('gravatar');
const keys = require('../../config/keys');
const passport = require('passport');

const User = require('../../models/User');

// $router POST api/users/register
// @desc 返回请求的json数据
// @access public
router.post('/register', (req, res) => {
// 查询一条记录
User.findOne({
email: req.body.email
}).then(user => {
if (user) {
return res.status(400).json('邮箱已被注册');
} else {
var avatar = gravatar.url('req.body.email', { s: '200', r: 'pg', d: 'mm' });
const newUser = new User({
avatar,
name: req.body.name,
email: req.body.email,
password: req.body.password,
identity: req.body.identity
});

// 用bcrypt对密码进行加密
bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash(newUser.password, salt, (err, hash) => {
if (err) {
throw err;
}
// hash为加密后的密码
newUser.password = hash;
// 调用存储方法
newUser
.save()
.then(user => {
res.json(user);
})
.catch(err => {
console.log(err);
});
});
});
}
});
});

// $router POST api/users/login
// @desc 返回token jwt password
// @access public
router.post('/login', (req, res) => {
const email = req.body.email;
const password = req.body.password;
User.findOne({ email }).then(user => {
if (!user) {
return res.status(404).json('用户不存在');
}
// 密码匹配
bcrypt.compare(password, user.password).then(isMatch => {
if (isMatch) {
// 规则,加密名字,过期时间,回调
const rule = {
id: user.id,
name: user.name,
avatar: user.avatar,
identity: user.identity
};
// 登录成功后返回token,相当于一个令牌,只有令牌校验成功,才可以获取想要的数据
jwt.sign(rule, keys.secretOrKey, { expiresIn: 3600 }, (err, token) => {
if (err) {
throw err;
}
res.json({
success: true,
// 必须加Bearer
token: 'Bearer ' + token
});
});
// res.json({ msg: 'success' });
} else {
return res.status(404).json('密码错误');
}
});
});
});

// $router GET api/users/current
// @desc 返回current user
// @access private
router.get('/current', passport.authenticate('jwt', { session: false }), (req, res) => {
res.json({
id: req.user.id,
name: req.user.name,
email: req.user.email,
identity: req.user.identity
});
});

module.exports = router;

User.js

功能:配置mongoose的模型

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
const mongoose = require('mongoose');
const Schema = mongoose.Schema;

// 创建模型和所需要的字段
const UserSchema = new Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
avatar: {
type: String
},
identity: {
type: String,
required: true
},
data: {
type: Date,
default: Date.now
}
});

module.exports = User = mongoose.model('users', UserSchema);

passport.js

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
const JwtStrategy = require('passport-jwt').Strategy,
ExtractJwt = require('passport-jwt').ExtractJwt;
const mongoose = require('mongoose');
const User = mongoose.model('users');
const keys = require('../config/keys');

const opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = keys.secretOrKey;

// passport验证token
module.exports = passport => {
passport.use(
new JwtStrategy(opts, (jwt_payload, done) => {
User.findById(jwt_payload.id)
.then(user => {
if (user) {
return done(null, user);
} else {
return done(null, false);
}
})
.catch(err => {
console.log(err);
});
})
);
};

keys.js

1
2
3
4
module.exports = {
mongoURL: 'mongodb://manager-web:twj123456@ds147233.mlab.com:47233/resful-api-prod',
secretOrKey: 'secret'
};