我正在开发基于ASP.NET Identity open source project的测试项目
在特定情况下,我有一个ASP.Net Web Api项目,在其中扩展了CookieAuthenticationProvider
并覆盖了ValidateIdentity
方法。
这是我扩展的课程
public sealed class MeteoraInternalCookieAuthenticationProvider : CookieAuthenticationProvider
{
private readonly TimeSpan _validateInterval;
public MeteoraInternalCookieAuthenticationProvider(TimeSpan validateInterval)
: base()
{
_validateInterval = validateInterval;
}
public override async Task ValidateIdentity(CookieValidateIdentityContext context)
{
var currentUtc = DateTimeOffset.UtcNow;
if (context.Options != null && context.Options.SystemClock != null)
{
currentUtc = context.Options.SystemClock.UtcNow;
}
var issuedUtc = context.Properties.IssuedUtc;
// Only validate if enough time has elapsed
var validate = (issuedUtc == null);
if (issuedUtc != null)
{
var timeElapsed = currentUtc.Subtract(issuedUtc.Value);
validate = timeElapsed > _validateInterval;
}
if (validate)
{
var manager = context.OwinContext.Get<UserManager>();
var userId = context.Identity.GetUserId<Guid>();
var app = await this.GetApplicationContextForValidateIdentity(context.OwinContext.Get<Guid>(MeteoraOwinSetsKeys.ApplicationId).ToString());
if (manager != null && userId != null)
{
var user = manager.FindById(userId);
var reject = true;
//Refresh the identity if the stamp matches, otherwise reject
if (user != null && manager.SupportsUserSecurityStamp)
{
var securityStamp = context.Identity.FindFirstValue(MeteoraClaimTypes.SecurityStampClaimType);
if (securityStamp == manager.GetSecurityStamp(userId))
{
reject = false;
// Regenerate fresh claims if possible and resign in
var identity = await this.RegenerateIdentity(manager, user, app);
/*
Other Code
*/
}
}
if (reject)
{
context.RejectIdentity();
context.OwinContext.Authentication.SignOut(context.Options.AuthenticationType);
}
}
}
}
private async Task<ClaimsIdentity> RegenerateIdentity(UserManager usrMgr, IdentityUser usr, IdentityApplication app)
{
ClaimsIdentity identity = await usrMgr.CreateIdentityAsync(usr, app, OAuthDefaults.AuthenticationType /* default is "Bearer" */).WithCurrentCulture();
identity.AddClaim(new IdentityClaim(MeteoraClaimTypes.ApplicationIdType, app.Id.ToString()));
return identity;
}
/*
Other Methods
*/
}
此应用程序托管在IIS中,当我发出请求时,将调用方法ValidateIdentity
。
然后在方法RegenerateIdentity
中调用。
RegenerateIdentity
使用UserManager类生成新的ClaimsIdentity
。
这里有所有代码
public class UserManager<TUser, TApplication, ... >
{
public virtual async Task<ClaimsIdentity> CreateIdentityAsync(TUser user, TApplication app, string authenticationType)
{
/* Other Code */
return await ClaimsIdentityFactory.CreateAsync(this, user, app, authenticationType);
}
public virtual async Task<IList<string>> GetRoleNamesAsync(TApplication app, TUser user)
{
/* Other Code */
var userRoleStore = GetUserRoleStore();
return await userRoleStore.GetRoleNamesAsync(app, user).WithCurrentCulture();
}
}
public class ClaimsIdentityFactory<TUser, TApplication, TRole, ... >
{
public virtual async Task<ClaimsIdentity> CreateAsync(UserManager<TUser, TApplication, ...> manager, TUser user, TApplication app, string authenticationType)
{
/* Other Code */
ClaimsIdentity cid = new ClaimsIdentity(authenticationType, UserNameClaimType, RoleClaimType);
cid.AddClaim(new Claim(UserIdClaimType, ConvertIdToString(user.Id), ClaimValueTypes.String));
cid.AddClaim(new Claim(UserNameClaimType, user.UserName, ClaimValueTypes.String));
cid.AddClaim(new Claim(IdentityProviderClaimType, DefaultIdentityProviderClaimValue, ClaimValueTypes.String));
/* Other code */
if (manager.SupportsUserRole)
{
IList<string> roles = await manager.GetRoleNamesAsync(app, user); // *** CALLING THIS METHOD ***
foreach (string roleName in roles)
cid.AddClaim(new Claim(RoleClaimType, roleName, ClaimValueTypes.String));
}
/* Other Code */
return cid;
}
}
public abstract class UserStore <TKey, TUser, TApplication ...>
{
public virtual async Task<IList<string>> GetRoleNamesAsync(TApplication app, TUser user)
{
/* Other Code */
return await this.GetRoleNamesAsync(app.Id, user.Id);
}
public virtual async Task<IList<string>> GetRoleNamesAsync(TKey appId, TKey userId)
{
/* Other Code */
var query = from userAppRole in _userApplicationRoles
where userAppRole.User.Id.Equals(userId) && userAppRole.Application.Id.Equals(appId)
join role in _roleStore.DbEntitySet on userAppRole.Role.Id equals role.Id
select role.InvariantName;
return await query.ToListAsync(); // *** DEADLOCK ????? ***
}
}
首先称为方法ClaimsIdentityFactory.CreateAsync
。
在ClaimsIdentityFactory
类CreateAsync
方法内部称为manager.GetRoleNamesAsync
,使用基于EntityFramework的存储调用userRoleStore.GetRoleNamesAsync
方法。
进入UserStore
类后,我似乎在调用return await query.ToListAsync();
之后尝试某种死锁情况,因为该方法永不返回。
这个问题没有出现在我的UnitTest项目中,但是它出现在IIS环境中。 我该怎么做才能理解现实中发生的事情?