前面在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. C# 读取Excel内容

    一.方法 1.OleD方法实现该功能. 2.本次随笔内容只包含读取Excel内容,并另存为. 二.代码 (1)找到文档代码 OpenFileDialog openFile = new OpenFile ...

  2. CString转换成char*

    CString转换成char* :charSource = (char*)strSource.GetBuffer(0); 法2:charSource = (char*)strSource.GetBuf ...

  3. 一段防盗连的PHP代码

    $ADMIN[defaulturl] = http://www.163.com/404.htm;  //盗链返回的地址  $okaysites = array("http://www.163 ...

  4. MS SQL Server之光标、存储过程和触发器

    光标 通常数据库操作被认为是以数据集为基础的操作,但是光标被用于以记录为单位来进行操作,来获取数据库中的数据的子集.光标一般用于过程化程序里的嵌入的SQL语句. 对光标的定义如下: DECLARE C ...

  5. wamp密码设置

    WAMP安装好后,mysql密码是为空的,那么要如何修改呢?其实很简单,通过几条指令就行了,下面我就一步步来操作. 首先,通过WAMP打开mysql控制台. 提示输入密码,因为现在是空,所以直接按回车 ...

  6. 转载:PuTTY的自动登录设置

    转自:http://blog.segmentfault.com/zair/1190000000639516 PuTTY是Windows下非常好用的SSH远程登陆客户端.本文介绍两种自动登录的设置方法. ...

  7. Android四大组件之——Activity的生命周期(图文详解)

        转载请在文章开头处注明本博客网址:http://www.cnblogs.com/JohnTsai       联系方式:JohnTsai.Work@gmail.com       [Andro ...

  8. C++STL学习笔记_(1)string知识

    /*============================================ string是STL的字符串类型,通常用来表示字符串 = ======================== ...

  9. CF(协同过滤算法)

    1 集体智慧和协同过滤 1.1 什么是集体智慧(社会计算)? 集体智慧 (Collective Intelligence) 并不是 Web2.0 时代特有的,只是在 Web2.0 时代,大家在 Web ...

  10. opencv写视频

    代码: #include<opencv2/opencv.hpp> using namespace cv; #include<string> using namespace st ...