如何使用PL/SQL生成FCM Token?Oracle APEX集成Firebase场景
在PL/SQL中生成Firebase Cloud Messaging访问令牌
完全可以实现,核心就是在PL/SQL里构造符合Google OAuth2要求的JWT并完成签名——和你贴的JS代码逻辑本质是一样的,都是用服务账号生成认证令牌。
核心逻辑拆解
Firebase用的访问令牌是标准JWT格式,分三部分:
- Header:固定使用RS256算法,声明令牌类型为JWT
- Payload:必须包含服务账号邮箱、固定受众地址
https://oauth2.googleapis.com/token、令牌签发时间和过期时间(最长1小时) - Signature:用服务账号的RSA私钥对前两部分的Base64URL编码结果做RS256签名
具体实现步骤
1. 准备服务账号信息
从Firebase控制台下载的服务账号JSON文件中,提取两个关键字段:
client_email:服务账号的邮箱地址private_key:RSA私钥,注意把JSON里的换行符替换成PL/SQL可识别的CHR(10)
2. PL/SQL代码实现
直接用Oracle自带的加密和编码包即可完成,无需额外依赖:
DECLARE -- 替换为你的服务账号信息 l_client_email VARCHAR2(200) := 'your-service-account@your-project.iam.gserviceaccount.com'; l_private_key VARCHAR2(4000) := '-----BEGIN PRIVATE KEY-----' || CHR(10) || 'MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC...' || CHR(10) || '-----END PRIVATE KEY-----'; -- JWT相关变量 l_header VARCHAR2(200); l_payload VARCHAR2(4000); l_encoded_header VARCHAR2(4000); l_encoded_payload VARCHAR2(4000); l_signature_input VARCHAR2(4000); l_signature RAW(2000); l_encoded_signature VARCHAR2(4000); l_access_token VARCHAR2(4000); -- 计算Unix时间戳(单位:秒) l_issued_at NUMBER := FLOOR(SYSDATE - TO_DATE('1970-01-01', 'YYYY-MM-DD')) * 86400 + FLOOR(SYS_EXTRACT_UTC(SYSTIMESTAMP) - TIMESTAMP '1970-01-01 00:00:00'); l_expires_at NUMBER := l_issued_at + 3600; -- 令牌有效期设为1小时 BEGIN -- 构造JWT Header l_header := '{"alg":"RS256","typ":"JWT"}'; -- 构造JWT Payload,需指定Firebase Messaging的权限范围 l_payload := '{"iss":"' || l_client_email || '",' || '"scope":"https://www.googleapis.com/auth/firebase.messaging",' || '"aud":"https://oauth2.googleapis.com/token",' || '"iat":' || l_issued_at || ',' || '"exp":' || l_expires_at || '}'; -- 对Header和Payload做Base64URL编码(替换+为-、/为_,去除末尾的=) l_encoded_header := REPLACE(REPLACE(UTL_ENCODE.base64_encode(UTL_I18N.string_to_raw(l_header, 'AL32UTF8')), '+', '-'), '/', '_'); l_encoded_header := RTRIM(l_encoded_header, '='); l_encoded_payload := REPLACE(REPLACE(UTL_ENCODE.base64_encode(UTL_I18N.string_to_raw(l_payload, 'AL32UTF8')), '+', '-'), '/', '_'); l_encoded_payload := RTRIM(l_encoded_payload, '='); -- 拼接签名输入字符串 l_signature_input := l_encoded_header || '.' || l_encoded_payload; -- 用私钥执行RS256签名(Oracle 12c及以上版本支持,低版本需用Java存储过程替代) l_signature := DBMS_CRYPTO.SIGN( src => UTL_I18N.string_to_raw(l_signature_input, 'AL32UTF8'), key => UTL_I18N.string_to_raw(l_private_key, 'AL32UTF8'), typ => DBMS_CRYPTO.SIGNATURE_RSA_SHA256 ); -- 对签名结果做Base64URL编码 l_encoded_signature := REPLACE(REPLACE(UTL_ENCODE.base64_encode(l_signature), '+', '-'), '/', '_'); l_encoded_signature := RTRIM(l_encoded_signature, '='); -- 拼接完整JWT即为访问令牌 l_access_token := l_encoded_header || '.' || l_encoded_payload || '.' || l_encoded_signature; -- 输出令牌,可修改为返回值供APEX调用 DBMS_OUTPUT.PUT_LINE('生成的访问令牌:' || l_access_token); END; /
3. 关键注意事项
- Oracle版本限制:12c及以上版本的
DBMS_CRYPTO才支持SIGNATURE_RSA_SHA256签名算法,低版本建议通过Java存储过程调用JJWT等第三方JWT库实现签名。 - 私钥格式:必须将私钥中的换行符替换为
CHR(10),否则会导致签名失败。 - 权限配置:执行代码的数据库用户需要拥有
DBMS_CRYPTO、UTL_ENCODE、UTL_I18N包的EXECUTE权限。 - 令牌有效期:JWT令牌最长有效期为1小时,需在过期前重新生成。
令牌有效性验证
生成令牌后,可通过Google的令牌校验接口传入access_token参数,确认令牌是否有效。
内容的提问来源于stack exchange,提问作者Pav4
相关产品推荐
相关产品推荐

