【问题标题】:Error code 401 trying to reach Twitter endpoint using OkHttp on Android错误代码 401 尝试在 Android 上使用 OkHttp 访问 Twitter 端点
【发布时间】:2019-02-12 20:45:26
【问题描述】:

我正在编写一个测试程序来学习使用 OkHttp 和 Scribe。我已经设法使用 Scribe 来检索我的令牌和令牌秘密。我当然已经有了消费者密钥和消费者秘密。现在我正在尝试访问 Twitter 端点。我收到错误代码 401,我知道这意味着我没有正确验证我的请求。我查看了许多处理错误代码 401 的网站,但其中大多数都是关于检索我已经完成的令牌。我浏览了几个网站上的代码,看不出我在做什么不同。我也上过这个网站oauth signature checker

一切正常。所以我被困住了。任何帮助将不胜感激。

package com.clarkgarrett.testokhttp;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Base64;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.SecureRandom;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

import static com.clarkgarrett.testokhttp.Utility.CONSUMER_API_KEY;
import static com.clarkgarrett.testokhttp.Utility.CONSUMER_API_SECRET;
import static com.clarkgarrett.testokhttp.Utility.accessTokenSecretString;
import static com.clarkgarrett.testokhttp.Utility.accessTokenString;

public class NextActivity2 extends AppCompatActivity {

    private OkHttpClient client = new OkHttpClient();
    //Picasso picasso = new Picasso.Builder(this).downloader(new OkHttp3Downloader(client)).build();
    private String  mCount = "25";
    private long mTimeStamp;
    private String mNonce, mSignatureString, mUrlString;
    private static final String BASE_URL_STRING = "https://api.twitter.com/1.1/statuses/home_timeline.json";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_next);

        OkHttpClient client = new OkHttpClient();

        HttpUrl.Builder urlBuilder = HttpUrl.parse(BASE_URL_STRING).newBuilder();
        urlBuilder.addQueryParameter("count", mCount);
        urlBuilder.addQueryParameter("since_id", "1");
        mUrlString = urlBuilder.build().toString();

        mNonce = nonceGenerator();
        mTimeStamp = System.currentTimeMillis()/1000L;
        String signatureBaseString = createSignatureBaseString();

        mSignatureString = generateSignature(signatureBaseString, CONSUMER_API_SECRET, accessTokenSecretString);

        String header = createHeader();

        Request request = new Request.Builder()
                .header("Authorization" , header)
                .url(mUrlString)
                .build();

        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                e.printStackTrace();
            }

            @Override
            public void onResponse(Call call, final Response response) throws IOException {
                if (!response.isSuccessful()) {
                    //Haven't even tried to do anything here yet.
                    throw new IOException("Unexpected code " + response +"  network Responce= " + response.networkResponse());
                }else{
                }
            }
        });

    }

    private String  nonceGenerator (){
        SecureRandom random = new SecureRandom();
        String s;
        s = java.util.UUID.randomUUID().toString().replace("-","");
        return s.substring(0,32);
    }

    private String encode(String s){
        String es = "";
        try {
            es = URLEncoder.encode(s, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return es;
    }

    private String createSignatureBaseString(){
        StringBuilder builder = new StringBuilder();

        builder.append("&count=");
        builder.append(mCount);
        builder.append("&oauth_consumer_key=");
        builder.append(encode(CONSUMER_API_KEY));
        builder.append("&oauth_nonce=");
        builder.append(mNonce);
        builder.append("&oauth_signature_method=HMAC-SHA1&oauth_timestamp=");
        builder.append(mTimeStamp);
        builder.append("&oauth_token=");
        builder.append(encode(accessTokenString));
        builder.append("&oauth_version=");
        builder.append(encode("1.0"));
        builder.append("&since_id=1");

        String intermediateString = encode(builder.toString());

        builder = new StringBuilder();
        builder.append("GET&");
        builder.append(encode(BASE_URL_STRING));
        builder.append("&");
        builder.append(intermediateString);

        return builder.toString();
    }

    private String createHeader(){
        StringBuilder builder = new StringBuilder();
        builder.append("OAuth oauth_consumer_key=\"");
        builder.append(encode(CONSUMER_API_KEY));
        builder.append("\", oauth_nonce=\"");
        builder.append(mNonce);
        builder.append("\", oauth_signature=\"");
        builder.append(encode(mSignatureString));
        builder.append("\", oauth_signature_method=\"HMAC-SHA1\", ");
        builder.append("oauth_timestamp=\"");
        builder.append(mTimeStamp);
        builder.append("\", oauth_token=\"");
        builder.append(encode(accessTokenString));
        builder.append("\", oauth_version=\"");
        builder.append(encode("1.0"));
        builder.append("\"");

        return builder.toString();
    }

    private String generateSignature(String signatueBaseStr, String oAuthConsumerSecret, String oAuthTokenSecret) {
        byte[] byteHMAC = null;
        try {
            Mac mac = Mac.getInstance("HmacSHA1");
            SecretKeySpec spec;
            if (null == oAuthTokenSecret) {
                String signingKey = encode(oAuthConsumerSecret) + '&';
                spec = new SecretKeySpec(signingKey.getBytes(), "HmacSHA1");
            } else {
                String signingKey = encode(oAuthConsumerSecret) + '&' + encode(oAuthTokenSecret);
                spec = new SecretKeySpec(signingKey.getBytes(), "HmacSHA1");
            }
            mac.init(spec);
            byteHMAC = mac.doFinal(signatueBaseStr.getBytes("UTF-8"));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Base64.encodeToString(byteHMAC,Base64.NO_WRAP);
    }
}

logcat 输出:

    05-08 16:14:34.694: I/art(14938): Late-enabling -Xcheck:jni
05-08 16:14:34.755: W/System(14938): ClassLoader referenced unknown path: /data/app/com.clarkgarrett.testokhttp-2/lib/arm
05-08 16:14:34.791: I/## My Info ##(14938): OauthActivity accessString= 2651797417-agpNu35sZbs5pI5yjHRibMfCezVjpuVuzzWxhAV
05-08 16:14:34.874: I/WebViewFactory(14938): Loading com.google.android.webview version 50.0.2661.86 (code 266108600)
05-08 16:14:34.947: I/cr_LibraryLoader(14938): Time to load native libraries: 3 ms (timestamps 648-651)
05-08 16:14:34.948: I/cr_LibraryLoader(14938): Expected native library version number "50.0.2661.86", actual native library version number "50.0.2661.86"
05-08 16:14:34.974: V/WebViewChromiumFactoryProvider(14938): Binding Chromium to main looper Looper (main, tid 1) {9910e04}
05-08 16:14:34.974: I/cr_LibraryLoader(14938): Expected native library version number "50.0.2661.86", actual native library version number "50.0.2661.86"
05-08 16:14:34.975: I/chromium(14938): [INFO:library_loader_hooks.cc(143)] Chromium logging enabled: level = 0, default verbosity = 0
05-08 16:14:34.990: I/cr_BrowserStartup(14938): Initializing chromium process, singleProcess=true
05-08 16:14:34.999: E/ApkAssets(14938): Error while loading asset assets/natives_blob_64.bin: java.io.FileNotFoundException: assets/natives_blob_64.bin
05-08 16:14:35.000: E/ApkAssets(14938): Error while loading asset assets/snapshot_blob_64.bin: java.io.FileNotFoundException: assets/snapshot_blob_64.bin
05-08 16:14:35.014: I/Adreno-EGL(14938): <qeglDrvAPI_eglInitialize:379>: QUALCOMM Build: 10/21/15, 369a2ea, I96aee987eb
05-08 16:14:35.068: W/cr_media(14938): Requires BLUETOOTH permission
05-08 16:14:35.107: D/cr_Ime(14938): [InputMethodManagerWrapper.java:30] Constructor
05-08 16:14:35.117: W/cr_AwContents(14938): onDetachedFromWindow called when already detached. Ignoring
05-08 16:14:35.119: D/cr_Ime(14938): [InputMethodManagerWrapper.java:59] isActive: false
05-08 16:14:35.175: D/OpenGLRenderer(14938): Use EGL_SWAP_BEHAVIOR_PRESERVED: true
05-08 16:14:35.232: I/OpenGLRenderer(14938): Initialized EGL, version 1.4
05-08 16:14:35.472: I/okhttp3.OkHttpClient(14938): Callback failure for call to https://api.twitter.com/...
**05-08 16:14:35.472: I/okhttp3.OkHttpClient(14938): java.io.IOException: Unexpected code Response{protocol=h2, code=401, message=, url=https://api.twitter.com/1.1/statuses/home_timeline.json?count=25&since_id=1}  network Responce= Response{protocol=h2, code=401, message=, url=https://api.twitter.com/1.1/statuses/home_timeline.json?count=25&since_id=1}**
05-08 16:14:35.472: I/okhttp3.OkHttpClient(14938):  at com.clarkgarrett.testokhttp.NextActivity2$1.onResponse(NextActivity2.java:71)
05-08 16:14:35.472: I/okhttp3.OkHttpClient(14938):  at okhttp3.RealCall$AsyncCall.execute(RealCall.java:133)
05-08 16:14:35.472: I/okhttp3.OkHttpClient(14938):  at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
05-08 16:14:35.472: I/okhttp3.OkHttpClient(14938):  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
05-08 16:14:35.472: I/okhttp3.OkHttpClient(14938):  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
05-08 16:14:35.472: I/okhttp3.OkHttpClient(14938):  at java.lang.Thread.run(Thread.java:818)
05-08 16:14:35.502: I/cr_Ime(14938): ImeThread is not enabled.
05-08 16:14:35.568: E/libEGL(14938): validate_display:255 error 3008 (EGL_BAD_DISPLAY)
05-08 16:14:36.133: W/cr_BindingManager(14938): Cannot call determinedVisibility() - never saw a connection for the pid: 14938
05-08 16:14:36.135: D/cr_Ime(14938): [InputMethodManagerWrapper.java:59] isActive: false
05-08 16:14:36.155: I/chromium(14938): [INFO:CONSOLE(7)] "The value "device-width;" for key "width" is invalid, and has been ignored.", source: https://api.twitter.com/oauth/authorize?oauth_token=zLafmwAAAAAAu_IxAAABVJKmrbA (7)
05-08 16:14:36.155: I/chromium(14938): [INFO:CONSOLE(7)] "The value "1.0;" for key "initial-scale" was truncated to its numeric prefix.", source: https://api.twitter.com/oauth/authorize?oauth_token=zLafmwAAAAAAu_IxAAABVJKmrbA (7)
05-08 16:14:36.155: I/chromium(14938): [INFO:CONSOLE(7)] "The value "1.0;" for key "maximum-scale" was truncated to its numeric prefix.", source: https://api.twitter.com/oauth/authorize?oauth_token=zLafmwAAAAAAu_IxAAABVJKmrbA (7)
05-08 16:14:36.155: I/chromium(14938): [INFO:CONSOLE(7)] "Error parsing a meta element's content: ';' is not a valid key-value pair separator. Please use ',' instead.", source: https://api.twitter.com/oauth/authorize?oauth_token=zLafmwAAAAAAu_IxAAABVJKmrbA (7)
05-08 16:14:42.132: I/art(14938): Debugger is no longer active

【问题讨论】:

    标签: android twitter oauth okhttp


    【解决方案1】:

    我发现了两个错误。第一行

    builder.append("&count=");
    

    在 createSignatureBaseString() 方法中不应该有 &。它应该是

     builder.append("count="); 
    

    这是一个剪切和粘贴错误。 其次,方法 generateSignature() 需要返回一个 UTF-8 字符串。返回语句

    return Base64.encodeToString(byteHMAC,Base64.NO_WRAP);
    

    做了如下修改:

    String s ="";
    try {
        s = new String(Base64.encode(byteHMAC, Base64.NO_WRAP), "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
    };
    
    return s;
    

    现在我正在从 Twitter 检索 json 数据。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-05-17
      • 1970-01-01
      • 1970-01-01
      • 2017-07-30
      • 2013-06-21
      • 2011-11-07
      • 2012-10-27
      相关资源
      最近更新 更多