Если все, что вы хотите сделать, - это войти в Google, нет необходимости в SignInManager
, UserManager
или самой идентификации ядра ASP.NET.Для этого нам сначала необходимо настроить службы аутентификации.Вот соответствующий код для этого, который я объясню после:
Startup.cs
services
.AddAuthentication(o =>
{
o.DefaultScheme = "Application";
o.DefaultSignInScheme = "External";
})
.AddCookie("Application")
.AddCookie("External")
.AddGoogle(o =>
{
o.ClientId = ...;
o.ClientSecret = ...;
});
Вызов AddAuthentication
настраивает DefaultScheme
, который в конечном итоге используется как схема Application и схема Challenge .Схема Приложение используется при попытке аутентификации пользователя (они вошли в систему?).Схема Challenge используется, когда пользователь не вошел в систему, но приложение хочет предоставить возможность сделать это.Я расскажу о DefaultSignInScheme
позже.
Два вызова AddCookie
добавляют схемы аутентификации на основе файлов cookie для обеих Application
(наша Приложение схема) и External
(наша схема SignIn ).AddCookie
также может принимать второй аргумент, который позволяет конфигурировать, например, время жизни соответствующего cookie и т. Д.
При этом процесс вызова перенаправит пользователя на /Account/Login
(по умолчанию - это также можно настроить с помощью опций аутентификации cookie).Вот реализация контроллера, которая обрабатывает процесс вызова (опять же, я объясню после):
AccountController.cs
public class AccountController : Controller
{
public IActionResult Login(string returnUrl)
{
return new ChallengeResult(
GoogleDefaults.AuthenticationScheme,
new AuthenticationProperties
{
RedirectUri = Url.Action(nameof(LoginCallback), new { returnUrl })
});
}
public async Task<IActionResult> LoginCallback(string returnUrl)
{
var authenticateResult = await HttpContext.AuthenticateAsync("External");
if (!authenticateResult.Succeeded)
return BadRequest(); // TODO: Handle this better.
var claimsIdentity = new ClaimsIdentity("Application");
claimsIdentity.AddClaim(authenticateResult.Principal.FindFirst(ClaimTypes.NameIdentifier));
claimsIdentity.AddClaim(authenticateResult.Principal.FindFirst(ClaimTypes.Email));
await HttpContext.SignInAsync(
"Application",
new ClaimsPrincipal(claimsIdentity));
return LocalRedirect(returnUrl);
}
}
Давайте разберем это на двадействия:
Login
Чтобы выполнить действие Login
, пользователю будет вызов .Это происходит, когда пользователь не вошел в систему по схеме Application
, но пытается получить доступ к странице, защищенной атрибутом Authorize
(или аналогичным).По вашему требованию, если пользователь не вошел в систему, мы хотим войти в него с помощью Google.Чтобы добиться этого, мы запускаем новый вызов , на этот раз для схемы Google
.Мы делаем это, используя ChallengeResult
, настроенный по схеме Google
, и RedirectUrl
, который используется для возврата к нашему собственному коду приложения после завершения процесса входа в Google.Как показывает код, мы возвращаемся к:
LoginCallback
Здесь DefaultSignInScheme
от нашего звонка до AddAuthentication
становится актуальным.Как часть процесса входа в Google, DefaultSignInScheme
используется для установки файла cookie, который содержит ClaimsPrincipal
, представляющий пользователя, возвращенного из Google (все это обрабатывается за кулисами).Первая строка кода в LoginCallback
захватывает этот экземпляр ClaimsPrincipal
, который заключен в AuthenticateResult
, который сначала проверяется на успешность.Если до сих пор все прошло успешно, мы заканчиваем тем, что создаем новый ClaimsPrincipal
, который содержит все требования, которые нам нужны (взятые от Google в данном случае), а затем регистрируем этот ClaimsPrincipal
, используя схему Application
.Наконец, мы перенаправляем на страницу, которая вызвала наш первый вызов .
Я создал репозиторий GitHub, содержащий полный пример, который я встроилчтобы написать этот ответ здесь .
В ответ на пару последующих комментариев / вопросов в комментариях ниже:
Могу ли ясделать вывод, что SignInManager
и UserManager
используются только при использовании аутентификации с базой данных?
В некотором смысле, да, я думаю, это справедливо.Хотя возможно реализовать хранилище в памяти, на самом деле это не имеет особого смысла, если нет постоянства.Однако настоящая причина не использовать эти классы в вашей ситуации просто потому, что вам не нужна локальная учетная запись пользователя для представления пользователя.Это идет рука об руку с постоянством, но стоит провести различие.
И какой код сильно отличается от того, что я прочитал в книге (которую я использовал для настройки своего входа в Google) ивсе остальные ответы, которые я прочитал.
Документация и книги охватывают наиболее распространенный вариант использования, при котором вы делаете хотите хранить локальных пользователей, которые могут быть связаны с внешними учетными записями, такими как Google и т. Д. Еслиесли вы посмотрите на источник SignInManager
, то увидите, что он действительно находится поверх кода, который я показал выше (например, здесь и здесь ).Другой код можно найти в пользовательском интерфейсе по умолчанию (например, здесь ) и в AddIdentity
.
Я предполагаю, что LoginCallback вызывается Google.HttpContext.AuthenticateAsync знает, как проверить данные, которые Google отправляет мне?И поскольку его название настолько общее, похоже, оно знает, как это сделать для всех внешних провайдеров?
Звонок AuthenticateAsync
здесь не знает ничего оGoogle - специфичная для Google обработка настраивается путем вызова на AddGoogle
с AddAuthentication
в ConfigureServices
.После перенаправления в Google для входа мы на самом деле возвращаемся к /signin-google
в нашем приложении.Опять же, это обрабатывается благодаря вызову AddGoogle
, но этот код на самом деле просто генерирует cookie в схеме External
, в которой хранятся утверждения, полученные от Google, и затем перенаправляется на нашу конечную точку LoginCallback
, которую мы настроили,Если вы добавите вызов к AddFacebook
, конечная точка /sigin-facebook
будет настроена на аналогичные действия.Вызов AuthenticateAsync
на самом деле просто повторяет запрос ClaimsPrincipal
из файла cookie, который был создан, например, конечной точкой /signin-google
, чтобы получить претензии.
Стоит также отметить, что Google / FacebookПроцесс входа в систему основан на протоколе OAuth 2 , поэтому он сам по себе является общим.Если вам нужна поддержка не только для Google, вы просто вызовете задачу для требуемой схемы, а не жестко закодируете ее для Google, как я делал в примере.Также можно добавить дополнительные свойства к запросу, чтобы иметь возможность определить, какой поставщик использовался при достижении вашей конечной точки LoginCallback
.