php手搓APNs苹果消息推送
2023-04-19
做App总少不了消息推送,公司的App为了开发敏捷方便都直接上的三方推送诸如某盟、某推、某光,安卓和苹果都一套代码,高效好维护,只要按时给推送vip付费就好了。最近心血来潮想手搓下苹果的消息推送,踩踩以前没踩过的坑。
看了下苹果官方文档感觉并不困难,文档详情戳这里
Sending Notification Requests to APNs | Apple Developer Documentation恰逢chatGPT大火赶下时髦,直接问了ai代码怎么写

ai回答得有模有样,我里面换上证书和相关参数尝试,然后发现死活不行,苹果那边没反应。
一查文档发现其实苹果这边服务器推送大致分了两种模式:
一种是基于ssl证书模式推送,另一种是根据p8证书生产jwt token授权来推送。
显然代码给的是第一种,然后看了苹果的官方文档2021年就已经废弃了第一种模式并且强行要求使用http2请求,看来chatGPT3.5的投喂数据截止于2021年所言非虚。

这里我踩了两个大坑:
第一个是苹果证书ssl模式下要pem证书,我拿到的是p12证书,只能先用OpenSSL转成pem公钥和私钥,后来发现要公钥和私钥放在一起合成一个pem第一张图的代码才能用。然后转为jwt授权模式后要的是p8证书,之前的pem证书生成的token死活过不了。
第二个是我这边的服务器环境curl并不支持http2,一直报错Unexpected HTTP/1.x request: POST /3/device/XXXX。后来我只好重新编译curl和php相关的扩展直接升级到了curl 8.1.0。参考连接
怎么让php curl发http2请求
怎么让curl支持http2
下面是推送和jwt生成demo
//jwt token模式下的苹果推送$device_token = 'your_device_token'; //苹果给的设备推送token$message = 'Hello, world!'; //要推送的消息// Create the notification payload$payload = array( 'aps' => array( 'alert' => $message, 'sound' => 'default' ));$payload_json = json_encode($payload);//正式环境推送地址$url = 'https://api.push.apple.com:443/3/device/' . $device_token;//沙盒推送地址//$url = "https://api.sandbox.push.apple.com:443/3/device/".$device_token;$headers = array( 'Authorization: Bearer 你生成的jwt_token', 'Content-Type: application/json');$ch = curl_init($url);curl_setopt($ch, CURLOPT_POSTFIELDS, $payload_json);curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0); //这里很关键,以http2发送请求,如果你的curl不支持http2则需要看前面的步骤curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);$response = curl_exec($ch);$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);curl_close($ch);// Check the responseif ($http_code == 200) { echo 'Notification sent successfully.';} else { echo 'Notification failed with error code: ' . $http_code;}//jwt token生成代码$key_id = 'your_key_id';$team_id = 'your_team_id';$pkey_file = 'AuthKey_your_key_id.p8'; //p8证书文件,我在这里填的绝对地址// token过期时间$expires = time() + 3600;// Generate the JWT header$header = array( 'alg' => 'ES256', 'kid' => $key_id);$jwt_header = base64_encode(json_encode($header));//JWT payload$payload = array( 'iss' => $team_id, 'iat' => time(), 'exp' => $expires, 'aud' => 'https://appleid.apple.com', 'sub' => 'your_bundle_id' //这里填你的App包名);$jwt_payload = base64_encode(json_encode($payload));//载入证书文件$pkey_contents = file_get_contents($pkey_file);$pkey = openssl_get_privatekey($pkey_contents);//JWT 签名$signature = '';openssl_sign($jwt_header . '.' . $jwt_payload, $signature, $pkey, 'sha256');$jwt_signature = base64_encode($signature);// 生成JWT$jwt = $jwt_header . '.' . $jwt_payload . '.' . $jwt_signature;echo $jwt;
PHP在负载均衡条件下获取客户端ip
2023-04-04
业务需要得写一个ip白名单来限制接口访问,laravel下习惯直接$request->ip()或者$request->getClientIp()来获取。测试的时候发现ip不对,原来我又忘了服务器上了负载均衡,所有请求都是负载均衡转发服务的ip。想了下应该从请求头里面拿ip了,问了下chatgt,AI回答很棒:
