在 UWP 中,如果要进行 OAuth 授权,那很大概率是会用上 WebAuthenticationBroker 这个类的,例如微博授权这种。

在一般情况下来说,WebAuthenticationBroker 是足够用的了,但是,如果你是碰上 Github 授权的话,那么就会碰到这样的情况:

蹦出一大个警告,让人看上去相当不爽。归根的原因是 WebAuthenticationBroker 使用的是 IE 内核,这个我们可以通过 https://www.whatismybrowser.com/ 验证。

连 Edge 内核都不是,不给力啊,老湿。

那么有没有办法把 WebAuthenticationBroker 换成 Edge 内核呢?简单的办法是没有的了,但我们还有 WebView,WebView 是使用 Edge 内核的,可以通过 WebView 来手动实现我们自己的 WebAuthenticationBroker。

参考 WebAuthenticationBroker 类的 AuthenticateAsync,编写如下代码:

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

WebAuthenticationBroker 的 AuthenticateAsync 这个方法有 3 个参数,但第一个参数并不是很常用,所以这里就只使用后面的两个参数了。另外因为 WebAuthenticationResult 没有公共构造函数,所以定义一个 MyWebAuthenticationResult 来代替。

public class MyWebAuthenticationResult
{
public string ResponseData { get; internal set; } public uint ResponseErrorDetail { get; internal set; } public WebAuthenticationStatus ResponseStatus { get; internal set; }
}

接下来就是如何实现的问题。这里我们可以使用一个 ContentDialog 套 WebView 的方式。

在项目添加一个内容对话框(这里我叫 AuthorizationDialog),并编写如下代码:

<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 代码:

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。

而且因为是使用 WebView,所以还能再进行一些定制化的操作,例如执行 JavaScript,获取 Cookie 之类的。

【UWP】手动实现 WebAuthenticationBroker的更多相关文章

  1. MVVM框架从WPF移植到UWP遇到的问题和解决方法

    MVVM框架从WPF移植到UWP遇到的问题和解决方法 0x00 起因 这几天开始学习UWP了,之前有WPF经验,所以总体感觉还可以,看了一些基础概念和主题,写了几个测试程序,突然想起来了前一段时间在W ...

  2. UWP开发必备:常用数据列表控件汇总比较

    今天是想通过实例将UWP开发常用的数据列表做汇总比较,作为以后项目开发参考.UWP开发必备知识点总结请参照[UWP开发必备以及常用知识点总结]. 本次主要讨论以下控件: GridView:用于显示数据 ...

  3. UWP开发之Template10实践二:拍照功能你合理使用了吗?(TempState临时目录问题)

    最近在忙Asp.Net MVC开发一直没空更新UWP这块,不过有时间的话还是需要将自己的经验和大家分享下,以求共同进步. 在上章[UWP开发之Template10实践:本地文件与照相机文件操作的MVV ...

  4. Win10 UWP开发系列——开源控件库:UWPCommunityToolkit

    在开发应用的过程中,不可避免的会使用第三方类库.之前用过一个WinRTXamlToolkit.UWP,现在微软官方发布了一个新的开源控件库—— UWPCommunityToolkit 项目代码托管在G ...

  5. 【开源】知乎日报UWP 更新

    说明 大概十天之前我更新了一次APP,后来又仔细看了一下Store里的评论,发现还有几个地方没有改过来.于是前天晚上抽时间改了一下,顺便完善了一下UI体验. 没有看前面文章的童鞋可以看一下下面的链接: ...

  6. 【完全开源】知乎日报UWP版:增加Live磁贴、Badge、以及Toast通知

    目录 说明 实现方法 APP生命期 后台任务 说明 之前网上有人建议增加磁贴(tile).徽章(badge)功能.利用周末的时间,将这两个功能添加上去了.如果将磁贴固定到开始屏幕,磁贴就会循环播放首页 ...

  7. Win10 UWP开发系列:使用VS2015 Update2+ionic开发第一个Cordova App

    安装VS2015 Update2的过程是非常曲折的.还好经过不懈的努力,终于折腾成功了. 如果开发Cordova项目的话,推荐大家用一下ionic这个框架,效果还不错.对于Cordova.PhoneG ...

  8. UWP中重用C/C++代码时踩过的一些坑

    标题中提到的UWP,主要是指用C#来写UWP的主工程,开发过程中可能需要调用C/C++实现的库. 为什么需要调用C/C++的库呢,举个例子,开源库OpenSSL实现了许多加密算法,稳定快速,我们想在应 ...

  9. Win10 UWP 开发系列:使用多语言工具包让应用支持多语言

    之前我在一篇blog中写过如何使用多语言工具包,见http://www.cnblogs.com/yanxiaodi/p/3800767.html 在WinEcos社区也发布过一篇详细的文章介绍多语言工 ...

随机推荐

  1. Java 日期比较大小

    import org.junit.Test; import java.text.SimpleDateFormat; import java.util.Date; /** * @author DateJ ...

  2. 学习笔记:AngularJs

    站点: http://www.angularjs.cn/  angularjs中文社区 http://www.jb51.net/article/60733.htm  AngularJS内置指令 基本页 ...

  3. hive 表锁和解锁

    场景: 在执行insert into或insert overwrite任务时,中途手动将程序停掉,会出现卡死情况(无法提交MapReduce),只能执行查询操作,而drop insert操作均不可操作 ...

  4. laravel表单操作

    $request->all()//获取所有参数if($request->isMethod('GET')){判断是否是GET请求}$res = $request->is('studen ...

  5. leetcode104

    /** * Definition for a binary tree node. * public class TreeNode { * public int val; * public TreeNo ...

  6. leetcode128

    class Solution: def longestConsecutive(self, nums: 'List[int]') -> int: if len(nums)<=1: retur ...

  7. Mybatis中DAO层接口没有写实现类,Mapper中的方法和DAO接口方法是怎么绑定到一起的,其内部是怎么实现的

    其实也就是通过接口名与mapper的id绑定在一起(即相同),通过SQL去写实现类,返回数据.

  8. React页面插入script

    项目中遇到插入广告的需要,而广告的信息只是一个url链接,这个链接返回的时一个js,和以前插入广告有点不同.所有找了很多方式. 先来展示广告链接返回的信息: 假设广告链接为:http://192.16 ...

  9. 分布式版本控制系统Git的安装和使用

    作业要求来自https://edu.cnblogs.com/campus/gzcc/GZCC-16SE2/homework/2097 GitHub远程仓库的地址:https://github.com/ ...

  10. Pandas合并数据集之concat、combine_first方法

    轴向连接(concat) Numpy import numpy as np import pandas as pd from pandas import Series arr = np.arange( ...