پیاده-سازی-Asp.Net-Identity-به-روش-AjaxReviewed by کارشناسان.نت on September 22Rating:5

 ASP.NET Identity از دو نوع Authentication یعنی Cookie و Bearer Token پشتیبانی می کند , روش Cookie که روش پیش فرض است و در مقالات قبلی پیاده سازی شد در این روش یک کوکی توسط Asp.net در مرورگر کاربر ذخیره می گردد , درهر درخواستی که از سمت کاربر به سرور می رود , این کوکی که حاوی Id کاربر است نیز ارسال می شود تا کاربر در سمت سرور شناخته گردد . اما در روش Bearer Token دیگر خبری از کوکی نیست , این عدم نیاز به کوکی دو مزیت دارد :

 1 - امنیت بیشتر چرا که کوکی توسط مرورگر ها در هر درخواستی به سرور ارسال می شود که این احتمال حملات CSRF را فزایش می دهد (توجه داشته باشین که از لحاظ امنیت , چه Cookie Authentication  و چه Bearer Token Authentication  بدون استفاده از SSL یک شوخی تمام عیار هستند).

 2 - قابلیت استفاده از سرویس های وبسایت شما در پلتفرم هایی که کوکی را پشتیبانی نمی کنند مثل برنامه های مخصوص موبایل های هوشمند بر روی ios و اندروید و ... 

 یکی از قابلیت های مهم Bearer Token , پیاده سازی مکانیزم Authentication و Authrization (مکانیزم ورود و تایید هویت کاربران) به صورت Ajax می باشد , من در اینجا قصد ندارم به مزایای Ajax  بپردازم اما انشالله در آینده راجع به SPA مقالاتی خواهم نوشت , فعلا" همین قدر بس که پایه اصلی هر SPA  , ورود و تایید هویت کاربران به صورت Ajax  است .

 بعد از این توضیحات مختصر می رسیم به پیاده سازی , مراحل کار به ترتیب :

 1 - ابتدا سورس کد مورد نیاز را از مقاله فارسی سازی خطاهای Asp.Net Identity Model با Localization دانلود کنید. (ما از مقاله اول تا کنون همین سورس را توسعه داده ایم)

 2 - پروزه را در ویژوال استدیو باز کرده و یکبار Rebuild کنید تا Missing Nuget ها نصب شوند . (اگر این سورس را با حجم بیشتر دانلود کنید , حاوی Nuget Pakage ها هم می باشد و نیازی به Rebuild برای دانلود مجدد پکیج ها ندارید.)

 3 - افزودن WebApi به پروژه MVC:

 فریم ورک استاندارد مایکروسافت برای کار با Ajax چیزی نیست جزء WebApi , مزیت WebApi نسبت به WCF و MVC Controller سبک بودن چرخه حیات است که منجر به افزایش سرعت می شود . البته این چرخه حیات سبک به علت امکانات کمتر از WCF است که البته در کار با Ajax  ما نیازی به این امکانات اضافی نداریم . (اگر در وب به جستجو بپردازید , فریم ورک های مدعی سبکتر و سریعتر بودن از WebApi را هم پیدا خواهید کرد.)

 

مراحل افزودن WebApi :

 

3.1 - نصب پکیج Microsoft.AspNet.WebApi

 3.2 - نصب پکیج Microsoft.AspNet.WebApi.Owin

 3.3 - ساختن فایل WebApiConfig.cs در فولدر App_Start با کدهای شکل زیر :

using System.Web.Http;

using Microsoft.Owin.Security.OAuth;

namespace OO.WebUI

{

public static class WebApiConfig

{

public static void Register(HttpConfiguration config)

{

// TODO: Add any additional configuration code.

// Web API routes

config.MapHttpAttributeRoutes();

// Configure Web API to use only bearer token authentication.

config.SuppressDefaultHostAuthentication();

config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));

//config.Routes.MapHttpRoute(

// name: "DefaultApi",

// routeTemplate: "api/{controller}/{id}",

// defaults: new { id = RouteParameter.Optional }

//);

// WebAPI when dealing with JSON & JavaScript!

// Setup json serialization to serialize classes to camel (std. Json format)

//*******************************************************************************

//var formatter = GlobalConfiguration.Configuration.Formatters.JsonFormatter;

//formatter.SerializerSettings.ContractResolver =

// new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver();

}

}

}

در کد های بالا 2 خط کد زیر , به WebApi می گویند به جای Cookie Authentication از Bearer Token Authentication استفاده کن :

config.SuppressDefaultHostAuthentication();

config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));

3.4 - افزودن یک خط کد با کامنت مشخص شده به فایل Global.asax (ترتیب خطوط مهم است , توضیحات بیشتر اینجا):

protected void Application_Start()

{

AreaRegistration.RegisterAllAreas();

GlobalConfiguration.Configure(WebApiConfig.Register);//این خط اضافه شود

FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);

RouteConfig.RegisterRoutes(RouteTable.Routes);

BundleConfig.RegisterBundles(BundleTable.Bundles);

ClientDataTypeModelValidatorProvider.ResourceClassKey = "Resources.ErrorMessages";

DefaultModelBinder.ResourceClassKey = "Resources.ErrorMessages";

}

 برای توضیحات بیشتر به مستندات WebApi مراجعه فرمایید .

4 - تزریق وابستگی های WebApi به وسیله AutoFac :

4.1 - نصب پکیج Autofac.WebApi2.Owin

4.2 - افزودن خطوط مشخص شده با کامنت در شکل زیر به فایل Startup.cs :

using System.Reflection;

using System.Web;

using System.Web.Http;

using System.Web.Mvc;

using Autofac;

using Autofac.Integration.Mvc;

using Autofac.Integration.WebApi;

using Microsoft.AspNet.Identity;

using Microsoft.Owin;

using Microsoft.Owin.Security;

using Microsoft.Owin.Security.DataProtection;

using OO.WebUI.Localization;

using OO.WebUI.Models;

using Owin;

[assembly: OwinStartupAttribute(typeof(OO.WebUI.Startup))]

namespace OO.WebUI

{

public partial class Startup

{

public void Configuration(IAppBuilder app)

{

var builder = new ContainerBuilder();

// Get your HttpConfiguration.

var config = new HttpConfiguration();//این خط کد اضافه گردد

// REGISTER DEPENDENCIES

builder.RegisterType<ApplicationDbContext>().AsSelf().InstancePerRequest();

builder.RegisterType<SmsService>().AsSelf().InstancePerRequest();

builder.RegisterType<EmailService>().AsSelf().InstancePerRequest();

builder.RegisterType<LocalizeUserValidator<ApplicationUser, int>>().As<IIdentityValidator<ApplicationUser>>().InstancePerRequest().OnActivating(e => e.Instance.Initialize());

builder.RegisterType<LocalizePasswordValidator>().As<IIdentityValidator<string>>().InstancePerRequest().OnActivating(e => e.Instance.Initialize());

builder.RegisterType<ApplicationUserStore>().As<IUserStore<ApplicationUser, int>>().InstancePerRequest();

builder.RegisterType<ApplicationUserManager>().AsSelf().InstancePerRequest().PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies);

//builder.RegisterType<ApplicationUserManager>().As<UserManager<ApplicationUser, int>>().InstancePerRequest().PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies);

builder.RegisterType<ApplicationRoleStore>().As<IRoleStore<ApplicationRole, int>>().InstancePerRequest();

builder.RegisterType<ApplicationRoleManager>().AsSelf().InstancePerRequest();

builder.RegisterType<GroupStoreBase>().AsSelf().InstancePerRequest();

builder.RegisterType<ApplicationGroupStore>().AsSelf().InstancePerRequest();

builder.RegisterType<ApplicationGroupManager>().AsSelf().InstancePerRequest();

builder.RegisterType<ApplicationSignInManager>().AsSelf().InstancePerRequest();

builder.Register<IAuthenticationManager>(c => HttpContext.Current.GetOwinContext().Authentication).InstancePerRequest();

builder.Register<IDataProtectionProvider>(c => app.GetDataProtectionProvider()).InstancePerRequest();

// REGISTER CONTROLLERS SO DEPENDENCIES ARE CONSTRUCTOR INJECTED

builder.RegisterControllers(typeof(MvcApplication).Assembly);

// Register your Web API controllers.

builder.RegisterApiControllers(Assembly.GetExecutingAssembly());//این خط کد اضافه گردد

// OPTIONAL: Register the Autofac filter provider.

builder.RegisterWebApiFilterProvider(config);//این خط کد اضافه گردد

// BUILD THE CONTAINER

var container = builder.Build();

// REPLACE THE MVC DEPENDENCY RESOLVER WITH AUTOFAC

DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

// Configure Web API with the dependency resolver.

GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);//این خط کد اضافه گردد

// REGISTER WITH OWIN

app.UseAutofacMiddleware(container);

app.UseAutofacMvc();

app.UseAutofacWebApi(config);//این خط کد اضافه گردد

ConfigureAuth(app);

}

}

}

 برای توضیحات بیشتر به مستندات AutoFac مراجعه فرمایید .

5 - ساخت یک OAuthAuthorizationServerProvider : این کلاس وظیفه اصلی Authentication را بر عهده دارد , ما این کلاس را بوسیله مراحل زیر می سازیم :

5.1 - ساخت فولدر با نام CoreDevelope در ریشه سایت و ساخت فولدر با نام Security در داخل آن .

5.2 - ساخت کلاس ApplicationOAuthAuthorizationServerProvider در داخل فولدر Security با کدهای شکل زیر :

using System;

using System.Security.Claims;

using System.Threading.Tasks;

using Autofac;

using Autofac.Integration.Owin;

using Microsoft.Owin.Security.OAuth;

namespace OO.WebUI.CoreDevelope.Security

{

public class ApplicationOAuthAuthorizationServerProvider : OAuthAuthorizationServerProvider

{

private readonly string _publicClientId;

public ApplicationOAuthAuthorizationServerProvider(string publicClientId)

{

if (publicClientId == null)

{

throw new ArgumentNullException("publicClientId");

}

_publicClientId = publicClientId;

}

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 async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)

{

using (var scope = context.OwinContext.GetAutofacLifetimeScope())

{

var userManager = scope.Resolve<ApplicationUserManager>();

var user = await userManager.FindAsync(context.UserName, context.Password);

}

var identity = new ClaimsIdentity(context.Options.AuthenticationType);

identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));

 

context.Validated(identity);

}

}

}

 اما مقداری توضیحات راجع به کد بالا :

 publicClientId_ : این عدد در حال حاضر برای شما کاربرد ندارد , فقط در کد قرار دادم تا بیشتر با قابلیتهای Asp.Net Identity آشنا بشید , در واقع publicClientId برای حالتی استفاده می شود که شما قصد دارید یک OAuthAuthorizationServer بسازید

این OAuthAuthorizationServer به Client های شما اجازه می دهد که کاربران آنها از طریق نام کاربری خود در وبسایت شما به آنها Login نمایند , یعنی وبسایت شما همانند Gmail یا فیسبوک عمل نماید , این ClientId , مشخص کننده نام سایت یا برنامه ایی است که از سرویس Authorization شما استفاده می کند , خوب این امکان احتمالا" بدرد تعداد بسیار محدودی از برنامه نویسان و شرکت ها خواهد خورد .

متد ValidateClientAuthentication : در این متد , گفته ایم حتی اگر context.ClientId برابر با null باشد باز هم Context معتبر باشد , چرا ؟ چون همانطور که گفتم ما به غیر از خودمان Client نداریم و در نتیجه context.ClientId برای ما ارسال نمی شود , مگر اینکه شما یک سرور مثل فیسبوک باشید .

متد GrantResourceOwnerCredentials : کار اصلی Authentication در داخل این متد انجام می شود ,  ما به خاط اینکه تزریق وابستگی ها توسط AutoFac انجام شود و نه Owin چهار خط کد زیر در کلاس StartUp در فایل Startup.Auth.cs کامنت کردیم :

//app.CreatePerOwinContext(ApplicationDbContext.Create);

//app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);

//app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);

//app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);

حال برای دسترسی به کلاس ApplicationUserManager , نیاز است تا به Owin از داخل AutoFac دسترسی پیدا کنیم , این مهم به کمک یک Extention متد AutoFac به نام GetAutofacLifetimeScope انجام می شود که در پیاده سازی متد GrantResourceOwnerCredentials در کدهای بالا روش آن را دیدید.

 اما توضیحات مربوط به خط زیر :

identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));

این خط , نام کاربری , کاربر وارد شده به سایت به کمک Bearer Token Authentication را در داخل System.Web.HttpContext.Current.User.Identity.Name قرار می دهد , اگر این خط را ننویسید مقدار System.Web.HttpContext.Current.User.Identity.Name همیشه برابر با null خواهد بود .

6 - استفاده از ApplicationOAuthAuthorizationServerProvider نوشته شده در مرحله قبل :

در داخل فایل Startup.Auth.cs در فولدر App_Start خطوط به رنگ زرد در کد زیر را اضافه می کنیم :

using System;

using Microsoft.AspNet.Identity;

using Microsoft.AspNet.Identity.Owin;

using Microsoft.Owin;

using Microsoft.Owin.Security.Cookies;

using Microsoft.Owin.Security.Google;

using Microsoft.Owin.Security.OAuth;

using OO.WebUI.CoreDevelope.Security;

using Owin;

using OO.WebUI.Models;

namespace OO.WebUI

{

public partial class Startup

{

// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
//این خط کد اضافه گردد
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
//این خط کد اضافه گردد
public static string PublicClientId { get; private set; }

public Startup()

{

// Configure the application for OAuth based flow
//این خط کد اضافه گردد
PublicClientId = "web";
//این خط کد اضافه گردد
OAuthOptions = new OAuthAuthorizationServerOptions
//این خط کد اضافه گردد
{
//این خط کد اضافه گردد
TokenEndpointPath = new PathString("/Token"),
//این خط کد اضافه گردد
Provider = new ApplicationOAuthAuthorizationServerProvider(PublicClientId),
//این خط کد اضافه گردد
AccessTokenExpireTimeSpan = TimeSpan.FromDays(30),
//این خط کد اضافه گردد
AuthenticationType = OAuthDefaults.AuthenticationType,
//این خط کد اضافه گردد
AllowInsecureHttp = true,
//این خط کد اضافه گردد
};
//این خط کد اضافه گردد
}

public void ConfigureAuth(IAppBuilder app)

{

// Configure the db context, user manager and signin manager to use a single instance per request

//app.CreatePerOwinContext(ApplicationDbContext.Create);

//app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);

//app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);

//app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);

// Enable the application to use a cookie to store information for the signed in user

// and to use a cookie to temporarily store information about a user logging in with a third party login provider

// Configure the sign in cookie

app.UseCookieAuthentication(new CookieAuthenticationOptions

{

AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,

LoginPath = new PathString("/Account/Login"),

Provider = new CookieAuthenticationProvider

{

// Enables the application to validate the security stamp when the user logs in.

// This is a security feature which is used when you change a password or add an external login to your account.

OnValidateIdentity = SecurityStampValidator

// ADD AN INT AS A THIRD TYPE ARGUMENT:

.OnValidateIdentity<ApplicationUserManager, ApplicationUser, int>(

validateInterval: TimeSpan.FromMinutes(30),

// THE NAMED ARGUMENT IS DIFFERENT:

regenerateIdentityCallback: (manager, user)

=> user.GenerateUserIdentityAsync(manager),

// Need to add THIS line because we added the third type argument (int) above:

getUserIdCallback: (claim) => int.Parse(claim.GetUserId()))

}

});

app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

// Enable the application to use bearer tokens to authenticate users
//این خط کد اضافه گردد
app.UseOAuthBearerTokens(OAuthOptions);

// Enables the application to temporarily store user information when they are verifying the second factor in the two-factor authentication process.

app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));

// Enables the application to remember the second login verification factor such as phone or email.

// Once you check this option, your second step of verification during the login process will be remembered on the device where you logged in from.

// This is similar to the RememberMe option when you log in.

app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);

// Uncomment the following lines to enable logging in with third party login providers

//app.UseMicrosoftAccountAuthentication(

// clientId: "",

// clientSecret: "");

//app.UseTwitterAuthentication(

// consumerKey: "",

// consumerSecret: "");

//app.UseFacebookAuthentication(

// appId: "",

// appSecret: "");

//app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()

//{

// ClientId = "",

// ClientSecret = ""

//});

}

}

}

اما توضیحات کد بالا :

کد خط زیر

TokenEndpointPath = new PathString("/Token")

 آدرس مسیریی است که شما باید برای Login یک درخواست با Ajax  به آن ارسال نمایید .

کد خط زیر :

AllowInsecureHttp = true

این کد به شما اجازه می دهد تا عملیات Authenticate بدون نیاز به SSL انجام شود .

 و در نهایت PublicClientId را برابر مقدار Web قرار داده ایم که با توجه به توضیحات قبلی در مورد OAuthAuthorizationServer , این خط کد ارزشی ندارد.

7 - ایجاد یک WebApi Controller برای تست :

 7.1 - بر روی فولدر Controllers راست کلیک کنید و Add > Controller را انتخاب نمایید سپس از دیالوگ باز شده

Web API 2 Controller-Empty را انتخاب و دکمه Add را بزنید و نام SecureController را برای این Controller انتخاب و مجددا" دکمه Add را بزنید .

7.2 - کد SecureController را به شکل زیر بنویسید :

using System.Web.Http;

using Newtonsoft.Json;

using Newtonsoft.Json.Linq;

using OO.Model.Messinger;

namespace OO.WebUI.Controllers

{

[Authorize]

public class SecureController : ApiController

{

[Route("Secure/Test")]

[HttpPost]

public JObject Test(JObject message)

{

dynamic mymessage = message;

var newMessage = new Message()

{

Id = mymessage.Id,

Text = mymessage.Text

};

 

return JObject.Parse(JsonConvert.SerializeObject(newMessage, Formatting.None));

}

}

}

همانطور که در کد بالا مشاهده می فرمایید ما از صفت Authorize بر روی کل Controller استفاده کردیم تا کاربری بدون Authenticate به این Controller دسترسی نداشته باشد , سپس برای تست , یک متد به نام Test ایجاد کرده ایم که یک Message می گیرد و دوباره همان را بر می گرداند , دقت فرمایید که با استفاده از صفت Route آدرس دسترسی به متد را Secure/Test قرار داده اییم و با استفاده از صفت HttpPost نحوه ارسال اطلاعات به متد را Post قرار داده ایم .

7.3 - حال زمان استفاده از متد تست به صورت Ajax می باشد , برای این منظور و فقط برای تست به شکل زیر , من خیلی ساده یک دکمه و تابع جاوا اسکریپتی به فایل Layout.cshtml_ که در فولدر Shared زیرفولدر View قرار دارد

اضافه کردم (خطوط با کامنت مشخص شده):

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8" />

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>@ViewBag.Title - My ASP.NET Application</title>

@Styles.Render("~/Content/css")

@Scripts.Render("~/bundles/modernizr")

</head>

<body>

<div class="navbar navbar-inverse navbar-fixed-top">

<div class="container">

<div class="navbar-header">

<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">

<span class="icon-bar"></span>

<span class="icon-bar"></span>

<span class="icon-bar"></span>

</button>

@Html.ActionLink("Application name", "Index", "Home", null, new { @class = "navbar-brand" })

</div>

<div class="navbar-collapse collapse">

<ul class="nav navbar-nav">

<li>@Html.ActionLink("Home", "Index", "Home")</li>

<li>@Html.ActionLink("About", "About", "Home")</li>

<li>@Html.ActionLink("Contact", "Contact", "Home")</li>

@if (Request.IsAuthenticated && User.IsInRole("Admin")) {

<li>@Html.ActionLink("RolesAdmin", "Index", "RolesAdmin")</li>

<li>@Html.ActionLink("UsersAdmin", "Index", "UsersAdmin")</li>

<li>@Html.ActionLink("GroupsAdmin", "Index", "GroupsAdmin")</li>

}

</ul>

@Html.Partial("_LoginPartial")

</div>

</div>

</div>

<div class="container body-content">

@RenderBody()

<hr />

<footer>
<!-- خط زیر را اضافه کنید -->
<input type="button" value="صدا زدن متد تست" onclick="callTest()" />

<p>&copy; @DateTime.Now.Year - My ASP.NET Application</p>

</footer>

</div>
<!-- اسکریپت زیر را اضفه کنید -->
<script>

function callTest() {

var message = {

Id: 1,

Text: 'تست'

};

$.ajax({

type: 'POST',

url: '/Secure/Test',

data: message

 

}).done(function (data) {

alert(data.Text);

}).fail(function (data) {

alert(data.responseText);

});

}

</script>

@Scripts.Render("~/bundles/jquery")

@Scripts.Render("~/bundles/bootstrap")

@RenderSection("scripts", required: false)

</body>

</html>

با کلیک بر روی دکمه صدا زدن متد تست , همانند شکل زیر خطای Authorizaion را دریافت خواهید کرد و این دقیقا" همان رفتاری است که ما انتظار داشتیم

7.4 - و اما مرحله نهایی , افزودن متد Login به صورت Ajax و صدا زدن مجدد متد تست و دریافت نتیجه بدون خطا به شکل زیر (خطوط با کامنت نشخص شده افزوده گردد) :

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8" />

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>@ViewBag.Title - My ASP.NET Application</title>

@Styles.Render("~/Content/css")

@Scripts.Render("~/bundles/modernizr")

</head>

<body>

<div class="navbar navbar-inverse navbar-fixed-top">

<div class="container">

<div class="navbar-header">

<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">

<span class="icon-bar"></span>

<span class="icon-bar"></span>

<span class="icon-bar"></span>

</button>

@Html.ActionLink("Application name", "Index", "Home", null, new { @class = "navbar-brand" })

</div>

<div class="navbar-collapse collapse">

<ul class="nav navbar-nav">

<li>@Html.ActionLink("Home", "Index", "Home")</li>

<li>@Html.ActionLink("About", "About", "Home")</li>

<li>@Html.ActionLink("Contact", "Contact", "Home")</li>

@if (Request.IsAuthenticated && User.IsInRole("Admin")) {

<li>@Html.ActionLink("RolesAdmin", "Index", "RolesAdmin")</li>

<li>@Html.ActionLink("UsersAdmin", "Index", "UsersAdmin")</li>

<li>@Html.ActionLink("GroupsAdmin", "Index", "GroupsAdmin")</li>

}

</ul>

@Html.Partial("_LoginPartial")

</div>

</div>

</div>

<div class="container body-content">

@RenderBody()

<hr />

<footer>

<input type="button" value="صدا زدن متد تست" onclick="callTest()" />
<!-- خط زیر را اضافه کنید -->
<input type="button" value="ورود به سایت" onclick="login()" />

<p>&copy; @DateTime.Now.Year - My ASP.NET Application</p>

</footer>

</div>

<script>

 
<!-- خط زیر را اضافه کنید -->
var tokenKey = "accessToken";
<!-- خط زیر را اضافه کنید -->
function login() {
<!-- خط زیر را اضافه کنید -->
if (typeof (Storage) !== "undefined") {
<!-- خط زیر را اضافه کنید -->
var loginData = {
<!-- خط زیر را اضافه کنید -->
grant_type: 'password',
<!-- خط زیر را اضافه کنید -->
username: 'admin@example.com',
<!-- خط زیر را اضافه کنید -->
password: 'Admin@123456'
<!-- خط زیر را اضافه کنید -->
};
<!-- خط زیر را اضافه کنید -->
$.ajax({
<!-- خط زیر را اضافه کنید -->
type: 'POST',
<!-- خط زیر را اضافه کنید -->
url: '/Token',
<!-- خط زیر را اضافه کنید -->
data: loginData
<!-- خط زیر را اضافه کنید -->
}).done(function (data) {
<!-- خط زیر را اضافه کنید -->
localStorage.setItem(tokenKey, data.access_token);
<!-- خط زیر را اضافه کنید -->
alert("ورود موفق");
<!-- خط زیر را اضافه کنید -->
}).fail(function (data) {
<!-- خط زیر را اضافه کنید -->
alert(data.responseText);
<!-- خط زیر را اضافه کنید -->
});
<!-- خط زیر را اضافه کنید -->
} else {
<!-- خط زیر را اضافه کنید -->
alert("Sorry! No Web Storage support..");
<!-- خط زیر را اضافه کنید -->
}
<!-- خط زیر را اضافه کنید -->
}
<!-- خط زیر را اضافه کنید -->
function callTest() {
<!-- خط زیر را اضافه کنید -->
var headers = {};
<!-- خط زیر را اضافه کنید -->
var token = localStorage.getItem(tokenKey);
<!-- خط زیر را اضافه کنید -->
if (token) {
<!-- خط زیر را اضافه کنید -->
headers.Authorization = 'Bearer ' + token;
<!-- خط زیر را اضافه کنید -->
}

var message = {

Id: 1,

Text: 'تست'

};

$.ajax({

type: 'POST',

url: '/Secure/Test',

data: message,
<!-- خط زیر را اضافه کنید -->
headers: headers

}).done(function (data) {

alert(data.Text);

}).fail(function (data) {

alert(data.responseText);

});

}

</script>

@Scripts.Render("~/bundles/jquery")

@Scripts.Render("~/bundles/bootstrap")

@RenderSection("scripts", required: false)

</body>

</html>

خوب کد بالا خیلی شفاف هست , یک متد login تعریف شده است که loginData را که شامل نام کاربری و کلمه عبور است را به آدرس Token\ پست می نماید و در صورت Login موفقیت آمیز یک Token دریافت و در localStorage با عنوان accessToken ذخیره می کند . توجه داشته باشین که ما Token دریافتی را باید برای استفاده های بعدی در جایی ذخیره کنیم , بهترین مکان localStorage یا sessionStorage می باشد , اگر از sessionStorage استفاده کنید , Token ذخیره شده برای یک Session می باشد و در Tab های دیگر مرورگر قابل مشاهده نمی باشد , اما localStorage هیچ انقضایی ندارد

آنگاه در متد callTest یک آبجکت با نام headers تعریف شده است که این  accessToken درون آن قرار داده می شود و به عنوان Header درخواست Ajax به سرور پست می شود .

خوب برنامه را اجرا نمایید و بر روی دکمه ورود به سایت کلیک نمایید و منتظر دریافت پیام "ورود موفق" بمانید , سپس بر روی دکمه صدا زدن متد تست کلیک کنید و اینبار باید همانند شکل زیر موفق باشید :

و اما نکته اخر , ممکن است شما سرویسی نوشته باشید که وبسایت های دیگر بخواهند از آن استفاده کنند , مثل سرویس وضعیت آب و هوا , به طور پیش فرض به دلیل مسایل امنیتی , مرورگر ها اجازه درخواست Ajax از یک وبسایت به وبسایت دیگر را نمی دهند , شما برای حل این مشکل باید یک پکیج دیگر به نام Microsoft.Owin.Cors را نصب کنید و سپس یک خط کد زیر را به متد Configuration در کلاس Startup بیافزایید (خط زرد رنگ) :

// REGISTER WITH OWIN

app.UseAutofacMiddleware(container);

app.UseAutofacMvc();

app.UseAutofacWebApi(config);

//Enabling Cross-Origin
//خط زیر اضافه گردد
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);

ConfigureAuth(app);

لینک دانلود سورس این مقاله در پایین صفحه موجود است , چون Nuget Pakage ها همراه سورس نیست , در نتیجه بعد از باز کردن Solution در ویژوال استدیو برای دریافت Missing Pakage ها باید یکبار Rebuild کنید .

خوب به پایان آمد این دفتر ولی ... این مقاله تنها نوک کوه یخ Asp.Net Identity است که از آب بیرون زده است , انشالله اگر زمان یاری کند این مدل حالا حالا ها جای بهبود و توسعه و البته مقاله دارد . امیدوارم این مقاله برای شما مفید بوده باشد .