前面在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. GPUImage相关(转)

    3.滤镜 除了上面提到的美颜和水印之外,视频中还有很多其它的处理效果也在这个环节完成.七牛直播云提供的 SDK 在开放性设计基础之上,通过数据源回调接口,可以支持各种自定义滤镜的接入. 为了实现丰富的 ...

  2. Linux相关文章

    1.linux 中特殊符号用法详解 2.linux之vim命令 3.linux各文件夹的作用 4.修改linux文件权限命令:chmod 5.CentOS 6.6下安装配置Tomcat环境 6.lin ...

  3. CSS3 display:flex和display:box有什么区别

    父级元素有display:box;属性之后.他的子元素里面加上box-flex属性.可以让子元素按照父元素的宽度进行一定比例的分占空间. 如: html: <article>   < ...

  4. xfire webServeic 例子

    xfire webServeic 例子,参考网上众多例子,自己写得完成了,给大家分享 大家只要按这个目录去建文件就可以了,然后运行,至于其中原理慢慢理会吧 环境:myeclipse 10 +xfire ...

  5. HQL

    以下内容全部摘自韩顺平老师Hibernate笔记 * uniqueResult方法 如果我们检索一个对象,明确知道最多只有一个对象,则建议使用该方法: 具体用法如下: Student s=(Stude ...

  6. zabbix快速安装

    运维什么的其实感觉根本没有什么好写的了,因为太简单了嘛,尤其是这个监控,整得那么麻烦要干嘛,不过貌似很多人喜欢这个,我就随手写一点吧 环境:centos7.2.1511最简安装,就是什么都不选直接就开 ...

  7. iOS button文字居中

    新建一个UIButton的category .h @interface UIButton (QXTitleInCenter) -(instancetype)init; @end .m @impleme ...

  8. reason: '-[__NSCFNumber rangeOfCharacterFromSet:]: unrecognized selector sent to instance

    类型的不匹配,把类型转化对应的数据类型,例: model.price 是模型数据,其值为1550: cell.label.text = [NSString stringWithFormat:@&quo ...

  9. Linux上安装php

    1.安装mysql  http://blog.csdn.net/wy3552128/article/details/8143686 2.安装配置Apache  http://blog.csdn.net ...

  10. ios 用户相册

    ## 获得自定义的所有相簿 // 获得所有的自定义相簿 PHFetchResult<PHAssetCollection *> *assetCollections = [PHAssetCol ...