PHP + iOS 实现移动端扫码登录

前言

最近周末闲着没事,就想建个站玩玩.于是就想起了后台登陆验证的方式是不是可以玩个新花样.比如搞个手机扫码登录验证的方式.说干就干,研究一下扫码登录的原理(当然,如果有什么地方有更好的实现方法或者错误的地方欢迎指正).

正文

原理

手机端扫码简单,就不做阐述.
后台使用PHP来实现.(暂时只考虑实现,先不考虑其他安全方面的问题,功能实现之后再做考虑).
基本原理:
1.Web前端展示二维码,二维码信息中包含有sessionId, 并用轮训的方式来不断的从服务端请求手机端验证的结果.
2.手机端扫码并获取sessionId,然后向服务器提交sessionId以及其他数据.
3.当服务器接收到手机端提交的sessionId后,就通过sessionId来打通手机端和Web前端之间的session,并标记登录状态为验证通过.
4.Web前端获取到验证通过信息后,跳转页面.

实现

iOS 部分实现:
iOS 端的实现很简单, 仅仅是扫码获取 sessionId, 然后通过接口发送 sessionId 以及其他一些必须数据.
二维码扫描不说了, 二维码扫描成功后使用 AFNetworking 向服务器确认登录信息.
发送参数为二维码信息的 sessionId. 服务器只要接收到 sessionId 就会标记接收到的 sessionId 所属回话为已登录状态.

/**
 扫描完成回调
 @param message 二维码扫描结果
 */
- (void)qrcodeScanSuccessWithMessage:(NSString *)message {
    if (!message) {
        NSLog(@"message = nil");
        return;
    }
    [[AFHTTPSessionManager manager] POST:@"http://192.168.3.7:8888/qrcodelogin/home/index/deviceLogin" parameters:@{@"sessionId" : message} progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        // 打印服务器返回信息
        NSLog(@"success = %@", responseObject);
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        //  打印报错信息方便调试
        NSString* errResponse = [[NSString alloc] initWithData:(NSData *)error.userInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] encoding:NSUTF8StringEncoding];
        NSLog(@"%@", errResponse);
    }];
}

Web 前端实现:
通过轮询的方式向服务器确认, 移动端是否已经通过扫码验证成功. 获取到验证成功信息则跳转或者提醒登录成功(事例中仅仅只是提醒). 否则在延迟2秒后进入下一次查询,直到获取到登录成功信息为止.
只上 js 代码, html 代码略掉.

// 计数,方便测试(可以直观的查看当前调用的次数)
var count = 0;

// 请求服务器登录确认(轮询方法)
function requestCheck(){
    $.post("login",{}, function(data){
           // 服务器返回非 "no" 字符串就算验证通过, 否则进入下一轮查询
           if (data != "no") {
               $("div.loginBox").html("登录成功");
           }else {
               // 显示当前轮询次数
               $("div.count").html(count++);
               // 每次请求完成后, 延迟 2 秒, 再次进行查询
               setTimeout("requestCheck()", 2000);
           }
    });
}

// button 点击事件响应方法
function cl() {
    // 显示二维码
    $("div.loginBox").html("![](qrcode)");
    // 调用轮询方法
    requestCheck();
}

PHP 实现:
php 实现使用了 ThinkPHP 框架
首先是 Web 前端显示二维码页面的请求方法:

// 登录页
public function index(){
    // 主要用于调试过程中, web 和移动端 session 是否已经打通
    $_SESSION["username"] = "test";
    echo "username = ".$_SESSION["username"];
    $this->display(T('login'));
}

其次是手机移动端扫描二维码后请求登录的方法:

// 移动端确认登录
public function deviceLogin() {
    $sid = I("post.sessionId");
    $result;
    if ($sid) {
        // 先销毁当前 session
        session('[destroy]');
        // 再获取 sessionId 对应的 session
        Session_id($sid);
        Session_start();
        // 设置登录状态(只要 deviceUUID 字段的值存在则视为已经登录)
        $_SESSION['deviceUUID'] = "ABCDESDASDEFSDSDA";
        $result["code"] = 1;
        $result["username"] = $_SESSION["username"];
    }else {
        $result["code"] = 0;
    }
    echo json_encode($result);
}

最后是 Web 前端 Ajax 轮询请求确认登录状态的方法:

// web 前端 ajax 请求确认登录状态方法
public function login(){
    if (isset($_SESSION['deviceUUID'])) {
        // 如果 $_SESSION['deviceUUID'] 值存在, 则表明移动端确认了登录信息, 表明验证通过
        // 返回登录确认信息
        echo $_SESSION['deviceUUID'];
    }else {
        // 否则表明移动端还未确认登录,
        // 返回还未确认登录信息
        echo "no";
    }
}

最后的最后附上生成二维码的方法:

public function qrcode(){
    $message = session_id();
    if ($message) {
        // 引入第三方库文件
        // 真实路径为(Vendor/Phpqrcode/phpqrcode.php)
        Vendor('Phpqrcode.phpqrcode');
        \QRcode::png($message, false, QR_ECLEVEL_L, 4, 2, false, 0xFFFFFF, 0x000000);
    }else {
        echo "error";
    }
}

总结

1.引入phpqrcode时候直接把phpqrcode.php文件放在ThinkPHP/Library/Vendor/Phpqrcode路径下.
2.调用phpqrcode时候需要\QRcode::png, 指定命名空间为空(因为phpqrcode并没有使用命名空间, 如果直接调用, 就会报错).
3.关于 短轮询长轮询,在一开始看了网上的介绍之后,感觉使用 长轮询 优点那么多, 就决定使用 长轮询, 结果尝试了之后发现如果在开启了session的站点使用 长轮询 会造成其他请求的阻塞(之后会再写一篇文章讨论这个问题,可以先参考这个帖子).因为当时并没有看到这个帖子,所以最终暂时选择了 短轮询 来实现.

你可能感兴趣的:(PHP + iOS 实现移动端扫码登录)