برنامه نویس وب


دوشنبه 23 مهر
MVC

JSON Web Token in ASP.NET Web API 2 using Owin

جمعه, 15 بهمن 1395

در این مطلب قصد دارم  اعتبارسنجی سمت سرور در web api را به وسیله owin راه اندازی کنم در این روش موقع لاگین کردن یک token برای کلاینت ارسال می شود و تا زمانی که منقضی نشده باشد می توان از این رشته جهت دسترسی به اکشن ها و کنترل هایی که نیاز به لاگین کردن دارن استفاده کرد و امنیت web api خود مون رو تأمین کنیم .

یک پروژه از نوع Empty ( تیک mvc  و web api را بزنید ) بسازید.

با استفاده از Package Manager Console که در منوی Tools قرار دارد کتابخانه های  مورد نیاز را با دستور زیر به پروژه اضافه می کنیم.

PM> Install-Package Microsoft.AspNet.WebApi.Owin

PM> Install-Package Microsoft.Owin.Host.SystemWeb

PM> Install-Package Microsoft.Owin.Cors

PM> Install-Package Microsoft.Owin.Security.OAuth

یک کلاس با نام Startup در پوشه App_Start با فضای نام اسم پروژه اضافه می کنیم و کدهای زیر را در این کلاس می نویسیم.

public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            HttpConfiguration config = new HttpConfiguration();
            config.MapHttpAttributeRoutes();
            ConfigureOAuth(app);
            app.UseCors(CorsOptions.AllowAll);
            app.UseWebApi(config);
        }

        public void ConfigureOAuth(IAppBuilder app)
        {
            OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
            {
                AllowInsecureHttp = true,
                TokenEndpointPath new PathString("/login"),
                AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
                Provider = new CustomOAuthProvider()
            };

            // OAuth 2.0 Bearer Access Token Generation
            app.UseOAuthAuthorizationServer(OAuthServerOptions);

            // Api controllers with an [Authorize] attribute will be validated
            app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());

        }
    }

در مورد کدهای بالا چند نکته هست در قسمت TokenEndpointPath آدرسی تعیین می کنیم برای لاگین کردن کلاینت ها و این مقدار هم  هر چیزی می تونه باشه  و همچنین در قسمت AccessTokenExpireTimeSpan مدت زمانی که قرار است لاگین کاربر اعتبار داشته باشد را تعیین می کنیم.

در قسمت Provider از یک کلاس استفاده کردیم برای شخصی سازی لاگین کاربر و برای این کار یک کلاس با نام CustomOAuthProvider می سازیم با کدهای زیر

public class CustomOAuthProvider : OAuthAuthorizationServerProvider
    {
        OwinDBEntities db = new OwinDBEntities();
        public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
        {
            string clientId = string.Empty;
            string clientSecret = string.Empty;

            if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
            {
                context.TryGetFormCredentials(out clientId, out clientSecret);
            }

            if (context.ClientId == null)
            {
                context.SetError("invalid_clientId""client_Id is not set");
                return Task.FromResult<object>(null);
            }

            context.Validated();
            return Task.FromResult<object>(null);
        }

        public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
        {

            context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin"new[] { "*" });
            if (!db.Users.Any(p=>p.Username==context.UserName && p.Password== context.Password && p.ClientID==context.ClientId))
            {
                context.SetError("invalid_grant""The user name or password is incorrect");
                return Task.FromResult<object>(null);
            }
            var user = db.Users.First(p => p.Username == context.UserName && p.Password == context.Password && p.ClientID == context.ClientId);
            var identity = new ClaimsIdentity("JWT");

            identity.AddClaim(new Claim(ClaimTypes.Name, user.Username));
            identity.AddClaim(new Claim("sub", user.Username));
            identity.AddClaim(new Claim(ClaimTypes.Role, user.Roles.RoleName));
            var props = new AuthenticationProperties(new Dictionary<stringstring>
                {
                    {
                         "audience", user.ClientID
                    }
                });

            var ticket = new AuthenticationTicket(identity, props);
            context.Validated(ticket);
            return Task.FromResult<object>(null);

        }
    }

برای لاگین کردن ما یک مقدار از کلاینت می گیریم به اسم client_Id که بیشتر به درد لاگین کردن کلاینت های اندرویدی می خوره که یک نفر نتونه از یک گوشی دیگه لاگین کنه که این اختیاری هست و می تونید از این مقدار استفاده نکنید در ValidateClientAuthentication این مقدار ست می کنیم و اگه کلاینت این مقدار خالی وارد کنه بهش ارور میدیم 

در قسمت GrantResourceOwnerCredentials ما به بانک اطلاعاتیمون وصل میشیم و چک می کنیم آیا همچین کاربری وجود داره یا نه  اگر وجود نداشت بهش پیغام غلط بودن نام کاربری و پسورد میدیم و بعدش اگه اطلاعات کلاینت صحیح بود نام کاربر و نقش اون ست می کنیم و عملیات لاگین کاربر تمام می شود.

عکس جداول استفاده شده به صورت زیر است شما برحسب نیازتون می تونید از فیلدهای بیشتری استفاده کنید.

Roles

Users

در نهایت در Global کدهای زیر را اضافه می کنیم.

            HttpConfiguration config = GlobalConfiguration.Configuration;

            config.Formatters.JsonFormatter
                        .SerializerSettings
                        .ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;

برای تست اینکار می تونید از برنامه postman که از Extensions گوگول هست استفاده کنید . 

postman

زمانی که access_token را دریافت کردید می توانید به روش زیر از اون استفاده کنید

postman

در هدر درخواستمون یک Authorization اضافه می کنیم و مقدار اون با کلمه bearer به اضافه یک فاصله و access_token درخواستمون ارسال می کنیم.



نظرات

Drag to order
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

میثممیثم

عالی آقای مهندس سیف، مطلب بسیار آموزنده و عالی بود، بسیاااااااااااااار استفاده کردیم آقای مهندس، منتظر مطالب آموزنده بعدی شما هستیم .

  پاسخ: مرسی از نظرتون مهندس heart


علیرضاعلیرضا

سلام ممنون از مطلب جالب و به روزتون

 


محسنمحسن

سلام خدمت شما مهندس عزیز اگر مطلب کاملتون کامل تر کنید ممنون میشم اینکه جداول بانکمون شامل چه فیلدهایی باشه

      پاسخ: سلام چشم عکس اونها رو هم می ذارم