php案例 用构建一套安全授权机制 + 核心逻辑逆向防护=✅ 授权有效!
需要运行的命令
composer require web-token/jwt-framework
composer global require phalcon/zephir
composer global config bin-dir --absolute
wsl2中:
webman@WIN-3RSG1B1SCQ2:/mnt/d/phpstudy_pro/WWW/static-php-cli-main$ export PATH="$PATH:/home/webman/.config/composer/vendor/bin"
webman@WIN-3RSG1B1SCQ2:/mnt/d/phpstudy_pro/WWW/static-php-cli-main$ zephir --version
_____ __ _
/__ / ___ ____ / /_ (_)____
/ / / _ \/ __ \/ __ \/ / ___/
/ /__/ __/ /_/ / / / / / /
/____/\___/ .___/_/ /_/_/_/
/_/
Zephir 0.19.0 by the Phalcon Team
Usage:
command [options] [arguments]
Options:
--dumpversion Print the version of the compiler and don't do anything else (also works with a single hyphen)
-h, --help Print this help message
--no-ansi Disable ANSI output
-v, --verbose Displays more detail in error messages from exceptions generated by commands (can also disable with -V)
--vernum Print the version of the compiler as integer
--version Print compiler version information and quit
Available commands:
api Generates a HTML API based on the classes exposed in the extension
build Generates/Compiles/Installs a Zephir extension
clean Cleans any object files created by the extension
compile Compile a Zephir extension
fullclean Cleans any object files created by the extension (including files generated by phpize)
generate Generates C code from the Zephir code without compiling it
help Display help for a command
init Initializes a Zephir extension
install Installs the extension in the extension directory (may require root password)
stubs Generates stubs that can be used in a PHP IDE
webman@WIN-3RSG1B1SCQ2:/mnt/d/phpstudy_pro/WWW/static-php-cli-main$ zephir build
Extension namespace cannot be loaded
webman@WIN-3RSG1B1SCQ2:/mnt/d/phpstudy_pro/WWW/static-php-cli-main$ sudo apt install golang-go
webman@WIN-3RSG1B1SCQ2:/mnt/d/phpstudy_pro/WWW/static-php-cli-main$ GOOS=windows GOARCH=amd64 go build -o license_verify.exe verify.go
powershell中:
payload.json
{"exp":1992012800,"domain":"localhost:85"}
$env:CLIENT_DOMAIN = "localhost:85"
PS D:\phpstudy_pro\WWW\static-php-cli-main> license_verify.exe .\payload.json
true
guard.zep
namespace LicenseGuard;
class Guard {
/**
* 核心验证:域名+过期时间
* @param array payload 授权数据
* @return bool 是否有效
*/
public static function verify(array payload) -> bool {
// 验证过期时间
if payload["exp"] < time() {
return false;
}
// 验证域名(获取当前客户端域名)
string currentDomain = _SERVER["HTTP_HOST"];
if payload["domain"] != currentDomain {
return false;
}
return true;
}
}
步骤 1:生成非对称密钥对(RSA)
用web-token/jwt-framework生成私钥(加签用)和公钥(验签用)
1.php
<?php
require __DIR__ . '/vendor/autoload.php';
use Jose\Component\KeyManagement\JWKFactory;
// -------------------------- 配置(替换成你的实际路径) --------------------------
$opensslCnf = "D:/EServer-data/childApp/php/php-8.4/extras/ssl/openssl.cnf";
// --------------------------------------------------------------------------------
try {
// 1. 原生OpenSSL生成RSA密钥(显式传递config参数)
$config = [
'private_key_bits' => 2048,
'private_key_type' => OPENSSL_KEYTYPE_RSA,
'config' => $opensslCnf // 关键:手动指定config文件
];
$privateKeyResource = openssl_pkey_new($config);
if (!$privateKeyResource) {
throw new \RuntimeException("原生密钥生成失败:" . openssl_error_string());
}
// 2. 导出私钥PEM
openssl_pkey_export($privateKeyResource, $privateKeyPem, null, $config);
if (!$privateKeyPem) {
throw new \RuntimeException("私钥导出失败:" . openssl_error_string());
}
// 3. 导出公钥PEM
$publicKeyDetails = openssl_pkey_get_details($privateKeyResource);
if (!$publicKeyDetails) {
throw new \RuntimeException("公钥获取失败:" . openssl_error_string());
}
$publicKeyPem = $publicKeyDetails['key'];
// 4. 转换成框架的JWK对象(保留alg和use属性)
$privateJwk = JWKFactory::createFromKey($privateKeyPem, null, [
'alg' => 'RS256',
'use' => 'sig'
]);
$publicJwk = $privateJwk->toPublic();
// 5. 保存密钥文件
$privatePemPath = __DIR__ . "/private_key.pem";
$publicPemPath = __DIR__ . "/public_key.pem";
$privateJwkPath = __DIR__ . "/private_key.jwk";
$publicJwkPath = __DIR__ . "/public_key.jwk";
file_put_contents($privatePemPath, $privateKeyPem);
file_put_contents($publicPemPath, $publicKeyPem);
file_put_contents($privateJwkPath, json_encode($privateJwk));
file_put_contents($publicJwkPath, json_encode($publicJwk));
echo "✅ 密钥生成成功!\n";
echo "私钥(PEM):{$privatePemPath}\n";
echo "公钥(PEM):{$publicPemPath}\n";
echo "私钥(JWK):{$privateJwkPath}\n";
echo "公钥(JWK):{$publicJwkPath}\n";
} catch (\Exception $e) {
echo "❌ 错误:{$e->getMessage()}\n";
while ($err = openssl_error_string()) {
echo " {$err}\n";
}
}
步骤 2:私钥加签授权文件
用私钥对授权数据(含过期时间、域名等)签名,生成不可篡改的 JWS:
2.php
<?php
require_once "vendor/autoload.php";
use Jose\Component\Core\JWK;
use Jose\Component\Signature\JWSBuilder;
use Jose\Component\Signature\Serializer\CompactSerializer;
use Jose\Component\Core\AlgorithmManager;
use Jose\Component\Signature\Algorithm\RS256;
// 授权数据结构
$payload = json_encode([
'exp' => time() + 3600*24*30,
'domain' => 'localhost:85',
'license_id' => 'LIC-2024-001'
]);
$privateKeyPath = 'private_key.jwk';
if (!file_exists($privateKeyPath)) {
die("错误:私钥文件不存在,请先运行 generate_rsa_keys.php!");
}
// 构建JWS签名
$algorithmManager = new AlgorithmManager([new RS256()]);
$jwsBuilder = new JWSBuilder($algorithmManager);
$privateKeyJson = file_get_contents($privateKeyPath);
$privateKey = JWK::createFromJson($privateKeyJson);
$jws = $jwsBuilder->create()
->withPayload($payload)
->addSignature($privateKey, ['alg' => 'RS256'])
->build();
// 序列化JWS
$serializer = new CompactSerializer();
$signedLicense = $serializer->serialize($jws, 0);
// 保存授权文件
file_put_contents('license.jws', $signedLicense);
echo "授权文件生成成功!\n";
echo "文件:license.jws\n";
?>
步骤 3:客户端验签(核心逻辑逆向保护)
客户端需验证签名有效性 + 授权项(过期、域名),核心逻辑用不易逆向的语言实现:
3.php
PHP 层:调用编译后的核心逻辑
PHP 仅负责调用二进制 / 扩展,不直接处理核心验证:
<?php
require_once "vendor/autoload.php";
// 补充缺失的类引入
use Jose\Component\Core\AlgorithmManager;
use Jose\Component\Signature\Algorithm\RS256;
use Jose\Component\Signature\JWSVerifier;
use Jose\Component\Signature\Serializer\CompactSerializer;
use Jose\Component\KeyManagement\JWKFactory;
try {
// 1. 读取PEM格式公钥(修正:用createFromKey处理PEM)
$publicKeyPem = file_get_contents('public_key.pem');
if (!$publicKeyPem) throw new \RuntimeException("公钥文件不存在或无法读取");
$publicKey = JWKFactory::createFromKey($publicKeyPem); // 正确方法
// 2. 读取授权文件
$signedLicense = file_get_contents('license.jws');
if (!$signedLicense) throw new \RuntimeException("授权文件不存在");
// 3. 反序列化JWS
$serializer = new CompactSerializer();
$jws = $serializer->unserialize($signedLicense);
// 4. 验证签名(补充AlgorithmManager和RS256的引入)
$algorithmManager = new AlgorithmManager([new RS256()]);
$verifier = new JWSVerifier($algorithmManager);
$isSignatureValid = $verifier->verifyWithKey($jws, $publicKey, 0);
if (!$isSignatureValid) die("❌ 签名无效!");
// 5. 后续授权验证(保持你的业务逻辑)
$payload = json_decode($jws->getPayload(), true);
$currentDomain = $_SERVER["HTTP_HOST"] ?? '';
// 示例:替换为你的核心验证逻辑
$isAuthorized = (isset($payload['domain']) && $payload['domain'] === $currentDomain);
if ($isAuthorized) echo "✅ 授权有效!";
else die("❌ 授权无效!");
} catch (\Exception $e) {
die("❌ 错误:" . $e->getMessage());
}
3.2 核心逻辑:用不易逆向的语言实现
将域名验证、过期时间检查等核心逻辑用C 扩展(Zephir)或Go实现:
示例:Zephir 写 C 扩展(最贴合 PHP)
Zephir 是高级语言,编译为 C 扩展,性能高且不易逆向:
verify.go
package main
import (
"encoding/json"
"fmt"
"os"
"time"
)
type Payload struct {
Exp int64 `json:"exp"`
Domain string `json:"domain"`
}
func main() {
// Step A: Check if a file path is provided (required!)
if len(os.Args) < 2 {
fmt.Fprintln(os.Stderr, "Usage: license_verify.exe <payload.json>")
fmt.Println("false")
return
}
filePath := os.Args[1]
// Step B: Read the JSON file
fileContent, err := os.ReadFile(filePath)
if err != nil {
fmt.Fprintf(os.Stderr, "Error reading file: %v\n", err)
fmt.Println("false")
return
}
// Step C: Parse JSON from file content
var payload Payload
err = json.Unmarshal(fileContent, &payload)
if err != nil {
fmt.Fprintf(os.Stderr, "JSON Parse Error: %v\n", err)
fmt.Println("false")
return
}
// Step D: Validate expiration and domain
now := time.Now().Unix()
if payload.Exp < now {
fmt.Fprintln(os.Stderr, "Error: Payload expired")
fmt.Println("false")
return
}
clientDomain := os.Getenv("CLIENT_DOMAIN")
if clientDomain == "" {
fmt.Fprintln(os.Stderr, "Error: CLIENT_DOMAIN env var not set")
fmt.Println("false")
return
}
if payload.Domain != clientDomain {
fmt.Fprintf(os.Stderr, "Domain mismatch: %s vs %s\n", payload.Domain, clientDomain)
fmt.Println("false")
return
}
// Step E: All checks passed
fmt.Println("true")
}
效果
✅ 授权有效!
PS D:\EServer-data\www\static-php-cli-main> license_verify.exe .\payload.json
true

浙公网安备 33010602011771号