Browse Source

养护版本增加滑动验证登录功能和专业版用户短信验证

laiguoran 6 years ago
parent
commit
d37b2e8ad8

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

@@ -8,6 +8,9 @@
 import UserModel from "../models/user_model";
 import SettingModel from "../models/setting_model";
 import CompilationModel from "../models/compilation_model";
+// import Captcha from "../models/captcha";
+// 验证码
+const Captcha = require("../models/captcha");
 
 class LoginController {
 
@@ -58,6 +61,44 @@ class LoginController {
             if (userData.mobile === '') {
                 return response.json({error: 2,ssoId: userData.id});
             }
+
+            //还要判断account是否是专业版用户
+            let isPro = false;
+            const userInfo = await userModel.findDataByAccount(account);
+
+            if (userInfo && userInfo.upgrade_list !== undefined) {
+                for (const ul of userInfo.upgrade_list) {
+                    if (ul.isUpgrade === true) {
+                        isPro = true;
+                        break;
+                    }
+                }
+            }
+            // 专业版短信验证码验证
+            if (isPro) {
+                const codeMsg = request.session.code;
+                if (codeMsg !== undefined && request.body.code !== '') {
+                    const code = codeMsg.split('_')[0];
+                    const time = codeMsg.split('_')[1];
+                    console.log(code);
+                    console.log(request.body.code);
+                    if (Date.parse(new Date())/1000 > time+60*5 || request.body.code !== code) {
+                        return response.json({error: 3, msg: '验证码错误。'});
+                    } else {
+                        delete request.session.code;
+                    }
+                } else {
+                    return response.json({error: 3, msg: '验证码错误。'});
+                }
+            }
+
+            // 判断极验验证码是否通过
+            const captcha = new Captcha();
+            const captchResult = await captcha.validate(request);
+            if (!captchResult) {
+                throw '验证码错误';
+            }
+
             let sessionUser = {
                 ssoId: userData.id,
                 username: userData.username,
@@ -110,6 +151,73 @@ class LoginController {
         });
     }
 
+    /**
+     * 验证码注册
+     *
+     * @param {object} request
+     * @param {object} response
+     * @return {string}
+     */
+    async captcha(request, response) {
+        const captcha = new Captcha();
+        const res = await captcha.register(request);
+        response.json(res);
+    }
+
+    /**
+     * 判断用户是否是专业版用户
+     * @param request
+     * @param response
+     * @returns {Promise<void>}
+     */
+    async accountIsPro(request, response) {
+        let res = {
+            error: 0,
+            msg: '',
+            result: false,
+        };
+        try{
+            const account = request.body.account;
+            const password = request.body.pw;
+
+            // 根据邮箱或手机号获取账号信息
+            let userModel = new UserModel();
+            // 调用接口验证登录信息
+            let responseData = await userModel.getInfoFromSSO(account, password);
+            console.log(responseData);
+            // 先判断返回值是否为未激活状态
+            if ( responseData === '-3') {
+                throw '因邮箱未完成认证,账号未激活;去<a href="https://sso.smartcost.com.cn" target="_blank">激活</a>。';
+            }
+            responseData = JSON.parse(responseData);
+            if (typeof responseData !== 'object') {
+                throw '邮箱/手机 或 密码错误';
+            }
+
+            if (responseData.length <= 0) {
+                throw '接口返回数据错误';
+            }
+
+            const userInfo = await userModel.findDataByAccount(account);
+            if (userInfo && userInfo.upgrade_list !== undefined) {
+                for (const ul of userInfo.upgrade_list) {
+                    if (ul.isUpgrade === true) {
+                        res.result = true;
+                        res.data = userInfo.mobile;
+                        break;
+                    }
+                }
+            } else {
+                res.msg = '当前未存在此用户';
+            }
+        } catch (err) {
+            res.error = 1;
+            res.msg = err;
+        }
+
+        response.json(res);
+    }
+
 }
 
-export default LoginController;
+export default LoginController;

+ 19 - 7
modules/users/controllers/sms_controller.js

@@ -6,6 +6,7 @@
  * @version
  */
 import SmsModel from "../models/sms_model";
+const SMS = require('../models/SMS');
 
 class SmsController {
 
@@ -25,13 +26,24 @@ class SmsController {
         let type = request.body.type;
         try {
             let smsModel = new SmsModel();
-            let returnStatus = await smsModel.sendSmsFromSSO(mobile, type);
-            if (returnStatus === null) {
-                throw '获取数据失败!';
-            }
-            if(returnStatus != 1){
-                responseData.err = 1;
-                responseData.msg = await smsModel.getStatusMsg(returnStatus);
+            if (parseInt(type) === 3) {
+                const Sms = new SMS();
+                const code = Sms.generateRandomString(6,2);
+                await Sms.send(mobile, code);
+                // console.log(JSON.stringify(returnStatus));
+                // if (parseInt(JSON.stringify(returnStatus).statusCode) !== 200) {
+                //     throw '短信发送失败!';
+                // }
+                request.session.code = code + '_' + Date.parse(new Date())/1000;
+            } else {
+                let returnStatus = await smsModel.sendSmsFromSSO(mobile, type);
+                if (returnStatus === null) {
+                    throw '获取数据失败!';
+                }
+                if(returnStatus != 1){
+                    responseData.err = 1;
+                    responseData.msg = await smsModel.getStatusMsg(returnStatus);
+                }
             }
         } catch (error) {
             console.log(error);

+ 81 - 0
modules/users/models/captcha.js

@@ -0,0 +1,81 @@
+'use strict';
+
+/**
+ * 验证码
+ *
+ * @author CaiAoLin
+ * @date 2017/10/13
+ * @version
+ */
+// import Request from "request";
+const Geetest = require('gt3-sdk');
+
+class Captcha {
+
+    /**
+     * 构造函数
+     *
+     * @return {void}
+     */
+    constructor() {
+        this.captcha = new Geetest({
+            geetest_id: '697bb3d9c16ab6d016c375f0a65e805b',
+            geetest_key: '1bde862a0898a7c72ffb82e95a0816c7',
+        });
+    }
+
+    /**
+     * 验证码注册
+     *
+     * @return {String} - 返回生成所需的json数据
+     */
+    async register(request) {
+        let response = {
+            success: 0,
+        };
+        try {
+            const data = await this.captcha.register(null);
+            request.session.fallback = !data.success;
+            response = data;
+        } catch (error) {
+            response.success = 0;
+            response.message = error;
+            JSON.stringify(response);
+        }
+
+        return response;
+    }
+
+    /**
+     * 验证码校验
+     *
+     * @return {Boolean} - 返回是否校验成功
+     */
+    async validate(request) {
+        let result = false;
+
+        const challenge = request.body.geetest_challenge;
+        const validate = request.body.geetest_validate;
+        const seccode = request.body.geetest_seccode;
+
+        try {
+            if (challenge === '' || validate === '' || seccode === '') {
+                throw '参数错误';
+            }
+            const fallback = request.session.fallback;
+
+            result = await this.captcha.validate(fallback, {
+                geetest_challenge: challenge,
+                geetest_validate: validate,
+                geetest_seccode: seccode,
+            });
+
+        } catch (error) {
+            result = false;
+        }
+
+        return result;
+    }
+}
+
+module.exports = Captcha;

+ 141 - 0
modules/users/models/sms.js

@@ -0,0 +1,141 @@
+'use strict';
+
+import Request from "request";
+
+/**
+ * 建筑短信发送相关接口
+ *
+ * @author CaiAoLin
+ * @date 2018/1/25
+ * @version
+ */
+
+const crypto = require('crypto');
+
+class SMS {
+
+    /**
+     * 构造函数
+     *
+     * @return {void}
+     */
+    constructor() {
+        this.url = 'http://www.sendcloud.net/smsapi/send';
+        this.smsUser = 'smartcost';
+        this.smskey = 'kuGmqTt10n6vBXivhxXsAuG8aoCsQ1x6';
+        this.templateId = 25595;
+    }
+
+    /**
+     * 发送信息
+     *
+     * @param {String|Array} mobile - 发送的电话号码
+     * @param {String} code - 验证码
+     * @return {Boolean} - 发送结果
+     */
+    async send(mobile, code) {
+        try {
+            const formData = {
+                smsUser: this.smsUser,
+                templateId: this.templateId,
+                msgType: 0,
+                phone: mobile,
+                vars: '{"%code%":'+ code +'}',
+            };
+            const signature = await this.getSignature(this.sortDict(formData), this.smskey);
+            formData.signature = signature;
+
+            let postData = {
+                url: this.url,
+                form: formData,
+                encoding: 'utf8'
+            };
+
+            return new Promise(function (resolve, reject) {
+                try {
+                    // 请求接口
+                    Request.post(postData, function (err, postResponse, body) {
+                        if (err) {
+                            throw '请求错误';
+                        }
+                        if (postResponse.statusCode !== 200) {
+                            throw '短信发送失败!';
+                        }
+                        resolve(body);
+                    });
+                } catch (error) {
+                    reject([]);
+                }
+            });
+        } catch (error) {
+           console.log(error);
+        }
+    }
+
+    md5(data) {
+        var str = data;
+        return crypto.createHash("md5").update(str).digest("hex");
+    }
+
+    sortDict(dict){
+        var dict2={},
+            keys = Object.keys(dict).sort();
+        for (var i = 0, n = keys.length, key; i < n; ++i) {
+            key = keys[i];
+            dict2[key] = dict[key];
+        }
+        return dict2;
+    }
+
+    async getSignature (sorted_param, smsKey) {
+        var param_str = "";
+        for(var key in sorted_param)
+            param_str += (key + '=' + sorted_param[key] + '&')
+        var param_str = smsKey + '&' + param_str + smsKey;
+        var sign = this.md5(param_str);
+        return sign.toUpperCase();
+    }
+
+    /**
+     * 生成随机字符串
+     *
+     * @param {Number} length - 需要生成字符串的长度
+     * @param {Number} type - 1为数字和字符 2为纯数字 3为纯字母
+     * @return {String} - 返回生成结果
+     */
+    generateRandomString(length, type = 1) {
+        length = parseInt(length);
+        length = isNaN(length) ? 1 : length;
+        let randSeed = [];
+        let numberSeed = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
+        let stringSeed = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
+            'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+            'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'];
+
+        switch (type) {
+            case 1:
+                randSeed = stringSeed.concat(numberSeed);
+                stringSeed = numberSeed = null;
+                break;
+            case 2:
+                randSeed = numberSeed;
+                break;
+            case 3:
+                randSeed = stringSeed;
+                break;
+            default:
+                break;
+        }
+
+        const seedLength = randSeed.length - 1;
+        let result = '';
+        for (let i = 0; i < length; i++) {
+            const index = Math.ceil(Math.random() * seedLength);
+            result += randSeed[index];
+        }
+
+        return result;
+    }
+}
+
+module.exports = SMS;

+ 13 - 1
modules/users/models/user_model.js

@@ -182,6 +182,18 @@ class UserModel extends BaseModel {
         return await this.db.findOne({_id: objId});
     }
 
+    async findDataByAccount(account) {
+        let userinfo = await this.db.findOne({mobile: account});
+        let userinfo2 = await this.db.findOne({email: account});
+        if (userinfo) {
+            return userinfo;
+        } else if (userinfo2) {
+            return userinfo2;
+        } else {
+            return false;
+        }
+    }
+
     /**
      * 新增用户
      *
@@ -252,4 +264,4 @@ class UserModel extends BaseModel {
 
 }
 
-export default UserModel;
+export default UserModel;

+ 5 - 0
modules/users/routes/login_route.js

@@ -19,6 +19,11 @@ module.exports = function (app) {
 // 登录操作
     router.post('/login', loginController.login);
 
+    router.post('/accountIsPro', loginController.accountIsPro);
+
+    // 验证码相关
+    router.get('/captcha', loginController.captcha);
+
     router.get("/logout", function (req, res) {
         delete req.session.sessionUser;
         delete req.session.sessionCompilation;

+ 3 - 2
package.json

@@ -25,6 +25,7 @@
     "babel-core": "^6.4.0",
     "bluebird": "^3.5.0",
     "del": "^1.1.1",
+    "gt3-sdk": "^2.0.0",
     "gulp": "^3.9.0",
     "gulp-autoprefixer": "^3.0.1",
     "gulp-babel": "^6.1.1",
@@ -43,6 +44,7 @@
     "ioredis": "^3.1.4",
     "jszip": "^3.1.3",
     "log4js": "~2.3.3",
+    "lz-string": "^1.4.4",
     "main-bower-files": "^2.5.0",
     "moment": "^2.18.1",
     "multiparty": "^4.1.3",
@@ -52,8 +54,7 @@
     "socket.io": "^2.0.3",
     "ua-parser-js": "^0.7.14",
     "uuid": "^3.1.0",
-    "wiredep": "^2.2.2",
-    "lz-string": "^1.4.4"
+    "wiredep": "^2.2.2"
   },
   "scripts": {
     "start": "C:\\Users\\mai\\AppData\\Roaming\\npm\\babel-node.cmd server.js"

+ 1 - 1
server.js

@@ -56,7 +56,7 @@ app.use(session({
 // 登录状态全局判断
 app.use(function (req, res, next) {
     let url = req.originalUrl;
-    if (/^\/login/.test(url) || /\.map|\.ico$/.test(url) || /^\/sms/.test(url) || /^\/cld/.test(url)) {
+    if (/^\/login/.test(url) || /\.map|\.ico$/.test(url) || /^\/sms/.test(url) || /^\/cld/.test(url) || /^\/captcha/.test(url)  || /^\/accountIsPro/.test(url)) {
         // 如果是登录页面或短信接口或cld接口则忽略判断数据
         next();
     } else {

+ 32 - 0
web/users/html/login.html

@@ -33,6 +33,9 @@
                 </div>
             </div>
             <div class="form-group">
+                <div id="captcha-box"></div>
+            </div>
+            <div class="form-group btn-area" style="display: none;">
                 <button id="login" class="btn btn-primary btn-block">登录</button>
             </div>
             <div class="pt-1 d-flex justify-content-center">
@@ -106,6 +109,34 @@
             </div>
         </div>
     </div>
+    <!--弹出手机验证码-->
+    <div class="modal fade" id="phonepass" data-backdrop="static">
+        <div class="modal-dialog" role="document">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <h5 class="modal-title">验证码安全登录</h5>
+                </div>
+                <div class="modal-body">
+                    <p class="">您是专业版用户,为您提供手机验证码安全登录。</p>
+                    <div class="form-row">
+                        <div class="form-group col-md-8">
+                            <input type="text" class="form-control" id="smsCode" placeholder="输入验证码">
+                            <div class="invalid-feedback">
+                            </div>
+                            <input type="hidden" class="form-control" id="proMobile">
+                        </div>
+                        <div class="form-group col-md-4">
+                            <button class="btn btn-primary" id="get-code2">获取验证码</button>
+                        </div>
+                    </div>
+                </div>
+                <div class="modal-footer">
+                    <button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
+                    <button class="btn btn-primary" id="loginPro">登录</button>
+                </div>
+            </div>
+        </div>
+    </div>
     <!-- JS. -->
     <!-- inject:js -->
     <script type="text/javascript" src="/public/web/scMathUtil.js"></script>
@@ -114,6 +145,7 @@
     <script src="/lib/popper/popper.min.js"></script>
     <script src="/lib/bootstrap/bootstrap.min.js"></script>
     <script src="/web/building_saas/js/global.js"></script>
+    <script type="text/javascript" src="/web/users/js/gt.js"></script>
     <script type="text/javascript" src="/web/users/js/login.js"></script>
     <!-- endinject -->
 </body>

+ 293 - 0
web/users/js/gt.js

@@ -0,0 +1,293 @@
+"v0.4.6 Geetest Inc.";
+
+(function (window) {
+    "use strict";
+    if (typeof window === 'undefined') {
+        throw new Error('Geetest requires browser environment');
+    }
+
+var document = window.document;
+var Math = window.Math;
+var head = document.getElementsByTagName("head")[0];
+
+function _Object(obj) {
+    this._obj = obj;
+}
+
+_Object.prototype = {
+    _each: function (process) {
+        var _obj = this._obj;
+        for (var k in _obj) {
+            if (_obj.hasOwnProperty(k)) {
+                process(k, _obj[k]);
+            }
+        }
+        return this;
+    }
+};
+
+function Config(config) {
+    var self = this;
+    new _Object(config)._each(function (key, value) {
+        self[key] = value;
+    });
+}
+
+Config.prototype = {
+    api_server: 'api.geetest.com',
+    protocol: 'http://',
+    typePath: '/gettype.php',
+    fallback_config: {
+        slide: {
+            static_servers: ["static.geetest.com", "dn-staticdown.qbox.me"],
+            type: 'slide',
+            slide: '/static/js/geetest.0.0.0.js'
+        },
+        fullpage: {
+            static_servers: ["static.geetest.com", "dn-staticdown.qbox.me"],
+            type: 'fullpage',
+            fullpage: '/static/js/fullpage.0.0.0.js'
+        }
+    },
+    _get_fallback_config: function () {
+        var self = this;
+        if (isString(self.type)) {
+            return self.fallback_config[self.type];
+        } else if (self.new_captcha) {
+            return self.fallback_config.fullpage;
+        } else {
+            return self.fallback_config.slide;
+        }
+    },
+    _extend: function (obj) {
+        var self = this;
+        new _Object(obj)._each(function (key, value) {
+            self[key] = value;
+        })
+    }
+};
+var isNumber = function (value) {
+    return (typeof value === 'number');
+};
+var isString = function (value) {
+    return (typeof value === 'string');
+};
+var isBoolean = function (value) {
+    return (typeof value === 'boolean');
+};
+var isObject = function (value) {
+    return (typeof value === 'object' && value !== null);
+};
+var isFunction = function (value) {
+    return (typeof value === 'function');
+};
+
+var callbacks = {};
+var status = {};
+
+var random = function () {
+    return parseInt(Math.random() * 10000) + (new Date()).valueOf();
+};
+
+var loadScript = function (url, cb) {
+    var script = document.createElement("script");
+    script.charset = "UTF-8";
+    script.async = true;
+
+    script.onerror = function () {
+        cb(true);
+    };
+    var loaded = false;
+    script.onload = script.onreadystatechange = function () {
+        if (!loaded &&
+            (!script.readyState ||
+            "loaded" === script.readyState ||
+            "complete" === script.readyState)) {
+
+            loaded = true;
+            setTimeout(function () {
+                cb(false);
+            }, 0);
+        }
+    };
+    script.src = url;
+    head.appendChild(script);
+};
+
+var normalizeDomain = function (domain) {
+    // special domain: uems.sysu.edu.cn/jwxt/geetest/
+    // return domain.replace(/^https?:\/\/|\/.*$/g, ''); uems.sysu.edu.cn
+    return domain.replace(/^https?:\/\/|\/$/g, ''); // uems.sysu.edu.cn/jwxt/geetest
+};
+var normalizePath = function (path) {
+    path = path.replace(/\/+/g, '/');
+    if (path.indexOf('/') !== 0) {
+        path = '/' + path;
+    }
+    return path;
+};
+var normalizeQuery = function (query) {
+    if (!query) {
+        return '';
+    }
+    var q = '?';
+    new _Object(query)._each(function (key, value) {
+        if (isString(value) || isNumber(value) || isBoolean(value)) {
+            q = q + encodeURIComponent(key) + '=' + encodeURIComponent(value) + '&';
+        }
+    });
+    if (q === '?') {
+        q = '';
+    }
+    return q.replace(/&$/, '');
+};
+var makeURL = function (protocol, domain, path, query) {
+    domain = normalizeDomain(domain);
+
+    var url = normalizePath(path) + normalizeQuery(query);
+    if (domain) {
+        url = protocol + domain + url;
+    }
+
+    return url;
+};
+
+var load = function (protocol, domains, path, query, cb) {
+    var tryRequest = function (at) {
+
+        var url = makeURL(protocol, domains[at], path, query);
+        loadScript(url, function (err) {
+            if (err) {
+                if (at >= domains.length - 1) {
+                    cb(true);
+                } else {
+                    tryRequest(at + 1);
+                }
+            } else {
+                cb(false);
+            }
+        });
+    };
+    tryRequest(0);
+};
+
+
+var jsonp = function (domains, path, config, callback) {
+    if (isObject(config.getLib)) {
+        config._extend(config.getLib);
+        callback(config);
+        return;
+    }
+    if (config.offline) {
+        callback(config._get_fallback_config());
+        return;
+    }
+
+    var cb = "geetest_" + random();
+    window[cb] = function (data) {
+        if (data.status == 'success') {
+            callback(data.data);
+        } else if (!data.status) {
+            callback(data);
+        } else {
+            callback(config._get_fallback_config());
+        }
+        window[cb] = undefined;
+        try {
+            delete window[cb];
+        } catch (e) {
+        }
+    };
+    load(config.protocol, domains, path, {
+        gt: config.gt,
+        callback: cb
+    }, function (err) {
+        if (err) {
+            callback(config._get_fallback_config());
+        }
+    });
+};
+
+var throwError = function (errorType, config) {
+    var errors = {
+        networkError: '网络错误',
+        gtTypeError: 'gt字段不是字符串类型'
+    };
+    if (typeof config.onError === 'function') {
+        config.onError(errors[errorType]);
+    } else {
+        throw new Error(errors[errorType]);
+    }
+};
+
+var detect = function () {
+    return window.Geetest || document.getElementById("gt_lib");
+};
+
+if (detect()) {
+    status.slide = "loaded";
+}
+
+window.initGeetest = function (userConfig, callback) {
+
+    var config = new Config(userConfig);
+
+    if (userConfig.https) {
+        config.protocol = 'https://';
+    } else if (!userConfig.protocol) {
+        config.protocol = window.location.protocol + '//';
+    }
+
+    // for KFC
+    if (userConfig.gt === '050cffef4ae57b5d5e529fea9540b0d1' ||
+        userConfig.gt === '3bd38408ae4af923ed36e13819b14d42') {
+        config.apiserver = 'yumchina.geetest.com/'; // for old js
+        config.api_server = 'yumchina.geetest.com';
+    }
+
+    if (isObject(userConfig.getType)) {
+        config._extend(userConfig.getType);
+    }
+    jsonp([config.api_server || config.apiserver], config.typePath, config, function (newConfig) {
+        var type = newConfig.type;
+        var init = function () {
+            config._extend(newConfig);
+            callback(new window.Geetest(config));
+        };
+
+        callbacks[type] = callbacks[type] || [];
+        var s = status[type] || 'init';
+        if (s === 'init') {
+            status[type] = 'loading';
+
+            callbacks[type].push(init);
+
+            load(config.protocol, newConfig.static_servers || newConfig.domains, newConfig[type] || newConfig.path, null, function (err) {
+                if (err) {
+                    status[type] = 'fail';
+                    throwError('networkError', config);
+                } else {
+                    status[type] = 'loaded';
+                    var cbs = callbacks[type];
+                    for (var i = 0, len = cbs.length; i < len; i = i + 1) {
+                        var cb = cbs[i];
+                        if (isFunction(cb)) {
+                            cb();
+                        }
+                    }
+                    callbacks[type] = [];
+                }
+            });
+        } else if (s === "loaded") {
+            init();
+        } else if (s === "fail") {
+            throwError('networkError', config);
+        } else if (s === "loading") {
+            callbacks[type].push(init);
+        }
+    });
+
+};
+
+
+})(window);

+ 189 - 67
web/users/js/login.js

@@ -8,47 +8,122 @@
 $(document).ready(function () {
     let referer = scUrlUtil.GetQueryString('referer');
 
-    $("#login").click(function () {
-        if (!valid()) {
-            return false;
+    // 载入时先获取相关参数
+    $.ajax({
+        url: '/captcha',
+        type: 'get',
+        data: '',
+        timeout: 5000,
+        error: function() {
+            $("#captcha-box").html('验证码加载失败');
+        },
+        beforeSend: function() {
+            $("#captcha-box").html('正在加载验证码');
+        },
+        success: function(response) {
+            $("#captcha-box").html('');
+            if (response.success === 0) {
+                alert('验证码初始化失败!');
+                return false;
+            }
+
+            initGeetest({
+                // 以下配置参数来自服务端 SDK
+                gt: response.gt,
+                challenge: response.challenge,
+                offline: !response.success,
+                new_captcha: response.new_captcha,
+                width: '100%'
+            }, handler);
         }
-        let account = $("#inputEmail").val();
-        let pw = $("#inputPassword").val();
+    });
 
-        $.ajax({
-            url: '/login',
-            type: 'post',
-            data: {"account": account, "pw": pw},
-            success: function (response) {
-                if (response.error === 0) {
-                    const url = response.last_page !== null && response.last_page !== undefined && response.last_page !== '' ?
-                        response.last_page : '/pm';
-                    if (response.login_ask === 0) {
-                        location.href = url;
-                    } else {
-                        response.compilation_list = response.compilation_list === undefined || response.compilation_list === '' ?
-                            null : JSON.parse(response.compilation_list);
-                        if (response.compilation_list === null || response.compilation_list.length <= 0) {
-                            location.href = url;
-                            return false;
+    const handler = function(captchaObj) {
+        captchaObj.appendTo('#captcha-box');
+        captchaObj.onSuccess(function () {
+            $(".btn-area").slideDown("fast");
+            $('#login').click();
+            captchaObj.getValidate();
+        });
+
+        $("#login").click(function () {
+            if (!valid()) {
+                return false;
+            }
+            let account = $("#inputEmail").val();
+            let pw = $("#inputPassword").val();
+
+            // 判断输入的邮箱/手机是否格式正确
+            if(/^1[3456789]\d{9}$/.test(account) || /^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/.test(account)) {
+                // 先判断是否是专业版用户,是的话弹出短信验证
+                $.ajax({
+                    url: '/accountIsPro',
+                    type: 'post',
+                    async: true,
+                    data: {"account": account, "pw": pw},
+                    success: function (response) {
+                        if (response.error === 0) {
+                            const ispro = response.result;
+                            if (!ispro) {
+                                login(captchaObj);
+                            } else {
+                                $('#phonepass').modal('show');
+                                $('#proMobile').val(response.data);
+                            }
+                        } else {
+                            let msg = response.msg !== undefined ? response.msg : '未知错误';
+                            showError(msg, $("input"));
                         }
-                        console.log(response.compilation_list);
-                        setVersion(response.compilation_list);
-                        $('#ver').modal('show');
                     }
-                } else if(response.error === 2) {
-                    $('#check_ssoId').val(response.ssoId);
-                    $('#phone').modal('show');
-                } else {
-                    let msg = response.msg !== undefined ? response.msg : '未知错误';
-                    showError(msg, $("input"));
-                }
-            },
-            error: function (result) {
-                showError('内部程序错误', null);
+                });
+            } else {
+                $('#emailHelp').text('您输入的 邮箱/手机 格式不对');
             }
         });
-    });
+
+        $('#loginPro').click(function () {
+            if ($('#smsCode').val() === '') {
+                showValidError('请输入验证码',$('#smsCode'));
+            } else {
+                login(captchaObj);
+            }
+        });
+
+        $('#check-code').click(function () {
+            const mobile = $("#mobile").val();
+            const ssoId = $("#check_ssoId").val();
+            const code = $("#code").val();
+            if(!validMobile(mobile)) {
+                return false;
+            }
+            if(ssoId === undefined || ssoId === '') {
+                showValidError('账号有误!', $('#code'));
+                return false;
+            }
+            if($.trim(code) === '') {
+                showValidError('验证码不能为空!', $('#code'));
+                return false;
+            }
+            $.ajax({
+                url: '/sms/mobile',
+                type: 'post',
+                data: {ssoId: ssoId, mobile: mobile, code: code},
+                error: function() {
+                    showValidError('接口出错!',$('#code'));
+                },
+                beforeSend: function() {
+                },
+                success: function(response) {
+                    if (response.err === 0) {
+                        $("#login").click();
+                        $('#phone').modal('hide');
+                    } else {
+                        showValidError(response.msg,$('#code'));
+                    }
+                }
+            })
+        });
+    };
 
     $("input").blur(function () {
         cleanError();
@@ -86,43 +161,90 @@ $(document).ready(function () {
         }
     });
 
-    $('#check-code').click(function () {
-        const mobile = $("#mobile").val();
-        const ssoId = $("#check_ssoId").val();
-        const code = $("#code").val();
-        if(!validMobile(mobile)) {
-            return false;
-        }
-        if(ssoId === undefined || ssoId === '') {
-            showValidError('账号有误!', $('#code'));
+    $("#get-code2").click(function() {
+        const mobile = $("#proMobile").val();
+        if(!validMobile(mobile)){
             return false;
         }
-        if($.trim(code) === '') {
-            showValidError('验证码不能为空!', $('#code'));
-            return false;
+        const btn = $(this);
+        if(!btn.hasClass('disabled')){
+            $.ajax({
+                url: '/sms/code',
+                type: 'post',
+                data: { mobile: mobile, type: 3},
+                error: function() {
+                    showValidError('短信接口出错!',$('#smsCode'));
+                },
+                beforeSend: function() {
+                },
+                success: function(response) {
+                    if (response.err === 0) {
+                        codeSuccess(btn);
+                    } else {
+                        showValidError(response.msg,$('#smsCode'));
+                    }
+                }
+            });
         }
-        $.ajax({
-            url: '/sms/mobile',
-            type: 'post',
-            data: {ssoId: ssoId, mobile: mobile, code: code},
-            error: function() {
-                showValidError('接口出错!',$('#code'));
-            },
-            beforeSend: function() {
-            },
-            success: function(response) {
-                if (response.err === 0) {
-                    $("#login").click();
-                    $('#phone').modal('hide');
+    });
+});
+
+function login(captchaObj) {
+    let account = $("#inputEmail").val();
+    let pw = $("#inputPassword").val();
+    let geetest_challenge = $('input[name="geetest_challenge"]').val();
+    let geetest_validate = $('input[name="geetest_validate"]').val();
+    let geetest_seccode = $('input[name="geetest_seccode"]').val();
+    let code = $("#smsCode").val();
+
+    $.ajax({
+        url: '/login',
+        type: 'post',
+        data: {
+            "account": account,
+            "pw": pw,
+            "geetest_challenge": geetest_challenge,
+            "geetest_validate": geetest_validate,
+            "geetest_seccode": geetest_seccode,
+            "code": code,
+        },
+        success: function (response) {
+            if (response.error === 0) {
+                $('#phonepass').modal('hide');
+                const url = response.last_page !== null && response.last_page !== undefined && response.last_page !== '' ?
+                    response.last_page : '/pm';
+                if (response.login_ask === 0) {
+                    location.href = url;
                 } else {
-                    showValidError(response.msg,$('#code'));
+                    response.compilation_list = response.compilation_list === undefined || response.compilation_list === '' ?
+                        null : JSON.parse(response.compilation_list);
+                    if (response.compilation_list === null || response.compilation_list.length <= 0) {
+                        location.href = url;
+                        return false;
+                    }
+                    console.log(response.compilation_list);
+                    setVersion(response.compilation_list);
+                    $('#ver').modal('show');
                 }
+            } else if(response.error === 2) {
+                $('#phonepass').modal('hide');
+                captchaObj.reset();
+                $('#check_ssoId').val(response.ssoId);
+                $('#phone').modal('show');
+            } else if(response.error === 3) {
+                showValidError(response.msg,$('#smsCode'));
+            } else {
+                $('#phonepass').modal('hide');
+                let msg = response.msg !== undefined ? response.msg : '未知错误';
+                showError(msg, $("input"));
+                captchaObj.reset();
             }
-        })
-
+        },
+        error: function (result) {
+            showError('内部程序错误', null);
+        }
     });
-
-});
+}
 
 /**
  * 获取成功后的操作
@@ -224,7 +346,7 @@ function showError(msg, element) {
     if (element !== null) {
         element.parent().addClass('has-danger');
     }
-    $("#message").text(msg);
+    $("#message").html(msg);
     $("#error-tips").show("fast");
 }
 
@@ -261,4 +383,4 @@ function setVersion(versionData) {
         html += tmpHtml;
     }
     $("#version-area").html(html);
-}
+}