Web API2 identity2 承载令牌权限更改
Web API2 identity2 bearer token permission change
使用 Owin + Oauth2 + Identity2。
我有一个 Web Api,其中包含我已修改的默认基本身份验证设置。
我的startup.cs部分class
public void ConfigureAuth(IAppBuilder app)
{
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);//TODO: prob wont need this
// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),//TODO: prob wont need this
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
// In production mode set AllowInsecureHttp = false
AllowInsecureHttp = true //TODO: set debug mode
};
// Token Generation
app.UseOAuthBearerTokens(OAuthOptions);
}
我的 startup.cs class 部分在根部
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
ConfigureAuth(app);
WebApiConfig.Register(config);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
}
我的applicationOAuthProvider.cs
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
//get user
var service = new CarrierApi.CarrierManagementClient();
var result = service.LoginAsync(context.UserName, context.Password);
var user = result.Result.Identity;
//TODO: log stuff here? i.e lastlogged etc?
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
ClaimsIdentity oAuthIdentity = user;
ClaimsIdentity cookiesIdentity = user;
AuthenticationProperties properties = CreateProperties(user.GetUserName());
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesIdentity);
}
如您所见,我实际上是通过对现有数据库的 wcf 调用来获取身份的。使用邮递员时,我得到 /token url 并获得我的不记名令牌,在下一个请求中我将它传递到 header 并调用我的控制器方法。
[Authorize(Roles = "Templates_Access")]
public string Post([FromBody]string value)
{
return "woo";
}
这很好用,如果用户有权限,它不会允许访问,如果他们允许的话。
但是,如果我访问我们使用相同 wcf 和 DB 的网站并更改用户权限,如果我向邮递员发送相同的请求,它仍然允许访问,即使我删除了分配给用户的角色的权限也是。
如何确保权限是 "refreshed" 或在每次请求时再次检查?
登录用户的每个角色都在登录时作为声明存储在持有者令牌中,在 GrantResourceOwnerCredentials 方法中。如果必须授权请求,则通过 AuthorizationFilter 的默认实现在不记名令牌中存储的列表中搜索角色;因此,如果您更改用户的权限,则需要重新登录。
这种行为尊重 Restfull 架构的无状态约束,正如 Fielding 在他的 dissertation 中所写的那样,这也是性能和安全性之间的良好平衡
如果您需要不同的行为,则有不止一种可能性。
刷新令牌
可以使用Refresh Token,实现applicationOAuthProvider的GrantRefreshToken方法class;您可以检索刷新的用户权限并创建新的访问令牌;这是一篇学习的好文章how。
牢记:
- 客户端更复杂
- 无实时效果;您必须等待访问令牌过期
- 如果Access Token寿命短,你必须经常更新它(即使用户权限没有改变)否则寿命长也不能解决问题
检查每个请求的权限
您可以实施自定义 AuthorizationFilter 并在数据库中检查用户的权限,但这是一个缓慢的解决方案。
缓存和登录会话
您可以在 GrantResourceOwnerCredentials 方法中为每次登录生成用户会话的密钥(如 guid),并将其作为声明存储在不记名令牌中。您还必须使用两个索引将其存储在缓存系统(如 Redis)中:用户会话的密钥和 userId。 Redis的官方文档有说明how。
当一个用户的权限改变时,你可以在缓存系统中使该用户的每个会话失效,通过userId搜索
您可以实现自定义 AuthorizationFilter 并检查缓存中的每个请求是否有效,通过用户会话的密钥进行搜索。
注意:这将违反无状态约束,您的架构将不会是 restfull
在这里您可以找到 AuthorizaAttribute filter 的标准实现。
您可以创建自定义过滤器,扩展 AuthorizeAttribute 并覆盖 IsAuthorized 方法。
很可能还有其他方法,但更改用户权限的频率如何?在许多系统中,也是在安全性是第一要求的系统中,如果在活动会话期间更改了用户的权限配置,则需要重新登录才能激活新登录。
您确定需要修改此标准行为吗?
如果你是,我建议使用缓存系统的解决方案。
使用 Owin + Oauth2 + Identity2。
我有一个 Web Api,其中包含我已修改的默认基本身份验证设置。
我的startup.cs部分class
public void ConfigureAuth(IAppBuilder app)
{
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);//TODO: prob wont need this
// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),//TODO: prob wont need this
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
// In production mode set AllowInsecureHttp = false
AllowInsecureHttp = true //TODO: set debug mode
};
// Token Generation
app.UseOAuthBearerTokens(OAuthOptions);
}
我的 startup.cs class 部分在根部
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
ConfigureAuth(app);
WebApiConfig.Register(config);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
}
我的applicationOAuthProvider.cs
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
//get user
var service = new CarrierApi.CarrierManagementClient();
var result = service.LoginAsync(context.UserName, context.Password);
var user = result.Result.Identity;
//TODO: log stuff here? i.e lastlogged etc?
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
ClaimsIdentity oAuthIdentity = user;
ClaimsIdentity cookiesIdentity = user;
AuthenticationProperties properties = CreateProperties(user.GetUserName());
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(cookiesIdentity);
}
如您所见,我实际上是通过对现有数据库的 wcf 调用来获取身份的。使用邮递员时,我得到 /token url 并获得我的不记名令牌,在下一个请求中我将它传递到 header 并调用我的控制器方法。
[Authorize(Roles = "Templates_Access")]
public string Post([FromBody]string value)
{
return "woo";
}
这很好用,如果用户有权限,它不会允许访问,如果他们允许的话。
但是,如果我访问我们使用相同 wcf 和 DB 的网站并更改用户权限,如果我向邮递员发送相同的请求,它仍然允许访问,即使我删除了分配给用户的角色的权限也是。
如何确保权限是 "refreshed" 或在每次请求时再次检查?
登录用户的每个角色都在登录时作为声明存储在持有者令牌中,在 GrantResourceOwnerCredentials 方法中。如果必须授权请求,则通过 AuthorizationFilter 的默认实现在不记名令牌中存储的列表中搜索角色;因此,如果您更改用户的权限,则需要重新登录。
这种行为尊重 Restfull 架构的无状态约束,正如 Fielding 在他的 dissertation 中所写的那样,这也是性能和安全性之间的良好平衡
如果您需要不同的行为,则有不止一种可能性。
刷新令牌
可以使用Refresh Token,实现applicationOAuthProvider的GrantRefreshToken方法class;您可以检索刷新的用户权限并创建新的访问令牌;这是一篇学习的好文章how。
牢记:
- 客户端更复杂
- 无实时效果;您必须等待访问令牌过期
- 如果Access Token寿命短,你必须经常更新它(即使用户权限没有改变)否则寿命长也不能解决问题
检查每个请求的权限
您可以实施自定义 AuthorizationFilter 并在数据库中检查用户的权限,但这是一个缓慢的解决方案。
缓存和登录会话
您可以在 GrantResourceOwnerCredentials 方法中为每次登录生成用户会话的密钥(如 guid),并将其作为声明存储在不记名令牌中。您还必须使用两个索引将其存储在缓存系统(如 Redis)中:用户会话的密钥和 userId。 Redis的官方文档有说明how。
当一个用户的权限改变时,你可以在缓存系统中使该用户的每个会话失效,通过userId搜索
您可以实现自定义 AuthorizationFilter 并检查缓存中的每个请求是否有效,通过用户会话的密钥进行搜索。
注意:这将违反无状态约束,您的架构将不会是 restfull
在这里您可以找到 AuthorizaAttribute filter 的标准实现。 您可以创建自定义过滤器,扩展 AuthorizeAttribute 并覆盖 IsAuthorized 方法。
很可能还有其他方法,但更改用户权限的频率如何?在许多系统中,也是在安全性是第一要求的系统中,如果在活动会话期间更改了用户的权限配置,则需要重新登录才能激活新登录。 您确定需要修改此标准行为吗?
如果你是,我建议使用缓存系统的解决方案。