短信防刷办法

短信接口在业务中是必然会有的,那么怎么保证接口被刷呢?
我简单总结一下我的想法

  1. 首先可以考虑在页面上加上 验证码。可以减少人为的刷票
  2. 大量的被刷还应该是直接对接口的调用,这里面应该怎样防止被刷呢
  3. 可以考虑业务端 和 借口段 对应 token 识别表单令牌是否合法,但是这个令牌必须要一直变。不变的话就没有意义了。(时间,其他的变化的参数都可以成为token的元素)
  4. 还可以考虑对IP的限制。每个IP不可以调用太多的接口
  5. 每个手机号每天的调用次数也要限制
  6. 这些都有可能被攻克,做好预警监控机制。一段时间短信变化明显,或者超过预期的数量,要及时查看系统是否正常运行,
/**
     * 手机登录验证码
     * @author marain
     * @time  2017-06-27
     * 
     */
    public function get_code(){
        $phone  =   $_REQUEST['tel'];
        $token_key =   $_REQUEST['token_key'];
        $key='marain';
        $now=date('Y-m-d');
        $signkey=md5($key.$now.$phone.'marain'); //根据手机号和时间产生 token

        if ($token_key !== $signkey){
            $result = array();
            $result['code']     =400;
            $result['msg']      ='4001:令牌错误';
            $result['info']     = '';
            echo json_encode($result);
            exit();
        }
        if ($phone==''){
            $result = array();
            $result['code']     =400;
            $result['msg']      ='4002:电话不能为空';
            $result['info']     = '';
            echo json_encode($result);
            exit();
        }
        if (!preg_match("/^1[34578]\d{9}$/", $phone)){
            $result = array();
            $result['code']     =400;
            $result['msg']      ='4003:电话格式不正确';
            $result['info']     = '';
            echo json_encode($result);
            exit();
        }
        $ip=get_client_ip();
        $where_ip['create_ip']=$ip;//ip控制

        $sms_data = M('App_sms')->where($where_ip)->select();
        $today_date = date('Y-m-d');
        $total_onoip_count=0;
        foreach ($sms_data as $k1=>$v1){
            if(substr($v1['create_time'],0,10) == $today_date){
                $total_onoip_count++;
            }
        }
        if (count($sms_data) > 500){
           //ip 大于500报警
        }
        if ($total_onoip_count> 100){
            //单日ip 大于100报警
        }

        $where_who['tel_number']=$phone;
        $sms_data = M('App_sms')->where($where_who)->order("id desc")->select();

        //这个手机号没有注册
        if(empty($sms_data)){
            $code = $this->_create_code();
            $this->send_sms($phone, $code, $ip);
            $result = array();
            $result['code']    =200;
            $result['msg']    ='获取成功';
            $result['info']     = $code;
            echo json_encode($result);
            exit();
        }

        $total_send_count = 0;
        foreach($sms_data as $key1=>$row1){
            if(empty($key1)){
                $last_send_time = $row1['create_time'];
            }
            if(substr($row1['create_time'],0,10) == $today_date){
                $total_send_count++;
                $code = $row1['code'];
            }
        }
        if(empty($code)) $code = $this->_create_code();

        if((strtotime($last_send_time) + 60) > time()){
            $result['code']    =400;
            $result['msg']    ='4004:获取失败,请不要频繁获取';
            $result['info']     = '';
        }else{
            if($total_send_count < 30){
                $this->send_sms($phone, $code, $ip);
                $result['code']     =200;
                $result['msg']      ='获取成功';
                $result['info']     = $code;
            }else{
                $result['code']    =400;
                $result['msg']    ='4005:获取失败,每人每天只能获取三十次验证码';
                $result['info']     = '';
            }
        }
        echo json_encode($result);
        exit();
    }

    /**
     * 产生验证码
     */
    public function _create_code($length=4,$type="number"){
        $array = array(
            'number' => '0123456789',
            'string' => 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
            'mixed' => '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ',
        );
        $string = $array[$type];
        $count = strlen($string)-1;
        $rand = '';
        for ($i = 0; $i < $length; $i++) {
            $rand .= $string[mt_rand(0, $count)];
        }
        return $rand;
    }
private function send_sms($mobile, $code, $ip){
        if(empty($mobile) || empty($code) || empty($ip)) return false;
        $content = '您的注册验证码是'.$code;
          $url='发送短信接口'
        //$send_result = file_get_contents($url);
        $data = array();
        $data['tel_number'] = $mobile;
        $data['content'] = $content;
        $data['code'] = $code;
        $data['create_time'] = date('Y-m-d H:i:s');
        $data['create_ip'] = get_client_ip();
        $data['send_result'] = $send_result;
        $insertid = M('App_sms')->add($data);
        if($insertid) return true;
        return false;
    }
<think>嗯,用户想了解如何实现多端登录功能。首先,我需要明确多端登录的定义,应该是指同一个账号可以在多个设备同时登录,比如手机、电脑、平板等。接下来要考虑的是用户的需求场景,比如是否需要限制设备数量,是否需要实时同步登录状态,安全性如何保障。 然后,我得回忆一下常见的解决方案。单点登录(SSO)可能相关,但SSO更多是解决多个系统间的登录问题,而多端登录可能更侧重于同一系统内的多个设备同时在线。可能需要参考引用中的单点登录方案,比如引用[2]提到的平台内部单点登录实现方案,或许可以借鉴其中的令牌机制。 接下来,用户可能需要的功能包括:令牌管理、设备管理、会话同步、安全控制。令牌管理方面,每个设备生成独立的令牌,这样即使一个设备退出,其他设备不受影响。设备管理需要记录设备信息,比如设备类型、登录时间、最后活跃时间等,方便用户查看和管理。会话同步可能需要通过WebSocket或长轮询来实时更新登录状态,但引用[4]提到的轮询机制也可以作为一种备选方案,虽然实时性稍差。 安全方面,需要考虑止账号盗用,比如异地登录检测、设备指纹识别、二次验证等。引用[1]提到的验证码存储和机制可能在这里有用,比如在陌生设备登录时要求短信验证码。同时,引用[3]中的时间戳记录方案可能对会话超时管理有帮助,设置不活跃超时自动退出。 在实现步骤上,可能需要分后端和前端。后端需要处理令牌生成、存储、验证,设备信息管理,会话状态的同步通知。前端则需要处理令牌的存储,定期发送心跳保持活跃,以及接收强制下线通知。 还需要考虑数据存储,比如使用Redis存储活跃令牌和设备信息,关系型数据库存储用户与设备的绑定关系。分布式环境下,Redis的Pub/Sub功能可以用来通知其他节点强制下线的事件,确保各端同步。 可能遇到的问题包括令牌冲突、设备数量限制的逻辑、心跳机制的设计,以及如何高效查询活跃会话。此外,用户体验方面,需要提供界面让用户管理已登录设备,查看登录历史,并支持手动踢出设备。 最后,生成相关问题时要覆盖安全性、技术实现细节、用户体验优化等方面,帮助用户深入理解不同层面的考虑。</think>### 如何实现多端登录功能 #### 一、核心需求分析 多端登录功能需要满足以下核心需求: 1. **多设备并行登录**:允许同一账号在多个终端(如手机、PC、平板)同时在线。 2. **会话独立性**:各终端的操作互不影响,例如手机端修改密码后PC端需重新验证[^2]。 3. **安全控制**:支持设备管理、异常登录检测、强制下线等安全措施。 4. **状态同步**:实时更新设备在线状态,如新设备登录时通知其他端。 --- #### 二、技术方案设计 ##### 1. **令牌管理机制** - **独立令牌生成**:每个设备生成唯一访问令牌(Token),例如: ```python def generate_token(user_id, device_id): return hashlib.sha256(f"{user_id}-{device_id}-{timestamp}".encode()).hexdigest() ``` - **存储结构**:使用Redis存储活跃令牌,数据结构示例: ```json { "user:123:tokens": [ {"device_id": "PC-001", "token": "a1b2c3", "expire_time": 1717020000}, {"device_id": "Mobile-002", "token": "d4e5f6", "expire_time": 1717023600} ] } ``` ##### 2. **设备识别与管理** - **设备指纹**:通过`User-Agent`、IP地址、设备硬件信息生成唯一设备ID。 - **设备绑定表**:数据库设计示例: ```sql CREATE TABLE user_devices ( user_id INT, device_id VARCHAR(64) PRIMARY KEY, device_type VARCHAR(20), last_login TIMESTAMP ); ``` ##### 3. **会话同步实现** - **强制下线通知**:通过WebSocket或消息队列(如RabbitMQ)广播下线事件。 - **心跳机制**:客户端每5分钟发送心跳请求新令牌有效期: ```python # 客户端伪代码 def send_heartbeat(): while True: post("/api/heartbeat", {"token": current_token}) sleep(300) ``` ##### 4. **安全增强措施** - **登录风控规则**: - 同一账号最多允许5个设备同时在线 - 新设备登录需短信验证(参考引用[1]的验证码方案) - 异地登录触发二次认证 --- #### 三、关键代码实现 ##### 后端核心逻辑 ```python # 登录接口示例 def login(user, device_info): if get_online_device_count(user.id) >= 5: raise Exception("达到设备数上限") token = generate_token(user.id, device_info['id']) redis.hset(f"user:{user.id}:tokens", device_info['id'], token) db.insert_device(user.id, device_info) # 记录设备信息 if is_new_device(device_info): # 新设备检测 send_sms_verification(user.phone) # 引用[1]的验证码实现 return {"token": token} # 强制下线实现 def force_logout(user_id, target_device_id): redis.hdel(f"user:{user_id}:tokens", target_device_id) publish_message("logout", {"user_id": user_id, "device_id": target_device_id}) # 消息队列通知 ``` ##### 前端关键处理 ```javascript // WebSocket监听强制下线 const ws = new WebSocket('wss://api.example.com/ws'); ws.onmessage = (event) => { if (event.data.type === 'FORCE_LOGOUT') { localStorage.removeItem('token'); alert('该账号已在其他设备登录'); redirectToLogin(); } }; ``` --- #### 四、典型应用场景 1. **跨设备同步**:用户在手机端浏览商品后,PC端购物车自动同步。 2. **安全管控**:用户可在"账号安全"页面查看所有登录设备,手动踢出可疑设备。 3. **服务连续性**:平板端播放视频时,手机端可暂停而不影响平板端会话。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值