[ Полезный рекламный блок ]
Попробуйте свои силы в игре, где ваши навыки программирования на C# станут решающим фактором. Переходите по ссылке 🔰.
Сегодня мы разработаем приложение типа Asp.Net Core MVC Identity, с использованием Google Авторизации.
Видите ли, в настоящее время у многих людей уже есть учетные записи, созданные в сторонних приложениях, таких как Microsoft, Google, Facebook, Twitter и т. д. Мы доверяем этим сторонним приложениям и используем их для аутентификации и идентификации пользователя. Поэтому такие сторонние приложения называются доверенными поставщиками идентификационных данных.
Google Авторизация с Identity
Если пользователь захочет войти в наше приложение, используя свой аккаунт Google, он нажмет на созданную нами кнопку Google:
Введет свои данные:
После чего, будет успешно авторизован на вашем сайте!
Создание учетной записи Google OAuth
Google Авторизация с Identity требует создание учетной записи у компании Google. Нам нужны ClientId и Client Secret в приложении для аутентификации Google. Перейдите на следующий сайт и войдите в систему с учетными данными Google:
https://console.cloud.google.com/apis/
Как только вы вошли в систему, первым шагом будет создание нового проекта, если у вас его еще нет. В данный момент у меня нет никаких проектов, поэтому давайте создадим новый проект, нажав на Select a project. Если у вас уже есть проекты, то сверху будет выведено название текущего проекта, по которому необходимо нажать:
Теперь дайте осмысленное имя своему проекту. Я назвал свой проект Google Auth STS. STS означает Security Token Service, потому что мы используем этот проект для обеспечения службы идентификации нашего приложения .NET Core:
Нажмем CREATE, чтобы создать этот новый проект.
После создания нового проекта следующим шагом будет включение API класса Google.
Для этого, нажмите на навигационное меню библиотеки слева, но если по какой-то причине вы не видите этого навигационного меню, нажмите на логотип Google APIs, и вы увидите его. В самом меню, выберите пункт – Library:
У вас откроется страница поиска, найдите здесь Google+ API и нажмите на него:
Теперь на этом экране единственным обязательным полем является имя приложения, это имя будет показано пользователю на экране согласия, в данный момент мы создаем эти учетные данные OAuth для нашего приложения .NET Core.
Тип пользователя – External:
Я указал ниже название приложения, почту поддержки пользователей и адрес электронной почты разработчика:
Теперь прокрутите страницу вниз и нажмите кнопку — Сохранить и продолжить.
После этого нужно нажать на навигационную панель учетных данных, а затем выбрать пункт: CREATE CREDENTIALS — OAuth client ID.
Теперь на этом экране выберите Web Application, потому что это тип нашего клиентского приложения, далее укажите имя для клиента. Я указал имя Identity client. Далее Authorised JavaScript origins, является URI адресом, на котором размещено клиентское приложение:
В настоящее время наш проект все еще работает на локальной машине. Чтобы получить URI, по которому наш проект размещен на локальной машине, в структуре проекта Solution Explorer – Properties — launchSettings.json, в данном файле будет локальный url адрес проекта:
1 2 3 4 5 6 7 8 9 10 |
"profiles": { "GoogleAuth": { "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, "applicationUrl": "https://localhost:7172;http://localhost:5172", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, |
Наконец, вы должны настроить авторизованные URI перенаправления. Это URI, на который пользователь будет перенаправлен после аутентификации в Google. Укажите там следующий адрес:
https://localhost:7172/signin-google
Страница с названием signin-google, обязательна!
Нажмем кнопку – Create. Итак, теперь вы получаете Client_Id и Client_Secret:
Нам нужны оба этих идентификатора для интеграции аутентификации google в наш проект, благодаря им Google Авторизация с Identity будет работать.
Создание проекта
Я ожидаю от вас знания концепций объектно-ориентированного программирования на C#. Я предполагаю, что вы также знаете концепции .NET Core, особенно паттерн MVC.
Откройте Visual Studio 2022.
Создайте проект «Asp.Net Core Web Application(.Net Core)» с шаблоном MVC Template. В графе “Authentication type”, оставляем значение None.
Итак, теперь вам нужно настроить аутентификацию Google для нашего приложения .NET Core, мы сделаем это в нашем начальном классе Program:
1 2 3 4 5 6 |
builder.Services.AddAuthentication() .AddGoogle(options => { options.ClientId = "898063592413-mobd4vo6t6jedcpi2d6lfr9s2958go82.apps.googleusercontent.com"; options.ClientSecret = "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"; }); |
Если у вас уже добавляется данный сервис, просто расширите его настройки, если не добавляется, добавьте как в примере. Код, необходимый для аутентификации Google, включая этот метод AddGoogle(), присутствует в пакете:
Microsoft.AspNetCore.Authentication.Google
Не забудьте добавить его в проект.
Теперь займемся настройкой Identity с нуля. Вначале добавим в папку Models новый класс User:
1 2 3 4 |
public class User : IdentityUser { public int Year { get; set; } } |
Класс User представляет пользователя и наследуется от класса IdentityUser, перенимая все его свойства. Кроме того, для примера здесь добавлено свойство Year, которое будет представлять год рождения пользователя. При желании можно определить любые другие свойства.
Для взаимодействия с MS SQL Server через ASP.NET Core Identity добавим в проект через Nuget пакеты:
1 2 3 |
Microsoft.AspNetCore.Identity.EntityFrameworkCore Microsoft.EntityFrameworkCore.SqlServer |
Далее добавим в папку Data класс контекста данных ApplicationContext. Так как мы используем Identity, то класс контекста данных будет наследоваться не от DbContext, а от IdentityDbContext:
1 2 3 4 5 6 7 8 |
public class ApplicationContext : IdentityDbContext<User> { public ApplicationContext(DbContextOptions<ApplicationContext> options) : base(options) { Database.EnsureCreated(); } } |
У нас есть контекст и модели, и теперь нам необходима база данных, которая будет хранить все данные. Вначале определим в файле appsettings.json строку подключения:
1 2 3 |
"ConnectionStrings": { "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=usersstoredb;Trusted_Connection=True;" }, |
Далее нам надо изменить класс Program, чтобы применить все необходимые сервисы для работы с Identity и базой данных:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
using GoogleAuth.Data; using GoogleAuth.Models; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllersWithViews(); builder.Services.AddAuthentication() .AddGoogle(options => { options.ClientId = "898063592413-mobd4vo6t6jedcpi2d6lfr9s2958go82.apps.googleusercontent.com"; options.ClientSecret = "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"; }); IConfigurationRoot _confString = new ConfigurationBuilder(). SetBasePath(AppDomain.CurrentDomain.BaseDirectory).AddJsonFile("appsettings.json").Build(); builder.Services.AddDbContext<ApplicationContext>(options => options.UseSqlServer(_confString.GetConnectionString("DefaultConnection"))); builder.Services.AddIdentity<User, IdentityRole>() .AddEntityFrameworkStores<ApplicationContext>(); var app = builder.Build(); // Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Home/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); //подключение аутентификации app.UseAuthorization(); //подключение авторизации app.UseAuthorization(); app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); app.Run(); |
Метод AddIdentity() позволяет установить некоторую начальную конфигурацию. Здесь мы указываем тип пользователя и тип роли, которые будут использоваться системой Identity. В качестве типа пользователя выступает созданный нами выше класс User, а в качестве типа роли взят стандартный класс IdentityRole, который находится в пространстве имен Microsoft.AspNetCore.Identity.EntityFrameworkCore.
Метод AddEntityFrameworkStores() устанавливает тип хранилища, которое будет применяться в Identity для хранения данных. В качестве типа хранилища здесь указывается класс контекста данных.
Затем, чтобы использовать Identity, в методе Configure() устанавливается компонент middeware — UseAuthentication. Причем это middleware вызывается перед app.UseEndpoints(), тем самым гарантируя, что ко времени обращения к системе маршрутизации, контроллерам и их методам, куки должным образом обработаны и установлены.
Теперь система Identity подключена в проект, и мы можем с ней работать.
Авторизация пользователей в Identity
Продолжим работу с проектом и добавим в него функционал авторизации пользователей. Пользователи в приложении представлены классом User, который наследует множество свойств. Однако нам необязательно всех их устанавливать. Достаточно установить ключевые свойства вроде логина и пароля.
Для этого лучше воспользоваться вспомогательной моделью, которая установит все необходимые свойства. Итак, добавим в проект новую папку, которую назовем ViewModels. Затем в этой папке определим новый класс LoginViewModel, который будет представлять авторизующего пользователя:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public class LoginViewModel { [Required] [Display(Name = "Email")] public string? Email { get; set; } [Required] [DataType(DataType.Password)] [Display(Name = "Пароль")] public string? Password { get; set; } [Display(Name = "Запомнить?")] public bool RememberMe { get; set; } public string? ReturnUrl { get; set; } public IList<AuthenticationScheme> ExternalLogins { get; set; } } |
Включите свойства ReturnUrl и ExternalLogins.
ReturnUrl — это URL, к которому пользователь пытался получить доступ до аутентификации. Мы сохраняем и передаем его между запросами с помощью свойства ReturnUrl, чтобы пользователь мог быть перенаправлен на этот URL после успешной аутентификации.
Так же необходимо показать все внешние логины (Facebook, Google и т.д.) на странице входа. Для этого добавляем свойство ExternalLogins в LoginViewModel.
Для работы с учетными записями пользователей добавим в папку Controllers новый контроллер AccountController и определим в нем метод для авторизации пользователей через обычный вход:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
public class AccountController : Controller { private readonly UserManager<User> _userManager; private readonly SignInManager<User> _signInManager; public AccountController(UserManager<User> userManager, SignInManager<User> signInManager) { _userManager = userManager; _signInManager = signInManager; } [HttpGet] [AllowAnonymous] public async Task<IActionResult> Login(string returnUrl) { LoginViewModel model = new LoginViewModel { ReturnUrl = returnUrl, ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList() }; return View(model); } [HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> Login(LoginViewModel model) { if (ModelState.IsValid) { var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, false); if (result.Succeeded) { // проверяем, принадлежит ли URL приложению if (!string.IsNullOrEmpty(model.ReturnUrl) && Url.IsLocalUrl(model.ReturnUrl)) { return Redirect(model.ReturnUrl); } else { return RedirectToAction("Index", "Home"); } } else { ModelState.AddModelError("", "Неправильный логин и (или) пароль"); } } return View(model); } [HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> Logout() { // удаляем аутентификационные куки await _signInManager.SignOutAsync(); return RedirectToAction("Index", "Home"); } } |
Для представлений этого контроллера в каталоге Views определим подкаталог Account, в который добавим новое представление Login.cshtml. Это представление будет служить для регистрации пользователя:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
@model GoogleAuth.ViewModels.LoginViewModel <div class="row"> <div class="col-md-4"> <h2>Авторизация</h2> <form method="post" asp-controller="Account" asp-action="Login" asp-route-returnUrl="@Model.ReturnUrl"> <div asp-validation-summary="ModelOnly"></div> <div class="form-group"> <label asp-for="Email" class="control-label"></label> <input asp-for="Email" class="form-control" /> <span asp-validation-for="Email" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Password" class="control-label"></label> <input asp-for="Password" class="form-control" /> <span asp-validation-for="Password" class="text-danger"></span> </div> <div> <label asp-for="RememberMe" class="control-label"></label><br /> <input asp-for="RememberMe" class="form-check" /> </div> <div class="form-group"> <br /> <input type="submit" value="Войти" class="btn btn-primary" /> </div> </form> </div> <div class="col-md-4"> <h2>Авторизация через Google</h2> <hr /> @{ if (Model.ExternalLogins.Count == 0) { <div>No external logins configured</div> } else { <form method="post" asp-action="ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl"> <div> @foreach (var provider in Model.ExternalLogins) { <button type="submit" class="btn btn-primary" style="width:auto" name="provider" value="@provider.Name" title="Login using your @provider.DisplayName account"> @provider.DisplayName </button> } </div> </form> } } </div> </div> |
Запустим приложение, при обращении по адресу:
https://localhost:7243/account/login
мы увидим следующую страницу:
Если вы нажмете на кнопку Google, эта кнопка является кнопкой типа Submit в коде представления, тогда вы обнаружите ошибку. Потому что вы еще не создали действие ExternalLogin в контроллере — AccountController. Создадим его:
1 2 3 4 5 6 7 8 9 10 11 12 |
ExternalLogin в контроллере - AccountController. Создадим его: [AllowAnonymous] [HttpPost] public IActionResult ExternalLogin(string provider, string returnUrl) { var redirectUrl = Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }); var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl); return new ChallengeResult(provider, properties); } |
Теперь запустим приложение снова, и нажмем на кнопку Google:
Вы увидите название вашего проекта, в моем случае – Auth, но сам процесс авторизации, еще не готов. В контроллер AccountController, добавим действие ExternalLoginCallback:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
[AllowAnonymous] public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null, string remoteError = null) { returnUrl = returnUrl ?? Url.Content("~/"); LoginViewModel loginViewModel = new LoginViewModel { ReturnUrl = returnUrl, ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList() }; if (remoteError != null) { ModelState .AddModelError(string.Empty, $"Error from external provider: {remoteError}"); return View("Login", loginViewModel); } // Get the login information about the user from the external login provider var info = await _signInManager.GetExternalLoginInfoAsync(); if (info == null) { ModelState .AddModelError(string.Empty, "Error loading external login information."); return View("Login", loginViewModel); } // If the user already has a login (i.e if there is a record in AspNetUserLogins // table) then sign-in the user with this external login provider var signInResult = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor: true); if (signInResult.Succeeded) { return LocalRedirect(returnUrl); } // If there is no record in AspNetUserLogins table, the user may not have // a local account else { // Get the email claim value var email = info.Principal.FindFirstValue(ClaimTypes.Email); if (email != null) { // Create a new user without password if we do not have a user already var user = await _userManager.FindByEmailAsync(email); if (user == null) { user = new User { UserName = info.Principal.FindFirstValue(ClaimTypes.Email), Email = info.Principal.FindFirstValue(ClaimTypes.Email) }; await _userManager.CreateAsync(user); } // Add a login (i.e insert a row for the user in AspNetUserLogins table) await _userManager.AddLoginAsync(user, info); await _signInManager.SignInAsync(user, isPersistent: false); return LocalRedirect(returnUrl); } ViewBag.ErrorTitle = $"Email claim not received from: {info.LoginProvider}"; ViewBag.ErrorMessage = "Please contact support on Pragim@PragimTech.com"; return View("Error"); } } |
Для проверки логина и удобства работы с приложением, определим в представлении Layout.cshtml вывод имени пользователя и ссылки на вход, если пользователь не авторизован и выход, если он авторизован:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<ul class="navbar-nav"> @if (User.Identity.IsAuthenticated) { <li class="nav-item"> <a class="nav-link text-dark" asp-area="" asp-controller="" asp-action="">@User.Identity.Name</a> </li> <li class="nav-item"> <form method="post" asp-controller="Account" asp-action="Logout"> @*Добавляем стили, что бы кнопка выглядела как ссылка*@ <style> .mybtn { background: none !important; border: none; cursor: pointer; } </style> @*Применяем класс стилей для кнопки*@ <input class="nav-link text-dark mybtn" type="submit" value="Выход" /> </form> </li> } else { <li class="nav-item"> <a class="nav-link text-dark" asp-controller="Account" asp-action="Login">Вход</a> </li> } </ul> |
Если вы запустите приложение и нажмете кнопку Google, затем выберите учетную запись Gmail, затем укажите свой пароль для входа в Gmail, то вы увидите результат, как и ожидалось:
Вы успешно вошли в свой аккаунт, используя внешнего поставщика услуг входа.
Если вы посмотрите на таблицу AspNetUsers, то обнаружите интересную вещь. В таблице AspNetUsers у нас есть новая строка, посмотрите на колонку PasswordHash, она равна NULL. На самом деле, нам не нужен пароль для этого пользователя. Нам больше не нужно его поддерживать, потому что этот пользователь переходит к внешнему провайдеру входа в аккаунт Google.
Надеюсь, вам понравилась эта статья. Вы можете скачать исходный код в моем репозитории — Github.
Поделитесь вашим опытом в комментариях, как вы выполняете Google Авторизация с Identity, в ваших приложениях?
Вы начинающий программист, который хочет изучить все тонкости языка C#?
Пройдите наш тест на 13 вопросов, чтобы узнать, как много вы знаете на самом деле!
C# Braincheck |
Вы хотите научится писать код на языке программирования C#?
Создавать различные информационные системы, состоящие из сайтов, мобильных клиентов, десктопных приложений, телеграмм-ботов и т.д.
Переходите к нам на страницу Dijix и ознакомьтесь с условиями обучения, мы специализируемся только на индивидуальных занятиях, как для начинающих, так и для более продвинутых программистов. Вы можете взять как одно занятие для проработки интересующего Вас вопроса, так и несколько, для более плотной работы. Благодаря личному кабинету, каждый студент повысит качество своего обучения, в вашем распоряжении:
- Доступ к пройденному материалу
- Тематические статьи
- Библиотека книг
- Онлайн тестирование
- Общение в закрытых группах
Живи в своем мире, программируй в нашем.
Спасибо большое!