如何基于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. DEV设计之自动流水号,DEV专家解答,自己折腾了半天也没有搞定,怪英文不好

    () 老外专家给了回答,结果没有全到懂,又折腾了20分钟朋友提示才搞定 获取一个自动增加1的流水号值, 第一个参数是本事的数据库连接对象,第2个参数是也这个值为唯一标识返回来一个增量的值,第三个好像没 ...

  2. python json模块

    import json dic = {"name":"liao","age":18} data = json.dumps(dic) #转化为 ...

  3. javascript里的封装

    用javascript闭包的特性,可以模拟实现私有变量.私有方法. var myObject = =(function(){ var privateValue; function privateMet ...

  4. button、label、textfield、页面跳转、传值

    .AppDelegate.m #import “OneViewController.h” //一打开就运行的 -(BOOL)application:(UIApplication *)applicati ...

  5. spring官网改版后,如何下载jar包

    http://repo.springsource.org/libs-release-local/org/springframework 通过以上地址,可树型查看所有发布的包 问题是,官网页面上为何找不 ...

  6. 堆排序Heap sort

    堆排序有点小复杂,分成三块 第一块,什么是堆,什么是最大堆 第二块,怎么将堆调整为最大堆,这部分是重点 第三块,堆排序介绍 第一块,什么是堆,什么是最大堆 什么是堆 这里的堆(二叉堆),指得不是堆栈的 ...

  7. JavaScript的Ajax请求示例

    //创建XMLHttpRequest对象              var request = false;            try {                request = new ...

  8. 46. Permutations 回溯算法

    https://leetcode.com/problems/permutations/ 求数列的所有排列组合.思路很清晰,将后面每一个元素依次同第一个元素交换,然后递归求接下来的(n-1)个元素的全排 ...

  9. 基于Chrome内核(WebKit.net)定制开发DoNet浏览器

    1.    源起 a)     定制.Net浏览器 本人是一名C#开发者,而作为C#开发者,做客户端应用中最头痛的一件事就是没有一个好的UI解决方案, WinFrom嘛,效率虽然还不错,但是做一些特殊 ...

  10. [UCSD白板题] Compute the Edit Distance Between Two Strings

    Problem Introduction The edit distinct between two strings is the minimum number of insertions, dele ...