工作中的"注册"流程以及代码备份

来源:互联网 发布:xss java 代码过滤 编辑:程序博客网 时间:2024/05/13 23:46

简要:

        我觉得我们项目中用的注册流程好麻烦啊,可能是我是第一次给APP端写接口,token设计,存储,处理以及传输 都有好多啊,下面我将分步介绍.......

       注意点: 两个token access_token 1 天 refresh_token 30 天 ,存储到redis, 采用string类型

      流程:

- 注册用户时,判断手机号格式是否正确- 在没发送验证码之前, 判断手机号是否已经被注册过- 密码和确认密码判断是否一致- 新创建两个token  setex access_token 1天setex refresh_token 30天- 注册完成后, 将两个token返回给用户, 并且默认用户是登录的
    设计格式:
        - 生成的两个token组合方式:$accessToken =  md5('at' . $mobile . $userId .time());              $refreshToken = md5('rt' . $mobile . $userId . time());        - 将两个token和user_id之间创建联系,然后存储到redis上,以后可以通过token获取对应的user_id            user:at:{$access_token} => {$user_id}            user:rt:{$refresh_token} => {$user_id}

说明: 当生成好两个token后, 将他们与user_id建立起来,然后我将两个token返回给APP端.

      注册:代码如下:

    /**     * Action Register     * 用户注册     */     public function actionRegister()     {        $params = Yii::$app->request->post();        $mAccount = new Account();        $util = Yii::$app->util;        //规格: 由8-16位的数字,字母组成的密码,其中不能有空格,不能全是数字,不能全是字母        $regular = "/((?=.*\d)(?=.*\D)|(?=.*[a-zA-Z])(?=.*[^a-zA-Z]))^.{8,16}$/";        if (!$params) {            return $util->formatResData(1101, '无参数提供', (object)[], false);        }        if (!isset($params['mobile']) || !$params['mobile']) {            return $util->formatResData(1102, '手机号不能为空', (object)[], false);        }        if (!$util->regularMobile($params['mobile'])) {            return $util->formatResData(1103, '手机号码格式不正确', (object)[], false);        }        if (!isset($params['vcode']) || !$params['vcode']) {            return yii::$app->util->formatResData(1104, '验证码不能为空', (object)[], false);        }        if (!isset($params['password']) || !$params['password']) {            return yii::$app->util->formatResData(1105, '密码不能为空', (object)[], false);        }        if (!isset($params['confirm_password']) || !$params['confirm_password']) {            return yii::$app->util->formatResData(1106, '确认密码不能为空', (object)[], false);        }        $users = $mAccount->isExistMobile($params['mobile'], 'user_id, salt, status');        $status = 0;        if ($users) {            $status = $users['status'];            //如果该用户存在,且状态为可用|禁用,都提示已注册            if (($status == Account::STATUS_ENABLE) || ($status == Account::STATUS_DISABLE)){                return $util->formatResData(1107, '该手机号已被注册', (object)[], false);            }        }        //TODO 判断验证码是否正确        $mobie_white_list = array('手机号', '手机号1');       if(in_array($params['mobile'], $mobie_white_list)) {       } else if (($result = $this->checkSmsVcode('reg', $params['mobile'], $params['vcode'])) !== true) {          return $result;       }        $mInviteCode = new Invitecode();        //判断邀请码是否存在        if (isset($params['code']) && $params['code']) {            $isBindMobile = $mInviteCode->isBindMobile($params['code'], $params['mobile']);            if (!$isBindMobile) {                return $util->formatResData(1108, '该手机号与邀请码未绑定', (object)[], false);            }            $data['is_receive_invokecode'] = HmcUser::STATUS_INVOKECODE_RECEIVED;        }        $tmp = $this->judgePassword($params['password'], $params['confirm_password'], $regular);        if ($tmp) {            return $tmp;        }        //从redis中获取用户ID        $data = $this->getUid();        if (!isset($data['user_id']) || !$data['user_id']) {            return $data;        }        $params['user_id'] = $data['user_id'];        unset($params['confirm_password'], $params['vcode'], $params['code']);        $mHmcUser = new HmcUser();        if (!$status) {            $result = $mAccount->add($params);        } else {            $params['user_id'] = $users['user_id'];            $updateVal['salt'] = $util->random(6);            $updateVal['password'] = md5(md5($params['password']).$updateVal['salt']);            $updateVal['status'] = Account::STATUS_ENABLE;            $result = $mAccount->renew($params['mobile'], $updateVal);        }        if ($result) {            $data['user_id'] = $params['user_id'];            $data['mobile'] = $params['mobile'];            if (!$status) {                $userDetail = $mHmcUser->add($data);            }            //创建token,并将token存储在redis            $handleData = $this->handleSession($params["user_id"], $params["mobile"]);            if (!isset($handleData['variable']) || !$handleData['variable']) {                return $handleData['error'];            }            $session = $handleData['variable'];            Yii::$app->api->account_import((string)$params["user_id"], (string)$params["user_id"], '');            try {                $smsData = ['mobile'=> $params['mobile'], 'business_type' => 'reg'];                $result = $this->renewSmsUseage($smsData);            } catch (\Exception $e) {                //TODO 将短信使用情况出现错误的地方写到日志里面            }            return $util->formatResData(0, '注册成功', $session, false);        } else {            return $util->formatResData(1201, '注册失败', (object)[], false);        }    }

 刷新refresh_token步骤

    流程如图所示:

代码如下:

                            /**     * Action RefreshToken     * 刷新refresh_token     */    public function actionRefreshToken()    {        $params = Yii::$app->request->get();        if (!isset($params['refresh_token']) || !$params['refresh_token']) {            return yii::$app->util->formatResData(1101, '未传递refresh_token参数', (object)[], false);        }        Yii::info($params['refresh_token'], 'api\refreshtoken');        $cache = Yii::$app->cache->instance('base');        $rtCountKey = 'user:rtcount:' . $params['refresh_token'];        $unitMseconds = 500000;        $count = $cache->incr($rtCountKey);        $cache->expire($rtCountKey, 10);        if ($count > 1 && $count <= 10) {            // usleep($unitMseconds * $count);            usleep($unitMseconds);        }        try {            $tokenKey = "user:rt:" . $params['refresh_token'];            $tmp = $cache->get($tokenKey);            //判断refresh_token是否过期            if (!$tmp) {                $tokens = $cache->exists('user:oldrt:' . $params['refresh_token']);                if (!$tokens) {                    return yii::$app->util->formatResData(1102, 'refresh_token过期', (object)[], false);                } else {                    $tokensKey = 'user:oldrt:' . $params['refresh_token'];                    $session['user_id'] = $cache->hget($tokensKey, 'user_id');                    $session['access_token'] = $cache->hget($tokensKey, 'access_token');                    $session['refresh_token'] = $cache->hget($tokensKey, 'refresh_token');                    $session['sig'] = $cache->hget($tokensKey, 'sig');                }            } else {                //如果不过期,user:rt:refresh_token 获取对应的用户ID,然后先删除rt,再重新创建两个token                $userId = $tmp;                $cache->del($tokenKey);                $mAccount = new Account();                $users = $mAccount->getByUid($userId, 'mobile');                //重新创建两个token                $handleData = $this->handleSession($userId, $users['mobile']);                //创建旧的refresh_token,与新的refresh_token之间关联的数据,防止并发导致找不到已经删过的refresh_token, 10s                if (!isset($handleData['variable']) || !$handleData['variable']) {                    return $handleData['error'];                }                $session = $handleData['variable'];                $accessToken = $session['access_token'];                $refreshToken = $session['refresh_token'];                $tmpTokenKey = "user:oldrt";                $oldRefreshToken = $params['refresh_token'];                $sig = Yii::$app->sig->getSig($userId);                $cache->hmset('user:oldrt:' . $oldRefreshToken, 'access_token', $accessToken, 'refresh_token', $refreshToken, 'user_id', $userId, 'sig', $sig);                $cache->expire('user:oldrt:' . $oldRefreshToken, 60);            }            return yii::$app->util->formatResData(0, 'refresh_token刷新成功', $session, false);        } catch (\Exception $e) {            return yii::$app->util->formatResData(-1, '服务器连接失败,请稍后重试', (object)[], false);        }    }

里面涉及的方法:

1: 创建token方法 handleSession

                    /**     * 创建token,并将token存储在redis上     *     * @param string $userId 用户ID     * @param string $mobile 手机号     * @param int  $atTime  access_token保存时间     * @param int  $rtTime  refresh_token保存时间     *     * @return array     * 说明: 返回的值: access_token,refresh_token,user_id,sig     */    // protected function handleSession($userId, $mobile, $atTime = 300, $rtTime = 2592000)    // protected function handleSession($userId, $mobile, $atTime = 120, $rtTime = 2592000)    protected function handleSession($userId, $mobile, $atTime = 86400, $rtTime = 2592000)    {        $now = time();        $accessToken =  md5('at' . $mobile . $userId . $now);        $refreshToken = md5('rt' . $mobile . $userId . $now);        $token = ['access_token' => $accessToken, 'refresh_token' => $refreshToken];        try {            // FIXED: 设置过期时间设置的为经过多少秒过去而不是时间点,如果是时间点应该单独使用expireat            $cache = Yii::$app->cache->instance('base');            $cache->setex('user:at:' . $accessToken, $atTime, $userId);            $cache->setex('user:rt:' . $refreshToken, $rtTime, $userId);            $sig = Yii::$app->sig->getSig($userId);            $result['variable'] = [                'access_token' => $accessToken,                'refresh_token' => $refreshToken,                'user_id' => $userId,                'sig' => $sig            ];        } catch (\Exception $e) {            $result['error'] = yii::$app->util->formatResData(-100, '连接redis服务器失败,请稍后重试 ', (object)[], false);        }        return $result;    }

2:验证手机号格式方法: regularMobile()

    /**     * 中国移动     */    const CHINA_MOBILE_EXPR = '/^134[0-8]\d{7}$|^(?:13[5-9]|147|15[0-27-9]|178|18[2-478])\d{8}$/';    /**     * 中国联通     */    const CHINA_UNION_EXPR = '/^(?:13[0-2]|145|15[56]|176|18[56])\d{8}$/';    /**     * 中国电信     */    const CHINA_TELCOM_EXPR = '/^(?:133|153|177|18[019])\d{8}$/';        /**     * 手机正则表达式验证     *     * @param string $mobile 手机     * @return int false 表示验证失败 true表示成功     */    public function regularMobile($mobile)    {        return preg_match(self::CHINA_MOBILE_EXPR, $mobile)            || preg_match(self::CHINA_UNION_EXPR, $mobile)            || preg_match(self::CHINA_TELCOM_EXPR, $mobile)            || preg_match(self::CHINA_OTHER_EXPR, $mobile);    }
3: 判断密码和确认密码 judgePassword()

    /**     * 判断密码和确认密码     */    public function judgePassword($password, $confirmPwd, $regular)    {        if (!preg_match($regular, $password)) {            return yii::$app->util->formatResData(1401, '密码格式不正确', (object)[], false);        }        if (!preg_match($regular, $confirmPwd)) {            return yii::$app->util->formatResData(1402, '确认密码格式不正确', (object)[], false);        }        if ($password !== $confirmPwd) {            return yii::$app->util->formatResData(1403, '密码与确认密码不一致', (object)[], false);        }    }

* 为什么要创建两个token ?
        - 假如你只设置一个token表示app端用户的登录状态, 那么如果你的token被别人采用非正当手段截取了, 他是不是就可以"为所欲为"了, 而我们设计两个token, access_token设置1天, refresh_token设置30天,
        如果access_token过期了, refresh_token会刷新重新创建新的两个token, 并且把旧的refresh_token删掉. 由于access_token生存时间短暂, 那么假如被人盗取了, 没关系, 我们access_token过期后, 会用refresh_token刷新, 再重新创建新的两个token, 即便是refresh_token被盗取了, 也没关系, 因为又重新创建两个新的access_token和refresh_token, 你说这样是不是就比较完美了!
 

总结:

         我只是把部分的流程列了出来,以后要是再碰到类似的,我就可以直接使用了,对于我来说,现在该学会的就是能够拿别人的代码应用到我的应用中,即便是我没明白,但是功能实现是第一步,因为工作领导不管你怎么样,人家要的是结果,因为现实的社会很残酷,不管你是谁,你是女生还是男生。

  所以加油,我可以的,不能被别人看不起!



0 0
原创粉丝点击