当前位置: 首页 > news >正文

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&timestamp=%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 软件包&#xff0c;既可以拖拽到 Applications&#xff08;应用程序&#xff09;下直接安装&#xff0c;也可以制作启动 U 盘安装&#xff0c;或者在虚拟机中启动安装。另外也支持在 Windows 和 Lin…...

Unity自定义后处理——用偏导数求图片颜色边缘

大家好&#xff0c;我是阿赵。   继续介绍屏幕后处理效果的做法。这次介绍一下用偏导数求图形边缘的技术。 一、原理介绍 先来看例子吧。   这个例子看起来好像是要给模型描边。之前其实也介绍过很多描边的方法&#xff0c;比如沿着法线方向放大模型&#xff0c;或者用Ndo…...

本地Git仓库和GitHub仓库SSH传输

SSH创建命令解释 ssh-keygen 用于创建密钥的程序 -m PEM 将密钥的格式设为 PEM -t rsa 要创建的密钥类型&#xff0c;本例中为 RSA 格式 -b 4096 密钥的位数&#xff0c;本例中为 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 右值引用…...

消息服务概述

消息服务的作用&#xff1a; 在多数应用尤其是分布式系统中&#xff0c;消息服务是不可或缺的重要部分&#xff0c;它使用起来比较简单&#xff0c;同时解决了不少难题&#xff0c;例如异步处理、应用解耦、流量削锋、分布式事务管理等&#xff0c;使用消息服务可以实现一个高…...

【Spring Boot】Web开发 — 数据验证

Web开发 — 数据验证 对于应用系统而言&#xff0c;任何客户端传入的数据都不是绝对安全有效的&#xff0c;这就要求我们在服务端接收到数据时也对数据的有效性进行验证&#xff0c;以确保传入的数据安全正确。接下来介绍Spring Boot是如何实现数据验证的。 1.Hibernate Vali…...

技术分享 | App常见bug解析

功能Bug 内容显示错误 前端页面展示的内容有误。 这种错误的产生有两种可能 1、前端代码写的文案错误 2、接口返回值错误 功能错误 功能错误是在测试过程中最常见的类型之一&#xff0c;也就是产品的功能没有实现。比如图中的公众号登录不成功的问题。 界面展示错乱 产…...

树莓派Pico|RP2040|使用SWD进行调试|构建 “Hello World“ debug版本

文章目录 使用SWD进行调试构建 "Hello World" debug版本安装 GDB使用 GDB 和 OpenOCD 来 debug Hello World TIP重要提示 使用SWD进行调试 基于rp2040的板上的SWD端口重置&#xff0c;加载和运行代码&#xff0c;如树莓派Pico可用于交互式调试已加载的程序。这包括:…...

Ubuntu18.04 下配置Clion

配置Clion 安装gcc、g、make Ubuntu中用到的编译工具是gcc©&#xff0c;g&#xff08;C&#xff09;&#xff0c;make(连接)。因此只需安装对应的工具包即可。Ubuntu下使用命令安装这些包&#xff1a; &#xff08;1&#xff09;安装gcc sudo apt install gcc&am…...

数据库管理-第九十四期 19c OCM之路-第四堂(02)(20230725)

第九十四期 19c OCM之路-第四堂&#xff08;02&#xff09;&#xff08;20230725&#xff09; 第四堂继续&#xff01; 考点3&#xff1a;SQL statement tuning SQL语句调优 收集Schema统计信息 exec dbms_stats.gather_schems_stats(HR);开启制定表索引监控 create index…...

以智慧监测模式守护燃气安全 ,汉威科技“传感芯”凸显智慧力

城市燃气工程作为城市基建的重要组成部分&#xff0c;与城市居民生活、工业生产紧密相关。提升城市燃气服务质量和安全水平&#xff0c;也一直是政府和民众关注的大事。然而&#xff0c;近年来居民住宅、餐饮等工商业场所燃气事故频发&#xff0c;时刻敲响的警钟也折射出我国在…...

【阅读笔记】一种暗通道优先的快速自动白平衡算法

解决问题: 自动白平衡算法中存在白色区域检测错误导致白平衡失效的问题,作者提出了一种基于暗通道优先的白平衡算法。 算法思想: 图像中白色区域或者高饱和度区域的光线透射率较低,根据以上特性利用暗通道法计算图像中白色区域。 算法概述: 作者使用何凯明提出的基于暗…...

OpenStack之云主机管理

一&#xff09;必备知识 1.云主机与快照管理 a-云主机管理 云主机管理是OpenStack云计算平台的核心功能&#xff0c;通常&#xff0c;云主机的管理包括创建、删除、查询等。可使用以下命令对OpenStack的云主机进行管理&#xff1a; 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 插件推荐可以参考&#xff1a; https://zhuanlan.zhihu.com/p/623580867 1、公共插件安装 下面是个人使用的插件&#xff1a; # 中文插件 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. 定义接…...

简单分享婚宴预订小程序怎么做

婚宴预订小程序需要具备一些功能&#xff0c;通过这些功能&#xff0c;新人可以更方便地选择婚宴场地、预订服务&#xff0c;并且更好地规划自己的婚礼。 1. 场地浏览与选择 婚宴预订小程序可以展示多个婚宴场地的照片和详细信息&#xff0c;包括容纳人数、场地设施、价格等。…...

【多模态】19、RegionCLIP | 基于 Region 来实现视觉语言模型预训练

文章目录 一、背景二、方法2.1 Region-based Language-Image Pretraining2.2 目标检测的迁移学习 三、效果3.1 数据集3.2 实现细节3.3 结果 论文&#xff1a; RegionCLIP: Region-based Language-Image Pretraining 代码&#xff1a;https://github.com/microsoft/RegionCLIP …...

React 第五十五节 Router 中 useAsyncError的使用详解

前言 useAsyncError 是 React Router v6.4 引入的一个钩子&#xff0c;用于处理异步操作&#xff08;如数据加载&#xff09;中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误&#xff1a;捕获在 loader 或 action 中发生的异步错误替…...

synchronized 学习

学习源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖&#xff0c;也要考虑性能问题&#xff08;场景&#xff09; 2.常见面试问题&#xff1a; sync出…...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成

厌倦手动写WordPress文章&#xff1f;AI自动生成&#xff0c;效率提升10倍&#xff01; 支持多语言、自动配图、定时发布&#xff0c;让内容创作更轻松&#xff01; AI内容生成 → 不想每天写文章&#xff1f;AI一键生成高质量内容&#xff01;多语言支持 → 跨境电商必备&am…...

html-<abbr> 缩写或首字母缩略词

定义与作用 <abbr> 标签用于表示缩写或首字母缩略词&#xff0c;它可以帮助用户更好地理解缩写的含义&#xff0c;尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时&#xff0c;会显示一个提示框。 示例&#x…...

FFmpeg:Windows系统小白安装及其使用

一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】&#xff0c;注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录&#xff08;即exe所在文件夹&#xff09;加入系统变量…...

Java 与 MySQL 性能优化:MySQL 慢 SQL 诊断与分析方法详解

文章目录 一、开启慢查询日志&#xff0c;定位耗时SQL1.1 查看慢查询日志是否开启1.2 临时开启慢查询日志1.3 永久开启慢查询日志1.4 分析慢查询日志 二、使用EXPLAIN分析SQL执行计划2.1 EXPLAIN的基本使用2.2 EXPLAIN分析案例2.3 根据EXPLAIN结果优化SQL 三、使用SHOW PROFILE…...

【Java】Ajax 技术详解

文章目录 1. Filter 过滤器1.1 Filter 概述1.2 Filter 快速入门开发步骤:1.3 Filter 执行流程1.4 Filter 拦截路径配置1.5 过滤器链2. Listener 监听器2.1 Listener 概述2.2 ServletContextListener3. Ajax 技术3.1 Ajax 概述3.2 Ajax 快速入门服务端实现:客户端实现:4. Axi…...

华为云Flexus+DeepSeek征文 | 基于Dify构建具备联网搜索能力的知识库问答助手

华为云FlexusDeepSeek征文 | 基于Dify构建具备联网搜索能力的知识库问答助手 一、构建知识库问答助手引言二、构建知识库问答助手环境2.1 基于FlexusX实例的Dify平台2.2 基于MaaS的模型API商用服务 三、构建知识库问答助手实战3.1 配置Dify环境3.2 创建知识库问答助手3.3 使用知…...

【AI News | 20250609】每日AI进展

AI Repos 1、OpenHands-Versa OpenHands-Versa 是一个通用型 AI 智能体&#xff0c;通过结合代码编辑与执行、网络搜索、多模态网络浏览和文件访问等通用工具&#xff0c;在软件工程、网络导航和工作流自动化等多个领域展现出卓越性能。它在 SWE-Bench Multimodal、GAIA 和 Th…...