Browse Source

一个账号不允许多处同时在线

vian 5 năm trước cách đây
mục cha
commit
b1f5c58092

+ 2 - 1
modules/all_models/user.js

@@ -111,7 +111,8 @@ let schema = {
         type: Number,
         default: 0
     },
-    welcomeShowTime:String
+    welcomeShowTime:String,
+    token: String
 
 };
 mongoose.model(collectionName, new Schema(schema, {versionKey: false}));

+ 100 - 0
modules/main/middleware/index.js

@@ -0,0 +1,100 @@
+/**
+ * Created by zhang on 2020/1/8.
+ */
+
+module.exports={
+    rationNumberChecking,
+    tenderNumberChecking,
+    stateChecking,
+};
+
+const mongoose = require("mongoose");
+const rationModel = mongoose.model("ration");
+const pmFacade = require('../../pm/facade/pm_facade');
+const online_logs = require('../../../logs/online_logs');
+import UserModel from '../../../modules/users/models/user_model';
+
+async function rationNumberChecking(req, res, next) {
+    if(req.session.systemSetting){
+        let type = req.session.compilationVersion.indexOf("免费") == -1?"professional":"normal";
+        let data = req.body.data;
+        if(typeof data === 'object'){
+            data = JSON.stringify(data);
+        }
+        data = JSON.parse(data);
+        let projectID = data.projectID;
+        let no = await rationModel.find({projectID:projectID}).count();
+        if(no >= req.session.systemSetting[type].ration){
+            let  result = {error:1,message:"您套用定额个数超限,请联系我们的客服人员。"};
+            return  res.json(result);
+        }
+    }
+    next();
+}
+
+async function tenderNumberChecking(req, res, next) {
+    const data = JSON.parse(req.body.data);
+    const tenderCount = data.tenderCount;
+    if (tenderCount) {
+        const tenderOverrun = await pmFacade.isTenderOverrun(tenderCount, req.session);
+        if (tenderOverrun) {
+            return res.json({
+                error: 1,
+                message: '您创建的项目个数超限,请联系我们的客服人员,或者导出建设项目保存到本地备份,删除云上数据。'
+            });
+        }
+    }
+    next();
+
+}
+
+function isAjax(req) {
+    return req.headers['x-requested-with'] === 'XMLHttpRequest';
+}
+
+// 登录状态全局判断
+async function stateChecking(req, res, next) {
+    const url = req.originalUrl;
+    if (url=="\/"|| /^\/login/.test(url) || /\.map|\.ico$/.test(url) || /^\/sms/.test(url) || /^\/cld/.test(url) || /^\/captcha/.test(url)) {
+        // 如果是登录页面或短信接口或cld接口则忽略判断数据
+        next();
+    } else {
+        try {
+            if (req.query.ssoID !== undefined && req.query.ssoID !== null && req.query.token !== undefined && req.query.token !== null) {
+                delete req.session.sessionUser;
+                delete req.session.sessionCompilation;
+                return res.redirect('/login' + url);
+            } else {
+                // 判断session
+                const sessionUser = req.session.sessionUser;
+                if (!sessionUser) {
+                    //处理 ajax 请求 session 过期问题
+                    if (isAjax(req)) {
+                        return res.json({ret_code: 99, ret_msg: '登录信息失效,请您重新登录'});
+                    } else {
+                        throw 'session error';
+                    }
+                } else {
+                    const userModel = new UserModel();
+                    const isValidToken = await userModel.checkToken(sessionUser.id, sessionUser.token);
+                    if (!isValidToken) {
+                        delete req.session.sessionUser;
+                        delete req.session.sessionCompilation;
+                        if (isAjax(req)) {
+                            return res.json({ ret_code: 99, ret_msg: '' });
+                        } else {
+                            throw 'session token invalid';
+                        }
+                    }
+                }
+                res.locals.sessionUser = sessionUser;
+            }
+        } catch (error) {
+            // 最后一个页面存入session
+            req.session.lastPage = url;
+            return res.redirect('/login');
+        }
+        next();
+        await online_logs.saveOnlineTime(req);//记录登录时长
+    }
+}

+ 0 - 53
modules/main/middleware/system_setting.js

@@ -1,53 +0,0 @@
-/**
- * Created by zhang on 2020/1/8.
- */
-
-module.exports={
-    getSystemSetting,
-    rationNumberChecking:rationNumberChecking,
-    tenderNumberChecking
-};
-
-let mongoose = require("mongoose");
-let rationModel = mongoose.model("ration");
-const systemSettingModel = mongoose.model('system_setting');
-const pmFacade = require('../../pm/facade/pm_facade');
-
-// 获取系统设置,这个系统设置正常情况下有存在session中
-async function getSystemSetting() {
-    return await systemSettingModel.findOne({}).lean();
-}
-
-async function rationNumberChecking(req, res, next) {
-    if(req.session.systemSetting){
-        let type = req.session.compilationVersion.indexOf("免费") == -1?"professional":"normal";
-        let data = req.body.data;
-        if(typeof data === 'object'){
-            data = JSON.stringify(data);
-        }
-        data = JSON.parse(data);
-        let projectID = data.projectID;
-        let no = await rationModel.find({projectID:projectID}).count();
-        if(no >= req.session.systemSetting[type].ration){
-            let  result = {error:1,message:"您套用定额个数超限,请联系我们的客服人员。"};
-            return  res.json(result);
-        }
-    }
-    next();
-}
-
-async function tenderNumberChecking(req, res, next) {
-    const data = JSON.parse(req.body.data);
-    const tenderCount = data.tenderCount;
-    if (tenderCount) {
-        const tenderOverrun = await pmFacade.isTenderOverrun(tenderCount, req.session);
-        if (tenderOverrun) {
-            return res.json({
-                error: 1,
-                message: '您创建的项目个数超限,请联系我们的客服人员,或者导出建设项目保存到本地备份,删除云上数据。'
-            });
-        }
-    }
-    next();
-
-}

+ 1 - 1
modules/main/routes/ration_route.js

@@ -2,7 +2,7 @@
  * Created by jimiz on 2017/4/7.
  */
 let express = require('express');
-let ss_middleware = require("../middleware/system_setting");
+let ss_middleware = require("../middleware/index");
 
 module.exports = function (app) {
     let rationRouter = express.Router();

+ 8 - 2
modules/pm/facade/pm_facade.js

@@ -130,7 +130,8 @@ let qiniu = require("qiniu");
 let fs = require("fs");
 let path = require("path");
 let request = require("request");
-const systemSettingMiddleware = require('../../main/middleware/system_setting');
+const systemSettingModel = mongoose.model('system_setting');
+
 
 let qiniu_config = {
     "AccessKey": "_gR1ed4vi1vT2G2YITGSf4_H0fJu_nRS9Tzk3T4z",
@@ -2960,6 +2961,11 @@ function uploadToken() {
     return result
 }
 
+// 获取系统设置,这个系统设置正常情况下有存在session中
+async function getSystemSetting() {
+    return await systemSettingModel.findOne({}).lean();
+}
+
 // 有些方法无法通过中间件就检查单位工程数量是否超限
 // 需要到具体的业务代码中进行判断
 // 这个方法就是具体业务代码中,需要检查单位工程数量是否超限用
@@ -2967,7 +2973,7 @@ async function isTenderOverrun(tenderCount, session) {
     const userID = session.sessionUser.id;
     const compilation = session.sessionCompilation._id;
     const compilationVersion = session.compilationVersion || '免费';
-    let systemSetting = session.systemSetting || (session.systemSetting = await systemSettingMiddleware.getSystemSetting());
+    let systemSetting = session.systemSetting || (session.systemSetting = await getSystemSetting());
     // 这种情况只有在刚上线此功能时会出现,不考虑时间差
     if (!systemSetting) {
         return false;

+ 1 - 1
modules/pm/routes/pm_route.js

@@ -6,7 +6,7 @@ import BaseController from "../../common/base/base_controller";
 let express = require('express');
 let pmController = require('./../controllers/pm_controller');
 const baseController = new BaseController();
-const systemMiddleware = require('../../main/middleware/system_setting');
+const systemMiddleware = require('../../main/middleware/index');
 
 module.exports = function (app) {
 

+ 4 - 1
modules/users/controllers/login_controller.js

@@ -16,6 +16,7 @@ const moment = require('moment');
 const Captcha = require("../models/captcha");
 let mongoose = require("mongoose");
 let systemSettingModel = mongoose.model("system_setting");
+const uuidV1 = require('uuid/v1');
 
 
 
@@ -78,6 +79,7 @@ class LoginController {
                     mobile: userData.mobile,
                     qq: userData.qq,
                     isUserActive: userData.isUserActive,
+                    token: uuidV1(),
                 };
 
                 request.session.sessionUser = sessionUser;
@@ -260,7 +262,8 @@ class LoginController {
                 mobile: userData.mobile,
                 qq: userData.qq,
                 isUserActive: userData.isUserActive,
-                newLogin:true
+                newLogin:true,
+                token: uuidV1(),
             };
 
             request.session.sessionUser = sessionUser;

+ 19 - 3
modules/users/models/user_model.js

@@ -184,6 +184,7 @@ class UserModel extends BaseModel {
                 qq: userData.qq,
                 latest_login:userData.latest_login,
                 isUserActive: userData.isUserActive,
+                token: userData.token
             };
             console.log("updateUser 开始 -------------------------------");
             let updateResult = await this.updateUser(condition,UpdateData);
@@ -258,9 +259,23 @@ class UserModel extends BaseModel {
      * @param {string} ssoId
      * @return {object}
      */
-    async findDataById(id) {
-        let objId = mongoose.Types.ObjectId(id);
-        return await this.db.findOne({_id: objId});
+    async findDataById(id, fields) {
+        const objId = mongoose.Types.ObjectId(id);
+        return fields ? await this.db.findOne({_id: objId}, fields) : await this.db.findOne({_id: objId});
+    }
+
+    /**
+     * 验证用户token正确性
+     * 一个账号不允许多处同时在线,每次登陆都会更新session和数据库的token,每个请求都会比对session和数据库的token
+     * @param {String} id - 用户ID
+     * @param {String} token - 登陆生成的token
+     */
+    async checkToken(id, token) {
+        const user = await this.findDataById(id, '-_id token');
+        if (!user.token) { // 兼容第一次上线,已登陆的用户还没token,需要返回验证正确
+            return true;
+        }
+        return user.token === token;
     }
 
     async findDataByAccount(account) {
@@ -291,6 +306,7 @@ class UserModel extends BaseModel {
             create_time: new Date().getTime(),
             latest_login: new Date().getTime(),
             isUserActive: userData.isUserActive,
+            token: userData.token
         };
         return this.db.create(insertData);
     }

+ 3 - 1
public/web/commonAlert.js

@@ -11,7 +11,9 @@
 window.alert = function(str) {
     /*$('#commonAlert').find('p').text(str);
      $('#commonAlert').modal('show');*/
-    hintBox.infoBox('系统提示', str, 1);
+    if (str) {
+        hintBox.infoBox('系统提示', str, 1);
+    }
 };
 
 

+ 3 - 1
public/web/common_ajax.js

@@ -154,7 +154,9 @@ var CommonAjax = {
 $.ajaxSetup({
     complete: function (data) {
         if (data.responseJSON&&data.responseJSON.ret_code && data.responseJSON.ret_code == 99) {
-            alert(data.responseJSON.ret_msg);
+            if (data.responseJSON.ret_msg) {
+                alert(data.responseJSON.ret_msg);
+            }
             var top = getTopWindow();
             setTimeout('top.location.href = "/login";', 300);
         }

+ 2 - 38
server.js

@@ -23,8 +23,6 @@ fileUtils.getGlobbedFiles('./modules/all_models/*.js').forEach(function(modelPat
 //config.setupCache();
 let cfgCacheUtil = require("./config/cacheCfg");
 cfgCacheUtil.setupDftCache();
-let online_logs = require("./logs/online_logs");
-
 
 let app = express();
 let _rootDir = __dirname;
@@ -56,42 +54,8 @@ app.use(session({
 }));
 
 // 登录状态全局判断
-app.use(async function (req, res, next) {
-    let url = req.originalUrl;
-    // if (/^\/login/.test(url) || /\.map|\.ico$/.test(url) || /^\/sms/.test(url) || /^\/cld/.test(url) || /^\/captcha/.test(url)  || /^\/accountIsPro/.test(url)) {
-    if (url=="\/"|| /^\/login/.test(url) || /\.map|\.ico$/.test(url) || /^\/sms/.test(url) || /^\/cld/.test(url) || /^\/captcha/.test(url)) {
-        // 如果是登录页面或短信接口或cld接口则忽略判断数据
-        next();
-    } else {
-        try {
-            if (req.query.ssoID !== undefined && req.query.ssoID !== null && req.query.token !== undefined && req.query.token !== null) {
-                delete req.session.sessionUser;
-                delete req.session.sessionCompilation;
-                return res.redirect('/login' + url);
-            } else {
-                // 判断session
-                let sessionUser = req.session.sessionUser;
-                if (!sessionUser) {
-                    //处理 ajax 请求 session 过期问题
-                    if (req.headers["x-requested-with"] != null
-                        && req.headers["x-requested-with"] == "XMLHttpRequest"
-                        && req.url != "/login") {
-                        return res.json({ret_code: 99, ret_msg: '登录信息失效,请您重新登录'});
-                    } else {
-                        throw 'session error';
-                    }
-                }
-                res.locals.sessionUser = sessionUser;
-            }
-        } catch (error) {
-            // 最后一个页面存入session
-            req.session.lastPage = url;
-            return res.redirect('/login');
-        }
-        next();
-        await online_logs.saveOnlineTime(req);//记录登录时长
-    }
-});
+const { stateChecking } = require('./modules/main/middleware/index');
+app.use(stateChecking);
 
 //加载路由文件
 fileUtils.getGlobbedFiles('./modules/**/routes/*.js').forEach(function(modelPath) {