错误代码401尝试使用Android上的OkHttp访问Twitter端点

时间:2016-05-09 01:24:39

标签: android twitter oauth okhttp

我正在编写测试程序以学习使用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

1 个答案:

答案 0 :(得分:1)

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

builder.append("&count=");
createSignatureBaseString()方法中的

不应该有&amp ;.它应该读

 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数据。