写在前面
原生微信小程序利用多端框架生成鸿蒙APP,做了一堆的各种APP的适配,满是期待的去提审APP,初次提交鸿蒙官方审核是不会管你是否接入华为登录,但是如果你想再次提交APP更新版本,就会检查你是否接入华为账号登录,并且会引导你接入APP原生的登录能力。
目前为止多端框架还不支持鸿蒙原生插件,也没有做一键登录华为账号的适配(因为多端框架官方确实有点忙)。为了满足华为账号登录的条件,可以借助WebView + 华为账号 Web 端授权方案就能轻松搞定,这也是当前适配微信生态且能满足华为 APP 审核要求的最优实现方式,此外,即便后续微信团队推出华为账号原生登录,其配置和开发流程大概率会像苹果登录一样繁琐,而 WebView 方案轻量、易维护,现网使用完全无压力,后续也可灵活切换,是兼顾当下需求和长期维护的优选。今天就给大家带来一步到位的实操教程。
核心实现逻辑
整体流程围绕 微信多端应用跳转 WebView→加载华为账号官方授权页面→用户授权→获取授权码 code→回传微信端→服务端兑换凭证 展开,无需关注华为账号登录的底层交互(不管用户是 手机号输入、一键授权、密码验证等),只需做好页面跳转和 code 的传递、解析工作,如果做过微信公众号网页授权登录就会轻松很多。逻辑如下:
- 微信多端应用 在用户登录页面,点击华为账号登录 跳转到 WebView 页面并加载华为账号 Web 端标准授权链接;
- 用户在 WebView 中完成华为 / 鸿蒙账号的登录 / 授权操作(全程由华为官方页面处理,开发者无需干预);
- 授权成功后,华为 OAuth 服务通过预设回调地址将授权码 code拼接在 URL 中返回至 WebView,我们需要自己再写一个接收code的页面;
- WebView 从回调 URL 中解析出 code,通过微信通信 API 回传至微信多端应用主程序;
- 微信应用将 code 传递至自身服务端,由服务端调用华为官方接口完成 code 兑换,获取 Access Token、用户信息等;
- 服务端解析用户信息,完成微信多端应用的华为账号登录态创建。
核心步骤及相关代码
第一步:用户选择“华为帐号”来进行登录,拼接OAuth服务请求授权的链接,跳转到webview到该请求链接,访问华为OAuth服务。华为官方文档地址: https://developer.huawei.com/consumer/cn/doc/HMSCore-Guides/web-get-access-token-0000001050048946
第二步:休息一会
第三步:做一个华为授权登录回调的页面用来接收华为授权码code ,需引入微信jssdk,并涉及到在webview里边网页向小程序发送消息,小程序webview官方文档:https://developers.weixin.qq.com/miniprogram/dev/component/web-view.html。
第三点五步:在获取到华为账号授权码code后,再次请求OAuth服务,调用https://oauth-login.cloud.huawei.com/oauth2/v3/token,通过授权码code换取Access Token、Refresh Token以及ID Token。这部分内容就不展开细说了,详细看华为官方文档,写的已经很详细了。https://developer.huawei.com/consumer/cn/doc/HMSCore-Guides/web-get-access-token-0000001050048946
小程序端代码
// webview页面
<web-view src="{{url}}" bindmessage="bindMessage"></web-view>
// webview 的 js
bindMessage(e) {
var eventChannel = this.getOpenerEventChannel()
eventChannel.emit('hwLoginBack', e)
},
网页代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>授权登录中...</title>
<!-- 1. 引入微信 JS-SDK (必须) -->
<script type="text/javascript" src="https://res.wx.qq.com/open/js/jweixin-1.6.2.js"></script>
<style>
/* 基础样式重置与居中布局 */
body {
background-color: #fff;
margin: 0;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
font-family: -apple-system, sans-serif;
color: #333;
}
.status-text {
font-size: 16px;
padding: 20px;
}
</style>
</head>
<body>
<div id="reqStatus" class="status-text">登录中...</div>
<script>
// 更新页面状态文字
function setReqStatus(text) {
const el = document.getElementById('reqStatus');
if (el) el.textContent = text;
}
/**
* 简易 Ajax 封装
* 兼容性优于 fetch,确保在各种 WebView 中稳定运行
*/
function ajax(options) {
const xhr = new XMLHttpRequest();
xhr.open(options.method || 'GET', options.url, true);
xhr.timeout = options.timeout || 10000;
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
try {
const res = JSON.parse(xhr.responseText);
options.success && options.success(res);
} catch (e) {
options.success && options.success(xhr.responseText);
}
} else {
options.error && options.error(new Error('HTTP Status: ' + xhr.status));
}
}
};
xhr.onerror = () => options.error && options.error(new Error('Network Error'));
xhr.send(JSON.stringify(options.data || {}));
}
// 页面加载完成后执行主逻辑
document.addEventListener('DOMContentLoaded', function () {
// 2. 获取 URL 中的 code 和 state 参数
const params = new URLSearchParams(window.location.search);
const code = params.get('code');
const state = params.get('state');
if (code && state) {
setReqStatus('正在验证身份...');
// 3. 调用后端接口换取登录态
ajax({
url: 'https://api.your-domain.com/login/callback', // 【替换】你的真实后端接口
method: 'POST',
data: { code: code, state: state },
success: function (res) {
setReqStatus('登录成功,正在跳转...');
// 4. 关键:将后端返回的数据通过 postMessage 传回小程序
// 小程序端需在 <web-view> 组件上绑定 bindmessage 事件接收
wx.miniProgram.postMessage({ data: res });
// 5. 返回小程序上一页(触发 bindmessage)
// 注意:只有在 navigateBack / redirectTo 等页面销毁/切换操作时,小程序才会收到 message
wx.miniProgram.navigateBack({ delta: 1 });
},
error: function (err) {
setReqStatus('登录失败,请重试');
console.error('Login Error:', err);
}
});
} else {
setReqStatus('参数缺失,无法登录');
}
});
</script>
</body>
</html>
如果觉得对你有帮助,欢迎点赞收藏加关注~
