基于OWIN ASP.NET WebAPI 使用OAUTH2授权服务的几点优化
前面在ASP.NET WEBAPI中集成了Client Credentials Grant与Resource 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授权服务的几点优化的更多相关文章
- 基于OWIN WebAPI 使用OAUTH2授权服务【授权码模式(Authorization Code)】
之前已经简单实现了OAUTH2的授权码模式(Authorization Code),但是基于JAVA的,今天花了点时间调试了OWIN的实现,基本就把基于OWIN的OAUHT2的四种模式实现完了.官方推 ...
- ASP.NET WebAPI构建API接口服务实战演练
一.课程介绍 一.王小二和他领导的第一次故事 有一天王小二和往常一下去上早班,刚吃完早餐刚一打开电脑没一会儿.王小二的领导宋大宝走到他的面前,我们现在的系统需要提供服务给其他内部业务系统,我看你平时喜 ...
- (转)基于OWIN WebAPI 使用OAuth授权服务【客户端模式(Client Credentials Grant)】
适应范围 采用Client Credentials方式,即应用公钥.密钥方式获取Access Token,适用于任何类型应用,但通过它所获取的Access Token只能用于访问与用户无关的Open ...
- 基于OWIN WebAPI 使用OAuth授权服务【客户端验证授权(Resource Owner Password Credentials Grant)】
适用范围 前面介绍了Client Credentials Grant ,只适合客户端的模式来使用,不涉及用户相关.而Resource Owner Password Credentials Grant模 ...
- 基于OWIN WebAPI 使用OAuth授权服务【客户端模式(Client Credentials Grant)】
适应范围 采用Client Credentials方式,即应用公钥.密钥方式获取Access Token,适用于任何类型应用,但通过它所获取的Access Token只能用于访问与用户无关的Open ...
- 在ASP.NET中基于Owin OAuth使用Client Credentials Grant授权发放Token
OAuth真是一个复杂的东东,即使你把OAuth规范倒背如流,在具体实现时也会无从下手.因此,Microsoft.Owin.Security.OAuth应运而生(它的实现代码在Katana项目中),帮 ...
- WebApi Owin SelfHost OAuth2 - 授权服务和资源服务分离方案
使用JWT 参考:http://www.cnblogs.com/grissom007/p/6294746.html
- 一站式WebAPI与认证授权服务
保护WEBAPI有哪些方法? 微软官方文档推荐了好几个: Azure Active Directory Azure Active Directory B2C (Azure AD B2C)] Ident ...
- Owin中间件搭建OAuth2.0认证授权服务体会
继两篇转载的Owin搭建OAuth 2.0的文章,使用Owin中间件搭建OAuth2.0认证授权服务器和理解OAuth 2.0之后,我想把最近整理的资料做一下总结. 前两篇主要是介绍概念和一个基本的D ...
随机推荐
- 將後台的Json數據返回到前台
前台JS代碼 $.post('/Book/GetBookClassIDByName', { BookName: "旅遊手冊" }, function (data) { if (da ...
- 如何防止ElasticSearch集群出现脑裂现象(转)
原文:http://xingxiudong.com/2015/01/05/resolve-elasticsearch-split-brain/ 什么是“脑裂”现象? 由于某些节点的失效,部分节点的网络 ...
- 使用Ganglia监控hadoop、hbase
Ganglia是一个监控服务器,集群的开源软件,能够用曲线图表现最近一个小时,最近一天,最近一周,最近一月,最近一年的服务器或者集群的cpu负载,内存,网络,硬盘等指标. Ganglia的强大在于:g ...
- asp.net下拉列表绑定数据库多个字段
select ID, CONVERT(varchar(10),TBName) +','+RoomName+ ',最大人数:'+CONVERT(varchar(10),MAXNum) as ClassR ...
- C/C++关键字 extern
1.基本解释:extern 可置于变量或函数前面,表示变量或函数的定义在别的文件中,以提示编译器遇到此变量或函数时在其他模块中寻找定义. extern还有另外2个作用.第一:与“C”连用时,如 ext ...
- 运行时报错-Verify the Developer App certificate for youraccount is trusted on your device. Open Settings on Mayoyi_sakura and navigate to General -> Device Management, then select your
解决方法:打开手机设置->通用->设备管理,找到编辑工程时的ID资料,点击允许即可.
- AVL树模板
///AVL树模板 typedef struct Node ///树的节点 { int val,data; int h; ///以当前结点为根结点的数的高度 int bf; ///平衡因子(左子树高度 ...
- Linux下守护进程初探
守护进程一直后台运行,与终端脱离,一般负责周期性地执行系统任务,服务器应用一般以守护进程进行运行.实现一个守护进程主要分为5部: 1.脱离父进程 2.setsid(可以选择再次fork) 3.umas ...
- 快速排序(java实现)
快速排序 算法思想:基于分治的思想,是冒泡排序的改进型.首先在数组中选择一个基准点(该基准点的选取可能影响快速排序的效率,后面讲解选取的方法),然后分别从数组的两端扫描数组,设两个指示标志(lo指向起 ...
- css3之过渡
transition属性 属性 描述 transition 设置4个过渡效果属性值 transition-property 设置过渡的属性名 transition-duration 设置过渡效果时间, ...