在前一篇博文中,我们通过以 OAuth 的 Client Credential Grant 授权方式(只验证调用客户端,不验证登录用户)拿到的 Access Token ,成功调用了与用户无关的 Web API。

在这篇博文中,我们将以 OAuth 的 Resource Owner Password Credentials Grant 的授权方式( grant_type=password )获取 Access Token,并以这个 Token 调用与用户相关的 Web API。

对应的应用场景是:为自家的网站开发手机 App(非第三方 App),只需用户在 App 上登录,无需用户对 App 所能访问的数据进行授权。

根据 OAuth 规范,客户端获取 Access Token 的请求方式如下:

POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded grant_type=password&username=johndoe&password=A3ddj3w

根据上面的请求方式,在 C# 中用 HttpClient 实现一个简单的客户端,代码如下:

public class OAuthClientTest
{
private HttpClient _httpClient; public OAuthClientTest()
{
_httpClient = new HttpClient();
_httpClient.BaseAddress = new Uri("http://openapi.cnblogs.com");
} [Fact]
public async Task Get_Accesss_Token_By_Resource_Owner_Password_Credentials_Grant()
{
Console.WriteLine(await GetAccessToken());
} private async Task<string> GetAccessToken()
{
var clientId = "";
var clientSecret = ""; var parameters = new Dictionary<string, string>();
parameters.Add("grant_type", "password");
parameters.Add("username", "博客园团队");
parameters.Add("password", "cnblogs.com"); _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
"Basic",
Convert.ToBase64String(Encoding.ASCII.GetBytes(clientId + ":" + clientSecret))
); var response = await _httpClient.PostAsync("/token", new FormUrlEncodedContent(parameters));
var responseValue = await response.Content.ReadAsStringAsync();
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
return JObject.Parse(responseValue)["access_token"].Value<string>();
}
else
{
Console.WriteLine(responseValue);
return string.Empty;
}
}
}

(注:与之前相比,这里的 client_id/client_secret 改为了 Basic Authorization,以更好的遵循 OAuth 规范)

在服务端,基于 Owin OAuth, 针对 Resource Owner Password Credentials Grant 的授权方式,只需重载 OAuthAuthorizationServerProvider.GrantResourceOwnerCredentials() 方法即可。代码如下:

public class CNBlogsAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
//... public override async Task GrantResourceOwnerCredentials(
OAuthGrantResourceOwnerCredentialsContext context)
{
//调用后台的登录服务验证用户名与密码 var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
oAuthIdentity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties());
context.Validated(ticket); await base.GrantResourceOwnerCredentials(context);
}
}

完整的CNBlogsAuthorizationServerProvider实现代码如下(与之前相比,context.TryGetFormCredentials 改为了 context.TryGetBasicCredentials):

public class CNBlogsAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
string clientId;
string clientSecret;
context.TryGetBasicCredentials(out clientId, out clientSecret); if (clientId == ""
&& clientSecret == "")
{
context.Validated(clientId);
} await base.ValidateClientAuthentication(context);
} public override async Task GrantClientCredentials(OAuthGrantClientCredentialsContext context)
{
var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties());
context.Validated(ticket); await base.GrantClientCredentials(context);
} public override async Task GrantResourceOwnerCredentials(
OAuthGrantResourceOwnerCredentialsContext context)
{
//调用后台的登录服务验证用户名与密码 var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
oAuthIdentity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties());
context.Validated(ticket); await base.GrantResourceOwnerCredentials(context);
}
}

CNBlogsAuthorizationServerProvider

这样,运行客户端程序就可以拿到 Access Token 了。

接下来,我们拿着以这种方式获取的 Access Token,就可以调用与用户相关的 Web API 了。

在服务端我们通过一个简单的 Web API 测试一下,代码如下:

public class UsersController : ApiController
{
[Authorize]
public string GetCurrent()
{
return User.Identity.Name;
//这里可以调用后台用户服务,获取用户相关数所,或者验证用户权限进行相应的操作
}
}

然后,客户端用以 grant_type=password 方式拿到的 Access Token 调用这个Web API,客户端增加的代码如下:

[Fact]
public async Task Call_WebAPI_By_Resource_Owner_Password_Credentials_Grant()
{
var token = await GetAccessToken();
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
Console.WriteLine(await (await _httpClient.GetAsync("/api/users/current")).Content.ReadAsStringAsync());
}

客户端运行结果如下:

"博客园团队"

调用成功!运行结果正是获取 Access Token 时所用的 username 。

结合 ASP.NET 现有的安全机制,借助 OWIN 的威力,Microsoft.Owin.Security.OAuth 的确让开发基于 OAuth 的 Web API 变得更简单。

ASP.NET Web API与Owin OAuth:调用与用户相关的Web API的更多相关文章

  1. ASP.NET Web API与Owin OAuth:调用与用户相关的Web API(非第三方登录)

    授权完成添加属性 ClaimsIdentity oAuthIdentity = await CreateAsync(user/*userManager*/, OAuthDefaults.Authent ...

  2. ASP.NET Web API与Owin OAuth:使用Access Toke调用受保护的API

    在前一篇博文中,我们使用OAuth的Client Credential Grant授权方式,在服务端通过CNBlogsAuthorizationServerProvider(Authorization ...

  3. 在ASP.NET Web API 2中使用Owin OAuth 刷新令牌(示例代码)

    在上篇文章介绍了Web Api中使用令牌进行授权的后端实现方法,基于WebApi2和OWIN OAuth实现了获取access token,使用token访问需授权的资源信息.本文将介绍在Web Ap ...

  4. 在WebApi中基于Owin OAuth使用授权发放Token

    如何基于Microsoft.Owin.Security.OAuth,使用Client Credentials Grant授权方式给客户端发放access token? Client Credentia ...

  5. IdentityServer4 ASP.NET Core的OpenID Connect OAuth 2.0框架学习保护API

    IdentityServer4 ASP.NET Core的OpenID Connect OAuth 2.0框架学习之保护API. 使用IdentityServer4 来实现使用客户端凭据保护ASP.N ...

  6. [ASP.NET] 结合Web API在OWIN下实现OAuth

    OAuth(Open Authorization) 为用户资源的授权提供了一个安全的.开放而又简易的标准.与以往的授权方式不同之处是OAuth的授权不会使第三方触及到用户的帐号信息(如用户名与密码), ...

  7. Web API在OWIN下实现OAuth

    OAuth(Open Authorization) 为用户资源的授权提供了一个安全的.开放而又简易的标准.与以往的授权方式不同之处是OAuth的授权不会使第三方触及到用户的帐号信息(如用户名与密码), ...

  8. 为IIS Host ASP.NET Web Api添加Owin Middleware

    将OWIN App部署在IIS上 要想将Owin App部署在IIS上,只添加Package:Microsoft.OWIN.Host.SystemWeb包即可.它提供了所有Owin配置,Middlew ...

  9. 在ASP dot Net Core MVC中用Controllers调用你的Asp dotnet Core Web API 实现CRUD到远程数据库中,构建你的分布式应用(附Git地址)

    本文所有的东西都是在dot Net Core 1.1环境+VS2017保证测试通过. 本文接着上次文章接着写的,不了解上篇文章的可能看着有点吃力.我尽量让大家都能看懂.这是上篇文章的连接http:// ...

随机推荐

  1. 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新

    [原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...

  2. 使用Monit监控本地进程

    目前用它监控某些服务,失败自动重启,同时监控特定的日志文件,如果有变化,就发邮件报警 安装不细写了,网上好多 我先用cat /proc/version看了下我的系统是el6的,于是wget http: ...

  3. .NET Core系列 : 2 、project.json 这葫芦里卖的什么药

    .NET Core系列 : 1..NET Core 环境搭建和命令行CLI入门 介绍了.NET Core环境,本文介绍.NET Core中最重要的一个配置文件project.json的相关内容.我们可 ...

  4. 深入理解DIP、IoC、DI以及IoC容器

    摘要 面向对象设计(OOD)有助于我们开发出高性能.易扩展以及易复用的程序.其中,OOD有一个重要的思想那就是依赖倒置原则(DIP),并由此引申出IoC.DI以及Ioc容器等概念.通过本文我们将一起学 ...

  5. PHP-会员登录与注册例子解析-学习笔记

    1.开始 最近开始学习李炎恢老师的<PHP第二季度视频>中的“章节5:使用OOP注册会员”,做一个学习笔记,通过绘制基本页面流程和UML类图,来对加深理解. 2.基本页面流程 3.通过UM ...

  6. .NET Core的日志[3]:将日志写入Debug窗口

    定义在NuGet包"Microsoft.Extensions.Logging.Debug"中的DebugLogger会直接调用Debug的WriteLine方法来写入分发给它的日志 ...

  7. 神马玩意,EntityFramework Core 1.1又更新了?走,赶紧去围观

    前言 哦,不搞SQL了么,当然会继续,周末会继续更新,估计写完还得几十篇,但是我会坚持把SQL更新完毕,绝不会烂尾,后续很长一段时间没更新的话,不要想我,那说明我是学习新的技能去了,那就是学习英语,本 ...

  8. Python多线程爬虫爬取电影天堂资源

    最近花些时间学习了一下Python,并写了一个多线程的爬虫程序来获取电影天堂上资源的迅雷下载地址,代码已经上传到GitHub上了,需要的同学可以自行下载.刚开始学习python希望可以获得宝贵的意见. ...

  9. 创建ABPboilerplate模版项目

    本文是根据角落的白板报的<通过ABPboilerplate模版创建项目>一文的学习总结,感谢原文作者角落的白板报. 1 准备 开发环境: Visual Studio 2015 update ...

  10. Quartz2D总结

    天了噜,脑子完全懵了,最起码说出来个上下文啊,连这个都给忘了,特此总结一下,并以此缅怀这次面试 Quartz2D的API来自于Core Graphics(这就是为什么CGContextRef是以CG开 ...