如何基于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. 复制mueclipse项目到eclipse

    本文适用于将MyEclipse上的项目projectA检出后重命名为projectB的情况,如果只是检出projectA到Eclipse,也可以部分参考 1.从svn上检出Myeclipse项目到Ec ...

  2. Android studio debug模式获取变量的值

    打断点.debug模式运行,Console界面旁边的Debugger界面,或者在变量上右键add to watches

  3. 【转】HTTP POST GET 本质区别详解

    一 原理区别 一般在浏览器中输入网址访问资源都是通过GET方式:在FORM提交中,可以通过Method指定提交方式为GET或者POST,默认为GET提交 Http定义了与服务器交互的不同方法,最基本的 ...

  4. ServiceStack.OrmLite中的一些"陷阱"(1)

    使用过ServiceStack.Ormlite的人都应该知道,其作为一个轻量级的ORM,使用的便捷度非常高,用起来就一个字:爽!而支撑其便捷度的,是库内大量地使用了扩展方法及静态变量. 首先先从源头入 ...

  5. java反射案例

     Java反射经典实例 2007-08-29 17:55:25 分类: Java Java提供了一套机制来动态执行方法和构造方法,以及数组操作等,这套机制就叫——反射.反射机制是如今很多流行框架的实现 ...

  6. 如何更改OS系统下截图生成图片格式 jpg pdf

    Mac系统下快速截屏的快捷键: 1.截全屏: shift + command + 3 2.选取截屏 shift + command + 4 生成的图片,系统默认格式忘了,反正不好用,用下面命令更改生成 ...

  7. Cracking-- 17.13 将二叉树转换成双向链表

    在书的105页 使用中根遍历的思想 left 之后 为 root 之后 为 right 则对左子树来说 left->right = root; root->left = left; 对右子 ...

  8. yii使用createCommand()增删改查

    查询单条数据$sql = "SELECT `name` FROM `table` WHERE id='7'";$users=Yii::$app->db->createC ...

  9. jQuery基础_2

    属性:attr: 属性 , 元素.attr("属性名");获取属性attr("attr","value"): 设置属性attr({value ...

  10. JavaScript-在当前显示区范围内实现点不到的小方块

    <!doctype html> <html> <head> <meta charset="UTF-8"> <title> ...