ChkMobilePwdController.class.php 8.18 KB
<?php
/**
 * 检查手机和密码, 并返回匹配记录列表
 * Created by IntelliJ IDEA.
 * User: zhuxun37
 * Date: 2016/7/29
 * Time: 15:35
 */

namespace Apicp\Controller\Admin;

use Com\Validator;
use Common\Service\CommonHidemenuService;
use VcySDK\Service;
use VcySDK\Adminer;

class ChkMobilePwdController extends AbstractAnonymousController
{
    // 登录最大错误次数
    const LOGIN_MAX_ERROR_NUM = 5;

    // 不提示错误信息
    const NOT_ERROR_MSG = 0;

    // 提示错误信息
    const SHOW_ERROR_MSG = 1;

    // 不能登录
    const NOT_LOGIN = 0;

    // 可以登录
    const LOGIN_ADMIN = 1;

    /**
     * SDK的Adminer对象
     *
     * @var Adminer
     */
    protected $_adminer;
    /**
     * 初始化Redis
     *
     * @var Redis
     */
    protected $_redis;


    public function before_action($action = '')
    {

        if (!parent::before_action($action)) {
            return false;
        }

        // 调用UC,登陆接口
        $serv_sdk = &Service::instance();

        // 实例化
        $this->_adminer = new Adminer($serv_sdk);

        // 初始化redis
        $this->_redis = new \Redis();
        $this->_redis->connect(cfg('REDIS_HOST'), cfg('REDIS_PORT'));
        $this->_redis->auth(cfg('REDIS_PWD'));

        return true;
    }

    public function Index()
    {

        // 接收数据
        $mobile = I('post.mobile'); // 用户手机
        $passwd = I('post.passwd'); // 密码

        // 是否报错
        $msg_error = $this->get_login_error($mobile);

        // 如果显示错误信息
        if (self::SHOW_ERROR_MSG == $msg_error['show_msg']) {

            E('_ERR_ADMIN_LOGIN_TIME');
        }

        // 如果是手机号密码登录
        $listAdminer = $this->_listAdminer($mobile, $passwd);
        // 登录失败
        if (empty($listAdminer)) {

            // 写入错误数据
            $this->set_login_error($mobile);

            return false;
        }

        $this->_redis->set('longin_' . $mobile, serialize(array()));

        // 获取菜单列表反序列化输出
        $this->_result = $listAdminer;

        return true;
    }

    /**
     * 手机号密码登录
     *
     * @param string $username 手机号码
     * @param string $passwd MD5密文密码
     *
     * @return string|array 登录成功返回ID,否则返回false
     */
    protected function _listAdminer($username, $passwd)
    {

        // 判断是否为手机号码
        if (!Validator::is_mobile($username)) {
            $this->_set_error("_ERR_PHONE_FORMAT");
            return false;
        }

        // 判断是否为 md5 之后的密码
        if (!Validator::is_password($passwd)) {
            $this->_set_error("_ERR_PWD_INVALID");
            return false;
        }
        $adminers = array();

        try {
            // 登录验证
            $adminers = $this->_adminer->checkPwd(array(
                'eaMobile' => $username,
                'eaPassword' => $passwd
            ));

        } catch (\Exception $e) {

        }

        // 重新检查数据格式, 保证数据格式的正确性
        if (!is_array($adminers) || empty($adminers)) {
            $this->_set_error("_ERR_ADMINER_NOT_EXIST");
            return false;
        }

        $servHideMenu = new CommonHidemenuService();

        // 遍历管理员列表, 反序列化权限字串
        foreach ($adminers as $key => &$_adminer) {
            // 如果用户信息不存在
            if (!isset($_adminer['adminerInfo']) || !is_array($_adminer['adminerInfo'])) {
                continue;
            }
            // 被禁用(过滤企业)
            if (isset($_adminer['adminerInfo']['eaUserstatus']) && $_adminer['adminerInfo']['eaUserstatus'] == Adminer::MANAGER_DISABLE_LOGIN) {
                unset($adminers[$key]);
            }

            // 生成登录用token, 用于之后的用户选择指定企业进行登陆的操作
            $_adminer['adminerInfo']['loginToken'] = $this->_login->generateLoginToken($_adminer['enterpriseInfo']['epEnumber'], $_adminer['adminerInfo']['eaId'], $passwd);

            // 定制企业用户需要隐藏的菜单
            $hideMenu = $servHideMenu->getMenus($_adminer['enterpriseInfo']['epEnumber']);
            $_adminer['enterpriseInfo']['hideMenu'] = empty($hideMenu['menus']) ? [] : unserialize($hideMenu['menus']);

            // 禁用的菜单Key
            //$disabled_keys = [];

            // FIXME zhonglei 2017年09月27日15:38:08 开哥要求圆圈套件上线时需要隐藏管理后台的一些菜单
            $disabled_keys = ['ability-setting', 'clear-cache'];

            // 禁用任务中心
            if (cfg('TASKCENTER_DISABLED') === true) {
                $disabled_keys[] = 'task-center';
            }

            // 禁用线下培训
            if (cfg('TRAIN_DISABLED') === true) {
                $disabled_keys[] = 'train';
            }

            if (!empty($disabled_keys)) {
                $hidemenu_keys = array_column($_adminer['enterpriseInfo']['hideMenu'], 'key');

                foreach ($disabled_keys as $k) {
                    if (!in_array($k, $hidemenu_keys)) {
                        $_adminer['enterpriseInfo']['hideMenu'][] = ['key' => $k];
                    }
                }
            }
        }

        return $adminers;
    }

    /**
     * 获取是否显示错误信息
     * @param string $loginToken 手机号
     * @return array
     */
    protected function get_login_error($loginToken = '')
    {

        // 初始化
        $show_msg = self::NOT_ERROR_MSG;

        // 获取错误数据详情
        $login_error_info = $this->_redis->get('login_' . $loginToken);

        $login_error_info=unserialize($login_error_info);

        // 如果首次错误时间不为空
        if (!empty($login_error_info['start_time'])) {

            // 开始时间+15分钟统计错误次数
             $check_time = $login_error_info['start_time'] + 15 * 60 * 1000;
            // 最后一次错误时间+1个小时
             $max_error_time = $login_error_info['last_time'] + 60 * 60 * 1000;
            // 如果当前时间小于首次错误+15分钟且错误次数到达5次或者最后错误时间+1个小时大于当前时间错误次数大于5次
            if (($check_time > MILLI_TIME
                    ||
                    $max_error_time > MILLI_TIME
                ) &&
                $login_error_info['is_login'] == self::NOT_LOGIN
            ) {
                // 显示错误信息
                $show_msg = self::SHOW_ERROR_MSG;
            }

        }

        return array('show_msg' => $show_msg, 'data' => $login_error_info);
    }
    /**
     * 记录用户错误信息且保存在redis中
     * @param string $loginToken 用户手机号
     * @return bool
     */
    protected function set_login_error($loginToken = '')
    {
        // 获取错误数据
        $list = $this->get_login_error($loginToken);

        // 获取错误数据赋值
        $data = $list['data'];

        // 首次错误时间+15分钟
        $check_time = $data['start_time'] + 15 * 60 * 1000;

        // 如果首次登录错误时间+15分钟小于当前时间
        if ($check_time <= MILLI_TIME) {

            $data['start_time'] = MILLI_TIME;
            $data['error_list'] = array();
            $data['is_login'] = self::LOGIN_ADMIN;
        }

        // 最后登录错误时间
        $data['last_time'] = MILLI_TIME;

        // 初始化错误列表
        if (empty($data['error_list'])) {
            $data['error_list'] = array();
        }

        // 赋值错误列表
        $error_list= $data['error_list'];

        // 记录每次的错误时间
        array_push($error_list,MILLI_TIME);

        $data['error_list']=$error_list;

        // 如果是登录错误次数等于最大错误次数
        if (self::LOGIN_MAX_ERROR_NUM == count($data['error_list'])) {

            $data['is_login'] = self::NOT_LOGIN;
        }
        // 写入信息
        $this->_redis->set('longin_' . $loginToken, serialize($data));

        // 如果是登录错误次数等于最大错误次数
        if (self::LOGIN_MAX_ERROR_NUM == count($data['error_list'])) {

            E('_ERR_ADMIN_LOGIN_TIME');
        }

        return true;
    }


}