该项目功能
页面内容:
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,//验证码字体大小
width: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。