暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

在鸿蒙上实现QQ第三方登录!

鸿蒙技术社区 2021-12-03
1512

因为鸿蒙系统刚出不久,官方的第三方登录 SDK 还没出来,下面就介绍下在鸿蒙应用中实现 QQ 登录的方法(支持唤起 QQ 安卓客户端进行授权)。


前期准备


登录 QQ 开放平台→应用管理→创建应用 ,创建一个网站应用。
https://connect.qq.com/index.html


注意:要选择网站应用,移动应用和小程序不适用该方案。


编写代码


①判断是否已登录


获取登录状态:在入口 AbilitySliceMainAbilitySlice 中进行判断。


从数据库获取 token 的值判断是否已经登录账号(已登录返回 token,未登录返回 null)

// 创建数据库(这里使用官方提供的“轻量级数据存储”,相关文档:https://developer.harmonyos.com/cn/docs/documentation/doc-guides/database-preference-guidelines-0000000000030083)
Preferences preferences = new DatabaseHelper(getApplicationContext()).getPreferences("DATA_NAME");
//  从数据库获取token的值判断是否已经登录账号 (已登录返回token,未登录返回null)
String token = preferences.getString("token",null);


进行相应跳转:已登录跳转至个人界面 MyAbility,未登录跳转至登录界面 LoginAbility。

if(token != null){
            // 已登录,跳转至MyAbility
            Intent myIntent = new Intent();
            myIntent.setParam("token", token);
            Operation myOperation = new Intent.OperationBuilder()
                    .withBundleName("cn.dsttl3.test")
                    .withAbilityName("cn.dsttl3.qqlogin.MyAbility")
                    .build();
            myIntent.setOperation(myOperation);
            startAbility(myIntent);
            terminateAbility();
}else {
            // 未登录,跳转至LoginAbility
            Intent loginIntent = new Intent();
            Operation loginOperation = new Intent.OperationBuilder()
                    .withBundleName("cn.dsttl3.test")
                    .withAbilityName("cn.dsttl3.qqlogin.LoginAbility")
                    .build();
            loginIntent.setOperation(loginOperation);
            startAbility(loginIntent);
            terminateAbility();
}


②登录界面的操作


申请网络访问权限:在 config.json 添加。

"reqPermissions": [
      {
        "name""ohos.permission.INTERNET"
      }
    ]


登录界面布局文件 ability_login.xml,在布局文件中添加以后 webview 组件。

<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:height="match_parent"
    ohos:width="match_parent"
    ohos:alignment="center"
    ohos:orientation="vertical">


    <ohos.agp.components.webengine.WebView
        ohos:id="$+id:WebView_qqlogin"
        ohos:height="match_parent"
        ohos:width="match_parent"/>


</DirectionalLayout>


登录界面的 AbilitySlice LoginAbilitySlice.java,需要用到的几个常量如下:

String state = UUID.randomUUID().toString();// 唯一标识,成功授权后回调时会原样带回。
String client_id = "101***151";//QQ开放平台 应用 APP ID
String redirect_uri = "https%3A%2F%2Fapi.dsttl3.cn%2FRedis%2FQQLogin"//应用 网站回调域 需进行url编码,授权成功后会跳转至该链接
String authorize_url = "https://graph.qq.com/oauth2.0/authorize?response_type=code" +
            "&client_id=" + client_id +
            "&redirect_uri=" + redirect_uri +
            "&state="+ state;


WebView 的配置:
WebView myWebView = (WebView) findComponentById(ResourceTable.Id_WebView_qqlogin);
        myWebView.getWebConfig().setJavaScriptPermit(true);//支持JavaScript
        myWebView.getWebConfig().setUserAgent("android");//将UserAgent设置为安卓,授权页才显示QQ客户端一键登录按钮


自定义 WebAgent,当 WebView 即将打开一个链接时调用 isNeedLoadUrl 方法,当在网页上点击“一键登录”时,打开 QQ 客户端。

wtloginmqq 是 QQ 安卓客户端 URL Scheme:

 if (request.getRequestUrl().toString().startsWith("wtloginmqq")){
                    // 打开QQ客户端
                    Intent qqIntent = new Intent();
                    Operation qqOperation = new Intent.OperationBuilder()
                            .withAction("android.intent.action.VIEW")
                            .withUri(Uri.parse(request.getRequestUrl().toString()))
                            .build();
                    qqIntent.setOperation(qqOperation);
                    startAbility(qqIntent);
}


因为目前还找不到网页端唤起鸿蒙应用的方法,所以 QQ 客户端回调的 code 放在自己服务器处理。


授权成功后,会打开之前在 QQ 开放平台设置的回调域 redirect_uri。

示例:

https://api.dsttl3.cn/Redis/QQLogin?code=********&state=*****


code:QQ 授权返回的 code,用于申请 token。


state:在 webview 请求 QQ 授权页面时传入的唯一标识,用于判断用户身份,方便后续从服务器请求 token。


出于安全考虑 ,请求 token 操作放在服务器上执行。获取到 token 后将 token 存入数据库,客户端通过请求 https://api.dsttl3.cn/Redis/Get?key= + state 来获取到 token。

客户端请求到 token 后,将 token 存储到数据库:

 // 将token存入数据库
Preferences preferences = new DatabaseHelper(getApplicationContext()).getPreferences("DATA_NAME");
preferences.putString("token",token);
preferences.flush();


token 存储完成后跳转至 MyAbility,自定义 WebAgent 完整代码:

myWebView.setWebAgent(new WebAgent(){
            // 当WebView即将打开一个链接时调用该方法
            @Override
            public boolean isNeedLoadUrl(WebView webView, ResourceRequest request) {
                // request.getRequestUrl().toString() WebView即将打开的链接地址
                if (request.getRequestUrl().toString().startsWith("wtloginmqq")){
                    // 打开QQ客户端
                    Intent qqIntent = new Intent();
                    Operation qqOperation = new Intent.OperationBuilder()
                            .withAction("android.intent.action.VIEW")
                            .withUri(Uri.parse(request.getRequestUrl().toString()))
                            .build();
                    qqIntent.setOperation(qqOperation);
                    startAbility(qqIntent);
                    // 向自己的服务器请求token
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            while (true){
                                String getTokenURL = "https://api.dsttl3.cn/Redis/Get?key=" + state;
                                try {
                                    OkHttpClient client = new OkHttpClient();
                                    Request request = new Request.Builder().url(getTokenURL).build();
                                    String token = client.newCall(request).execute().body().string();
                                    if (token.length() == 32){
                                        getUITaskDispatcher().asyncDispatch(new Runnable() {
                                            @Override
                                            public void run() {
                                                // 将token存入数据库
                                                Preferences preferences = new DatabaseHelper(getApplicationContext()).getPreferences("DATA_NAME");
                                                preferences.putString("token",token);
                                                preferences.flush();
                                                // 跳转至用户界面
                                                Intent myIntent = new Intent();
                                                Operation myOperation = new Intent.OperationBuilder()
                                                        .withBundleName("cn.dsttl3.test")
                                                        .withAbilityName("cn.dsttl3.qqlogin.MyAbility")
                                                        .build();
                                                myIntent.setOperation(myOperation);
                                                startAbility(myIntent);
                                                terminateAbility();
                                            }
                                        });
                                        break;
                                    }
                                    Time.sleep(1500);
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }).start();
                    return false;
                }
                return true;
            }
        });


加载网页:
myWebView.load(authorize_url);


LoginAbilitySlice.java 完整代码:
import cn.dsttl3.qqlogin.ResourceTable;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.aafwk.content.Operation;
import ohos.agp.components.webengine.ResourceRequest;
import ohos.agp.components.webengine.WebAgent;
import ohos.agp.components.webengine.WebView;
import ohos.data.DatabaseHelper;
import ohos.data.preferences.Preferences;
import ohos.miscservices.timeutility.Time;
import ohos.utils.net.Uri;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import java.io.IOException;
import java.util.UUID;

public class LoginAbilitySlice extends AbilitySlice {

    //QQ开放平台登录授权文档 https://wiki.connect.qq.com/%e5%87%86%e5%a4%87%e5%b7%a5%e4%bd%9c_oauth2-0
    String state = UUID.randomUUID().toString();// 唯一标识,成功授权后回调时会原样带回。
    String client_id = "101547151";//QQ开放平台 应用 APP ID
    String redirect_uri = "https%3A%2F%2Fapi.dsttl3.cn%2FRedis%2FQQLogin"//应用 网站回调域 需进行url编码,授权成功后会跳转至该链接
    String authorize_url = "https://graph.qq.com/oauth2.0/authorize?response_type=code" +
            "&client_id=" + client_id +
            "&redirect_uri=" + redirect_uri +
            "&state="+ state;
    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_ability_login);
        WebView myWebView = (WebView) findComponentById(ResourceTable.Id_WebView_qqlogin);
        myWebView.getWebConfig().setJavaScriptPermit(true);
        myWebView.getWebConfig().setUserAgent("android");
        myWebView.setWebAgent(new WebAgent(){
            // 当WebView即将打开一个链接时调用该方法
            @Override
            public boolean isNeedLoadUrl(WebView webView, ResourceRequest request) {
                // request.getRequestUrl().toString() WebView即将打开的链接地址
                if (request.getRequestUrl().toString().startsWith("wtloginmqq")){
                    // 打开QQ客户端
                    Intent qqIntent = new Intent();
                    Operation qqOperation = new Intent.OperationBuilder()
                            .withAction("android.intent.action.VIEW")
                            .withUri(Uri.parse(request.getRequestUrl().toString()))
                            .build();
                    qqIntent.setOperation(qqOperation);
                    startAbility(qqIntent);
                    // 向自己的服务器请求token
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            while (true){
                                String getTokenURL = "https://api.dsttl3.cn/Redis/Get?key=" + state;
                                try {
                                    OkHttpClient client = new OkHttpClient();
                                    Request request = new Request.Builder().url(getTokenURL).build();
                                    String token = client.newCall(request).execute().body().string();
                                    if (token.length() == 32){
                                        getUITaskDispatcher().asyncDispatch(new Runnable() {
                                            @Override
                                            public void run() {
                                                // 将token存入数据库
                                                Preferences preferences = new DatabaseHelper(getApplicationContext()).getPreferences("DATA_NAME");
                                                preferences.putString("token",token);
                                                preferences.flush();
                                                // 跳转至用户界面
                                                Intent myIntent = new Intent();
                                                Operation myOperation = new Intent.OperationBuilder()
                                                        .withBundleName("cn.dsttl3.test")
                                                        .withAbilityName("cn.dsttl3.qqlogin.MyAbility")
                                                        .build();
                                                myIntent.setOperation(myOperation);
                                                startAbility(myIntent);
                                                terminateAbility();
                                            }
                                        });
                                        break;
                                    }
                                    Time.sleep(1500);
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }).start();
                    return false;
                }
                return true;
            }
        });
        myWebView.load(authorize_url);
    }
}


个人界面,获取 token 信息:

Preferences preferences = new DatabaseHelper(getApplicationContext()).getPreferences("DATA_NAME");
String token = preferences.getString("token",null);


更新 Text 数据:
Text text = findComponentById(ResourceTable.Id_text_helloworld);
text.setText(token);


后续操作


获取用户信息请参考 QQ 开放平台文档:

https://wiki.connect.qq.com/get_user_info


附件下载:
https://harmonyos.51cto.com/posts/9448


👇扫码报名下周三的鸿蒙直播课👇

👇点击关注鸿蒙技术社区👇
了解鸿蒙一手资讯

求分享

求点赞

求在看

文章转载自鸿蒙技术社区,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论