シンプルなショッピングプラットフォームを最初から構築する(1):https://blog.csdn.net/time_____/article/details/105191286
プロジェクトのソースコード(継続的な更新):https://gitee.com/DieHunter/myCode/ツリー/マスター/ショッピング
に関する前回の記事では、プロジェクトの本体とプラグインモジュールの構築が完了しました。この記事の目的は、サーバー側のトークン検証とログイン機能を構築することです。
サーバ:
ファイルディレクトリ構造
エントリファイルはserver.jsです。設定するだけで、実行できます。
const express = require("express");
const app = express();
app.listen(1024, () => {
console.log("Server Start~");
});
ターミナルを使用してserver.jsディレクトリにcdし、ノードサーバーに入ってエントリファイルを実行し、Server Start〜を表示して操作が成功したことを示してから、いくつかのモジュールを紹介します。
const express = require("express");
const app = express();
const routes = require("./routes/routes");
const cors = require("cors"); //引入cors模块(解决跨域问题)
const path = require("path");
app.use(cors());
app.all("*", function (req, res, next) {
//设置允许跨域的域名,*代表允许任意域名跨域
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "content-type"); //允许的header类型
res.header("Access-Control-Allow-Methods", "DELETE,PUT,POST,GET,OPTIONS"); //跨域允许的请求方式
next(); //是否继续向下执行
});
let bodyParser = require("body-parser"); //post传输数据类型
app.use(
bodyParser.urlencoded({
extended: false,
})
);
new routes(app);//初始化路由
app.use(bodyParser.json());
app.use("/public", express.static(path.join(__dirname, "./public")));//静态目录
app.listen(1024, () => {
console.log("Server Start~");
});
すべての設定が完了したら、次の手順に進み、ルーティングとトークンの検証を構成します
utils.jsファイルに新しい静的クラスを作成し、jwt(jsonwebtoken)を導入し、config.jsファイルに新しい構成静的クラスを作成して構成変数(インターフェイス名、公開キー、定数)を格納し、それを使用する必要がありますCryptoはデータの暗号化と復号化を実行し、Bcryptパスワードソルト暗号化を実行します
utils.js
const jwt = require("jsonwebtoken");
const config = require("../config/config");
const cryptoJS = require("crypto-js");//用来加密解密前端参数
let { UserKey, AdminKey,CryptoKey} = config;//token加密关键字,秘钥
let key = cryptoJS.enc.Utf8.parse(CryptoKey);//生成16进制秘钥
module.exports = class Utils {
static parseUrl(req, res) {//获取前端传递的参数
return req.method == "POST" ? req.body : this.urlSplit(req.url);
}
static urlSplit(url) {//get获取的参数解析
let list = url.split("?")[1].split("&");
let leng = list.length;
let obj = {};
for (let i = 0; i < leng; i++) {
let key = list[i].split("=")[0];
let val = list[i].split("=")[1];
obj[key] = val;
}
return obj;
}
/*
* @param {string} type 'user'||'admin' 用户类型
* @param {string} user 用户名
* @param {bool} rempsd 是否记住密码
*/
static createToken = (type, user, rempsd) => {//生成token,用户登录时调用
let payload = {
user: user,
};
return jwt.sign(payload, type == "admin" ? AdminKey : UserKey, {
expiresIn: rempsd ? "3d" : "6h",
});
};
/*
* @param {object} req 前端请求对象
* @param {object} res 服务端接收对象
* @param {fn} next 中间件响应方法
*/
static checkToken = (req, res, next) => {
let _data = this.parseUrl(req, res); //解析前端参数
if (_data.crypto) {
_data = this.getCrypto(_data.crypto); //对前端参数解密
}
let isUser = true; //用户
let isAdmin = true; //管理员
let _decoded = ""; //加密的用户名
jwt.verify(_data.token, UserKey, function (err, decoded) {
if (err) {
isUser = false;
} else {
_decoded = decoded;
}
});
jwt.verify(_data.token, AdminKey, function (err, decoded) {
if (err) {
isAdmin = false;
} else {
_decoded = decoded;
}
});
if (isUser || isAdmin) {
_data.id = _decoded;
_data.userTokenType = isAdmin ? "admin" : "user";
res._data = _data;
next(); //中间件响应
} else {
res.send({
result: -999,
msg: "登录超时,请重新登录",
});
}
};
/* Crypto加密方法
* @param {object} _data 对用户请求后端的参数进行加密
*/
static setCrypto(_data) {
let encrypted = cryptoJS.AES.encrypt(JSON.stringify(_data), key, {
mode: cryptoJS.mode.ECB,
padding: cryptoJS.pad.Pkcs7,
});
return encrypted.toString();
}
/* Crypto解密方法
* @param {string} _token 将秘文解密成对象形式
*/
static getCrypto(_token) {
_token = decodeURIComponent(_token); //前端传参有特殊字符(中文)时转义(替换百分号)
let decrypt = cryptoJS.AES.decrypt(_token, key, {
mode: cryptoJS.mode.ECB,
padding: cryptoJS.pad.Pkcs7,
});
return JSON.parse(cryptoJS.enc.Utf8.stringify(decrypt).toString());
}
static createBcrypt(password) {//加密密码
return bcrypt.hashSync(password, bcrypt.genSaltSync(10));
}
static checkBcrypt(_password, _hash) {//对比密码
return bcrypt.compareSync(_password, _hash);
}
}
utilsを構成した後、ルーティングを構成し、routesフォルダーの下に新しいroutes.jsファイルを作成し、トークンを使用してユーザーが正しいことを確認します
const Util = require("../utils/utils");
const Config = require("../config/config");
module.exports = class Route {
constructor(app) {
app.get(Config.ServerApi.checkToken, Util.checkToken, (req, res) => {
res.send({
result: 1,
msg: "验证成功",
data: res._data
});
});
}
};
config.jsを構成します
module.exports = {
Agreement: "http://",//协议
DataBaseUrl: "127.0.0.1",//ip或域名
DataBasePort: ":27017",//数据库端口
DataBaseName: "shopping",//数据库文档名称
ServerUrl: "",
ServerPort: ":1024",//服务端请求端口
Path: "/",//路由名
UserKey: "user",//用户token加密标识
AdminKey: "admin",//管理员token加密标识
CryptoKey: "tokenkey",//Crypto加密关键字,用于生成16进制秘钥
ServerApi: {//接口名称
checkToken: "/checkToken",//token验证
userLogin: "/userLogin",//用户登录
}
}
続いて、新しいユーザー管理ルーティングインターフェイスを作成しますが、その前に、データベースを構成する必要があります
- Robo3tをダウンロードしてインストールします
- 新しいデータベースを作成します。ここで私の新しいデータベース名はShoppingです。
- 新しいデータベーステーブルを作成します。ここではusersテーブルを作成しています
データベースに接続する
- model.jsで新しいデータベース接続を作成します(カスタムイベントを監視するソケットと同様のプリンシパルオブザーバーモード)
const mongoose = require('mongoose'); const config = require('../../config/config') module.exports = class Mongoose { constructor() { mongoose.connect(`mongodb://${config.DataBaseUrl+config.DataBasePort+config.Path+config.DataBaseName}`, { useNewUrlParser: true }); this.db = mongoose.connection; this.db.on("error", function (error) { console.log("Err:" + error); }); this.db.on("open", function () { console.log("Connet Success~"); }); this.db.on('disconnected', function () { console.log('Connet Stop~'); }); return mongoose } }
-
新しいデータベーススキーマを使用してデータテーブルに接続します。ここでは、構成アイテムをconfig.jsファイルに直接削除します。
const _mongoose = require('./model'); let mongoose = new _mongoose() const _schema = mongoose.Schema; module.exports = class Schema { constructor(config) { let schema = new _schema(config.data); let Model = mongoose.model(config.modelName, schema); //新建数据库 return Model } }
-
config.jsの新しいユーザーテーブルのデフォルトフィールド構成
Collections: { Users: { modelName: "users", data: { headPic: {//头像 type: String, required: false, default: "public/assets/img/default.gif" }, userType: {//用户类型(管理员/用户) type: String, required: true, }, username: {//用户名 type: String, required: true, }, password: {//密码 type: String, required: true }, sex: {//性别 type: String, required: true }, mailaddress: {//邮箱地址 type: String, required: true }, mailurl: {//邮箱类型 type: String, required: true }, alladdress: {//省市县 type: Array, required: false, default: [] }, address: {//具体地址 type: String, required: false, default: '' }, descript: {//个人说明 type: String, required: false, default: '' }, time: {//注册时间 type: String, required: true }, isactive: {//是否冻结用户 type: Boolean, default: true } } } },
上記は、構成、トークン検証、およびログイン用のバックエンドコードです。
総括する
プロジェクトをMVCと同様の設計アイデアに組み込む必要があります。データモデルと制御層を分離することで、コードを明確にし、再利用可能で、保守しやすくすることができます。