现在越来越多的 App 开始支持本机号码一键登录的功能了,通过和运营商合作,获取到当前使用网络的手机卡号码,进行登录流程,能够减少用户的操作步骤,提高转化率。
![]() |
![]() |
众所周知,国内现在主流通讯运营商有三家,分别是中国移动、中国联通和中国电信。
这就意味着,对接三家运营商就有三套对接流程,要对接三次 SDK,但,正如所有的聚合服务所需,总会产生聚合这三家运营商一键登录服务的 SDK 供大家使用。
阿里云作为国内数字经济基础设施比较完善的代表,往往会作为许多企业的首选。我所在的企业,自然也是选择了它。
文前预警,本文需配合阿里云本机号码一键登录文档一同食用,建议先按照文档和官方提供的 Demo 先接一遍了解流程。

接入阿里云本机号码一键登录服务首先要到阿里云后台申请参数,申请参数需要提供应用的包名和签名,然后阿里云会为我们生成一个密钥。
之前的文章有提到过,我目前从事游戏行业,国内游戏行乱象大家都知道,马甲包遍地都是,拥有不同包名的马甲包如果想要都接入本机号码一键登录功能的话,就得申请多套不同的参数,这大大增加了对接成本。
那么,我们能不能绕过阿里云对包名的检测,让多个马甲包都共用同一套参数呢?
来试试看。
我这里拿阿里云官方 Demo 提供的参数进行演示,实际项目按需修改。
记得签名也要配置,参考之前『「Android Studio」用 Gradle 实现自动签名』一文:
首先需要了解阿里云本机号码一键登录 SDK 是如何检测包名的,很简单,跟我们获取包名的操作类似,通过调用 Context
的 getPackageName()
来获取,再与服务端校验。
Context
如何获取,看初始化就可以,大多数 SDK 的初始化操作都是为了获取 Context
对象,而幸运的是,阿里云本机号码一键登录 SDK 并没有使用 App Startup 这些新技术,而是让对接方手动调用:
这就简单许多了,假如我们能够自定义一个 Context
传给本机号码一键登录的 SDK,使得 SDK 在获取包名时,返回我们申请参数的包名而不是实际包名,就能够绕过它的检测机制了。
Context
是一个抽象类,而在 Android 中对 Context
的实现非常多,但往往继承越深,代码也就越复杂,根据开发经验,继承自 Context
的 Application
对于获取这种全局的东西往往会比其他组件要稍微方便一些,所以可以自定义一个 Application
来实现我们的需求。
因为这个 Application
我们只用来欺骗阿里云本机号码一键登录 SDK,所以不会在 AndroidManifest
中注册它,那么这个 Application
的一些初始化方法就不能得到执行。
所以需要采用反射获取,并使用 invoke()
执行实例对应的方法。
当我们把这个 Application
对象传给 SDK 后,包名和签名都会由这个 Application
来提供。
先来看签名,因为我们的签名不需要改变,所以只需要将应用本身的签名回传即可。
写一个获取应用签名的方法:
这时候你应该发现,获取应用签名需要提供一个 PackageManager
对象,阿里云本机号码一键登录 SDK 也是如此,那就得再向它提供一个自定义的 PackageManager
了。
PackageManager
也是一个抽象类,需要实现很多个方法,但这些方法大多数我们都用不上,所以可以忽略,只将 getPackageInfo()
方法实现即可,主要就是封装包名与签名。
接下来就要在 AliApplication
中调用:
这个时候调用:
你就发现,应用崩溃了…
实际上还漏了一个方法:
你可能会有疑惑,为什么不需要重写 Application
的 getPackageName()
。只不过 SDK 似乎都是通过 PackageManager
来获取的,实际上你重写也没有问题。
再运行,你会发现,应用没有崩溃,但是却没有拉起授权页,SDK 回调了以下错误:
这个错误信息你查文档的话并没有什么作用,但是它能够告诉我们签名和包名都已获取正确了,也就是上面的步骤操作正确。
接下来就要开始对 SDK 进行魔改了。
在进行这一步之前,我必须要提个醒,阿里云提供的接入文档中有相关服务声明:
注意:
一键登录或注册需用户确认授权方可使用,开发者不得通过任何技术手段跳过或模拟此步骤,否则我方有权停止服务并追究相关法律责任。
登录按钮文字描述必须包含“登录”或“注册”等文字,不得诱导用户授权。
对于接入移动认证SDK并上线的应用,我方会对上线的应用授权页面做审查,如果有出现未按要求弹出或设计授权页面的,将关闭应用的一键登录或注册服务。
以下内容仅作学习交流,如产生任何责任由应用的开发者承担。
回到正题,魔改的话要从哪里入手?
不难注意到接入流程要我们手动注册三个 Activity
,很明显,LoginAuthActivity
就是我们的目标,而且你也发现有两个 LoginAuthActivity
。
另一方面,我们无法正常唤起授权页,证明唤起的流程也需要修改。
这两个问题合在一起思考,我们能不能在修改唤起流程的时候,将两个 LoginAuthActivity
合并,即无论是移动、联通还是电信,都只唤起同一个 LoginAuthActivity
,因为同样的功能分两个页面我实在想不到原因。
想要篡改,首先就得弄清楚流程,先反编译看看代码。
根据包名可以定位到是「phoneNumber-L-AuthSDK.arr」这个库里面的,解压拿到 Jar 包丢进之前在『Android 反编译入门指南』一文中提到的工具『JADX』里面就可以了,但反编译后我瞬间就懵了,阿里云对这个 SDK 做了混淆,并且还有一些方法写在了 SO 库中,大大增加了我们修改的难度。
通过代码搜索,最终定位到唤起流程在一个混淆名为 d
的类中:
中间那一堆判断我也不知道它有啥用,干脆直接去掉:
不管三七二十一,反正 Intent
封装好了,那就直接拉起 LoginAuthActivity
吧,为了更好的处理回调,这里通过 startActivityForResult()
拉起。
然后处理 LoginAuthActivity
,对于 Activity
一般先看 onCreate()
方法:
可惜的是,LoginAuthActivity
直接把 onCreate()
写在了 SO 库中,所以我们不能直接修改,不要紧,幸好还有别的生命周期:
可以看到 SDK 里的 onResume()
并没有做什么操作。
首先我们需要写一个自己的界面来处理登录回调,我这里就用一个 TextView
来显示手机号码,一个 Button
来点击登录,还有一个 TextView
用来显示运营商协议,太简单我就不写了。
因为 SDK 本来就有授权页,所以我们要覆盖它,可以通过 LayoutInflater
来操作:
手机号码实际上展示的是掩码,和文章开头的各家应用截图一致。运营商协议则根据 mSlogan
这个字段处理一下,三大运营商的协议我已经把链接抓下来了,跳转到浏览器打开就可以。
按钮点击响应执行登录事件,并回调给 TokenResultListener
。
可以发现,这里的 TokenResultListener
和前面初始化的 TokenResultListener
并不是同一个,因为初始化的那个 TokenResultListener
我没有想到优雅的办法传递过来。
看看授权完成的 TokenResultListener
回调处理:
这个 onTokenSuccess()
只需处理获取 Token
的 Code
即可,解析对应的 Token
,并回传给服务端,完成登录流程,可以参考文档提供的流程图。

己方服务端登录成功后,再销毁 LoginAuthActivity
,不要忘了将结果告知拉起授权页的 Activity
,也不要忘了在拉起授权页的 Activity
处理结果:
最后再看看一开始初始化时的 TokenResultListener
:
修改完之后,我们要将原来「phoneNumber-L-AuthSDK.arr」这个库文件重新导入,因为重写了 LoginAuthActivity
和 d
两个类,所以需要把库文件中对应的 CLASS 文件删除掉。
之前『「Android Studio」如何导入 AAR 包』一文提到,AAR 是以 Zip 格式构建的,所以可以直接通过压缩工具打开将其删除,也可以改用文章中提到的 JAR 的方式接入,解压后将资源放到对应的目录即可,由『「Android Studio」如何导入 JAR 包』一文可知,JAR 也是以 Zip 格式构建的,所以同样也可以用压缩软件打开并删除掉对应的 CLASS 文件。
而我们所修改的 LoginAuthActivity
和 d
,则根据原来 SDK 内的包名,放到项目中对应的包目录下即可。
最终运行效果如下:

关于后面魔改 SDK 的相关代码,文章讲的比较简单,所以我写了个 Demo 放在 Github,需要的话可以参考我的 Demo 进行修改。
Gitalking ...