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

如何用Apex实现Xero与Salesforce免重复鉴权?解决令牌过期问题

解决Xero与Salesforce Apex鉴权无需重复授权的方案

针对你遇到的Xero刷新令牌60天过期、需要重复鉴权的问题,有两种可靠的解决方案,可根据集成场景选择:

方案1:使用Xero私有应用(Private App)实现永久鉴权

私有应用是Xero面向内部集成场景提供的鉴权方式,无需OAuth2流程,直接通过RSA证书和API密钥鉴权,只要证书不过期即可永久使用,完全规避刷新令牌过期问题。

实施步骤:

  1. 在Xero开发者后台创建私有应用:
    • 登录Xero Developer Portal,新建Private App
    • 用OpenSSL生成RSA密钥对,将公钥上传至Xero,本地留存私钥文件
    • 记录Xero分配的Consumer Key
  2. 在Salesforce中安全存储敏感信息:
    • 将私钥和Consumer Key存入自定义设置或Named Credential的加密字段,禁止硬编码
  3. Apex实现签名逻辑调用Xero API:
public class XeroPrivateAppAuth {
    // 从自定义设置读取密钥配置
    private static final String CONSUMER_KEY = Xero_Settings__c.getInstance().Consumer_Key__c;
    private static final String PRIVATE_KEY = Xero_Settings__c.getInstance().Private_Key__c;

    public static HttpResponse callXeroApi(String endpoint, String method) {
        HttpRequest req = new HttpRequest();
        req.setEndpoint('https://api.xero.com' + endpoint);
        req.setMethod(method);
        // 生成并设置鉴权头部
        req.setHeader('Authorization', generateAuthHeader(req, method));
        req.setHeader('Accept', 'application/json');
        
        Http http = new Http();
        return http.send(req);
    }

    private static String generateAuthHeader(HttpRequest req, String method) {
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
        String nonce = String.valueOf(Crypto.getRandomLong());
        // 构造签名基础串
        String baseString = method + '&' + EncodingUtil.urlEncode(req.getEndpoint(), 'UTF-8') + '&' + 
                            EncodingUtil.urlEncode('oauth_consumer_key=' + CONSUMER_KEY + '&oauth_nonce=' + nonce + 
                            '&oauth_signature_method=RSA-SHA1&oauth_timestamp=' + timestamp + '&oauth_version=1.0', 'UTF-8');
        
        // 用私钥签名
        Blob privateKeyBlob = Blob.valueOf(PRIVATE_KEY);
        Blob signatureBlob = Crypto.sign('RSA-SHA1', Blob.valueOf(baseString), privateKeyBlob);
        String signature = EncodingUtil.base64Encode(signatureBlob);
        
        // 组装Authorization头部
        return 'OAuth oauth_consumer_key="' + CONSUMER_KEY + '", oauth_nonce="' + nonce + '", oauth_signature="' + 
               EncodingUtil.urlEncode(signature, 'UTF-8') + '", oauth_signature_method="RSA-SHA1", oauth_timestamp="' + 
               timestamp + '", oauth_version="1.0"';
    }
}

方案2:升级为Xero Partner应用获取永久刷新令牌

如果集成需要面向多Xero租户(如ISV场景),可申请加入Xero Partner Program,通过Partner应用获取永久刷新令牌,该令牌不会过期,只需定期自动用它刷新访问令牌,无需用户重新授权。

实施步骤:

  1. 申请Xero Partner Program:
    • 提交Partner应用申请,通过Xero审核后即可获得永久刷新令牌权限
  2. 在Salesforce中存储永久刷新令牌:
    • 将刷新令牌、客户端ID、客户端密钥存入安全位置(如自定义对象加密字段、Named Credential)
  3. Apex实现自动令牌刷新逻辑:
public class XeroPartnerAuth {
    private static final String CLIENT_ID = Xero_Partner_Settings__c.getInstance().Client_ID__c;
    private static final String CLIENT_SECRET = Xero_Partner_Settings__c.getInstance().Client_Secret__c;
    private static final String REFRESH_TOKEN = Xero_Partner_Settings__c.getInstance().Refresh_Token__c;
    private static String accessToken;
    private static Long tokenExpiryTime;

    public static HttpResponse callXeroApi(String endpoint, String method) {
        // 检查访问令牌是否过期,过期则自动刷新
        if (accessToken == null || System.currentTimeMillis() >= tokenExpiryTime) {
            refreshAccessToken();
        }

        HttpRequest req = new HttpRequest();
        req.setEndpoint('https://api.xero.com' + endpoint);
        req.setMethod(method);
        req.setHeader('Authorization', 'Bearer ' + accessToken);
        req.setHeader('Accept', 'application/json');
        
        Http http = new Http();
        return http.send(req);
    }

    private static void refreshAccessToken() {
        HttpRequest req = new HttpRequest();
        req.setEndpoint('https://identity.xero.com/connect/token');
        req.setMethod('POST');
        req.setHeader('Content-Type', 'application/x-www-form-urlencoded');
        
        String body = 'grant_type=refresh_token&client_id=' + CLIENT_ID + 
                      '&client_secret=' + CLIENT_SECRET + '&refresh_token=' + REFRESH_TOKEN;
        req.setBody(body);
        
        Http http = new Http();
        HttpResponse res = http.send(req);
        
        if (res.getStatusCode() == 200) {
            Map<String, Object> tokenResponse = (Map<String, Object>)JSON.deserializeUntyped(res.getBody());
            accessToken = (String)tokenResponse.get('access_token');
            // 计算令牌过期时间
            tokenExpiryTime = System.currentTimeMillis() + ((Integer)tokenResponse.get('expires_in') * 1000);
        } else {
            throw new CalloutException('Xero令牌刷新失败: ' + res.getBody());
        }
    }
}

注意事项:

  • 所有敏感信息(私钥、密钥、令牌)必须按Salesforce安全规范存储,禁止明文或硬编码
  • 私有应用的RSA证书需定期更新(建议每年一次),避免证书过期导致集成中断
  • Partner应用需遵守Xero的Partner规则,确保集成符合平台条款

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 17:55:10