Beh*_*ooz 6 authentication oauth access-token asp.net-web-api2 asp.net-identity-2
我已经在Asp.Net Web Api 2中开发了一个认证机制,它具有授予刷新令牌的功能,基于Taiseer博客上的教程.
这是我的问题.假设以下情形:用户使用密码登录并获取刷新令牌和访问令牌.访问令牌实际上包括他所处的角色(因此他在应用程序中的权限).同时系统管理员将更改此人的角色,因此一旦他的访问令牌到期并且他想使用刷新令牌获取新的访问令牌,他的新访问令牌必须包括新更新的角色.
在我的"RefreshTokenProvider"类中,我使用"GrantResourceOwnerCredentials"方法中的以下代码从数据库中获取用户角色并将其添加到声明中:
var roleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(new ApplicationDbContext()));
var y = roleManager.Roles.ToList();
var id = new ClaimsIdentity(context.Options.AuthenticationType);
id.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
id.AddClaim(new Claim("sub", context.UserName));
var roles2 = UserRoleManagerProvider.RoleManager().Roles.ToList();
foreach (IdentityRole i in roles2)
{
if (roleIds.Contains(i.Id))
id.AddClaim(new Claim(ClaimTypes.Role, i.Name));
}
Run Code Online (Sandbox Code Playgroud)
这件作品很好(即使我相信应该有更好的方法吗?!)
但是,无法正常工作的部分是在"GrantRefreshToken"方法中,我们需要更新角色以便在新的访问令牌中反映它们:
var newId = new ClaimsIdentity(context.Ticket.Identity);
// *** Add shit here....
var userId = context.Ticket.Properties.Dictionary["userId"];
IdentityUser user = UserRoleManagerProvider.UserManager().FindById(userId);
foreach (Claim c in newId.Claims)
{
if (c.Type == ClaimTypes.Role) newId.RemoveClaim(c);
}
if (user.Roles.Count > 0)
{
var roleIds = new List<string>();
var roles2 = UserRoleManagerProvider.RoleManager().Roles.ToList();
foreach (IdentityUserRole ir in user.Roles)
{
roleIds.Add(ir.RoleId);
}
foreach (IdentityRole r in roles2)
{
if (roleIds.Contains(r.Id))
newId.AddClaim(new Claim(ClaimTypes.Role, r.Name));
}
}
Run Code Online (Sandbox Code Playgroud)
再说一次,如果有更好的方法可以做到这一点,我会感激你的帮助!但主要是,我的问题是删除不再生效的角色的部分不起作用.你有没有机会知道这件作品有什么问题?!
仅供参考,在上面的代码中,"UserRoleManagerProvider"是我创建的一个简单的静态类,如下所示:
public static class UserRoleManagerProvider
{
public static RoleManager<IdentityRole> RoleManager()
{
var roleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(new ApplicationDbContext()));
return roleManager;
}
public static UserManager<IdentityUser> UserManager()
{
var userManager = new UserManager<IdentityUser>(new UserStore<IdentityUser>(new ApplicationDbContext()));
return userManager;
}
}
Run Code Online (Sandbox Code Playgroud)
小智 5
这个问题很难回答,而且由于需要包含的内容很多,所以我尝试将一些问题分开。
索赔
有两种方法可以将声明添加到 ClaimsIdentity。
请注意区别!虽然在所有情况下都称为 AddClaim,但第一个变体将声明添加到存储中,而第二个变体将声明直接添加到 ClaimsIdentity。
那么如何将持久声明添加到 ClaimsIdentity 中呢?这是自动完成的!
顺便说一句,您可以使用属性扩展 IdentityUser,但也可以将用户声明添加到存储中。在这两种情况下,声明都会添加到 ClaimsIdentity 中。必须在 ApplicationUser.GenerateUserIdentityAsync 中添加扩展属性:
public class ApplicationUser : IdentityUser
{
public string DisplayName { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
userIdentity.AddClaim(new Claim("DisplayName", DisplayName));
return userIdentity;
}
}
Run Code Online (Sandbox Code Playgroud)
流动
在发出新的 access_token 之前,服务器必须验证用户。可能有一些原因导致服务器无法颁发新的access_token。还必须考虑更改的配置。有两个提供者可以进行此设置。access_token 提供程序和refresh_token 提供程序。
当客户端向令牌端点发出请求 (grant_type = *) 时,首先执行 AccessTokenProvider.ValidateClientAuthentication。如果您正在使用 client_credentials 那么您可以在这里做一些事情。但对于当前的流量,我们假设context.Validated();
提供商支持各种流程。您可以在这里阅读:https://msdn.microsoft.com/en-us/library/microsoft.owin.security.oauth.oauthauthorizationserverprovider (v=vs.113).aspx
该提供商是作为选择加入而构建的。如果您不重写某些方法,则访问将被拒绝。
访问令牌
要获取访问令牌,必须发送凭据。对于这个例子,我将假设“grant_type = password”。在 AccessTokenProvider.GrantResourceOwnerCredentials 中,检查凭据、设置 ClaimsIdentity 并颁发令牌。
为了将refresh_token添加到票证中,我们需要重写AccessTokenProvider.GrantRefreshToken。这里你有两个选择:拒绝令牌。因为refresh_token被撤销或者由于其他原因不允许用户再使用刷新令牌。或者设置一个新的 ClaimsIdentity 来为票证生成一个新的 access_token。
class AccessTokenProvider : OAuthAuthorizationServerProvider
{
public override async Task GrantRefreshToken(OAuthGrantRefreshTokenContext context)
{
// Reject token: context.Rejected(); Or:
// chance to change authentication ticket for refresh token requests
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
var appUser = await userManager.FindByNameAsync(context.Ticket.Identity.Name);
var oAuthIdentity = await appUser.GenerateUserIdentityAsync(userManager);
var newTicket = new AuthenticationTicket(oAuthIdentity, context.Ticket.Properties);
context.Validated(newTicket);
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
var appUser = await userManager.FindAsync(context.UserName, context.Password);
if (appUser == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
var propertyDictionary = new Dictionary<string, string> { { "userName", appUser.UserName } };
var properties = new AuthenticationProperties(propertyDictionary);
var oAuthIdentity = await appUser.GenerateUserIdentityAsync(userManager);
var ticket = new AuthenticationTicket(oAuthIdentity, properties);
// Token is validated.
context.Validated(ticket);
}
}
Run Code Online (Sandbox Code Playgroud)
如果上下文有经过验证的票证,则调用 RefreshTokenProvider。在 Create 方法中,您可以设置过期时间并选择将刷新令牌添加到票证中。当前令牌尚未过期时,请勿发行新令牌。否则用户可能永远不必再次登录!
如果刷新令牌以某种方式保留下来,您始终可以添加它。或者您可以仅在登录时添加新的刷新令牌。用户已被识别,因此“旧”refresh_token 不再重要,因为它会在新的refresh_token 之前过期。如果您只想使用一个活动的 refesh_token,那么您必须保留它。
class RefreshTokenProvider : AuthenticationTokenProvider
{
public override void Create(AuthenticationTokenCreateContext context)
{
var form = context.Request.ReadFormAsync().Result;
var grantType = form.GetValues("grant_type");
// do not issue a new refresh_token if a refresh_token was used.
if (grantType[0] != "refresh_token")
{
// 35 days.
int expire = 35 * 24 * 60 * 60;
context.Ticket.Properties.ExpiresUtc = new DateTimeOffset(DateTime.Now.AddSeconds(expire));
// Add the refresh_token to the ticket.
context.SetToken(context.SerializeTicket());
}
base.Create(context);
}
public override void Receive(AuthenticationTokenReceiveContext context)
{
context.DeserializeTicket(context.Token);
base.Receive(context);
}
}
Run Code Online (Sandbox Code Playgroud)
这只是刷新令牌流程的简单实现,尚未完成或经过测试。这只是为了给您一些关于实现刷新令牌流程的想法。正如您所看到的,向 ClaimsIdentity 添加声明并不难。我没有在维护持久声明的地方添加代码。重要的是持久声明会自动添加!
请注意,我在使用refresh_token刷新access_token时重置了ClaimsIdentity(新票证)。这将创建一个具有当前声明状态的新 ClaimsIdentity。
我将以最后一句话结束。我说的是角色就是主张。您可能期望 User.IsInRole 检查 AspNetUserRoles 表。但事实并非如此。由于角色是声明,因此它会检查声明集合中是否有可用的角色。
归档时间: |
|
查看次数: |
1714 次 |
最近记录: |