【UWP】手動實現 WebAuthenticationBroker

在 UWP 中,若是要進行 OAuth 受權,那很大機率是會用上 WebAuthenticationBroker 這個類的,例如微博受權這種。web

在通常狀況下來講,WebAuthenticationBroker 是足夠用的了,可是,若是你是碰上 Github 受權的話,那麼就會碰到這樣的狀況:express

Snipaste_2019-04-09_09-33-53

蹦出一大個警告,讓人看上去至關不爽。歸根的緣由是 WebAuthenticationBroker 使用的是 IE 內核,這個咱們能夠經過 https://www.whatismybrowser.com/ 驗證。c#

Snipaste_2019-04-09_09-36-59

連 Edge 內核都不是,不給力啊,老溼。windows

 

那麼有沒有辦法把 WebAuthenticationBroker 換成 Edge 內核呢?簡單的辦法是沒有的了,但咱們還有 WebView,WebView 是使用 Edge 內核的,能夠經過 WebView 來手動實現咱們本身的 WebAuthenticationBroker。api

參考 WebAuthenticationBroker 類的 AuthenticateAsync,編寫以下代碼:async

public static class MyWebAuthenticationBroker
    {
        public static Task<MyWebAuthenticationResult> AuthenticateAsync(Uri requestUri, Uri callbackUri)
        {
            throw new NotImplementedException();
        }
    }

WebAuthenticationBroker 的 AuthenticateAsync 這個方法有 3 個參數,但第一個參數並非很經常使用,因此這裏就只使用後面的兩個參數了。另外由於 WebAuthenticationResult 沒有公共構造函數,因此定義一個 MyWebAuthenticationResult 來代替。ide

public class MyWebAuthenticationResult
    {
        public string ResponseData { get; internal set; }

        public uint ResponseErrorDetail { get; internal set; }

        public WebAuthenticationStatus ResponseStatus { get; internal set; }
    }

接下來就是如何實現的問題。這裏咱們可使用一個 ContentDialog 套 WebView 的方式。函數

在項目添加一個內容對話框(這裏我叫 AuthorizationDialog),並編寫以下代碼:ui

<ContentDialog x:Class="WebAuthenticationBrokerDemo.AuthorizationDialog"
               xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
               xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
               xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
               xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
               Title="正在鏈接到服務"
               CloseButtonText="取消"
               FullSizeDesired="True"
               mc:Ignorable="d">
    <ContentDialog.Resources>
        <ResourceDictionary>
            <x:Double x:Key="ContentDialogMinWidth">800</x:Double>
        </ResourceDictionary>
    </ContentDialog.Resources>
    <Grid>
        <WebView x:Name="WebView"
                 NavigationFailed="WebView_NavigationFailed"
                 NavigationStarting="WebView_NavigationStarting" />
    </Grid>
</ContentDialog>

設置 FullSizeDesired 使高度佔滿窗口,資源字典中覆蓋默認的對話框寬度。WebView 則訂閱 Starting 和 Failed 事件。編寫後臺 cs 代碼:spa

public sealed partial class AuthorizationDialog
    {
        private readonly Uri _callbackUri;

        public AuthorizationDialog(Uri requestUri, Uri callbackUri)
        {
            if (requestUri == null)
            {
                throw new ArgumentNullException(nameof(requestUri));
            }
            if (callbackUri == null)
            {
                throw new ArgumentNullException(nameof(callbackUri));
            }

            _callbackUri = callbackUri;

            InitializeComponent();

            WebView.Source = requestUri;
        }

        public Uri ResponseUri { get; private set; }

        public WebAuthenticationStatus Result { get; private set; } = WebAuthenticationStatus.UserCancel;

        public WebErrorStatus WebErrorStatus { get; private set; }

        private bool CheckUri(Uri uri)
        {
            if (uri.Host == _callbackUri.Host)
            {
                Result = WebAuthenticationStatus.Success;
                ResponseUri = uri;
                return true;
            }

            return false;
        }

        private void WebView_NavigationFailed(object sender, WebViewNavigationFailedEventArgs e)
        {
            if (CheckUri(e.Uri))
            {
                Hide();
                return;
            }

            Result = WebAuthenticationStatus.ErrorHttp;
            ResponseUri = e.Uri;
            WebErrorStatus = e.WebErrorStatus;
            Hide();
        }

        private void WebView_NavigationStarting(WebView sender, WebViewNavigationStartingEventArgs args)
        {
            if (CheckUri(args.Uri))
            {
                Hide();
            }
        }
    }

能夠經過 Hide 方法來關閉對話框。須要注意的是 Failed 的時候須要先檢查一次,由於可能 callback 的地址是沒法訪問的。

接下來能夠補完一開始的 AuthenticateAsync 方法了。

public static class MyWebAuthenticationBroker
    {
        public static async Task<MyWebAuthenticationResult> AuthenticateAsync(Uri requestUri, Uri callbackUri)
        {
            var authorizationDialog = new AuthorizationDialog(requestUri, callbackUri);
            await authorizationDialog.ShowAsync();
            if (authorizationDialog.Result == WebAuthenticationStatus.UserCancel)
            {
                return new MyWebAuthenticationResult
                {
                    ResponseStatus = WebAuthenticationStatus.UserCancel
                };
            }
            else if (authorizationDialog.Result == WebAuthenticationStatus.Success)
            {
                return new MyWebAuthenticationResult
                {
                    ResponseStatus = WebAuthenticationStatus.Success,
                    ResponseData = authorizationDialog.ResponseUri.OriginalString
                };
            }
            else
            {
                return new MyWebAuthenticationResult
                {
                    ResponseStatus = WebAuthenticationStatus.ErrorHttp,
                    ResponseData = authorizationDialog.ResponseUri.OriginalString,
                    ResponseErrorDetail = (uint)authorizationDialog.WebErrorStatus
                };
            }
        }
    }

那麼如今咱們再去連 Github 之類的受權就不會有警告了,由於內核已經換成了 Edge。

Snipaste_2019-04-09_15-56-26

並且由於是使用 WebView,因此還能再進行一些定製化的操做,例如執行 JavaScript,獲取 Cookie 之類的。

相關文章
相關標籤/搜索