У меня есть сайт ASP MVC 5, использующий новую структуру идентификации со сторонней аутентификацией с использованием структуры сущностей, которая поставляется из коробки. У меня есть зарегистрированный пользователь, и у него есть 3 утверждения (стандартный идентификатор имени и провайдер аутентификации, которые, по-видимому, предоставляются по умолчанию ClaimsIdentityFactory).
Когда пользователь выполняет какую-либо операцию, я добавляю для него заявку, используя это:
userManager.AddClaim(userId, new Claim(claimType,claimValue));
Если я посмотрю в базу данных в таблице AspNetUserClaims, то увижу строку для моего нового утверждения. Большой.
Однако, когда мой пользователь затем посещает другую страницу, я проверяю это новое заявление, но не нахожу его.
Моя первоначальная проблема, я думал, заключалась в том, что вошедший в систему пользователь никогда не обновлял свой файл cookie, чтобы содержать новое утверждение, поэтому я вышел из системы, думая, что, когда они снова войдут в структуру, прочитают утверждения и создадут новые утверждения для пользователя, и тогда они будут в куки. Но мне должно быть что-то не хватает, потому что даже когда мой пользователь снова входит в систему, у него нет претензии.
Фактически, если я регистрирую активность БД, когда эта строка вызывается в ExternalLoginCallback:
// Sign in the user with this external login provider if the user already has a login
ApplicationUser user = await UserManager.FindAsync(loginInfo.Login);
я получаю вот что:
iisexpress.exe' (CLR v4.0.30319: /LM/W3SVC/2/ROOT-2-130416316906840516): Loaded
'EntityFrameworkDynamicProxies-Microsoft.AspNet.Identity.EntityFramework'.
SELECT TOP (1)
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name]
FROM [dbo].[AspNetRoles] AS [Extent1]
WHERE (((UPPER([Extent1].[Name])) = (UPPER(@p__linq__0))) AND ( NOT ((UPPER([Extent1].[Name]) IS NULL) OR (UPPER(@p__linq__0) IS NULL)))) OR ((UPPER([Extent1].[Name]) IS NULL) AND (UPPER(@p__linq__0) IS NULL))
-- p__linq__0: 'TeamManagement' (Type = String, Size = -1)
-- Executing at 10/04/2014 20:28:29 +01:00
-- Completed in 2 ms with result: GlimpseDbDataReader
'iisexpress.exe' (CLR v4.0.30319: /LM/W3SVC/2/ROOT-2-130416316906840516): Loaded 'EntityFrameworkDynamicProxies-Haccapp.Model'.
SELECT
[Extent1].[Discriminator] AS [Discriminator],
[Extent1].[Id] AS [Id],
[Extent1].[UserName] AS [UserName],
[Extent1].[PasswordHash] AS [PasswordHash],
[Extent1].[SecurityStamp] AS [SecurityStamp],
[Extent1].[PreferredEmailAddress] AS [PreferredEmailAddress]
FROM [dbo].[AspNetUsers] AS [Extent1]
WHERE ([Extent1].[Discriminator] IN (N'ApplicationUser',N'IdentityUser')) AND ([Extent1].[Id] = @p0)
-- p0: 'SiteOwner' (Type = String, Size = -1)
-- Executing asynchronously at 10/04/2014 20:28:29 +01:00
GlimpseDbCommand.TimerStrategy is null
-- Completed in 6 ms with result: GlimpseDbDataReader
SELECT
[Limit1].[Discriminator] AS [Discriminator],
[Limit1].[Id] AS [Id],
[Limit1].[UserName] AS [UserName],
[Limit1].[PasswordHash] AS [PasswordHash],
[Limit1].[SecurityStamp] AS [SecurityStamp],
[Limit1].[PreferredEmailAddress] AS [PreferredEmailAddress]
FROM ( SELECT TOP (1)
[Extent2].[Id] AS [Id],
[Extent2].[UserName] AS [UserName],
[Extent2].[PasswordHash] AS [PasswordHash],
[Extent2].[SecurityStamp] AS [SecurityStamp],
[Extent2].[PreferredEmailAddress] AS [PreferredEmailAddress],
[Extent2].[Discriminator] AS [Discriminator]
FROM [dbo].[AspNetUserLogins] AS [Extent1]
LEFT OUTER JOIN [dbo].[AspNetUsers] AS [Extent2] ON ([Extent2].[Discriminator] IN (N'ApplicationUser',N'IdentityUser')) AND ([Extent1].[UserId] = [Extent2].[Id])
WHERE ([Extent1].[LoginProvider] = @p__linq__0) AND (@p__linq__0 IS NOT NULL) AND ([Extent1].[ProviderKey] = @p__linq__1) AND (@p__linq__1 IS NOT NULL)
) AS [Limit1]
-- p__linq__0: 'Google' (Type = String, Size = -1)
-- p__linq__1: 'https://www.google.com/accounts/o8/id?id=some_account_id' (Type = String, Size = -1)
-- Executing asynchronously at 10/04/2014 20:28:29 +01:00
который не содержит ничего, что вообще пытается запросить базу данных для утверждений, и действительно, у моего возвращенного пользователя есть 0 утверждений.
Затем код делает следующее:
ClaimsIdentity identity = await UserManager.CreateIdentityAsync(user, defaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties {IsPersistent = isPersistent}, identity);
и тогда личность имеет 3 утверждения.
Что мне нужно сделать, чтобы утверждения загружались из базы данных при загрузке своего пользователя? И как только я это сделаю, что мне нужно сделать, чтобы все новые претензии, которые мой пользователь получает во время использования сайта, обновлялись в пользовательских файлах cookie?
Я чувствую, что мне здесь не хватает чего-то фундаментального.
ИЗМЕНИТЬ
Так что я немного поигрался с этим и в итоге реализовал свой собственный ClaimsIdentityFactory
вот так:
public class FullyLoadingClaimsIdentityFactory : ClaimsIdentityFactory<ApplicationUser>
{
private readonly ApplicationDb db;
public FullyLoadingClaimsIdentityFactory(ApplicationDb db)
{
this.db = db;
}
public override async Task<ClaimsIdentity> CreateAsync(UserManager<ApplicationUser> manager, ApplicationUser user, string authenticationType)
{
var currentClaims = db.UserClaims.Where(x => x.User.Id == user.Id).ToList();
var claimsIdentity = await base.CreateAsync(manager, user, authenticationType);
claimsIdentity.AddClaims(currentClaims.Select(c=>new Claim(c.ClaimType,c.ClaimValue)));
return claimsIdentity;
}
}
Теперь это явно запрашивает базу данных для загрузки пользовательских утверждений. Мне пришлось добавить свойство к моему классу, производное от IdentityDbContext
, чтобы я мог раскрыть объекты IdentityUserClaim фреймворка:
public IDbSet<IdentityUserClaim> UserClaims { get; set; }
и теперь, когда я смотрю на мои текущие претензии, они действительно правильно содержат утверждения, которые находятся в таблице. Ура!
Кроме тех случаев, когда я звоню:
var claimsIdentity = await base.CreateAsync(manager, user, authenticationType);
он также загружает мои утверждения, что раньше он не загружался.
на самом деле, если я перейду на это:
var claimsIdentity = await base.CreateAsync(manager, user, authenticationType);
Trace.WriteLine(claimsIdentity.Claims.Count()); <-- traces 3
var currentClaims = db.UserClaims.Where(x => x.User.Id == user.Id).ToList();
claimsIdentity = await base.CreateAsync(manager, user, authenticationType);
Trace.WriteLine(claimsIdentity.Claims.Count()); <-- traces 6!
тогда я не получаю утверждения в БД (из которых 3) загружаются, когда я впервые создаю ClaimsIdentity, но во второй раз я создаю удостоверение после того, как я вручную запросил БД, претензии есть.
Почему это могло быть? Теперь я чувствую, что это связано с EF, но не знаю, почему ...