首先新建文件夾命名koa-server,npm init,相關包的安裝就不說了,這是我的package.json

新建index.js文件,編碼如下,config全局配置不用管,redis是一個簡單的get和set操作的封裝,也不用管
const bodyParser = require("koa-bodyParser");
const Koa = require("koa");
const koaStatic = require("koa-static");
const path = require("path");
const cors = require("koa2-cors");
const koaJwt = require('koa-jwt');
// const
const token = require("./model/paytoken")
const constroller = require("./controller");
const config = require("./model/config");
const db = require("./model/redis");
const app = new Koa();
//全局配置
global.config = config;
global.db = db;
global.secret = "tokensecret";
//設置靜態目錄
app.use(koaStatic(path.resolve(__dirname,"./public")));
//跨域
app.use(cors());
//post 參數解析
app.use(bodyParser());
//token驗證是否過期或失效
app.use(token);
//容錯處理
app.use((ctx,next)=>{
return next().catch((error)=>{
if(error.status === 401){
ctx.status = 401;
ctx.body = {
code:-1,
msg:"token error 401"
}
}
else{
throw error;
}
});
});
//token過濾規則
app.use(koaJwt({secret:global.secret}).unless({
path:[
/^\/api\/login/,
/^\/api\/register/,
/^((?!\/api).)*$/
]
}));
//導入controller middleware
app.use(constroller());
//啟動
app.listen(config.port);
console.log(`server start at 3000`);
其中paytoken是token的解析與驗證,在這里可以獲取加密的相關信息,如userid等,這里保存到ctx上下文,保證是本次請求,命名token_data
const jwt = require('jsonwebtoken');
async function verify(ctx,next){
let url = ctx.request.url;
let authorization = ctx.request.headers["authorization"];
if(authorization){
let token = authorization.split(" ")[1];
let payload = jwt.verify(token,global.secret,(error,decoded)=>{
if(error){
ctx.body = {
status:-1,
msg:"登陸失效"
};
}
else{
ctx.token_data = decoded;
return next();
}
});
}
else{
return next();
}
}
module.exports = verify;
controller是路由控制,主要用於路由自動解析,原理是掃描controllers文件夾下的所有js文件,同時對其require導出,規則是必須導出前綴為"PSOT "或"GET "的路由,用於api接口的訪問
const fs = require("fs");
function addMapping(router,mapping){
for(let url in mapping){
if(url.startsWith('GET')){
//如果url類似get xxx
let path = url.substring(4);
router.get(path,mapping[url]);
}
else if(url.startsWith("POST")){
//如果url 類似post xxx
let path = url.substring(5);
router.post(path,mapping[url]);
}
else{
console.log(`invalid URL:${url}`);
}
}
}
function addControllers(router,dir){
let files = fs.readdirSync(__dirname + "/" + dir);
//過濾js文件
let js_files = files.filter((f)=>{
return f.endsWith(".js");
});
//處理每個js文件
for(let f of js_files){
let mapping = require(__dirname + '/controllers/' + f);
addMapping(router,mapping);
}
}
module.exports = function(dir){
let controllers_dir = dir || "controllers";
let router = require("koa-router")();
addControllers(router,controllers_dir);
return router.routes();
}
controllers文件夾下新建sign.js文件,處理login和register
const jwt = require('jsonwebtoken');
const db = global.db;
//注冊路由
let register_func = async (ctx,next)=>{
let name = ctx.request.body.userName;
let password = ctx.request.body.password;
let userdata = {name,password};
let body;
if(!name || !password){
body = {
code:-1,
msg:"用戶名或密碼不能為空"
}
}
else if(name.length < 4){
body = {
code:-1,
msg:"用戶名長度必須大於等於4"
}
}
else if(password.length < 8){
body = {
code:-1,
msg:"密碼至少為8位"
}
}
else{
let data = await db.getKey(name);
if(data){
body = {
code:-1,
msg:"用戶名重復",
}
}
else{
let result = await db.setKey(name,JSON.stringify(userdata));
if(result == "OK"){
body = {
code:0,
msg:"注冊成功",
token:jwt.sign(userdata,global.secret,{expiresIn:'4h'})
}
}
else{
body = {
code:-1,
msg:"注冊失敗,原因未知"
}
}
}
}
ctx.body = body;
}
//登錄路由
let login_func = async (ctx,next)=>{
let name = ctx.request.body.userName || "";
let password = ctx.request.body.password || "";
if(!name || !password){
ctx.body = {
code:-1,
msg:"用戶名或密碼錯誤"
}
}
else{
let result = await db.getKey(name);
let body;
if(!result){
body = {
code:-1,
msg:"用戶不存在"
}
}
else{
let data = JSON.parse(result);
if(data.password === password){
body = {
code:0,
msg:"登錄成功",
token:jwt.sign({name,password},global.secret,{expiresIn:'4h'})
}
}
else{
body = {
code:-1,
msg:"密碼錯誤"
}
}
}
ctx.body = body;
}
}
module.exports = {
"POST /api/login":login_func,
"POST /api/register":register_func
}
其中生成簽名token代碼如下
jwt.sign(userdata,global.secret,{expiresIn:'4h'})
接下來post請求/api/login,傳入參數成功則生成token返回給客戶端,否則失敗,注意redis數據庫必須有數據
或者/api/register注冊,存一個新的
驗證token只需要新建一個js,比如排行榜rank.js,如果前端沒有在header中設置token或者token設置為不存在的,則會返回401,token error
前端附加token必須是(以vue、axios為例)

大膽嘗試是進步的主力,如果連嘗試都不去,那什么也做不成,就像有錢人的兒子都很有錢一樣,你窮並不是因為你代碼寫的不好,而是因為世界是不和平的,所以活出自己吧!
