中间件
@Koa/cors
- 前后端分离 所以用了 @koa/cors 来解决跨域
app.use(cors({
origin(ctx) {
const origin = ctx.accept.headers.origin;
return origin;
},
credentials: true,
allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH,OPTIONS',
}));
koa-session
- 用来生成session信息,塞到header里面
koa-body
- 用来解析前端请求的数据类型
koa-bodyparser
- 用来解析前端的请求数据类型
app.use(bodyParser({
jsonLimit: '10mb' // 增大数据的大小限制(图片的存储) 默认1M 超过限制报413错误
}))
koa-static
- 用来解析静态资源
app.use(require('koa-static')(path.join(__dirname, '..', "public")));
koa-router
错误拦截
// 中间件捕捉不到的错误 这里可以拦截
process.on('uncaughtException', err => {
console.error('app uncaughtException', err.stack);
});
process.on('unhandledRejection', (reason, p) => {
console.error('app Unhandled Rejection at:', p, 'reason:', reason);
});
mongoose
- 初始化
const mongoose = require('mongoose');
const db = mongoose.connection;
function initMongoose() {
mongoose.connect('mongodb://127.0.0.1:27017/myblog');
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function() {
console.log('链接数据库成功!');
});
}
module.exports = initMongoose;
- 创建一个model
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
let UserSchema = new Schema({
username: String,
age: {
type: Number,
default: 18
},
password: String,
token: String
});
UserSchema.statics.findUser = async function(options) {
return await this.findOne(options, {
password : 0 // 返回结果不包含密码字段
});
}
UserSchema.statics.addUser = async function(info, cb) {
let User = this;
new User(info).save(cb);
}
UserSchema.statics.updateUser = async function(option, tarValue) {
// this.where(option)
// .update(tarValue, cb)
await this.updateOne(option, tarValue);
}
const User = mongoose.model('User', UserSchema);
module.exports = User;
- 数据的存取
const User = require('../model/user');
const encrypt = require('../help/encrypt');
const token = require('../help/token');
const register = async (ctx, next) => {
try {
let userInfo = ctx.request.body;
let newPas = encrypt(userInfo.password) // 密码加密处理
let newInfo = {
password: newPas,
username: userInfo.username,
}
let user = await User.findUser({username: userInfo.username});
if(user) {
ctx.fail(false,'用户名被注册!')
}else {
User.addUser({
...newInfo,
token: token.createToken(userInfo.username)
});
ctx.success(true, '注册成功!')
}
}catch(e) {
ctx.fail();
}
}
module.exports = register;
密码加密
- crypto 模块 (yarn add crypto)
const crypto = require('crypto');
function encrypt(options) {
let md5 = crypto.createHash("md5");
return md5.update(options).digest("hex");
}
module.exports = encrypt;
token
生成token
- yarn add jsonwebtoken
function createToken(user_name){
// 创建token时,我们把用户名作为JWT Payload的一个属性,并且把密钥设置为liuxinya,token过期时间设置为60s。意思是登录之后,60s内刷新页面不需要再重新登录。
const token = jwt.sign({user_name: user_name}, 'liuxinya', {expiresIn: '3600s'});
// console.log(token)
return token;
};
验证token
jwt.verify(token, 'liuxinya');//如果token过期或验证失败,将抛出错误
从token里面拿到用户信息
function getUserNameFromToken(ctx) {
const token = ctx.cookies.get("userId");
return jwt.decode(token).user_name;
}
验证用户权限
- 用户登录成功之后, node端会生成一个token并塞到cookie里面去
- token里面含有用户信息还有一段标志token有效性的时间戳
- 前端每次请求会携带此token
- 后端通过验证token的有效性决定是否允许前端请求相应的资源
模块
遇到的问题
前端每次请求不自动携带cookie, 后端设置不上cookie
- 前端 axios 默认不携cookie 需要设置
axios.defaults.withCredentials = true;
- 后端在初始化也要有相应的配置
const cors = require('@koa/cors');
app.use(cors({
origin(ctx) {
const origin = ctx.accept.headers.origin;
return origin; // 生产环境要做白名单限制,符合条件的origin才返回
},
credentials: true,
allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH,OPTIONS',
}));
项目地址: https://github.com/liuxinya/node-koa-mongoose develop分支