微信小程序应用的用户登录授权机制相当复杂,官方给出了下面一张流程图来解释:
下面结合这张图来详细讲述下小程序的登录验证授权机制。
首先,小程序应用实现登录验证的前提是需要在微信开放平台注册一个开发者账号,申请到AppID 和 AppSecret。并申请开启“获取用户信息”的权限。
然后 ,在小程序中使用微信提供的 wx.login 接口获取用户的临时登录凭证 code。代码示例如下:
// 检查用户是否已经授权
wx.getSetting({
success: res => {
if (res.authSetting['scope.userInfo']) {
// 用户已经授权,可以直接调用 wx.getUserProfile 获取用户信息
wx.getUserProfile({
desc: '获取用户信息',
success: res => {
// 获取用户信息
const userInfo = res.userInfo;
// 获取用户登录凭证
wx.login({
success: res => {
// 获取用户登录凭证
const code = res.code;
// 将 code 和 userInfo 发送到后台服务器进行处理
wx.request({
url: 'https://example.com/login.php',
data: {
code: code,
userInfo: userInfo
},
method: 'POST',
success: function (res) {
console.log(res.data);
}
});
}
})
}
})
} else {
// 用户未授权,需要显示授权弹窗
wx.authorize({
scope: 'scope.userInfo',
success: () => {
// 用户完成授权,可以继续获取用户信息
wx.getUserProfile({
desc: '获取用户信息',
success: res => {
// 获取用户信息
const userInfo = res.userInfo;
// 获取用户登录凭证
wx.login({
success: res => {
// 获取用户登录凭证
const code = res.code;
// 将 code 和 userInfo 发送到后台服务器进行处理
wx.request({
url: 'https://example.com/login.php',
data: {
code: code,
userInfo: userInfo
},
method: 'POST',
success: function (res) {
wx.setStorage({
key: "token",
data: res.data.token
})
console.log(res.data);
}
});
}
})
}
})
}
})
}
}
})
接着,小程序前台应用将获取到的 code 发送给后台服务器进行登录验证。后台服务器需要使用微信提供的接口(例如 wx.login 和 wx.getUserInfo)通过 code 获取用户的唯一标识 OpenID 和会话密钥 session_key。然后将用户的 OpenID 和 session_key 存储到后台数据库中,并将一个自定义的 token 返回给小程序前端。php代码示例如下:
$appid = 'your_appid';
$appsecret = 'your_appsecret';
$code = $_POST['code'];
$url = "https://api.weixin.qq.com/sns/jscode2session?appid=$appid&secret=$appsecret&js_code=$code&grant_type=authorization_code"; $result = file_get_contents($url);
$data = json_decode($result, true);
$openid = $data['openid'];
$session_key = $data['session_key'];
// 存储到数据库中
$db = new mysqli('localhost', 'username', 'password', 'database');
$sql = "INSERT INTO `user`(`openid`, `session_key`) VALUES ('{$openid}', '{$session_key}')";
$result = $db->query($sql);
接下来,php根据openid和session_key再生成token,返还给前台小程序应用。
function generateToken($openid, $session_key) {
$header = [
'typ' => 'JWT',
'alg' => 'HS256'
];
$payload = [
'openid' => $openid,
'iat' => time(),
'exp' => time() + 3600
];
$secret = 'my_secret_key';
// 生成 JWT Token
$base64UrlHeader = base64UrlEncode(json_encode($header));
$base64UrlPayload = base64UrlEncode(json_encode($payload));
$signature = hash_hmac('sha256', $base64UrlHeader . '.' . $base64UrlPayload, $secret, true);
$base64UrlSignature = base64UrlEncode($signature);
$token = $base64UrlHeader . '.' . $base64UrlPayload . '.' . $base64UrlSignature;
return $token;
}
function base64UrlEncode($data) {
$urlSafeData = strtr(base64_encode($data), '+/', '-_');
return rtrim($urlSafeData, '=');
}
$token = generateToken($openid, $session_key);
echo json_encode(['token' => $token]);
前台小程序应用接收到后台返回的token后,将其存储在小程序的本地缓存中,以后每次用户打开小程序时,我们可以从本地缓存中读取token并发送给后台进行验证。
如下示例:
// 获取本地缓存中的token
const token = wx.getStorageSync('token');
// 如果本地缓存中存在token,则向后台发送请求获取用户信息
if (token) {
wx.request({
url: 'https://example.com/userinfo',
header: {
'Authorization': `Bearer ${token}`
},
success: res => {
// 处理响应结果
console.log(res.data);
}
});
} else {
// 如果本地缓存中不存在token,则说明用户还未登录,需要重新登录获取token
wx.login({
success: res => {
// 向后台发送code,获取token
wx.request({
url: 'https://example.com/login',
data: {
code: res.code
},
success: res => {
// 将后台返回的token存储到本地缓存中
wx.setStorageSync('token', res.data.token);
// 向后台发送请求获取用户信息
wx.request({
url: 'https://example.com/userinfo',
header: {
'Authorization': `Bearer ${res.data.token}`
},
success: res => {
// 处理响应结果
console.log(res.data);
}
});
}
});
}
});
}
php后台获取到前台发来的请求及token后,就直接根据token来验证用户,如下代码所示:
<?php
use \Firebase\JWT\JWT;
// JWT secret key
$secret_key = "your_secret_key";
// Token received from front-end $secret_key和$token如存在数据库中,则从数据库中获取
$token = "token_received_from_front_end";
try {
// Decode token and get the payload
$payload = JWT::decode($token, $secret_key, array('HS256'));
// You can now access the token payload data
$user_id = $payload->user_id;
$username = $payload->username;
// ...
// Token is valid
echo "Token is valid!";
} catch (Exception $e) {
// Token is invalid
echo "Token is invalid: " . $e->getMessage();
}
?>
如此,则完成整个登录验证机制的实现。