微信小程式結合php後臺實現登入授權機制詳解

2023-03-05 06:02:43

微信小程式應用的使用者登入授權機制相當複雜,官方給出了下面一張流程圖來解釋:

下面結合這張圖來詳細講述下小程式的登入驗證授權機制。

首先,小程式應用實現登入驗證的前提是需要在微信開放平臺註冊一個開發者賬號,申請到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();
}
?>

如此,則完成整個登入驗證機制的實現。