在Xamarin Forms Android WebView中为所有请求添加授权头

3
我正在尝试给Webview客户端添加自定义的HTTP标头(用于授权)。在某些情况下,它似乎是有效的,我可以登录到一个网页而不需要输入用户名和密码,并且我被重定向到另一个页面。但当该页面调用其他资源以获取数据填充元素时,会抛出错误并调用OnReceivedHttpError。我得到的错误是401未经授权,当我查看IWebResourceRequest上的标头时,根本找不到授权标头。我错过了什么还是有人遇到同样的问题吗?
使用Xamarin Forms 2.3.3.180并针对API 21(Android 5.0 Lollipop)进行编译,使用Android 7.1 Nougat。
我已经在Postman中尝试添加请求标头,它完美地工作。
渲染器:
public class MyWebViewRenderer : WebViewRenderer
{
    private MyWebViewClient _webViewClient;

    protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
    {
        base.OnElementChanged(e);

        if(_webViewClient == null)
            _webViewClient = new MyWebViewClient();

        Control.SetWebViewClient(_webViewClient);
        Control.LongClickable = false;
        Control.HapticFeedbackEnabled = false;
        Control.Settings.JavaScriptEnabled = true;

        var data = Encoding.GetEncoding("ISO-8859-1").GetBytes("username:password");
        var base64string = Base64.EncodeToString(data, Base64Flags.NoWrap);

        var headers = new Dictionary<string, string>();
        headers.Add("Authorization", $"Basic {base64string}")

        Control.LoadUrl(Control.Url, headers);
    }
}

WebViewClient:

public override bool ShouldOverrideUrlLoading(WebView view, string url)
{
    WebView.SetWebContentsDebuggingEnabled(true);

    var data = Encoding.GetEncoding("ISO-8859-1").GetBytes("username:password");
    var base64string = Base64.EncodeToString(data, Base64Flags.NoWrap);

    var headers = new Dictionary<string, string>();
    headers.Add("Authorization", $"Basic {base64string}")

    view.LoadUrl(url, headers);
    return true;
}

public override WebResourceResponse ShouldInterceptRequest(WebView view, IWebResourceRequest urlResource)
{
    //headers does not always contains authorization header, so let's add it.
    if (!urlResource.RequestHeaders.ContainsKey("authorization") && !urlResource.RequestHeaders.ContainsKey("Authorization"))
    {
       var data = Encoding.GetEncoding("ISO-8859-1").GetBytes("username:password");
       var base64string = Base64.EncodeToString(data, Base64Flags.NoWrap);

       urlResource.RequestHeaders.Add("Authorization", $"{base64string}");
     }

        return base.ShouldInterceptRequest(view, urlResource);
}

public override void OnReceivedHttpError(WebView view, IWebResourceRequest request, WebResourceResponse errorResponse)
{
    base.OnReceivedHttpError(view, request, errorResponse);
}
1个回答

3
如果您仅需要获取GET请求的标题,则下面的代码将起作用。但POST请求是一个不同的问题。我需要做类似的事情(不仅是GET,而是所有请求),我能说的是没有简单明了的解决方案,至少没有我找到的(我已经尝试了除了编写自己的网络驱动程序之外的所有方法)。我尝试了许多方法(ShouldOverrideUrlLoading,ShouldInterceptRequest,custom LoadUrl和PostUrl等), 但它们都没有给出100%的解决方案。关于此存在很多误解,因此我认为需要澄清,因为我已经花了两天时间,但仍然没有成功。

所以这就是我学到的:

如果您仅需要获取GET请求中的标题,那么这很简单。只需创建WebViewClient 的实现并像这样覆盖 ShouldOverrideUrlLoading:

[assembly: ExportRenderer(typeof(Xamarin.Forms.WebView), typeof(App.Android.HybridWebViewRenderer))]
namespace App.Android
{
    public class HybridWebViewRenderer : WebViewRenderer
    {

        public HybridWebViewRenderer(Context context) : base(context)
        {
        }

        protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
        {
            base.OnElementChanged(e);
            Control.SetWebViewClient(new CustomWebViewClient());
        }
    }
    public class CustomWebViewClient : WebViewClient
    {
        public override bool ShouldOverrideUrlLoading(Android.Webkit.WebView view, string url)
        {
            Dictionary<string, string> headers = new Dictionary<string, string>
            {
                ["Name"] = "value"
            };
            view.LoadUrl(url, headers);
            return true;
        }
    }
}

然而,如果您需要其他请求中的标头(特别是POST请求),那么真的没有完美的解决方案。许多答案告诉您覆盖“ShouldInterceptRequest”,但这不太可能有所帮助。“ShouldInterceptRequest”提供了一个包含请求URL、方法(即POST)和标头的“IWebResourceRequest”。有一些答案声称通过执行“request.Headers.Add(“Name”,“Value”)”添加标头是可行的解决方案,但这是错误的。IWebResourceRequest不被WebView的内部逻辑使用,因此修改它是无用的!
您可以在“ShouldInterceptRequest”中编写自己的HTTP客户端,其中包括自己的标头以执行请求并返回WebResourceResponse对象。同样,这适用于GET请求,但问题在于,尽管我们可以拦截POST请求,但是由于请求内容未包含在“IWebResourceRequest”对象中,因此“我们无法确定请求中的内容”。结果,我们无法准确地手动执行请求。因此,除非POST请求的内容不重要或某种方式可以获取,否则此方法不可行。
关于此方法的另一个说明:“返回null告诉WebView为我们处理请求”。换句话说,“我不想拦截请求”。但是,如果返回值不为空,则WebView将显示WebResourceResponse对象中的任何内容。
我还尝试覆盖WebView本身中的PostUrl和LoadUrl方法。这些方法不被内部逻辑调用,因此除非您自己调用它们,否则这是行不通的。
那么可以做什么?有一些hacky解决方案(请参见github.com/KeejOow/android-post-webview)可解决此问题,但它们依赖于JavaScript,并且并非在所有情况下都适用(我已经读到它们无法在表单中使用)。如果您想在Xamarin中使用它们,您需要为C#调整代码,而且不能保证它会解决您的问题。
我写这篇文章,以便其他人不必浪费无数小时寻找不存在的解决方案。如果只有Android开发人员决定在“IWebResourceRequest”对象中包含POST内容……对于长度的道歉,如果您已经阅读到这一点,您可能像我一样绝望。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接