使用Scribe Java调用Twitter v2 API POST关注请求遇401未授权
问题分析与解决
核心原因
OAuth 1.0a 签名要求请求的**所有参数(包括请求体中的参数)**都要纳入签名计算。你的GET请求成功是因为参数都在URL中,Scribe会自动将其加入签名基串;但POST请求使用application/json格式时,Scribe的addBodyParameter()方法仅适用于application/x-www-form-urlencoded编码的表单参数,不会将JSON请求体的内容纳入签名计算,导致Twitter验证签名时不匹配,返回401未授权。
解决方案
方案1:改用表单编码格式发送请求
Twitter API支持接收application/x-www-form-urlencoded格式的POST参数,这种格式下Scribe能正确处理签名:
public boolean followUser(String accountId, String userId) throws IOException, ExecutionException, InterruptedException { OAuth1AccessToken twitterUserAccessToken = getTwitterUserAccessToken(); request = new OAuthRequest(Verb.POST, BASE_URL + "/users/" + accountId + "/following"); // 改用表单编码的Content-Type request.addHeader("Content-Type", "application/x-www-form-urlencoded"); // 直接添加表单参数,Scribe会自动将其纳入签名 request.addBodyParameter("target_user_id", userId); service.signRequest(twitterUserAccessToken, request); com.github.scribejava.core.model.Response response = service.execute(request); System.out.println(response.getBody()); return response.isSuccessful(); }
方案2:手动处理JSON请求体的签名(适合必须用JSON的场景)
如果必须使用JSON格式,需要手动计算请求体的SHA-1哈希,并将其作为oauth_body_hash参数加入OAuth签名流程:
import java.security.MessageDigest; import java.util.Base64; public boolean followUser(String accountId, String userId) throws IOException, ExecutionException, InterruptedException, NoSuchAlgorithmException { OAuth1AccessToken twitterUserAccessToken = getTwitterUserAccessToken(); // 构造JSON请求体 String jsonBody = "{\"target_user_id\":\"" + userId + "\"}"; request = new OAuthRequest(Verb.POST, BASE_URL + "/users/" + accountId + "/following"); request.addHeader("Content-Type", "application/json"); // 设置原始JSON请求体 request.setPayload(jsonBody); // 计算JSON体的SHA-1哈希并Base64编码 MessageDigest md = MessageDigest.getInstance("SHA-1"); byte[] hashBytes = md.digest(jsonBody.getBytes("UTF-8")); String bodyHash = Base64.getEncoder().encodeToString(hashBytes); // 将body hash加入OAuth参数,让Scribe签名时包含这个值 request.addOAuthParameter("oauth_body_hash", bodyHash); service.signRequest(twitterUserAccessToken, request); com.github.scribejava.core.model.Response response = service.execute(request); System.out.println(response.getBody()); return response.isSuccessful(); }
补充说明
关于你提到的RestAssured也出现问题,大概率是同样的签名逻辑问题——没有将JSON请求体纳入OAuth1签名计算,按照上述方案调整签名逻辑即可解决。
内容的提问来源于stack exchange,提问作者mikegrep
相关产品推荐
相关产品推荐

