该项目功能
页面内容:
1.注册
2.登录
3.退登、修改密码/修改昵称
4.列表页(可新增,删除,修改,查询)
5.子账号页(可增,删,改,查)==》仅对超级管理员开放
用到技术:
1.反向代理
2.数据库建表,请求mysql数据库
3.进行node模块化开发,分模块处理请求()
具体功能:
1.生成验证码
2.token:前端用uuid加密生成token实现验证码一一对应
3.用redis(可支持:键/值对,哈希表,链表,集合)缓存token以及用户信息,并设置过期时效
redis使用:
1.先跑起来redis服务:到redis文件目录下:redis-server redis.window.conf
2.在打开可视化工具(redis desktop manager)查看
**前端代码 https://github.com/miaSlady/modular_html.git **
**node后台代码 https://github.com/miaSlady/modular.git **
具体实现过程
数据库部分
数据库搭建
1.建立读书计划表
2.建立用户列表
后台node部分
1.安装以下依赖
"koa": "^2.12.0",
"koa-bodyparser": "^4.3.0",
"koa-router": "^8.0.8",
"koa-session": "^6.0.0",
"koa2-cors": "^2.0.6",
"mysql": "^2.18.1",
"redis": "^3.0.2",
"svg-captcha": "^1.4.0"
"crypto": "^1.0.1"
koa 写后台;mysql连接服务器;svg-captcha生成二维码;redis写token校验;crypto配合redis使用
2.配置跨域
1.在公共方法utils里头新建中间件koa-cors.js
注:1.allowDomain允许访问的域名
2.对options进行处理:method = OPTIONS 时, 属于预检(复杂请求), 当为预检时, 可以直接返回空响应体
module.exports = async (ctx, next) => {
const allowDomain=[
"http://172.16.0.128:1818/",
"http://172.16.0.25:1818/",
"http://localhost:1818/",
]
if(allowDomain.includes(ctx.header.referer) || allowDomain.includes(ctx.header.origin + '/')){
// ctx.set('Access-Control-Allow-Origin', '*'); //允许来自所有域名请求(不携带cookie请求可以用*,如果有携带cookie请求必须指定域名)
ctx.set("Access-Control-Allow-Origin", ctx.header.origin); // 只允许指定域名http://localhost:8080的请求
ctx.set('Access-Control-Allow-Methods', 'OPTIONS, GET, PUT, POST, DELETE'); // 设置所允许的HTTP请求方法
ctx.set('Access-Control-Allow-Headers', 'x-requested-with, accept, origin, content-type,token'); // 字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段.
// 服务器收到请求以后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应。
ctx.set('Content-Type', 'application/json;charset=utf-8'); // Content-Type表示具体请求中的媒体类型信息
ctx.set('Access-Control-Allow-Credentials', true); // 该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。
// 当设置成允许请求携带cookie时,需要保证"Access-Control-Allow-Origin"是服务器有的域名,而不能是"*";
ctx.set('Access-Control-Max-Age', 300); // 该字段可选,用来指定本次预检请求的有效期,单位为秒。
// 当请求方法是PUT或DELETE等特殊方法或者Content-Type字段的类型是application/json时,服务器会提前发送一次请求进行验证
// 下面的的设置只本次验证的有效时间,即在该时间段内服务端可以不用进行验证
ctx.set('Access-Control-Expose-Headers', 'myData'); // 需要获取其他字段时,使用Access-Control-Expose-Headers,
// getResponseHeader('myData')可以返回我们所需的值
/*
CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:
Cache-Control、
Content-Language、
Content-Type、
Expires、
Last-Modified、
Pragma。
*/
/* 解决OPTIONS请求 */
if (ctx.request.method == 'OPTIONS') {
ctx.status = 200;
} else {
await next();
}
}else{
ctx.throw(500);
}
};
3.连接服务器
1.在middlewares文件下创建databases.js。
const mysql=require("mysql");
const db=mysql.createConnection({
host:"localhost",
port:3306,
user:'root',
password:'123456',
database:'modular',//数据库名
multipleStatements: true,//可执行多条sql语句
});
const connectDb=function(){
db.connect(err=>{
if(err) throw err;
console.log("连接成功");
})
}
module.exports={
db,
connectDb
}
> 暴露出db用于sql查询,connectDb在app.js中进行全局引入
2.在app.js添加
const {connectDb}=require('./middlewares/databases')
connectDb();
3.分模块管理路由
注:
(1)app.js写全局注入
(2)controller.js里头写接口封装
(3)controllers 里头写各个模块调用的接口封装
1.在app.js
// 导入controller middleware:
const controller = require('./controller');//对路由分模块处理
controller(app);
2.在controller.js(具体写法查看github项目:https://github.com/miaSlady/modular.git)
module.exports = function (app,dir) {
let
controllers_dir = dir || 'controllers',
router = require('koa-router')();
addControllers(router, controllers_dir);
//token拦截中间件
app.use(async (ctx,next)=>{
...
})
app.use(router.routes());
// return router.routes();
};
3.token拦截中间件配置
配置逻辑:
(1)除获取验证码接口,其他接口需携带token
(2)注册、登录接口无需校验,直接去调用接口await next(),会在接口处去校验token、code是否匹配
(3)其他接口需校验token判断code上传是否正确(先判断token是否存在,再获取用户信息),在ctx里头存用户信息,便于其他接口调用ctx.state.user=user;
//token拦截中间件
app.use(async (ctx,next)=>{
var url=ctx.request.URL.pathname;
if(url!='/login/code'){//非获取验证码(需携带token)
const {token}=ctx.request.header;
let response={
code:401,
success:false,
msg:'登录失效,请重新登录'
};
if(token){
ctx.state.token=token;
return new Promise((res,req)=>{
console.log(3)
if(url!='/login/signUp' && url!='/login/login'){//不是注册且不是登录进行token校验
client.hexists('codeUserInfo',token, (err, data)=>{//判断token是否失效
console.log(4,data)
if(data){//token未失效获取用户信息
client.hgetall('codeUserInfo', (err, obj)=>{
let user=obj[token];
user=JSON.parse(user);
ctx.state.user=user;
res(true)
});
}else{//token失效
res(false)
}
})
}else{//注册或登录无需进行token校验
res(true)
}
}).then(async(bool)=>{
if(bool){
console.log('调接口去');
await next();
}else{
ctx.response.body=response
}
})
}else{//没携带token
ctx.response.body=response
}
}else{//获取验证码code
await next();
}
})
4.用基于uuid生成随机数用于token
const uuidv4 = require('uuid/v4');
token=uuidv4()
5.生成验证码
const svgCaptcha = require('svg-captcha');//生成验证码
let captcha = svgCaptcha.create({
size:4,//验证码个数
fontSize:50,//验证码字体大小
135,//宽度
heigth:47,//高度
background:'#cc9966'//背景大小
});
ctx.response.type="image/svg+xml";//设置返回的数据格式
let token=uuidv4(),obj={};
obj[token]=captcha.text;
client.hmset('codeVerify', obj, redis.print);
client.expire('codeVerify',180);//3分钟自动过期
6.redis存取(我用的hash)
var redis = require('redis')//中间件:缓存哈希表(1.注册/登录判断code是否输入正确;2.登录用token对应用户信息)
var client = redis.createClient(6379, '127.0.0.1')
client.hmset('codeUserInfo', userInfo , redis.print);//存
client.expire('codeUserInfo',24*60*60);//1天自动过期(设置过期时间)
client.hgetall('codeVerify', (err, obj)=>{//获取
console.log('我获取到了',obj);
if(err){
rej(err)
}else{
res(obj)
}
})
client.hexists('codeUserInfo',token, (err, data)=>{//查存在
if(data){
client.hdel('codeUserInfo', token,async (err,data)=>{//删除
res()
});
}else{
res()
}
});
7.登录逻辑
(1)调接口获取验证码,后台在验证码接口用uuid生成随机数作为token返给前端,存在redis里头的codeVerify对象(自己可命名,设置时效3分钟,可自己定),键为token(这串uuid加密的随机数),值为code的值
(2)前端登录/注册请求头带token请求接口,后台去redis里头的codeVerify校验code是否正确,若正确获取用户信息,存在redis里头的codeUserInfo对象(键token,值用户信息,设置时效3天,可自己定),以后每个接口请求头携带token。