在 android 中使用 okhttp 在 webview 中的每个请求中发送授权标头

Posted

技术标签:

【中文标题】在 android 中使用 okhttp 在 webview 中的每个请求中发送授权标头【英文标题】:Send authorization header with every request in webview using okhttp in android 【发布时间】:2017-05-17 09:09:21 【问题描述】:

我正在使用 WebView 来显示网页,但服务器希望我的 webview 的每个请求都有一个授权令牌。有人知道这是否可能吗? 我在#SO adding-header-to-all-request-with-retrofit-2 中提到了这篇文章。但我无法得到结果。

这是我的代码(我的编码标准,我是初学者)

    public class TableViewTest extends AppCompatActivity 

    ScrollView scrollView;
    WebView webView;
    SharedPreferences pref;

    @SuppressLint("SetjavascriptEnabled")
    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_table_view_test);

        //final ProgressDialog pd = ProgressDialog.show(this, "", "Loading...", true);


        webView = (WebView) findViewById(R.id.webView1Id)

        webView.setVerticalScrollBarEnabled(true);
        webView.setHorizontalScrollBarEnabled(true);
        webView.getSettings().setJavaScriptEnabled(true);
        webView.getSettings().setBuiltInZoomControls(true);

        webView.setWebViewClient(new WebViewClient() 

            @SuppressWarnings("deprecation")
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) 
                //return true load with system-default-browser or other browsers, false with your webView

                pref = getSharedPreferences("app", Context.MODE_PRIVATE);
                final String acToken = pref.getString("token", "DEFAULT");


                HashMap<String, String> headerMap = new HashMap<>();
                //put all headers in this header map
                headerMap.put("Authorization", acToken);


                view.loadUrl(url, headerMap);


                return false;
            

            @Override
            public void onLoadResource(WebView view, String url) 
                super.onLoadResource(view, url);
            

            @Override
            public void onPageStarted(WebView view, String url, Bitmap favicon) 
                super.onPageStarted(view, url, favicon);
            
        );

        webView.loadUrl("myurl");

    


    @Override
    protected void onSaveInstanceState(Bundle outState) 
        super.onSaveInstanceState(outState);
        webView.saveState(outState);
    

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) 
        super.onRestoreInstanceState(savedInstanceState);
        webView.restoreState(savedInstanceState);
    


    public WebViewClient getWebViewClientWithCustomHeader()

        pref = getSharedPreferences("app", Context.MODE_PRIVATE);
        final String acToken = pref.getString("token", "DEFAULT");

        return new WebViewClient() 

            @SuppressWarnings("deprecation")
            public WebResourceResponse shouldInterceptRequest(WebView view, String url)
                try 
                    OkHttpClient okHttpClient = new OkHttpClient();
                    Request request = new Request.Builder().url(url).addHeader("Authorization" , "Bearer " + acToken)
                            .build();

                    Response response = okHttpClient.newCall(request).execute();

                    return new WebResourceResponse(response.header("content-type", response.body().contentType().type()), // You can set something other as default content-type
                            response.header("content-encoding", "utf-8"),  // Again, you can set another encoding as default
                            response.body().byteStream());


                 catch (ClientProtocolException e) 
                    //return null to tell WebView we failed to fetch it WebView should try again.
                    return null;
                 catch (IOException e) 
                    e.printStackTrace();
                    return null;
                
            
        ;
    

感谢任何帮助

编辑

现在我能够获得服务器响应,但使用纯 html,无法正确呈现 css,不知道为什么。

这是新代码,

    public class TableViewTest extends AppCompatActivity 

    ScrollView scrollView;
    WebView webView;
    SharedPreferences pref;

    @SuppressLint("SetJavaScriptEnabled")
    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_table_view_test);

        //final ProgressDialog pd = ProgressDialog.show(this, "", "Loading...", true);

        String url = "myurl";
        pref = getSharedPreferences("app", Context.MODE_PRIVATE);


        webView = (WebView) findViewById(R.id.webView1Id);

        webView.setVerticalScrollBarEnabled(true);
        webView.setHorizontalScrollBarEnabled(true);
        webView.getSettings().setJavaScriptEnabled(true);
        webView.getSettings().setBuiltInZoomControls(true);
        webView.setWebViewClient(wvc);

        webView.loadUrl("myurl");

    


    @Override
    protected void onSaveInstanceState(Bundle outState) 
        super.onSaveInstanceState(outState);
        webView.saveState(outState);
    

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) 
        super.onRestoreInstanceState(savedInstanceState);
        webView.restoreState(savedInstanceState);
    


    public WebViewClient wvc =  new WebViewClient() 

            @SuppressWarnings("deprecation")
            public WebResourceResponse shouldInterceptRequest(WebView view, String url)
                try 
                    final String acToken = pref.getString("token", "DEFAULT");

                    OkHttpClient okHttpClient = new OkHttpClient();
                    Request request = new Request.Builder().url(url).addHeader("Authorization" , "Bearer " + acToken)
                            .build();

                    Response response = okHttpClient.newCall(request).execute();

                    return new WebResourceResponse(response.header("content-type", response.body().contentType().type()), // You can set something other as default content-type
                            response.header("content-encoding", "utf-8"),  // Again, you can set another encoding as default
                            response.body().byteStream());


                 catch (ClientProtocolException e) 
                    //return null to tell WebView we failed to fetch it WebView should try again.
                    return null;
                 catch (IOException e) 
                    e.printStackTrace();
                    return null;
                
            
        ;

在 stacktrace 中找到了这个,在 stacktrace 中仅此而已

 E/DataReductionProxySettingListener: No DRP key due to exception:java.lang.ClassNotFoundException: com.android.webview.chromium.Drp

【问题讨论】:

请发布您收到的任何日志和错误。也许仔细检查服务器不期望带有身份验证值的前缀(“Bearer”)。 @asadmshah 到目前为止没有错误,我只是重新编辑了代码,现在在 webview 中获取纯 HTML,现在服务器正在获取我的身份验证令牌标头。请查看有问题的编辑 @asadmshah 只有登录的用户才能访问该网页,我的 php 后端对用户使用 Bearer 身份验证。所以我必须通过“Bearer”+tokenValue才能访问网页。 【参考方案1】:

最后,这将纠正 html 呈现问题(对不起,我之前没有注意到这一点)。

content-type改成

return new WebResourceResponse(response.header("content-type", response.body().contentType().type()), // You can set something other as default content-type
                        response.header("content-encoding", "utf-8"),  // Again, you can set another encoding as default
                        response.body().byteStream());

text/html,所以新代码是

return new WebResourceResponse(response.header("text/html", response.body().contentType().type()), // You can set something other as default content-type
                    response.header("content-encoding", "utf-8"),  // Again, you can set another encoding as default
                    response.body().byteStream());

如果我的解决方案需要任何修改,请随时进行编辑。总是接受更好的解决方案。快乐的编码......感谢大家#SO准备提供帮助。

【讨论】:

嗯? text/html 是一个内容类型,而不是它自己的标题。当然,他应该将 content-type 标头 to text/html? shouldInterceptRequestwith url 作为参数现在已弃用【参考方案2】:

Sudheesh 的回答很棒,这是更新版本,因为 shouldOverrideUrlLoading(WebView view, String url) 在 API 21 中已被弃用:

webView.setWebViewClient(new WebViewClient() 
            public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest webResourceRequest) 
                // Filter out any requests we not interested in
                if (webResourceRequest.getUrl().getHost() == null ||
                        !webResourceRequest.getUrl().getHost().equals(MyAPI.getServer().toHost())) 
                    return super.shouldInterceptRequest(view, webResourceRequest);
                
                try 
                    OkHttpClient okHttpClient = new OkHttpClient();
                    Request request = new Request.Builder().url(webResourceRequest.getUrl().toString())
                            .addHeader(HttpConnector.DefaultConnector().getAuthorizationHeaderKey(), HttpConnector.DefaultConnector().getAuthorizationHeaderValue())
                            .build();
                    Response response = okHttpClient.newCall(request).execute();
                    return new WebResourceResponse(response.header("text/html", response.body().contentType().type()), // You can set something other as default content-type
                            response.header("content-encoding", "utf-8"),  // Again, you can set another encoding as default
                            response.body().byteStream());
                 catch (ClientProtocolException e) 
                    //return null to tell WebView we failed to fetch it WebView should try again.
                    return null;
                 catch (IOException e) 
                    e.printStackTrace();
                    return null;
                
            
        );

【讨论】:

这会将所有请求转换为get @mrid 请参阅 Send Post request along with HttpHeaders on Android 以及链接(重复)问题的许多答案。【参考方案3】:

这可能无法回答您的问题,但我认为这对其有类似问题的其他人会有所帮助。

如果服务器资源需要像 Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ= 这样的基本身份验证标头,那么您应该像这样覆盖 WebViewClient 中的 onReceivedHttpAuthRequest 方法。

webView.webViewClient = object : WebViewClient() 

    override fun onReceivedHttpAuthRequest(view: WebView?, handler: HttpAuthHandler?, host: String?, realm: String?) 
        handler?.proceed("username", "password")
    


还要注意,如果可能,您应该始终避免拦截 WebView 请求。阅读更多关于它的信息here。

【讨论】:

但这只是调用一次。如果用户注销并使用错误数据重新登录,它仍然在登录

以上是关于在 android 中使用 okhttp 在 webview 中的每个请求中发送授权标头的主要内容,如果未能解决你的问题,请参考以下文章

浅谈Android studio中OKHttp安装及简单使用

在 Android 上使用 okhttp 2 的持久 Cookie 存储

Android 内部使用 OkHttp 吗?

如何使用 OkHttp 在 Android 上实现 cookie 处理?

在 android 中使用 okhttp 在 webview 中的每个请求中发送授权标头

Android 在 OKHttp 中启用 TLSv1.2