前面在ASP.NET WEBAPI中集成了Client Credentials GrantResource Owner Password Credentials Grant两种OAUTH2模式,今天在调试Client Credentials Grant想到如下几点

  • 建议TryGetBasicCredentials认证 validate client credentials should be stored securely (salted, hashed, iterated),参考PDF设计
  • 增加token额外字段
  • 增加scope授权字段
  • 持久化Token
  • 刷新Token后失效老的Token
  • 自定义验证【重启IIS池Token失效,验证权限】

优化点

1.启用TryGetBasicCredentials认证:Basic Authentication传递clientId与clientSecret,服务端中的TryGetFormCredentials()改为TryGetBasicCredentials()
2.增加token额外字段:需要重写TokenEndpoint方法 http://stackoverflow.com/questions/26357054/return-more-info-to-the-client-using-oauth-bearer-tokens-generation-and-owin-in ,一般无特殊要求,不建议加
3.参数中传soap字段,以空格分隔的权限列表,若不传递此参数,代表请求用户的默认权限
4.重写AccessTokenProvider中CreateAsync方法,生成Token值持久化相关信息到DB
5.重写AccessTokenProvider中ReceiveAsync方法,验证Token是否有效

服务实现

配置Startup

/// <summary>
/// IOS App OAuth2 Credential Grant Password Service
/// </summary>
/// <param name="app"></param>
public void ConfigureAuth(IAppBuilder app)
{
//ClientApplicationOAuthProvider
app.UseOAuthBearerTokens(new OAuthAuthorizationServerOptions
{
//AuthorizeEndpointPath = new PathString("/authorize")
TokenEndpointPath = new PathString("/token"),
Provider = GlobalConfiguration.Configuration.DependencyResolver.GetRootLifetimeScope().Resolve<ClientAuthorizationServerProvider>(),
AccessTokenProvider = GlobalConfiguration.Configuration.DependencyResolver.GetRootLifetimeScope().Resolve<AccessTokenAuthorizationServerProvider>(),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(1),
AuthenticationMode = AuthenticationMode.Active,
//HTTPS is allowed only AllowInsecureHttp = false
#if DEBUG
AllowInsecureHttp = true,
#endif
ApplicationCanDisplayErrors = true,
}); /*
//PasswordAuthorizationServerProvider
app.UseOAuthBearerTokens(new OAuthAuthorizationServerOptions
{
//!!!
// AccessTokenProvider=
TokenEndpointPath = new PathString("/token"),
//Provider = new ClientApplicationOAuthProvider(),
//Provider = new PasswordAuthorizationServerProvider(),
//Provider = DependencyInjectionConfig.container.Resolve<PasswordAuthorizationServerProvider>(),
//Provider = DependencyResolver.Current.GetService<PasswordAuthorizationServerProvider>(),
Provider = GlobalConfiguration.Configuration.DependencyResolver.GetRootLifetimeScope().Resolve<PasswordAuthorizationServerProvider>(),
RefreshTokenProvider = GlobalConfiguration.Configuration.DependencyResolver.GetRootLifetimeScope().Resolve<RefreshAuthenticationTokenProvider>(),
AccessTokenExpireTimeSpan = TimeSpan.FromHours(2),
AuthenticationMode = AuthenticationMode.Active,
//HTTPS is allowed only AllowInsecureHttp = false
#if DEBUG
AllowInsecureHttp = true,
#endif
});
*/ //app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
//app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}

集成Autofac

            //注册 Password Grant 授权服务
builder.RegisterType<PasswordAuthorizationServerProvider>().AsSelf().SingleInstance();
builder.RegisterType<RefreshAuthenticationTokenProvider>().AsSelf().SingleInstance();
//注册 Credential Grant Password
builder.RegisterType<ClientAuthorizationServerProvider>().AsSelf().SingleInstance();
builder.RegisterType<AccessTokenAuthorizationServerProvider>().AsSelf().SingleInstance();
//在Autofac中注册Redis的连接,并设置为Singleton (官方建議保留Connection,重複使用)
//builder.Register(r =>{ return ConnectionMultiplexer.Connect(DBSetting.Redis);}).AsSelf().SingleInstance();
var container = builder.Build();
GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);

启用不记名验证

public static void Register(HttpConfiguration config)
{
// Web API 配置和服务 // Configure Web API to use only bearer token authentication.
config.SuppressDefaultHostAuthentication(); config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Web API 路由
config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}

服务端

/// <summary>
/// Client Credentials 授权
/// </summary>
public class ClientAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
/// <summary>
/// 授权服务
/// </summary>
private readonly IClientAuthorizationService _clientAuthorizationService; /// <summary>
/// 账户服务
/// </summary>
private readonly IAccountService _accountService; /// <summary>
/// 构造函数
/// </summary>
/// <param name="clientAuthorizationService">授权服务</param>
/// <param name="accountService">用户服务</param>
public ClientAuthorizationServerProvider(IClientAuthorizationService clientAuthorizationService, IAccountService accountService)
{
_clientAuthorizationService = clientAuthorizationService;
_accountService = accountService;
} /// <summary>
/// 验证Client Credentials[client_id与client_secret]
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
//http://localhost:48339/token
//grant_type=client_credentials&client_id=irving&client_secret=123456&scope=user order
/*
grant_type 授与方式(固定为 “client_credentials”)
client_id 分配的调用oauth的应用端ID
client_secret 分配的调用oaut的应用端Secret
scope 授权权限。以空格分隔的权限列表,若不传递此参数,代表请求用户的默认权限
*/
//validate client credentials should be stored securely (salted, hashed, iterated)
string clientId;
string clientSecret;
context.TryGetBasicCredentials(out clientId, out clientSecret);
//验证用户名密码
var clientValid = await _clientAuthorizationService.ValidateClientAuthorizationSecretAsync(clientId, clientSecret);
if (!clientValid)
{
//Flurl 404 问题
//context.Response.StatusCode = Convert.ToInt32(HttpStatusCode.OK);
//context.Rejected();
context.SetError(AbpConstants.InvalidClient, AbpConstants.InvalidClientErrorDescription);
return;
}
//need to make the client_id available for later security checks
context.OwinContext.Set<string>("as:client_id", clientId);
context.Validated(clientId);
} /// <summary>
/// 客户端授权[生成access token]
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public override Task GrantClientCredentials(OAuthGrantClientCredentialsContext context)
{
/*
var client = _oauthClientService.GetClient(context.ClientId);
claimsIdentity.AddClaim(new Claim(ClaimTypes.Name, client.ClientName));
*/
//验证权限
int scopeCount = context.Scope.Count;
if (scopeCount > 0)
{
string name = context.Scope[0].ToString();
}
//默认权限
var claimsIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
//!!!
claimsIdentity.AddClaim(new Claim(ClaimTypes.Name, context.ClientId));
var props = new AuthenticationProperties(new Dictionary<string, string> {
{
"client_id",context.ClientId
},
{
"scope",string.Join(" ",context.Scope)
}
});
var ticket = new AuthenticationTicket(claimsIdentity, props);
context.Validated(ticket);
return base.GrantClientCredentials(context);
} /// <summary>
/// http://stackoverflow.com/questions/26357054/return-more-info-to-the-client-using-oauth-bearer-tokens-generation-and-owin-in
/// My recommendation is not to add extra claims to the token if not needed, because will increase the size of the token and you will keep sending it with each request. As LeftyX advised add them as properties but make sure you override TokenEndPoint method to get those properties as a response when you obtain the toke successfully, without this end point the properties will not return in the response.
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
{
foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
{
context.AdditionalResponseParameters.Add(property.Key, property.Value);
}
return base.TokenEndpoint(context);
}
}

Token生成与验证

/// <summary>
/// 生成与验证Token
/// </summary>
public class AccessTokenAuthorizationServerProvider : AuthenticationTokenProvider
{
/// <summary>
/// 授权服务
/// </summary>
private readonly IClientAuthorizationService _clientAuthorizationService; /// <summary>
/// 构造函数
/// </summary>
/// <param name="clientAuthorizationService">授权服务</param>
public AccessTokenAuthorizationServerProvider(IClientAuthorizationService clientAuthorizationService)
{
_clientAuthorizationService = clientAuthorizationService;
} //<summary>
//创建Token
//</summary>
//<param name="context">上下文</param>
//<returns></returns>
public override async Task CreateAsync(AuthenticationTokenCreateContext context)
{
if (string.IsNullOrEmpty(context.Ticket.Identity.Name)) return;
string IpAddress = context.Request.RemoteIpAddress + ":" + context.Request.RemotePort;
var token = new Token()
{
ClientId = context.Ticket.Identity.Name,
ClientType = "client_credentials",
Scope = context.Ticket.Properties.Dictionary["scope"],
UserName = context.Ticket.Identity.Name,
IssuedUtc = DateTime.Parse(context.Ticket.Properties.IssuedUtc.ToString()),
ExpiresUtc = DateTime.Parse(context.Ticket.Properties.IssuedUtc.ToString()),
IpAddress = IpAddress
};
token.AccessToken = context.SerializeTicket();
token.RefreshToken = string.Empty;//await _clientAuthorizationService.GenerateOAuthClientSecretAsync();
//Token没有过期的情况强行刷新,删除老的Token保存新的Token
if (await _clientAuthorizationService.SaveTokenAsync(token))
{
context.SetToken(token.AccessToken);
}
} //<summary>
//验证Token
//</summary>
//<param name="context">上下文</param>
//<returns></returns>
public override async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
var request = new OAuthRequestTokenContext(context.OwinContext, context.Token);
var ticket = new AuthenticationTicket(new ClaimsIdentity(), new AuthenticationProperties()
{
IssuedUtc = DateTime.UtcNow.AddYears(-1),
ExpiresUtc = DateTime.UtcNow.AddYears(-1)
});
if (request == null || request.Token.IsNullOrEmpty())
{
context.SetTicket(ticket);
}
//验证Token是否过期
var vaild = await _clientAuthorizationService.VaildOAuthClientSecretAsync();
if (vaild)
{
context.SetTicket(ticket);
}
}
}

有赞API文档

无意看到有赞的API文档:http://open.koudaitong.com/doc,大致分为三个部分。

1.基于OAUTH2授权【几种授权模式全实现】

2.基于签名的方式【HMAC】

3.各种语言的SDK

大致设计比较规范,后续时间再参考规范基于ASP.NET WEBAPI 集成优化OAUTH2与HMAC部分。

基于OWIN ASP.NET WebAPI 使用OAUTH2授权服务的几点优化的更多相关文章

  1. 基于OWIN WebAPI 使用OAUTH2授权服务【授权码模式(Authorization Code)】

    之前已经简单实现了OAUTH2的授权码模式(Authorization Code),但是基于JAVA的,今天花了点时间调试了OWIN的实现,基本就把基于OWIN的OAUHT2的四种模式实现完了.官方推 ...

  2. ASP.NET WebAPI构建API接口服务实战演练

    一.课程介绍 一.王小二和他领导的第一次故事 有一天王小二和往常一下去上早班,刚吃完早餐刚一打开电脑没一会儿.王小二的领导宋大宝走到他的面前,我们现在的系统需要提供服务给其他内部业务系统,我看你平时喜 ...

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

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

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

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

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

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

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

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

  7. WebApi Owin SelfHost OAuth2 - 授权服务和资源服务分离方案

    使用JWT 参考:http://www.cnblogs.com/grissom007/p/6294746.html

  8. 一站式WebAPI与认证授权服务

    保护WEBAPI有哪些方法? 微软官方文档推荐了好几个: Azure Active Directory Azure Active Directory B2C (Azure AD B2C)] Ident ...

  9. Owin中间件搭建OAuth2.0认证授权服务体会

    继两篇转载的Owin搭建OAuth 2.0的文章,使用Owin中间件搭建OAuth2.0认证授权服务器和理解OAuth 2.0之后,我想把最近整理的资料做一下总结. 前两篇主要是介绍概念和一个基本的D ...

随机推荐

  1. Python自动化 【第三篇】:Python基础-集合、文件操作、字符编码与转码、函数

    1.        集合 1.1      特性 集合是一个无序的,不重复的数据组合,主要作用如下: 去重,把一个列表变成集合实现自动去重. set可以看成数学意义上的无序和无重复元素的集合,因此,两 ...

  2. gulp-htmlmin压缩html

    通过一条命令用Npm安装gulp-htmlmin: npm install gulp-htmlmin --save-dev 安装完毕后,打开gulpfile.js文件,我们里面编写一个task用来专门 ...

  3. nginx rewirte

    server { listen 8888; server_name jobPhp; root F:\ck\Porject\quanRelease\quanJob; try_files $uri $ur ...

  4. VC++ 如何在显示对话框的时候,指定焦点控件!

    很简单: 在你的CAddDlg类的OnInitDialog函数中加上你上面的代码GetDlgItem(IDC_EDIT1)->SetFocus();最后记得return FALSE; 其实,不知 ...

  5. [转]Debug 和 Release 编译方式的区别

    本文主要包含如下内容: 1. Debug 和 Release 编译方式的本质区别 2. 哪些情况下 Release 版会出错 3. 怎样“调试” Release 版的程序 Debug 和 Releas ...

  6. poj 2987 最大权闭合图

    Language: Default Firing Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 8744   Accept ...

  7. ORACLE 数据块dump

    1. rdba(Tablespace relative database block address) 是相对数据块地址,是数据所在的地址,rdba可就是rowid 中rfile#+block#. 根 ...

  8. windows 系统下 Firefox hostadmin插件无法修改Host

    firefox hostAdmin插件无法修改Host了,提示“ write hosts file failed check permissions”,肯定是权限出现了问题??? 使用管理员权限打开c ...

  9. cocos2d-x 坐标系

    OPenGL坐标系:原点为屏幕左下角 屏幕坐标系:原点在屏幕左上角

  10. android APK应用安装过程以及默认安装路径[转]

    一:安装过程 APK是类似Symbian Sis或Sisx的文件格式.通过将APK文件直接传到Android模拟器或Android手机中执行即可安装. Android应用安装有如下四种方式 1.   ...