You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Capacitor 4中Apple Sign In对接PHP接口出现500错误求助

Capacitor 4中Apple Sign In接口500错误排查

问题现象

  • Apple ID密码弹窗可正常弹出,但调用PHP接口时出现卡顿
  • 其他PHP接口均可正常工作,仅Apple Sign In接口返回500错误

后端返回500错误详情

Failed to load resource: the server responded with a status of 500 ()
ERROR
HttpErrorResponse

error: null
headers: HttpHeaders
lazyInit: function()
arguments: TypeError: 'arguments', 'callee', and 'caller' cannot be accessed in this context.
caller: TypeError: 'arguments', 'callee', and 'caller' cannot be accessed in this context.
length: 0
name: ""
Function Prototype
lazyUpdate: null
normalizedNames: Map {} (0)
HttpHeaders Prototype
message: "Http failure response for https://project.com/project_api/api/sign_in_with_apple: 500 OK"
name: "HttpErrorResponse"
ok: false
status: 500
statusText: "OK"
url: "https://project.com/project_api/api/sign_in_with_apple"

网络请求详情

Summary
URL: https://project.com/project_api/api/sign_in_with_apple
Status: 500
Source: Network
Address: 185.35.33.55:553
Initiator: zone.js:3454

Request
:method: POST
:scheme: https
:authority: project.com
:path: /project_api/api/sign_in_with_apple
Accept: application/json, text/plain, */*
Content-Type: application/x-www-form-urlencoded
Origin: capacitor://localhost
Accept-Encoding: gzip, deflate, br
Host: rovespier.com
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 16_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148
Content-Length: 1074
Accept-Language: el-GR,el;q=0.9
Connection: keep-alive

Response
:status: 500
Content-Type: text/html; charset=UTF-8
Pragma: no-cache
Set-Cookie: PHPSESSID=9ca0e5311a0376ade70056525d9a765f; path=/
X-Content-Type-Options: nosniff, nosniff
Access-Control-Allow-Origin: *
Date: Mon, 30 Jan 2023 09:20:35 GMT
X-XSS-Protection: 1; mode=block
Content-Length: 0
Cache-Control: no-store, no-cache, must-revalidate
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Server: nginx

Request Data
MIME Type: application/x-www-form-urlencoded
login_data[response][identityToken]: eyJraWQiOiJmaDZCczhDIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoiY29tLnJvdmVzcGllci5tb2JpbGUiLCJleHAiOjE2NzUxNTY4MzQsImlhdCI6MTY3NTA3MDQzNCwic3ViIjoiMDAwNjc0LjZiMDUzYzSI6Im5vbmNlIiwiY19oYXNoIjoiZUY2WEdNRDhtYzViT3o0U0JVQnF5QSIsImVtYWlsIjoic2ltb25pb2FubmlkZXNAaG90bWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6InRydWUiLCJhdXRoX3RpbWUiOjE2NzUwNzA0MzQsIm5vbmNlX3N1cHBvcnRlZCI6dHJ1ZX0.bvXBlch8O-H690PsIFNZpJqauCcdozuZggt0I1nGY0PHQ075OSHKYlUXvsY_aOTIEzxyHnrygOAtuk_OkzqmwPplQGfOKZPyGFWBo2xHPChysVX-VmRk81s3Qkza76Ty-tmVymcdTQ-59Eumq0bn7BH_X8mWEdr6NLGP5JOw7kjAGKBVJbHZ9fgUd72KDgFWiHOF-brEkXABL4IgeDqOhAuDP30bAyRFbtDWme5GPcnZU3t13qLDLEDPSK1jrl3X-OU8d2WOALBrnaJhmPw8z7H20Nx61vuio7KoCRPqD_3HT_Plga7dHT43DkQ
login_data[response][authorizationCode]: cf7d3de8d4c7f4277a128d2453423235.0.swxu.KmQXI352ayadDUh3cBGCjA
login_data[response][user]: 000674.6b053c49f8cc45b1bce5623f23343.1836
app_version: 1.0.1
timezone_offset: 120

前端代码

login.ts

async checkApple(login_data) {
    const loader = await this.loadingCtrl.create({
      spinner: 'bubbles',
    });
    loader.present();

    this.userData.appleLogin(login_data).pipe(
      map((data: any) => {
        loader.dismiss();
        if (data.success) {
          this.userData.setSession('HAS_LOGGED_IN', true);
          this.userData.setUserData(data.userData);
          this.router.navigate(["app/tablinks/home"]);
        }
        else {
         
        }
      },
        err => {
          loader.dismiss();
          
        })
    ).subscribe()
  }

  loginApple() {
    let options: SignInWithAppleOptions = {
      clientId: 'com.projectr.mobile',
      redirectURI: 'https://www.project.com/',
      scopes: 'email name',
      state: '12345',
      nonce: 'nonce',
    };
    
    SignInWithApple.authorize(options)
      .then((result: SignInWithAppleResponse) => {
        console.log(result);
        this.checkApple(result);
      })
      .catch(error => {
        // Handle error
      });

    (<any>window).cordova.plugins.SignInWithApple.signin(
      { requestedScopes: [0, 1] },
      (succ) => {
        console.log('APPLE', JSON.stringify(succ));
        this.checkApple(succ);
      },
      (err) => {
        console.error(err)
        console.log(JSON.stringify(err))
      }
    )
  }

user-data.ts

appleLogin(login_data) {
        let device = {};

        if (this.platform.is('cordova')) {
            /*device = {
                platform: this.device.platform,
                uuid: this.device.uuid,
                cordova: this.device.cordova,
                model: this.device.model,
                manufacturer: this.device.manufacturer,
                version: this.device.version,
            };*/
        }

        var timezone_offset = new Date().getTimezoneOffset();
        timezone_offset = timezone_offset == 0 ? 0 : -timezone_offset;

        const url = this.appData.getApiUrl() + 'sign_in_with_apple';
        const data = this.jsonToURLEncoded({
            login_data: login_data,
            device: device,
            app_version: this.appData.APP_VERSION,
            timezone_offset: timezone_offset
        });

        return this.http.post(url, data, { headers: this.options });
    };

后端PHP代码

function sign_in_with_apple()
{
    $request = \Slim\Slim::getInstance()->request();
    $data = json_decode($request->getBody());
    $response['success'] = false;

    $loginData = $_POST['login_data'];
    $identityToken = $loginData['identityToken'];
    $clientUser = $loginData['user'];
    $clientAuthorizationCode = $loginData['authorizationCode'];

    $appleSignInPayload = ASDecoder::getAppleSignInPayload($identityToken);

    $email = $appleSignInPayload->getEmail();
    $user = $appleSignInPayload->getUser();

    $isValid = $appleSignInPayload->verifyUser($clientUser);

    if ($isValid) {
        try
        { 
            $username = strstr($email, '@', true);

            $db = getDB();
            $sql = "SELECT uid,notification_created,username,name,profile_pic,tour,email, api_signature,first_name,last_name, artisticBehaviour,location,bio,friend_count
            FROM users WHERE (username=:username or email=:email) AND status='1' ";
            $stmt = $db->prepare($sql);
            $stmt->bindParam("username", $username, PDO::PARAM_STR);
            $stmt->bindParam("email", $email, PDO::PARAM_STR);
            $stmt->execute();
            $mainCount = $stmt->rowCount();
            $userData = $stmt->fetch(PDO::FETCH_OBJ);

            $created = time();
            if ($mainCount == 0) {

                $api_signature = md5(time() . $username . rand(1, 1000));

                $status = '1';
                if (SMTP_CONNECTION > 0) {
                    $status = '0';
                }
                $email_active_code = $email . time();
                $sql1 = "INSERT INTO users(username,password,email,last_login,email_activation,status, api_signature)VALUES(:username,:password,:email,:created,:email_activation,:status,:api_signature)";
                $stmt1 = $db->prepare($sql1);
                $stmt1->bindParam("username", $username, PDO::PARAM_STR);
                $password = hash('sha256', $signupData['password']);
                $stmt1->bindParam("password", $password, PDO::PARAM_STR);
                $stmt1->bindParam("email", $email, PDO::PARAM_STR);
                $stmt1->bindParam("created", $created);
                $stmt1->bindParam("status", $status);
                $stmt1->bindParam("api_signature", $api_signature);
                $stmt1->bindParam("email_activation", $email_active_code);
                $stmt1->execute();

                $stmt2 = $db->prepare("SELECT uid,notification_created,username,name,first_name,last_name, profile_pic,tour,email,friend_count FROM users WHERE username=:username");
                $stmt2->bindParam("username", $data->username, PDO::PARAM_STR);
                $stmt2->execute();
                $userData = $stmt2->fetchAll(PDO::FETCH_OBJ);
                $uid = $userData[0]->uid;

                $stmt3->bindParam("created", $time, PDO::PARAM_STR);
                $me = 'me';
                $stmt3->bindParam("me", $me, PDO::PARAM_STR);
                $stmt3->execute();

                $userData = internalUserDetails($email);

                $response['success'] = true;
                $response['userData'] = $userData;

            } else {
                $photos_query = $db->query("SELECT id FROM user_uploads WHERE uid_fk='$uid' and group_id_fk='0'");
                $photos_count = $photos_query->rowCount();
                $updates_query = $db->query("SELECT msg_id FROM messages WHERE uid_fk='$uid' and group_id_fk='0'");
                $updates_count = $updates_query->rowCount();
                $time = time();
                $updates_query = $db->query("UPDATE users SET last_login='$time',photos_count='$photos_count',updates_count='$updates_count' WHERE uid='$uid'");

                $userData->profile_pic = profilePic($userData->profile_pic);
                $userData->configurations = configurations();

                $response['success'] = true;
                $response['userData'] = $userData;
            }
        } catch (PDOException $e) {
            echo '{"error":{"text35":' . $e->getMessage() . '}}';
        }
    }
    else {
        $response['error'] = 'Please allow access to your email';
    }

    echo json_encode($response);
}

错误原因分析

  1. 参数路径不匹配:前端传递的login_data嵌套在response对象中,但后端直接取$loginData['identityToken'],导致Undefined索引错误
  2. 未定义变量引用:
    • 注册分支中使用未定义的$signupData['password']
    • 未初始化$stmt3就调用bindParam和execute
    • 登录分支中使用未赋值的$uid
  3. SQL注入风险+变量未定义:登录分支直接用$uid拼接SQL,且$uid仅在注册分支赋值,登录时会报错
  4. 错误处理不完整:仅捕获PDOException,其他致命错误(如Undefined变量)无法被捕获,直接返回500;错误输出的JSON格式易被破坏

解决方法

1. 修正参数获取路径

$loginData = $_POST['login_data'];
$identityToken = $loginData['response']['identityToken'];
$clientUser = $loginData['response']['user'];
$clientAuthorizationCode = $loginData['response']['authorizationCode'];

2. 处理未定义变量

  • 注册分支中替换未定义的密码生成逻辑:
    $password = hash('sha256', uniqid()); // 生成随机密码
    
  • 删除未初始化的$stmt3相关代码,或补充完整其初始化逻辑
  • 登录分支中从$userData获取uid:
    $uid = $userData->uid;
    

3. 修复SQL注入问题

将登录分支的查询改为预处理语句:

// 照片数量查询
$photos_stmt = $db->prepare("SELECT id FROM user_uploads WHERE uid_fk=:uid AND group_id_fk='0'");
$photos_stmt->bindParam(":uid", $uid, PDO::PARAM_INT);
$photos_stmt->execute();
$photos_count = $photos_stmt->rowCount();

// 更新数量查询
$updates_stmt = $db->prepare("SELECT msg_id FROM messages WHERE uid_fk=:uid AND group_id_fk='0'");
$updates_stmt->bindParam(":uid", $uid, PDO::PARAM_INT);
$updates_stmt->execute();
$updates_count = $updates_stmt->rowCount();

// 更新用户登录信息
$update_stmt = $db->prepare("UPDATE users SET last_login=:time, photos_count=:photos_count, updates_count=:updates_count WHERE uid=:uid");
$update_stmt->bindParam(":time", $time, PDO::PARAM_INT);
$update_stmt->bindParam(":photos_count", $photos_count, PDO::PARAM_INT);
$update_stmt->bindParam(":updates_count", $updates_count, PDO::PARAM_INT);
$update_stmt->bindParam(":uid", $uid, PDO::PARAM_INT);
$update_stmt->execute();

4. 完善错误处理

catch (PDOException $e) {
    $response['success'] = false;
    $response['error'] = $e->getMessage();
    echo json_encode($response);
    exit;
}
// 开启PHP错误日志,方便排查致命错误
// php.ini中设置:log_errors = On; error_log = /path/to/error.log

5. 清理前端冗余代码

loginApple函数同时调用了Capacitor和Cordova的Apple登录接口,保留其中一种即可,避免重复请求冲突。

内容的提问来源于stack exchange,提问作者SimonIoa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 23:03:08