第三方登录踩坑记录

背景

最近做了注册登录的功能,其中涉及到第三方登录,就是通过 QQ 或微信或 FaceBook 等的账号进行登录。这种通过第三方的账号进行登录的逻辑都差不多,就是通过第三方的 sdk 拿到对应的 token,然后再用 token 向自己的业务后台进行注册,后台可以通过 token 获取到第三方平台的用户信息。

具体的流程如下图所示,其中 QQ 登录的路径和其他任何第三方登录的路径是一样的。
在这里插入图片描述
在接入第三方登录的过程当中踩了一些坑,这里记录一下。

遇到的坑

签名问题

第三方登录的时候需要先在对应的网站上注册自己的 appid,注册的时候就需要提供应用的签名。一般我们的应用会有两个签名,正式签名和测试签名,所以这里最好在网站上注册两个 appid,分别对应正式签名和测试签名,这样就不用来回切换,特别是像微信,每次修改一下都需要审核,就非常的麻烦。appid 注册好了之后,我们在应用内部进行判断,debug 版使用测试签名,release 版使用正式签名。

另外,这里博主还遇到了另外一个问题,由于应用的签名是从公司内网获取的,没办法直接看到 release 版本的签名信息,所以可以在 App 内部运行一段代码,将当前的签名信息打印出来。

执行 getSign 方法就可以获取到当前应用的签名信息了。


    public static String getSign(Context context) {
    
    
        PackageManager manager = context.getPackageManager();
        /** 通过包管理器获得指定包名包含签名的包信息 **/
        PackageInfo packageInfo = null;
        try {
    
    
            packageInfo = manager.getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);
        } catch (PackageManager.NameNotFoundException e) {
    
    
            e.printStackTrace();
        }
        /******* 通过返回的包信息获得签名数组 *******/
        Signature[] signatures = packageInfo.signatures;
        String ss = hexdigest(signatures[0].toByteArray());
        return ss;
    }

    private static final char[] hexDigits = {
    
     48, 49, 50, 51, 52, 53, 54, 55,
            56, 57, 97, 98, 99, 100, 101, 102 };

    private static String hexdigest(String paramString) {
    
    
        try {
    
    
            String str = hexdigest(paramString.getBytes());
            return str;
        } catch (Exception localException) {
    
    
        }
        return null;
    }

    private static String hexdigest(byte[] paramArrayOfByte) {
    
    
        try {
    
    
            MessageDigest localMessageDigest = MessageDigest.getInstance("MD5");
            localMessageDigest.update(paramArrayOfByte);
            byte[] arrayOfByte = localMessageDigest.digest();
            char[] arrayOfChar = new char[32];
            int i = 0;
            int j = 0;
            while (true) {
    
    
                if (i >= 16)
                    return new String(arrayOfChar);
                int k = arrayOfByte[i];
                int m = j + 1;
                arrayOfChar[j] = hexDigits[(0xF & k >>> 4)];
                j = m + 1;
                arrayOfChar[m] = hexDigits[(k & 0xF)];
                i++;
            }
        } catch (Exception localException) {
    
    
        }
        return null;
    }

收不到回调

在接入微信和 QQ sdk 的时候就遇到了这样的问题,死活收不到回调,感觉按照官网的步骤一步一步的配置了,但就是收不到回调信息。
按照我的理解,调用 sdk 的登录授权功能,应该是这样:


val sdkinstance = SDKInstance(appid)
sdkinstance.login(context,object:listener{
    
    
   override fun onComplete(p0: Any?) {
    
    
       Log.i(TAG, "onComplete,${
      
      p0}")
   }

   override fun onError(errorCode:Int, errorMsg:String) {
    
    
       Log.i(TAG, "onError,errorCode:${
      
      errorCode},errorMsg:${
      
      errorMsg}")
      
   }
})

直接调用一下 sdk 的初始化方法,然后塞入一个回调方法,然后就可以在回调方法里面获取到结果了,但事实上并没有想的这么简单。

微信 sdk 获取到回调

想要微信 sdk 获取到回调信息,需要创建一个 WXEntryActivity,其包名必须为 ${applicationId}.wxapi
在这里插入图片描述
让 WXEntryActivity 继承 IWXAPIEventHandler 接口,或者创建一个 IWXAPIEventHandler 的成员变量,
然后在其 onCreate 和 onNewIntent 的生命周期方法里面将处这个 handler 塞进 sdk 当中,这样就可以获取到回调了。

public class WXEntryActivity extends Activity implements IWXAPIEventHandler {
    
    

    private static String TAG = "MicroMsg.WXEntryActivity";

    private IWXAPI api;

    @Override
    public void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        api = WechatLogin.INSTANCE.getWechatApi();
        try {
    
    
            Intent intent = getIntent();
            api.handleIntent(intent, this);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }

    @Override
    protected void onNewIntent(Intent intent) {
    
    
        super.onNewIntent(intent);
        setIntent(intent);
        api.handleIntent(intent, this);
    }

    @Override
    public void onResp(BaseResp resp) {
    
    
       //这里收到回调
    }
}

QQ sdk 获取到回调

获取到 QQ sdk 的回调也没有那么直接,需要在对应的 activity 的 onActivityResult 里面设置一下监听方法才能收到。

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    
    
    Log.d(TAG, "-->onActivityResult $requestCode resultCode=$resultCode")
    when (requestCode) {
    
    
        Constants.REQUEST_LOGIN,
        Constants.REQUEST_QQ_SHARE,
        Constants.REQUEST_APPBAR -> {
    
    
            Tencent.onActivityResultData(
                requestCode,
                resultCode,
                data,
                QQLogin.delegateListener
            )
        }
        else -> {
    
    }
    }
    super.onActivityResult(requestCode, resultCode, data)
    finish()
}

所以这里可以创建一个透明的 activity,用作代理,这样调用起来就会比较方便。

图片分享

接入 sdk 之后调用图片分享,就遇到了下面的问题。
在这里插入图片描述
经过排查,发现是文件路径没有权限的问题,分享的图片在应用的私有目录,其他的应用没有权限读到,所以还必须要配置 FileProvider 属性。

 <provider
     android:authorities="com.tencent.sample.fileprovider"
     android:name="androidx.core.content.FileProvider"
     android:exported="false"
     android:grantUriPermissions="true"
     >
     <meta-data
         android:name="android.support.FILE_PROVIDER_PATHS"
         android:resource="@xml/file_paths"/>
 </provider>

file_paths:

<paths>
    <external-files-path name="opensdk_external" path="Images/tmp"/>
    <root-path name="opensdk_root" path=""/>
</paths>

所以我们分享图片的时候,需要将原有的图片复制到分享的目录,这样其他的应用才能够读到。

 //将bitmap写入分享目录
 fun copyBitmapToSharePath(bitmap: Bitmap): String {
    
    
     val file = createShareFile()
     file.outputStream().let {
    
    
         bitmap?.compress(Bitmap.CompressFormat.PNG, 100, it)
     }
     return file.absolutePath
 }

//将图片文件写入分享目录
fun copyImgToSharePath(imgPath: String): String? {
    
    
    val imgFile = File(imgPath)
    if (!imgFile.exists()){
    
    
        return null
    }
    val outputFile = createShareFile()
    imgFile.inputStream().use {
    
    
        it.copyTo(outputFile.outputStream())
    }
    return outputFile.absolutePath
}

private fun createShareFile(): File {
    
    
    val uuid = UUID.randomUUID()
    val path =
        BaseApplication.context.getExternalFilesDir("Images")!!.absolutePath + "/tmp/${
      
      uuid}"
    val file = File(path)
    if (file.exists()) {
    
    
        file.delete()
    }
    if (!file.parentFile.exists()) {
    
    
        file.parentFile.mkdirs()
    }
    file.createNewFile()
    return file
}

总结

在进行任务初评的时候,感觉接入 sdk 应该花不了多长时间,一天最多两天的就能搞定,但事实上还是花了三四天时间。特别是回调的方式,用起来总感觉不是那么的得劲儿,不知道是因为历史原因还是有什么其他方面的考量,为什么不能够简单直接的塞个 callback ,业务层只需要在 callback 里面获得结果就行了呢?

猜你喜欢

转载自blog.csdn.net/hbdatouerzi/article/details/129779448
今日推荐