Android进阶之微信扫码登录
遇到新需求要搭建微信扫码登录功能,这篇文章是随着我的编码过程一并写的,希望能够帮助有需求的人和以后再次用到此功能的自己。
首先想到的就是百度各种文章,当然去开发者平台申请AppID和密钥是必不可少的,等注册好发现需要创建应用以及审核(要官网,流程图及其他信息),想着先写一个Demo开发着,还不需要这么麻烦,
通过一篇文章发现微信还有测试账号 -> 微信扫码登录详细操作流程(微信公众平台开发)
那不正中我心怀,根据现有的比较清晰的文章写出了逻辑 -> Android安卓开发集成微信第三方扫描二维码登录-超级无敌具详细
将我申请的测试 AppID 和密钥 投进去之后,在回调的时候一直走不到 onAuthGotQrcode 方法里面去,看 oauth.auth() 获取二维码方法返回的也是 True,每次都直接走到 onAuthFinish 方法里面,这不扯呢,AppID 和 密钥都是申请好的,代码逻辑也是参考了多篇文章,理论上也没啥问题,而且获取方法也返回的 True,但是每次确报以下错误 ->
E/MicroMsg.SDK.GetQRCodeResult: resp errcode = -21
E/MicroMsg.SDK.GetQRCodeTask: onPostExecute, get qrcode fail, OAuthErrCode = OAuthErrCode:-1
遇到问题就解决呗,去文档上找错误码 -21 -1 啥的没找到,mmp,去百度此类错误,发现遇到的人不少,给出解决方案的都没有,但是有一篇文章的错误和我的差不多,也给了解决办法 ->
android 获取微信二维码 DiffDevOAuth.auth
果然没有解决,文档里还有说字段大小写字母错误的,参数名字错误的,这些我都检查过了, 但是通过错误可以看出来就是获取二维码失败了吧,失败的原因有很多,可能我们不是同一个问题导致的
在网上地毯式搜索之后发现了一个可以解释过去的问题 微信还分公众平台和开放平台,公众平台是以小程序为业务线的,我一瞅我这测试账号不就是从 公众平台申请的吗,难不成是这个问题,早晚都得申请应用,那就先申请了吧,这里要注意下,申请的话需要公司信息(可能还要往公司账户打一笔费用验证),正经官网,APP流程图,要提前做好准备,那就等到几天时间,等正经id和密钥出来再说吧,
过了差不多三四天,应用审核通过了,但是要申请开通微信登录,需要进行开发者资质认证,审核费用三百块,还要填写企业各种信息,营业执照,信用代码啥的
生成密钥还需要绑定了管理员银行卡的微信扫码验证
差不多经历了四五天,终于拿到了AppID 和密码,也开通了微信登录权限, 激动人心的时刻到了,我把正经的数据塞到那套逻辑里,二维码正常显示出来了!获取用户数据也是正常的!
总结:
微信公众平台申请的测试 AppID 和密钥 不能用于微信开放平台的操作,比如APP扫码登录,支付等
接下来我将我的源码贴出来(本人以下代码正常运行,如有需要只需替换 appID、appsecret即可,其他逻辑亲测可用),尽量写了详细的注释,或者通过上面我贴的链接也可以的
导入依赖比不可少
implementation 'com.squareup.okhttp3:okhttp:3.12.0'implementation 'com.google.code.gson:gson:2.8.9'//微信登录implementation 'com.tencent.mm.opensdk:wechat-sdk-android:+'
需要的实体类先建好
GetAccessTokenBean
public class GetAccessTokenBean {String access_token;int expires_in;
}
GetTicketBean
public class GetTicketBean {int errcode;String errmsg;String ticket;int expires_in;
}
UserData
public class UserData {private String access_token;private int expires_in;private String refresh_token;private String openid;private String scope;private String unionid;public void setAccess_token(String access_token) {this.access_token = access_token;}public String getAccess_token() {return access_token;}public void setExpires_in(int expires_in) {this.expires_in = expires_in;}public int getExpires_in() {return expires_in;}public void setRefresh_token(String refresh_token) {this.refresh_token = refresh_token;}public String getRefresh_token() {return refresh_token;}public void setOpenid(String openid) {this.openid = openid;}public String getOpenid() {return openid;}public void setScope(String scope) {this.scope = scope;}public String getScope() {return scope;}public void setUnionid(String unionid) {this.unionid = unionid;}public String getUnionid() {return unionid;}
}
UserInfo
public class UserInfo {private String openid;private String nickname;private int sex;private String language;private String city;private String province;private String country;private String headimgurl;private List<String> privilege;private String unionid;public void setOpenid(String openid) {this.openid = openid;}public String getOpenid() {return openid;}public void setNickname(String nickname) {this.nickname = nickname;}public String getNickname() {return nickname;}public void setSex(int sex) {this.sex = sex;}public int getSex() {return sex;}public void setLanguage(String language) {this.language = language;}public String getLanguage() {return language;}public void setCity(String city) {this.city = city;}public String getCity() {return city;}public void setProvince(String province) {this.province = province;}public String getProvince() {return province;}public void setCountry(String country) {this.country = country;}public String getCountry() {return country;}public void setHeadimgurl(String headimgurl) {this.headimgurl = headimgurl;}public String getHeadimgurl() {return headimgurl;}public void setPrivilege(List<String> privilege) {this.privilege = privilege;}public List<String> getPrivilege() {return privilege;}public void setUnionid(String unionid) {this.unionid = unionid;}public String getUnionid() {return unionid;}}
EncryptUtils
public class EncryptUtils {public static String getSHA(String info) {byte[] digesta = null;try {// 得到一个SHA-1的消息摘要MessageDigest alga = MessageDigest.getInstance("SHA-1");// 添加要进行计算摘要的信息alga.update(info.getBytes());// 得到该摘要digesta = alga.digest();} catch (NoSuchAlgorithmException e) {e.printStackTrace();}// 将摘要转为字符串String rs = byte2hex(digesta);return rs;}private static String byte2hex(byte[] b) {String hs = "";String stmp = "";for (byte aB : b) {stmp = (Integer.toHexString(aB & 0XFF));if (stmp.length() == 1) {hs = hs + "0" + stmp;} else {hs = hs + stmp;}}return hs;}
}
获取二维码
public class MainActivity extends AppCompatActivity implements OAuthListener {//图片控件,用于显示二维码private ImageView ivQrCode;//获取微信二维码需要用到的对象IDiffDevOAuth oauth = null;//时间转换格式private final String TIME_FORMAT = "yyyyMMddHHmmss";//开放平台创建应用产生的AppIDprivate final String appID = "xxxxxxxxxxxxxxx";//开放平台创建应用产生的密钥private final String appsecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxx";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//初始化获取二维码的对象oauth = DiffDevOAuthFactory.getDiffDevOAuth();//获取图片控件对象ivQrCode = findViewById(R.id.iv_qr_code);//开始获取数据(第一步)getAccessToken();}private void getAccessToken() {//第一步请求OkHttpClient client = new OkHttpClient();Request.Builder builder = new Request.Builder();String getAccessToken = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appID + "&secret=" + appsecret;Request request1 = builder.get().url(getAccessToken).build();client.newCall(request1).enqueue(new Callback() {@Overridepublic void onFailure(@NonNull Call call, @NonNull IOException e) {}@Overridepublic void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {ResponseBody mBean = response.body();if (mBean != null){String res = mBean.string();//请求成功时返回的东西GetAccessTokenBean mData = new Gson().fromJson(res,GetAccessTokenBean.class);if (mData.access_token != null && !"".equals(mData.access_token)){//拿到第一步数据,开始第二步getTicket(mData.access_token);}}}});}private void getTicket(String l){//第二步网络请求OkHttpClient client = new OkHttpClient();Request.Builder builder = new Request.Builder();Request request1 = builder.get().url("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + l + "&type=2").build();client.newCall(request1).enqueue(new Callback() {@Overridepublic void onFailure(@NonNull Call call, @NonNull IOException e) {}@Overridepublic void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {ResponseBody mBean = response.body();if (mBean != null){//拿到数据进行数据组合String res = mBean.string();//请求成功时返回的东西GetTicketBean mData = new Gson().fromJson(res,GetTicketBean.class);if (mData.ticket != null){StringBuilder str = new StringBuilder();Random random = new Random();for (int i = 0; i < 8; i++){str.append(random.nextInt(10));}String noncestr = str.toString();String timeStamp = new SimpleDateFormat(TIME_FORMAT).format(new Date());String string1 = java.lang.String.format("appid=%s&noncestr=%s&sdk_ticket=%s×tamp=%s", appID, noncestr, mData.ticket, timeStamp);String sha = EncryptUtils.getSHA(string1);//开始进行第三步sign(noncestr,timeStamp,sha);}}}});}private void sign(String noncestr,String timeStamp,String sha){if (oauth != null){oauth.removeAllListeners();oauth.stopAuth();oauth.detach();//第四步,获取二维码,获取到的二维码从回调(onAuthGotQrcode)里面显示Boolean s = oauth.auth(appID,"snsapi_userinfo",noncestr,timeStamp,sha,this);}}private void getUserData(String c){//开始第五步OkHttpClient client = new OkHttpClient();Request.Builder builder = new Request.Builder();Request request1 = builder.get().url("https://api.weixin.qq.com/sns/oauth2/access_token?appid="+ appID+"&secret=" + appsecret + "&code=" +c+ "&grant_type=authorization_code").build();client.newCall(request1).enqueue(new Callback() {@Overridepublic void onFailure(@NonNull Call call, @NonNull IOException e) {}@Overridepublic void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {ResponseBody mBean = response.body();if (mBean != null){String res = mBean.string();//请求成功时返回的东西UserData mData = new Gson().fromJson(res,UserData.class);if (mData.getOpenid() != null && !Objects.equals(mData.getOpenid(), "") &&mData.getAccess_token() != null && !Objects.equals(mData.getAccess_token(), "")){//拿到数据,最后一步获取用户信息getUserInfo(mData.getOpenid(),mData.getAccess_token());}}}});}private void getUserInfo(String openID, String aToken){//获取用户信息OkHttpClient client = new OkHttpClient();Request.Builder builder = new Request.Builder();Request request1 = builder.get().url("https://api.weixin.qq.com/sns/userinfo?access_token=" +aToken+"&openid=" + openID).build();client.newCall(request1).enqueue(new Callback() {@Overridepublic void onFailure(@NonNull Call call, @NonNull IOException e) {}@Overridepublic void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {ResponseBody mBean = response.body();if (mBean != null){String res = mBean.string();//请求成功时返回的东西UserInfo mData = new Gson().fromJson(res,UserInfo.class);}}});}@Overridepublic void onAuthGotQrcode(String s, byte[] bytes) {//获取二维码图片。并显示出来,用户扫码二维码之后从回调(onAuthFinish)显示Bitmap bmp = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);if (ivQrCode != null){ivQrCode.setImageBitmap(bmp);}}@Overridepublic void onQrcodeScanned() {}@Overridepublic void onAuthFinish(OAuthErrCode oAuthErrCode, String s) {//用户授权成功之后可以从这里拿到数据,或者错误信息//获取用户信息 第五步getUserData(s);}
}
activity_main
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><ImageViewandroid:id="@+id/iv_qr_code"app:layout_constraintTop_toTopOf="parent"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintEnd_toEndOf="parent"android:layout_width="200px"android:layout_height="200px"/></androidx.constraintlayout.widget.ConstraintLayout>
相关文章:
Android进阶之微信扫码登录
遇到新需求要搭建微信扫码登录功能,这篇文章是随着我的编码过程一并写的,希望能够帮助有需求的人和以后再次用到此功能的自己。 首先想到的就是百度各种文章,当然去开发者平台申请AppID和密钥是必不可少的,等注册好发现需要创建应用以及审核(要官网,流程图及其他信息),想着先写…...
macOS Monterey 12.6.8 (21G725) Boot ISO 原版可引导镜像
macOS Monterey 12.6.8 (21G725) Boot ISO 原版可引导镜像 本站下载的 macOS 软件包,既可以拖拽到 Applications(应用程序)下直接安装,也可以制作启动 U 盘安装,或者在虚拟机中启动安装。另外也支持在 Windows 和 Lin…...
Unity自定义后处理——用偏导数求图片颜色边缘
大家好,我是阿赵。 继续介绍屏幕后处理效果的做法。这次介绍一下用偏导数求图形边缘的技术。 一、原理介绍 先来看例子吧。 这个例子看起来好像是要给模型描边。之前其实也介绍过很多描边的方法,比如沿着法线方向放大模型,或者用Ndo…...
本地Git仓库和GitHub仓库SSH传输
SSH创建命令解释 ssh-keygen 用于创建密钥的程序 -m PEM 将密钥的格式设为 PEM -t rsa 要创建的密钥类型,本例中为 RSA 格式 -b 4096 密钥的位数,本例中为 4096 -C “azureusermyserver” 追加到公钥文件末尾以便于识别的注释。 通常以电子邮件地址…...
【C++11】——右值引用、移动语义
目录 1. 基本概念 1.1 左值与左值引用 1.2 右值和右值引用 1.3 左值引用与右值引用 2. 右值引用实用场景和意义 2.1 左值引用的使用场景 2.2 左值引用的短板 2.3 右值引用和移动语义 2.3.1 移动构造 2.3.2 移动赋值 2.3.3 编译器做的优化 2.3.4 总结 2.4 右值引用…...
消息服务概述
消息服务的作用: 在多数应用尤其是分布式系统中,消息服务是不可或缺的重要部分,它使用起来比较简单,同时解决了不少难题,例如异步处理、应用解耦、流量削锋、分布式事务管理等,使用消息服务可以实现一个高…...
【Spring Boot】Web开发 — 数据验证
Web开发 — 数据验证 对于应用系统而言,任何客户端传入的数据都不是绝对安全有效的,这就要求我们在服务端接收到数据时也对数据的有效性进行验证,以确保传入的数据安全正确。接下来介绍Spring Boot是如何实现数据验证的。 1.Hibernate Vali…...
技术分享 | App常见bug解析
功能Bug 内容显示错误 前端页面展示的内容有误。 这种错误的产生有两种可能 1、前端代码写的文案错误 2、接口返回值错误 功能错误 功能错误是在测试过程中最常见的类型之一,也就是产品的功能没有实现。比如图中的公众号登录不成功的问题。 界面展示错乱 产…...
树莓派Pico|RP2040|使用SWD进行调试|构建 “Hello World“ debug版本
文章目录 使用SWD进行调试构建 "Hello World" debug版本安装 GDB使用 GDB 和 OpenOCD 来 debug Hello World TIP重要提示 使用SWD进行调试 基于rp2040的板上的SWD端口重置,加载和运行代码,如树莓派Pico可用于交互式调试已加载的程序。这包括:…...
Ubuntu18.04 下配置Clion
配置Clion 安装gcc、g、make Ubuntu中用到的编译工具是gcc©,g(C),make(连接)。因此只需安装对应的工具包即可。Ubuntu下使用命令安装这些包: (1)安装gcc sudo apt install gcc&am…...
数据库管理-第九十四期 19c OCM之路-第四堂(02)(20230725)
第九十四期 19c OCM之路-第四堂(02)(20230725) 第四堂继续! 考点3:SQL statement tuning SQL语句调优 收集Schema统计信息 exec dbms_stats.gather_schems_stats(HR);开启制定表索引监控 create index…...
以智慧监测模式守护燃气安全 ,汉威科技“传感芯”凸显智慧力
城市燃气工程作为城市基建的重要组成部分,与城市居民生活、工业生产紧密相关。提升城市燃气服务质量和安全水平,也一直是政府和民众关注的大事。然而,近年来居民住宅、餐饮等工商业场所燃气事故频发,时刻敲响的警钟也折射出我国在…...
【阅读笔记】一种暗通道优先的快速自动白平衡算法
解决问题: 自动白平衡算法中存在白色区域检测错误导致白平衡失效的问题,作者提出了一种基于暗通道优先的白平衡算法。 算法思想: 图像中白色区域或者高饱和度区域的光线透射率较低,根据以上特性利用暗通道法计算图像中白色区域。 算法概述: 作者使用何凯明提出的基于暗…...
OpenStack之云主机管理
一)必备知识 1.云主机与快照管理 a-云主机管理 云主机管理是OpenStack云计算平台的核心功能,通常,云主机的管理包括创建、删除、查询等。可使用以下命令对OpenStack的云主机进行管理: openstack server <操作><云主机…...
Linux系列---【Ubuntu 20.04安装KVM】
Ubuntu 20.04安装KVM 一、安装kvm 1.安装kvm sudo apt install qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils 2. 将当前用户添加至libvirt 、 kvm组 sudo adduser $USER libvirt sudo adduser $USER kvm 3.验证安装 virsh list --all 4.启动libvert sudo syst…...
【Vue3】局部组件和全局组件
1. 局部组件 Card.vue <template><div class"card"><header><div>标题</div><div>副标题</div></header><section>内容</section></div> </template><script setup lang"ts"…...
vscode开发Go和Java
vscode开发Go和Java 最新最全 vscode 插件推荐可以参考: https://zhuanlan.zhihu.com/p/623580867 1、公共插件安装 下面是个人使用的插件: # 中文插件 Chinese (Simplified) (简体中文) Language Pack for Visual Studio Code https://marketplace…...
自定义MVC
目录 一.什么是MVC 1.1.三层架构和MVC的区别 二.自定义MVC工作原理图 三.自定义mvc实现 3.1 创建web工程 3.2 中央处理器 3.3 Action接口定义 3.4 实现子控制器 3.5 完善中央控制器 3.5.1 请求分发功能 3.5.2 使用配置文件配置action 3.5.3 请求参数处理 1. 定义接…...
简单分享婚宴预订小程序怎么做
婚宴预订小程序需要具备一些功能,通过这些功能,新人可以更方便地选择婚宴场地、预订服务,并且更好地规划自己的婚礼。 1. 场地浏览与选择 婚宴预订小程序可以展示多个婚宴场地的照片和详细信息,包括容纳人数、场地设施、价格等。…...
【多模态】19、RegionCLIP | 基于 Region 来实现视觉语言模型预训练
文章目录 一、背景二、方法2.1 Region-based Language-Image Pretraining2.2 目标检测的迁移学习 三、效果3.1 数据集3.2 实现细节3.3 结果 论文: RegionCLIP: Region-based Language-Image Pretraining 代码:https://github.com/microsoft/RegionCLIP …...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...
蓝桥杯3498 01串的熵
问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798, 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...
短视频矩阵系统文案创作功能开发实践,定制化开发
在短视频行业迅猛发展的当下,企业和个人创作者为了扩大影响力、提升传播效果,纷纷采用短视频矩阵运营策略,同时管理多个平台、多个账号的内容发布。然而,频繁的文案创作需求让运营者疲于应对,如何高效产出高质量文案成…...
Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
腾讯云V3签名
想要接入腾讯云的Api,必然先按其文档计算出所要求的签名。 之前也调用过腾讯云的接口,但总是卡在签名这一步,最后放弃选择SDK,这次终于自己代码实现。 可能腾讯云翻新了接口文档,现在阅读起来,清晰了很多&…...
R 语言科研绘图第 55 期 --- 网络图-聚类
在发表科研论文的过程中,科研绘图是必不可少的,一张好看的图形会是文章很大的加分项。 为了便于使用,本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中,获取方式: R 语言科研绘图模板 --- sciRplothttps://mp.…...
0x-3-Oracle 23 ai-sqlcl 25.1 集成安装-配置和优化
是不是受够了安装了oracle database之后sqlplus的简陋,无法删除无法上下翻页的苦恼。 可以安装readline和rlwrap插件的话,配置.bahs_profile后也能解决上下翻页这些,但是很多生产环境无法安装rpm包。 oracle提供了sqlcl免费许可,…...
