Разработка сайта для Вашего бизнеса. Веб дизайн. Дизайн логотипа, фирменного стиля, рекламная фотография . Комплексный рекламный креатив.

Ralex. We do the work.
На рынке с 1999го года. Средняя ценовая категория. Ориентация на эффективность решений.
Ознакомтесь с нашим портфолио
Узнайте больше о услугах
Свяжитесь с нами:
E-mail: [email protected]
Tel: (044) 587 - 84 - 78
Custom web design & дизайн и разработка сайта "под ключ"
Креативный, эффективный дизайн. Система управления сайтом (СУС).
Custom flexible разработка систем электронной коммерции
Система e-commerce разрабатывается под индивидуальные потребности. Гибкая функциональность.
Search Engine Optimzation & оптимизация под поисковые системы (SEO)
Постоянная оптимизация и мониторинг сайта в поисковых системах. Достигаем результата быстро и эффективно
Custom logo design & дизайн логотипа и фирменного стиля
Многолетний опыт. Огромное портфолио. Уникальное предложение и цена.
профессиональная рекламная фотография
креативно, смело, качественно
Custom logo design & рекламный креатив. дизайн рекламы
Многолетний опыт. Огромное портфолио. Уникальное предложение и цена.

Web API авторизація Bearer з підтримкою cookies

  1. опис
  2. завдання
  3. Використовувані інструменти і технології
  4. висновок
  5. посилання

Наша взаимовыгодная связь https://banwar.org/

ru-RU | створено: 27.10.2016 | опубліковано: 27.10.2016 | оновлено: 02.01.2018 | переглядів за весь час: 10020

У статті описується як для Web API використовувати OAuth 2.0 аутентифікацію і авторизацію на основі access_token (Bearer), і як цей токен зберігати в cookie щоб не доводилося при кожному новому відкритті сайту вводити дані для отримання цього токена.

опис

Якщо ви захочете використовувати OAuth 2.0 аутентифікацію (і авторизацію) на основі access_token (наприклад, Bearer), то вам доведеться в в заголовку кожного запиту передавати цей самий токен. Тут, як то кажуть, нічого нового, все вже придумали за нас. Але якщо це так, то постає резонне питання: де його брати, щоб його передавати? Про це та про багато іншого йтиметься в цій статті.

завдання

Коли у мене визріла ідея написання цієї статті, переді мною стояло завдання запустити лише однієї сторінки сайт (Single Page Application) без використання ASP.NET MVC, але з можливістю використання Web API. Завдання вирішена. Давайте її розкладемо по пунктам. Отже, для виконання завдання потрібно вирішити наступні завдання:

  • отримати токен (acces_token);
  • зберегти отриманий токен в cookie;
  • при наступних відвідинах сайту читати токен з cookie, щоб аутентифицировать користувача автоматично без введення пароля і Лонин.

Дрібні нюанси типу "підняти токен сервіс" або "запросити у користувача логін, якщо токен що невиявлений в cookie" опущені, хоча і є обов'язковими.

Використовувані інструменти і технології

Я буду використовувати Visual Studio 2015. При створення проекту я не використовував ASP.NET MVC 5, а створив порожній solution.

Після цього я встановив потрібні nuget-пакети, щоб у мене вийшов проект з однією HTML-сторінкою (Single Page Application) і підключеним Web API. Для повноти картини приведу весь список встановлених пакетів.

Зверніть увагу, що у мене немає пакетів ASP.NET MVC в списку встановлених.

Якщо ви скопіювали файл packages.config з іншого джерела або створили його вручну без установки кожного з пакетів, то в Package Managment Console досить ввести наступну команду, щоб всі пакети встановилися автоматично.

> Update-Package -Reinstall

Далі підключимо OWIN для сайту. Для цього створюємо файл Startup.cs:

Тепер приведу його повне вміст і після чого, як уже прийнято приведу пояснення до коду.

using System; using Autofac; using Autofac.Integration.WebApi; using Calabonga.Facts; using Microsoft.AspNet.Identity; using Microsoft.Owin; using Microsoft.Owin.Security.OAuth; using Owin; [Assembly: OwinStartup (typeof (Startup))] namespace Calabonga.Facts {/// <summary> /// Start for Owin /// </ summary> public class Startup {/// <summary> /// Server OAuthorization Options /// </ summary> public static OAuthAuthorizationServerOptions OAuthAuthorizationServer {get; set; } Public void Configuration (IAppBuilder app) {var config = ConfigurationBuilder.Create (); var container = DependencyContainer.Initialize (app); config.DependencyResolver = new AutofacWebApiDependencyResolver (container); var provider = container.Resolve <ApplicationOAuthProvider> (); OAuthAuthorizationServer = new OAuthAuthorizationServerOptions {AllowInsecureHttp = true, TokenEndpointPath = new PathString ( "/ custom-token"), AccessTokenExpireTimeSpan = TimeSpan.FromDays (1), Provider = provider}; app.UseOAuthAuthorizationServer (OAuthAuthorizationServer); app.UseBearerOnCookieAuthentication (); app.UseAutofacMiddleware (container); app.UseAutofacWebApi (config); app.UseWebApi (config); app.UseOAuthBearerAuthentication (new OAuthBearerAuthenticationOptions ()); app.UseExternalSignInCookie (DefaultAuthenticationTypes.ExternalCookie); app.UseSpaWebApi (); }}}

Так як цей файл, і зокрема метод Configuration використовує класи, які буду показані пізніше, проект не може бути зібраний і запущений. Тому не намагайтеся це зробити. Тепер, прокоментую код по порядку проходження рядків, створюючи відсутні класи і настройки.

Startup: Рядки 6-7

Використовується OWIN як основна специфікація взаємодії між компонентами. ASP.NET MVC при цьому не використовується.

Startup: Рядок 10

Стандартний для OWIN атрибут вказує на те що при старті додатка клас c настройками для запуску є Startup.cs.

Startup: Рядок 25

Створюємо конфігурацію Web API і налаштовуємо основні параметри.

using System.Web.Http; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; namespace Calabonga.Facts {public static class ConfigurationBuilder {public static HttpConfiguration Create () {var config = new HttpConfiguration (); config.SuppressDefaultHostAuthentication (); config.Filters.Add (new AuthorizationBearerFilter ()); // Attribute routing. config.MapHttpAttributeRoutes (); // Convention-based routing. config.Routes.MapHttpRoute (name: "DefaultApi", routeTemplate: "api / {controller} / {id}", defaults: new {id = RouteParameter.Optional}); // formatter settings config.Formatters.JsonFormatter.SerializerSettings.Formatting = Formatting.Indented; config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver (); config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; return config; }}}

Важливі рядки виділені кольором. Тут відключаємо стандартну аутентифікацію на сайті і реєструємо свій власний AuthorizationBearerFilter, який виглядає наступним чином.

using System; using System.Collections.Generic; using System.Net.Http.Headers; using System.Security.Claims; using System.Threading; using System.Threading.Tasks; using System.Web.Http.Filters; namespace WebApplication1 {/// <summary> /// Custom authorization filter /// </ summary> public class AuthorizationBearerFilter: Attribute, IAuthenticationFilter {public bool AllowMultiple {get {return false; }} Public Task AuthenticateAsync (HttpAuthenticationContext context, CancellationToken cancellationToken) {var request = context.Request; var authorization = request.Headers.Authorization; if (authorization == null) {return null; } If (authorization.Scheme! = "Bearer") return null; cancellationToken.ThrowIfCancellationRequested (); var ticket = Startup.OAuthAuthorizationServer.AccessTokenFormat.Unprotect (authorization.Parameter); if (ticket == null) return Task.CompletedTask; // do validation with ticket var nameClaim = new Claim (ClaimTypes.Name, "UserName"); var claims = new List <Claim> {nameClaim}; var identity = new ClaimsIdentity (claims, "Bearer"); var principal = new ClaimsPrincipal (identity); context.Principal = principal; return Task.CompletedTask; } Public Task ChallengeAsync (HttpAuthenticationChallengeContext context, CancellationToken cancellationToken) {var challenge = new AuthenticationHeaderValue ( "Bearer"); context.Result = new AddChallengeOnUnauthorizedResult (challenge, context.Result); return Task.FromResult (0); }}}

Коментарі з цього коду: в рядку 11 перевіряємо наявність Authorization в Header; в рядку 19 розпаковуємо (розшифровка) ticket і проробляємо потрібні нам маніпуляції для перевірки користувача; в 27 рядку встановлюємо IPrincipal для поточного запиту. Саме в 27 рядку ви можете вказати потрібні параметри для користувача: ролі, права, настройки і т.д., які, до речі, можна отримати з БД або з іншого сервісу.

Повертаємося до Startup, наступний рядок, що вимагає уваги, це рядок номер 26, де відбувається ініціалізація DependencyContainer. Я також приведу його повністю, закоментувавши неважливі рядки:

using System.Reflection; using Autofac; using Autofac.Integration.WebApi; using log4net; using Owin; namespace WebApplication1 {/// <summary> /// Dependancy Container /// </ summary> public static class DependencyContainer {/// <summary> /// Initialize container /// </ summary> /// <param name = "app"> </ param> internal static IContainer Initialize (IAppBuilder app) {var builder = new ContainerBuilder (); builder.RegisterApiControllers (Assembly.GetExecutingAssembly ()); // ------------------------------------------------ ----------------- // my services and DbContext registered here // ----------------------- ------------------------------------------ // builder.RegisterType <FactService> () .As <IFactService> (); // builder.RegisterType <TagService> (). As <ITagService> (); // builder.RegisterType <ApplicationDbContext> (). As <IContext> (); // builder.RegisterType <AccountMananger> (). As <IAccountMananger> (); // builder.RegisterType <Config> (). As <IAppConfig> (); // builder.RegisterType <CacheService> (). As <ICacheService> (); // builder.RegisterType <DefaultConfigSerializer> (). As <IConfigSerializer> (); builder.RegisterType <ApplicationOAuthProvider> (). AsSelf (). SingleInstance (); builder.RegisterInstance (LogManager.GetLogger (typeof (Startup))). As <ILog> (); return builder.Build (); }}}

Ви можете і зовсім не використовувати DI-контейнер, або використовувати інший на свій розсуд. Насправді, нам цікава виділена 33 рядок. У цьому рядку в DI-контейнері реєструється, мабуть найголовніший клас, який відповідає за авторизацію і аутентифікацію. Наведу його повністю, і як водиться з коментарями після:

using System; using System.Collections.Generic; using System.Security.Claims; using System.Threading.Tasks; using Calabonga.Facts.Extensions; using Calabonga.Facts.ViewModels; using Microsoft.AspNet.Identity; using Microsoft.Owin.Security; using Microsoft.Owin.Security.OAuth; namespace Calabonga.Facts {public class ApplicationOAuthProvider: OAuthAuthorizationServerProvider {private readonly IAccountMananger _mobileAccountMananger; private readonly string _publicClientId; public ApplicationOAuthProvider (IAccountMananger mobileAccountMananger) {_mobileAccountMananger = mobileAccountMananger; _publicClientId = DefaultAuthenticationTypes.ExternalBearer; } Public override async Task GrantResourceOwnerCredentials (OAuthGrantResourceOwnerCredentialsContext context) {var client = new LoginViewModel {Password = context.Password, UserName = context.UserName}; var validateOperation = await _mobileAccountMananger.AuthorizeUserAsync (client); if (! validateOperation.Ok) {context.SetError ( "invalid_grant", validateOperation.GetMetadataMessages ()); } Else {var oAuthIdentity = new ClaimsIdentity (validateOperation.Result.Claims, _publicClientId); var cookiesIdentity = new ClaimsIdentity (validateOperation.Result.Claims, _publicClientId); var properties = CreateProperties (context.UserName, GetType (). Namespace); var ticket = new AuthenticationTicket (oAuthIdentity, properties); context.OwinContext.Response.Headers.Add ( "Access-Control-Allow-Origin", new [] { "*"}); context.Request.Context.Authentication.SignIn (cookiesIdentity); context.Response.Cookies.Append (TokenName, context.Options.AccessTokenFormat.Protect (ticket)); context.Validated (ticket); }} Internal static string TokenName {get; } = "Token"; public override Task TokenEndpoint (OAuthTokenEndpointContext context) {foreach (var property in context.Properties.Dictionary) {context.AdditionalResponseParameters.Add (property.Key, property.Value); } Return Task.FromResult <object> (null); } Public override Task ValidateClientAuthentication (OAuthValidateClientAuthenticationContext context) {// Resource owner password credentials does not provide a client ID. if (context.ClientId == null) {context.Validated (); } Return Task.FromResult <object> (null); } Public override Task ValidateClientRedirectUri (OAuthValidateClientRedirectUriContext context) {if (context.ClientId == _publicClientId) {var expectedRootUri = new Uri (context.Request.Uri, "/"); if (expectedRootUri.AbsoluteUri == context.RedirectUri) {context.Validated (); }} Return Task.FromResult <object> (null); } /// <summary> /// Create Authentication properties /// </ summary> /// <param name = "userName"> </ param> /// <param name = "appName"> </ param> /// <returns> </ returns> private static AuthenticationProperties CreateProperties (string userName, string appName) {IDictionary <string, string> data = new Dictionary <string, string> {{ "UserName", userName}, { "ApplicationName" , appName}}; return new AuthenticationProperties (data); }}}

ApplicationOAuthProvider: Рядок 13

Потрібно створити свою реалізацію OAuthAuthorizationServerProvider, тому наш клас успадкований від цього класу.

ApplicationOAuthProvider: Рядок 19

Вказуємо тип аутентифікації.

ApplicationOAuthProvider: Рядок 24

Я використовую врутренній ViewModel для перекидання даних в сервіс аутентифікації в рядку 29.

ApplicationOAuthProvider: Рядок 31

Повертає негативну відповідь про те, що аутентифікація не увінчалася успіхом з зазначенням причин.

ApplicationOAuthProvider: Рядок 41

Вирощує зашифрований AutenticationTicket, як результат успішної аутентифікації.

Тепер знову повернемося до Startup класу.

Startup: Рядок 36 і 42

У 36 як і 42 рядку використовується розширення AppBuilder.

using Owin; namespace WebApplication1 {/// <summary> /// Static extensions for AppFunc /// </ summary> public static class AppFuncExtensions {/// <summary> /// Setup to use WebApiAllication as default framework for Application /// < / summary> /// <param name = "app"> </ param> public static void UseSpaWebApi (this IAppBuilder app) {app.Use <SinglePageWithWebApi> (); } /// <summary> /// Use bearer authentication on cookie /// </ summary> /// <param name = "app"> </ param> public static void UseBearerOnCookieAuthentication (this IAppBuilder app) {app.Use <BearerOnCookieAuthentication> (); }}}

У рядку 14 по суті відбувається запуск сайту. Так як я не використовую ASP.NET MVC, то все-таки хоч щось повинно якось виводитися в браузер. Саме цей клас SinglePageWithWebApi і читає файл з папки Views і рендерить його в потік виклику без змін HTML.

/// <summary> /// Web API application for Single Page Application /// </ summary> public class SinglePageWithWebApi: OwinMiddleware {public SinglePageWithWebApi (OwinMiddleware next): base (next) {} public override async Task Invoke (IOwinContext context ) {var filePath = HttpContext.Current.Server.MapPath (string.Concat ( "~ /", "views / index.html")); var content = File.ReadAllText (filePath); await context.Response.WriteAsync (content); }}

У рядку 22 підключається клас BearerOnCookieAuthentication (middleware), заради якого і затівалася ця стаття. Саме цей представлений нижче клас проробляє основну роботу по забезпеченню читання Cookie і записи його наявності в властивість Authorization в колекцію Header:

using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.Owin; namespace WebApplication1 {/// <summary> /// Middleware for OWIN enables using bearer authentication over cookies /// </ summary> public class BearerOnCookieAuthentication: OwinMiddleware {public BearerOnCookieAuthentication (OwinMiddleware next): base (next) {} public override async Task Invoke (IOwinContext context) {var cookies = context.Request.Cookies; var cookie = cookies.FirstOrDefault (c => c.Key == ApplicationOAuthProvider.TokenName); if (! context.Request.Headers.ContainsKey ( "Authorization")) {if (! cookie.Equals (default (KeyValuePair <string, string>))) {var ticket = cookie.Value; context.Request.Headers.Add ( "Authorization", new [] {$ "Bearer {ticket}"}); }} Await Next.Invoke (context); }}}

Тепер весь код зібраний воєдино, отже можна скомпілювати проект. Після успішної побудови я запустив проект, і виявилося, що я випадково закоментувавши зайві рядки з реєстрацією IAccountManager в DependencyContainer. Щоб запуск відбувся, вам буде потрібно розкоментувати рядок з IAccountManager, а також вам буде потрібно файл index.html в папці Views.

Для зручності, теж приведу повний зміст файлу index.html:

<! DOCTYPE html> <html xmlns = "http://www.w3.org/1999/xhtml"> <head> <meta charset = "utf-8"> <meta http-equiv = "X-UA-Compatible "content =" IE = edge "> <meta name =" viewport "content =" width = device-width, initial-scale = 1 "> <title> Тільки факти </ title> <meta name =" description "content = "Calabonga.Owin.Application" /> <meta name = "keywords" content = "Calabonga Owin Application" /> <! - HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -> <! - - WARNING: Respond.js does not work if you view the page via file: // -> <! - [if lt IE 9]> <script src = "https://oss.maxcdn.com/html5shiv /3.7.3/html5shiv.min.js "> </ script> <script src =" https://oss.maxcdn.com/respond/1.4.2/respond.min.js "> </ script> <! [endif] -> <link rel = "Shortcut Icon" href = "/ favicon.ico" /> <link href = "../ Content / bootstrap.min.css" rel = "stylesheet" /> <link href = "../ Content / font-awesome.min.css" rel = "stylesheet" /> <link href = "../ Content / toastr.min.css" rel = "stylesheet" /> <link href = " ../Content/site.css "rel = "Stylesheet" /> </ head> <body> <div class = "container"> <nav class = "navbar navbar-default navbar-fixed-top"> <div class = "container-fluid"> <div class = "navbar-header"> <button type = "button" class = "navbar-toggle collapsed" data-toggle = "collapse" data-target = "# bs-example-navbar-collapse-1" aria-expanded = "false "> <span class =" sr-only "> Toggle navigation </ span> <span class =" icon-bar "> </ span> <span class =" icon-bar "> </ span> <span class = "icon-bar"> </ span> </ button> <a class="navbar-brand" href="/"> <img alt = "Brand" src = "/ Content / logo.png" class = "img -responsive "> </a> </ div> <div class =" collapse navbar-collapse "id =" bs-example-navbar-collapse-1 "> <ul class =" nav navbar-nav "> <li> <a href="http://www.calabonga.net"> Блог розробника </a> </ li> <li> <a href = "http://www.calabonga.net/blog/post/184" > Посилання на статтю </a> </ li> <li> <a href="http://www.calabonga.net/blog/post/141"> Що таке SPA </a> </ li> </ ul> <div class = "navbar-form navbar-right"> <div class = "form-group"> <Div class = "input-group"> <span class = "input-group-addon"> <i class = "fa fa-filter"> </ i> </ span> <input type = "text" class = "form-control" placeholder = "Search"> </ div> </ div> </ div> </ div> </ div> </ nav> <div class = "row"> <h1> Welcome </ h1 > <p> Авторизація налаштована, хоча це і не видно. Єдине, що вам доведеться зробити самостійно, так це реалізувати JavaScript функціонал, який буде звертатися до ApiController. </ P> <p> ApiController тепер може бути позначений атрибутом Autorize. Авторизація та аутентифікація буде здійснюватися через access_token. </ P> </ div> </ div> <script src = "../ Scripts / jquery-3.1.1.min.js"> </ script> </ body> </ html>

висновок

Авторизація налаштована, хоча це і не видно. Єдине, що вам доведеться зробити самостійно, так це реалізувати JavaScript функціонал, який буде звертатися до ApiController, який ви теж повинні будете створити самостійно. ApiController тепер може бути позначений атрибутом Autorize. Авторизація та аутентифікація буде здійснюватися через access_token.

посилання

У висновку посилання на новостворений проект , Який викладений на GitHub.

Але якщо це так, то постає резонне питання: де його брати, щоб його передавати?
Категории
  • Биология
  • Математика
  • Краеведению
  • Лечебная
  • Наука
  • Физике
  • Природоведение
  • Информатика
  • Новости

  • Новости
    https://banwar.org/
    Наша взаимовыгодная связь https://banwar.org/. Запустив новый сайт, "Пари Матч" обещает своим клиентам незабываемый опыт и возможность выиграть крупные суммы.


    Наши клиенты
    Клиенты

    Быстрая связь

    Тел.: (044) 587-84-78
    E-mail: [email protected]

    Имя:
    E-mail:
    Телефон:
    Вопрос\Комментарий: