如何基于Microsoft.Owin.Security.OAuth,使用Client Credentials

Grant授权方式给客户端发放access token? Client Credentials

Grant的授权方式就是只验证客户端(Client),不验证用户(Resource Owner),只要客户端通过验证就发access

token。举一个对应的应用场景例子,比如我们想提供一个“获取网站首页最新博文列表”的WebAPI给客户端App调用。由于这个数据与用户无关,所以不涉及用户登录与授权,不需要Resource

Owner的参与。但我们不想任何人都可以调用这个WebAPI,所以要对客户端进行验证,而使用OAuth中的 Client

Credentials Grant 授权方式可以很好地解决这个问题。

在App_Start文件夹下新增ApplicationDbInitializer,代码如下:

public class ApplicationDbInitializer : DropCreateDatabaseIfModelChanges<ApplicationDbContext>
{
protected override void Seed(ApplicationDbContext context)
{
InitializeIdentityForEF(context);
base.Seed(context);
} //创建用户名为admin@123.com,密码为“Admin@123456”
public static void InitializeIdentityForEF(ApplicationDbContext dbContext)
{
var userManager = HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();
const string name = "admin@123.com";//用户名
const string email = "admin@123.com";//邮箱
const string password = "Admin@123456";//密码 //如果没有admin@123.com用户则创建该用户
var user = userManager.FindByName(name);
if (user == null)
{
user = new ApplicationUser
{
UserName = name,
Email = email
};
var result = userManager.Create(user, password);
result = userManager.SetLockoutEnabled(user.Id, false);
} }
}

修改Model文件夹下的IdentityModels.cs,添加斜体部分代码,需添加命名空间:using System.Data.Entity;

public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
// 在第一次启动网站时初始化数据库添加管理员用户凭据到数据库
Database.SetInitializer<ApplicationDbContext>(new ApplicationDbInitializer());
}

我把WebApi的Controller放到一个新建的文件夹APIControllers中,TestController的View的js的测试代码

打开Startup.Auth.cs,以下代码是Oauth相关的配置代码

public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
var OAuthOptions = new OAuthAuthorizationServerOptions
{
//获取Token的路径
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
//Token 过期时间,默认20分钟
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
//在生产模式下设 AllowInsecureHttp = false
AllowInsecureHttp = true
};
app.UseOAuthBearerTokens(OAuthOptions);
}
}

使用Client Credentials Grant的授权方式( grant_type= client_credentials)获取 Access Token,并以这个 Token 调用与用户相关的 Web API。

我们需要修改部分代码,修改ValidateClientAuthentication()方法,继承实现GrantClientCredentials()方法。代码如下

public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
{
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
string clientId, clientSecret;
context.TryGetBasicCredentials(out clientId, out clientSecret);
if (clientId == "Mobile" && clientSecret == "Xiaomi")
{
context.Validated();
}
return Task.FromResult<object>(null);
} public override Task GrantClientCredentials(OAuthGrantClientCredentialsContext context)
{
var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
oAuthIdentity.AddClaim(new Claim(ClaimTypes.Name, "Xiaomi"));
var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties());
context.Validated(ticket);
return base.GrantClientCredentials(context);
}
}

在 ValidateClientAuthentication() 方法中获取客户端的 client_id 与 client_secret 进行验证。在 GrantClientCredentials() 方法中对客户端进行授权,授了权就能发 access token 。这样,OAuth的ClientCredentials授权服务端代码就完成了。在ASP.NET Web API中启用OAuth的Access Token验证非常简单,只需在相应的Controller或Action加上[Authorize]标记,VS已生成部分代码,详细查看APIController文件夹下的ValuesController

下面我们在客户端调用一下,添加TestController,生成Index的View,然后在View中添加如下

$(function () {
$("#clientCredentials").on("click", function () {
GetClientCredentialsAccessToken();
});
}); function GetClientCredentialsAccessToken() {
$("#clientResult").html("Requesting");
var clientId = "Mobile";
var clientSecret = "Xiaomi";
$.ajax({
url: "/Token",
type: "post",
data: { "grant_type": "client_credentials" },
dataType: "json",
headers: {
"Authorization": "Basic " + Base64_Encode(clientId + ":" + clientSecret)
},
success: function (data) {
var accessToken = data.access_token;
GetValues(accessToken);
}
});
} function GetValues(accessToken) {
var html = "Token:" + accessToken + "<br/><br/>";
$.ajax({
url: "/api/Values",
type: "get",
dataType: "json",
headers: {
"Authorization": "Bearer " + accessToken
},
success: function (values) {
for (var i = 0; i < values.length; i++) {
html += "values[" + i + "] :" + values[i] + "<br/>";
}
$("#clientResult").html(html);
}
});
}
function Base64_Encode(str) {
var c1, c2, c3;
var base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var i = 0, len = str.length, string = ''; while (i < len) {
c1 = str.charCodeAt(i++) & 0xff;
if (i === len) {
string += base64EncodeChars.charAt(c1 >> 2);
string += base64EncodeChars.charAt((c1 & 0x3) << 4);
string += "==";
break;
}
c2 = str.charCodeAt(i++);
if (i === len) {
string += base64EncodeChars.charAt(c1 >> 2);
string += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
string += base64EncodeChars.charAt((c2 & 0xF) << 2);
string += "=";
break;
}
c3 = str.charCodeAt(i++);
string += base64EncodeChars.charAt(c1 >> 2);
string += base64EncodeChars.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
string += base64EncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6));
string += base64EncodeChars.charAt(c3 & 0x3F);
}
return string;
}

测试结果:

Token:4iIu7HProfJaxRiklsl-ORRO3hdyrsu50pQc1Eh2-Q5lSWK8UJgz6719ZaeeULhwkMPpEFYfk6QDOOMEyFqULULk65Sb0JY29wskyZyQhKJ3_P-eSVQ2PlbKbjH9ZcziAZsVOiNLp8CfUqL5qWUq8ggVAa8KRcnlJ1DIVWnEu0XvTEDZaLDpFqqj2Cex2CX7TmTgfs07RUBdx5_3WDavNA

Ps:

传递clientId与clientSecret有两种方式,上例使用BasicAuthentication,服务端使用TryGetBasicCredentials();另外一种方式是普通From的,把参数放到Ajax的data中,如:

{“clientId”: id ,” clientSecret”:”secret”, "grant_type":"client_credentials"}

对应服务端使用TryGetFormCredentials()获取clientId和clientSecret;

推荐使用Basic Authentication方式;


使用Resource Owner Password Credentials Grant 的授权方式( grant_type=password )获取 Access Token,并以这个 Token 调用与用户相关的 Web API。

Resource Owner Password Credentials Grant 授权方式(需要验证登录用户)

因为我们刚开始时已经初始化EF,添加了一个用户信息。ApplicationOAuthProvider.cs 的GrantResourceOwnerCredentials()方法(VS帮我们自动生成了),已经实现了先关的代码

public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
//调用后台的登录服务验证用户名与密码 var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "用户名或密码不正确。");
return;
}
ClaimsIdentity oAuthIdentity = await user.GenerateUserIdentityAsync(userManager, OAuthDefaults.AuthenticationType);
ClaimsIdentity cookiesIdentity = await user.GenerateUserIdentityAsync(userManager, CookieAuthenticationDefaults.AuthenticationType);
AuthenticationProperties properties = CreateProperties(user.UserName);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesIdentity);
}

添加一个测试用的Controller

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

在Test的index.cshtml 中新增测试的代码,如下

function GetResourceOwnerCredentialsAccessToken() {
$("#resourceOwnerresult").html("Requesting");
var clientId = "Mobile";
var clientSecret = "Xiaomi";
$.ajax({
url: "/Token",
type: "post",
data: {
"grant_type": "password",
"username": "admin@123.com",
"password": "Admin@123456"
},
dataType: "json",
headers: {
"Authorization": "Basic " + Base64_Encode(clientId + ":" + clientSecret)
},
success: function (data) {
var accessToken = data.access_token;
GetCurrentUserName(accessToken);
}
});
} function GetCurrentUserName(accessToken) {
var html = "Token:" + accessToken + "<br/><br/>";
$.ajax({
url: "/api/User",
type: "get",
dataType: "text",
headers: {
"Authorization": "Bearer " + accessToken
},
success: function (userName) {
html += "CurrentUserName:" + userName + "<br/>";
$("#resourceOwnerresult").html(html);
}
});
}

测试结果如下

Token:Cvct6BAKix_xLNEEOfidpEG0ymJihOSjdACazP2R2tJSB3TKVnxicgQK27DzDrICUC4A7vITqhkhBRsT5cRgiow--VkbiR4we3yQ54tc6B_W8KRrdGabjase_gpmFv8oYUPGLpI82acDpcZPzCkmgLLwAq8qfkmlK7iHm5tLM6-NRR8tgfEeOVBljHq4smIXw_eVuces3sRQm-PXTD4xmp05JdrJ9zFeRb_SAN0ADqDJfJxk1nNooCtdJyeHB6r1S2D81H6P7bhRK_edneWdkX5QCNBHL8b39UKnnk0ywza6vXcWct4RaATBYOw20iNu0XR6JRx5opP9vqqC2ag8Ux6s3GHl-vAZTaYuwunmWyY0FyJJWpjNnFpPo-pkxZaK1XJxgGPpSV-JJjEZLarnq9O57hQGfbVLCd3KtWuJflo5rMnfkAz2nXlcd3gAgjIhipAIlpsG72StzN0qBL8Ml2XvV9Re1Z8U4QtrE7tzjkE

CurrentUserName:"admin@123.com"

至此,使用WebApi 的两种授权方式发放Token和两种授权方式区别,以及使用Token调用受保护的api已经介绍完了,Oauth其实还有一个refresh token,refresh token 是专用于刷新 access token 的 token。一是因为 access token 是有过期时间的,到了过期时间这个 access token 就失效,需要刷新;二是因为一个 access token 会关联一定的用户权限,如果用户授权更改了,这个 access token 需要被刷新以关联新的权限。

这个就不单独介绍了,有兴趣的可以自己研究下。

Asp.Net 高级技术群 89336052,群共享有源码

感谢:晨风的指导和教育,提供demo和文章,我只是一个发布文章的战五渣!

在WebApi中基于Owin OAuth使用授权发放Token的更多相关文章

  1. 在ASP.NET中基于Owin OAuth使用Client Credentials Grant授权发放Token

    OAuth真是一个复杂的东东,即使你把OAuth规范倒背如流,在具体实现时也会无从下手.因此,Microsoft.Owin.Security.OAuth应运而生(它的实现代码在Katana项目中),帮 ...

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

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

  3. 使用Resource Owner Password Credentials Grant授权发放Token

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

  4. OAuth在WebApi中的使用,前后台分离的调用方式

    前段时间由于公司架构服务层向WebApi转换,就研究了OAuth在WebApi中的使用,这中间遇到了很多坑,在此记录一下OAuth的正确使用方式. 1.  OAuth是做什么的? 在网上浏览时,大家都 ...

  5. owin Oauth

    原文:http://www.cnblogs.com/richieyang/p/4918819.html 一.什么是OAuth OAuth是一个关于授权(Authorization)的开放网络标准,目前 ...

  6. ASP.NET Web API与Owin OAuth:调用与用户相关的Web API

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

  7. (转)基于OWIN WebAPI 使用OAuth授权服务【客户端模式(Client Credentials Grant)】

    适应范围 采用Client Credentials方式,即应用公钥.密钥方式获取Access Token,适用于任何类型应用,但通过它所获取的Access Token只能用于访问与用户无关的Open ...

  8. 基于OWIN WebAPI 使用OAuth授权服务【客户端验证授权(Resource Owner Password Credentials Grant)】

    适用范围 前面介绍了Client Credentials Grant ,只适合客户端的模式来使用,不涉及用户相关.而Resource Owner Password Credentials Grant模 ...

  9. 基于OWIN WebAPI 使用OAuth授权服务【客户端模式(Client Credentials Grant)】

    适应范围 采用Client Credentials方式,即应用公钥.密钥方式获取Access Token,适用于任何类型应用,但通过它所获取的Access Token只能用于访问与用户无关的Open ...

随机推荐

  1. 如何使cookie全站都生效?

    一个页面设置的cookie, 默认在其同级目录下,及子目录下可以读取. 如果想让cookie整站有效,可以在根目录下setcookie 也可以用第4个参数,来指定cookie生效路径 setcooki ...

  2. [Docker]Docker快速上手学习笔记

    0. 学习的一些疑问 如何热更新镜像(images)?(你可以快速启动或者销毁容器.这种时间几乎是实时的) 如何热更新游戏服? 好处在于各个应用之间环境相互独立,即使某一个容器崩溃也不会影响到其它容器 ...

  3. linux bash 笔记

    Bash的简单使用笔记: 1- 命令行参数(调用脚本后面跟的参数) 2- 命令行选项(修改命令行为的单字符串) 3- 获取键盘输入 4- 读文件 5- 函数 1.命令行参数(调用脚本后面跟的参数) x ...

  4. SQL数据库

    SQL是Structured Query Language(结构化查询语言)的缩写.SQL是专为数据库而建立的操作命令集,是一种功能齐全的数据库语言.在使用它时,只需要发出“做什么”的命令,“怎么做” ...

  5. js高阶函数

    我是一个对js还不是很精通的选手: 关于高阶函数详细的解释 一个高阶函数需要满足的条件(任选其一即可) 1:函数可以作为参数被传递 2:函数可以作为返回值输出 吧函数作为参数传递,这代表我们可以抽离一 ...

  6. zabbix微信告警(虚拟机脚本测试成功,zabbix上收不到信息)

    前言: 使用zabbix直接运行脚本又可以正常接收.但是登录zabbix  web界面,测试!  动作显示已送达,但是微信并没有收到信息! 解决: 添加脚本参数,因为不添加脚本参数,调用不了你这个脚本 ...

  7. hadoop多次搭建后,完整总结(累死宝宝了,搭建了十多遍了)

    1.安装JDK1.1上传运用软件FileZilla,将windows上的jdk压缩包放到linux的root目录下 1.2解压jdk #创建文件夹 mkdir /usr/java(不要挂在在" ...

  8. Sublime Text 3 Install Markdown Preview Plugins

    Sublime Text 3 Install Markdown Preview Plugins. [TOC] 前言 什么是Markdown Markdown是一种可以使用普通文本编辑器编写的标记语言, ...

  9. Vertica数据查询优化

    vertica是惠普公司推出的列式分布式数据库,在OLAP领域有其独到的地方,目前社区版免费,但是只能存放1T的数据.我在工作中维护的bi系统后端就是使用的vertica数据库,平时也经常需要对于数据 ...

  10. 使用 AForge.NET 做视频采集

    AForge.NET 是基于C#设计的,在计算机视觉和人工智能方向拥有很强大功能的框架.btw... it's an open source framework. 附上官网地址: http://www ...