安卓接入Twitter三方登录
接twitter登录进行验证的时候需要科学上网,这个前提应该都明白,不多说了。
按照官方给的案例,接twitter登录用的是twitter的sdk进行验证,我们以前也用这个方式跑通。
官方文档如下:
https://github.com/twitter-archive/twitter-kit-android/wiki/Log-In-with-Twitter
https://github.com/twitter-archive/twitter-kit-android
参考官方文档后,实现此需求难度不大。使用这种方式进行登录的博文也比较多,可以参考这篇:
https://www.jianshu.com/p/a31f05fa30af
不过最近在给一个新app接twitter登录时使用一样的接法,却出现问题了,一直报证书问题,让我一度以为是后台配置有什么问题。报错的内容大概是说twitter sdk的证书哈希与服务器返回的证书哈希值对不上。
后面把新app后台配置的key和secret填到旧app的登录代码里面发现没什么问题,可以正常登录;旧app的key和secret填到新app的登录代码同样报错证书问题,并且就算随便填什么东西都一样报证书问题,于是怀疑后台配置没什么问题,AI给出的回答是twitter sdk版本太老了,并且其sdk内部是写死了证书哈希的,如果是后台新创建的app,其证书是服务器新生成的,就会和sdk对不上导致报错,不过网上也找不到什么确切回答,只能暂且认为是这样,如果各位大佬知道可以回复一下。
既然有这种问题,只能另外找方法了,这里提供两个方法,可以参考一下。(都只能网页授权,不能拉起app)
目录
1.通过firebase代替twitter sdk进行验证和授权
2.通过X API方式进行验证和授权(手动进行OAuth认证)
如果不想看流程,可以直接看代码
如何使用twitter-api-java-sdk简化twitter三方登录流程
1.通过firebase代替twitter sdk进行验证和授权
这种方法优点是同样有文档可以参考,而且接入也不麻烦,一般会接twitter登录的app都是上谷歌的,firebase也都配置过,而且firebase可以一起集成google登录、facebook登录、twitter登录、apple登录,正好都统一了,不用接这么多sdk。
问题也是有的,首先和twitter登录不一样,使用firebase代替twitter sdk进行授权登录,返回的用户唯一标识是firebase自己生成的uid,而twitter sdk返回的唯一标识是authtoken,格式上不一样,如果是一个新app且没有ios端或者其它端,那这种方式可能适合你。如果是升级或者有多端,可能会出现什么问题呢,首先第一个是由于同个用户的唯一标识不一样了,导致账号会重新创建;另外ios没有firebase可以接入,就会导致两端对于同个twitter用户,拿到的唯一标识格式不一样,进而双端账号不互通。
文档参考:https://firebase.google.cn/docs/auth/android/twitter-login?hl=zh-cn#java_6
2.通过X API方式进行验证和授权(手动进行OAuth认证)
前面的使用Twitter sdk或者firebase的方式其实都是帮我们做了OAuth认证过程,我们也可以手动去处理这个过程,可以参考文档里认证部分的内容。文档链接:https://docs.x.com/fundamentals/authentication/overview 这里面各项内容都看一下

如果不想看流程,可以直接看代码
       先看看概述,这里面有说到几种身份验证方法,并且在注意里面特意提到要代表其它用户发请求需要使用3-legged Oauth生成令牌,并使用OAuth 1.0a或者OAuth2.0传递,那我们后面使用的身份验证就是OAuth 1.0a或者是PKCE的OAuth 2.0
之后点到指南的使用X登录,要求我们知道如何使用OAuth 1.0a,那我们要先看看1.0a怎么用

点到OAuth 1.0a的授权请求页面看看,文档说需要修改http请求以向X API发送授权请求,host是api.x.com 这个后续会用到,继续往下看

        文档说任何http库都可以发出以上http请求,但是上面的例子无效,因为有很多信息不知道,即需要我们传参,总共有7个东西要传,再往下是解释这些参数是什么,可能一开始看着很难搞,不过后面用了库之后都会直接帮我们处理好,需要做的事情很简单。
看完授权请求后回到之前的使用X登录,根据文档可知需要3个步骤完成登录

第一步请求令牌,调用oauth/request_token接口,使用post方式,需要传参oauth_callback,其它参数在签名时添加,oauth_callback需要在twitter的后台callbacks里面配置,配置内容应该只要符合scheme://host就行,文档给的例子是http://localhost/sign-in-with-twitter/,我之前配的是myappname://twitter-callback也没问题,只要对的上就行

第二步重定向用户,调用oauth/authenticate接口,使用get方法,根据示例url应该很容易明白,不过这里有个坑,可以点到OAuth 1.0a的用户访问令牌看看。
        另外响应的callback_url将收到包含oauth_token和oauth_verifier参数的请求。应用程序应验证令牌是否与步骤 1 中收到的请求令牌匹配。这个验证令牌不是必须
使用3-legged OAuth需要换oauth/authorize接口,并且每一次点击登录都需要重新授权,哪怕之前授权过,实际表现是后续对接成功后,每次点击登录都会让你授权,不像sdk登录那样只第一次需要进行授权。所以之前第二步最终请求的应该是https://api.x.com/oauth/authenticate再拼接参数。

第三步将请求令牌转换为访问令牌,需请求接口oauth/access_token,使用post方法,需要将第二步拿到的oauth_verifier作为参数传给服务器,响应中的oauth_token字段就是用户的唯一标识,可以传给自己后端服务器去创建账号了。(注意第一步响应也会出现oauth_token字段,第二步响应同样会返回的callback_url中也包含这个字段,正常情况下这两个oauth_token一样,同时和第三步拿到的oauth_token不一样,第三步这个才是用户唯一标识)
如何使用twitter-api-java-sdk简化twitter三方登录流程
了解过程之后,我们可以使用一些库简化这个数据交互过程,例如我是使用了twitter的api sdk,地址如下:https://github.com/xdevplatform/twitter-api-java-sdk

不过进入github项目发现给的案例是OAuth 2.0 only-app 的验证方式,没有OAuth 1.0a和OAuth 2.0 PKCE的方式,only-app这种方式无法进行三方登录验证和授权,所以我只能结合ai写了一个OAuth 1.0a的授权登录,OAuth 2.0 PKCE的方式你需要去看OAuth 2.0 PKCE的授权流程,然后再摸索twitter-api-java-sdk怎么用,我懒得搞了,反正官方流程用的1.0a,实际上也可以跑通,就用了1.0a
尝试1.0a之后发现只用到twitter-api-java-sdk里面一小部分内容,一个OAuth认证的库,可以直接引入,不用将twitter-api-java-sdk整个引入
implementation 'com.github.scribejava:scribejava-apis:8.3.3' 
或者  
implementation "com.twitter:twitter-api-java-sdk:2.0.3" 
对进行三方登录的Activity配置intent-filter以接收twitter后台配置的callback,同时记得把activity进行exported,我后台配置的callback是myappname://twitter-callback,看完你应该也知道怎么填了,如果你使用twitter官方案例callback,http://localhost/sign-in-with-twitter/,那scheme应该是http,host应该是localhost/sign-in-with-twitter

接下来就是实际登录代码了
    //twitter登陆使用private OAuth10aService mTwitterService;  //为我们执行请求的类private OAuth1RequestToken mTwitterRequestToken;  //第一步拿到的requestTokenpublic static final String TWITTER_API_KAY = "ababababba";//OAuth 1.0public static final String TWITTER_API_KAY_SECRET = "ababbabab";//OAuth 1.0public static final String TWITTER_CALLBACK_URL = "myappname://twitter-callback";        //后台定义的callback,需要和LoginActivity的intent-filter里面scheme、host对上private void initTwitterLogin() {// 创建 OAuth1.0a Service(ScribeJava)mTwitterService = new ServiceBuilder(TWITTER_API_KAY).apiSecret(TWITTER_API_KAY_SECRET).callback(TWITTER_CALLBACK_URL).build(new DefaultApi10a() {@Overridepublic String getRequestTokenEndpoint() {return "https://api.x.com/oauth/request_token"; }@Overridepublic String getAccessTokenEndpoint() {return "https://api.x.com/oauth/access_token";}@Overrideprotected String getAuthorizationBaseUrl() {return "https://api.x.com/oauth/authorize";}});binding.ivTwitterLogin.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {// 显示加载对话框//showLoadingDialog(false, "", null);new Thread(new Runnable() {@Overridepublic void run() {try {// 第一步获取request tokenmTwitterRequestToken = mTwitterService.getRequestToken();// 获取重定向URLString authorizationUrl = mTwitterService.getAuthorizationUrl(mTwitterRequestToken);// 在主线程中启动浏览器runOnUiThread(new Runnable() {@Overridepublic void run() {//dismissLoadingDialog();// 启动浏览器进行授权Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(authorizationUrl));startActivity(intent);}});} catch (Exception e) {Log.e(TAG, "Twitter OAuth error: " + e.getMessage());}}}).start();}});
}@Override
protected void onNewIntent(Intent intent) {super.onNewIntent(intent);LogUtil.d(TAG, "onNewIntent intent= " + intent);//第二步浏览器回调的callback_urlUri uri = intent.getData();if (uri != null && GlobalDefines.TWITTER_CALLBACK_URL.equals(uri.getScheme() + "://" + uri.getHost())) {String verifier = uri.getQueryParameter(OAuthConstants.VERIFIER);//          可以检查此token与第一步的是否一致
//          String token = uri.getQueryParameter(OAuthConstants.TOKEN);if (!TextUtils.isEmpty(verifier)) {exchangeAccessToken(verifier);} else {Toast.makeText(this, "Twitter login cancelled", Toast.LENGTH_SHORT).show();}}
}
private void exchangeAccessToken(String verifier) {
//        showLoadingDialog(false, "", null);new Thread(() -> {try {//第三步获取access_tokenOAuth1AccessToken accessToken = mTwitterService.getAccessToken(mTwitterRequestToken, verifier);//第三步拿到的oauth_token才作为用户唯一标识String oauth_token = accessToken.getParameter(OAuthConstants.TOKEN); LogUtil.d(TAG, " oauth_token= " + oauth_token);runOnUiThread(() -> {
//                    dismissLoadingDialog();
//                    authorizationLogin(oauth_token); //你自己的三方登录逻辑});} catch (Exception e) {Log.e(TAG, "exchangeAccessToken error= " + e);runOnUiThread(() -> {
//                    dismissLoadingDialog();});}}).start();
} 
和谷歌比起来,twitter这个破东西真的不好搞,文档也没给什么案例抄,报错也很难找到答案,搞了好久才搞定,总之就是好想吐槽。

