احراز هویت در Web Api 2

در این مقاله قصد داریم راجع به احراز هویت در ASP.NET WEB API2 صحبت کنیم.فیلتر های احراز هویت درخواست های HTTP را هویت سنجی می کنند.هر دو Web API 2 و MVC 5 از فیلترهای authentication پشتیبانی می کنند. منتها کمی با هم متفاوت هست

احراز هویت در  Web Api 2

فیلتر های احراز هویت درخواست های HTTP را هویت سنجی می کنند.هر دو Web API 2  و MVC 5 از فیلترهای authentication پشتیبانی می کنند. منتها کمی با هم متفاوت هستند.در این مقاله از احراز هویت در Web Api صحبت خواهیم کرد.

با فیلتر های احراز هویت می توانید بر روی کنترلر ها و اکشن هایی که می خواهید احراز هویت انجام دهید.پروژه ای که ضمیمه مقاله است فیلتر های احراز هویت را که احراز هویت های پایه بر روی HTTP را انجام می دهد نشان میدهد.فیلتر ها در کلاس IdentityBasicAuthenticationAttribute پیاده سازی و تعریف شده اند.

تنظیم فیلتر های احراز هویت

مانند سایر فیلتر هایی که در دات نت وجود دارند می توانید این فیلتر ها را هم بر روی هر کنترلر هر اکشن  اعمال نمود.در کد زیر یک کنترلر WEB API نشان داده شده است .دو فیلتر در بالای آن نوشته شده است .فیلتر [IdentityBasicAuthentication] به تمام اکشن های این کنترلر احراز هویت پایه را معرفی می کند. و برای این که هر اکشن آن را اعمال کند کافی است که بالای اکشن از فیلتر [Authorize] استفاده کنیم.

این فیلتر کار محدود ساختن دسترسی به متدهای کنترلرها را انجام می‌دهد. وقتی بالای اکشن یه فیلتر را می نویسیم به این معنا است که کاربران اعتبارسنجی نشده، امکان دسترسی به این اکشن را ندارند

برای اعمال فیلتر به همه اکشن ها(که دیگر نیاز نباشد بالای تک تک آنها کد فیلتر را بنویسیم ) در GlobalConfiguration.Filters. به صورت زیر عمل می کنیم

[IdentityBasicAuthentication] // Enable Basic authentication for this controller.
[Authorize] // Require authenticated requests.
public class HomeController : ApiController
{
    public IHttpActionResult Get() { . . . }
    public IHttpActionResult Post() { . . . }
}

پیاده سازی فیلتر های احراز هویت در Web API 
فیلتر ها اینترفیس System.Web.Http.Filters.IAuthenticationFilter  را پیاده سازی می کنندو همچنین از کلاس System.Attribute مشتق می شوند.

فرآیند احراز هویت در وب چگونه است

فر آیند احراز هویت در وب به این صورت است که قبل از اجرا شدن اکشن مورد نظر وب یک لیستی از فیلتر های احراز هویتی که برای این اکشن در نظر گرفته شده است می سازد.این لیست شامل تمام فیلتر های با محدوده کنترلر اکشن و Global است.

پس از این مرحله وب AuthenticateAsync  هر فیلتر را فراخوانی می کند.هر فیلتر می تواند درخواست رسیده را بررسی کرده و از صحت آن مطمئن شود.اگر درخواست درست بود و از فیلتر رد شد فیلتر مورد نظر به انتهای درخواست IPrincipal  را اضافه می کندو اگر درخواست درست نباشد یک خطایی نشان داده میشود.

با فرض اینکه خطایی وجود نداشته باشد درخواست  بقیه مسیر  برای اجرا در سرور و بازگشت پاسخ را ادامه می دهد.

در انتها وب ChallengeAsync  هر فیلتر را فراخوانی می کند.فیلتر ها از این متد برای افزودن challenge  به انتهای Response یا پاسخ استفاده می کنند.

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

در سناریو دوم  درخواست از فیلتر احراز هویت می گذرد ولی از فیلتر دوم که مجوز های دسترسی در آن چک می شود نمی تواند رد شودبنابر این خطای 401 رخ می دهد.

همان طور که می دانید  Authentication یعنی  چه کاربری به سایت وارد شده است. Authorization ، سطح دسترسی کاربر وارد شده به سیستم و اعمالی را که مجاز است انجام دهد را مشخص می‌کند.

پیاده سازی متد AuthenticateAsync 

متد AuthenticateAsync  تلاش می کند که درخواست را احراز هویت کند.کد زیر متعلق به این متد است

Task AuthenticateAsync(
    HttpAuthenticationContext context,
    CancellationToken cancellationToken
)

این متد باید یک از کارها سه گانه زیر را انجام دهد.1- هیچ کاری نکند2- ساخت IPrincipal  و افزودن آن به انتهای درخواست.3-ایجاد خطا در صورت احراز نشدن هویت

کد زیر متد AuthenticateAsync  را به طور کامل نشان میدهد.

public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
    // 1. Look for credentials in the request.
    HttpRequestMessage request = context.Request;
    AuthenticationHeaderValue authorization = request.Headers.Authorization;

    // 2. If there are no credentials, do nothing.
    if (authorization == null)
    {
        return;
    }

    // 3. If there are credentials but the filter does not recognize the 
    //    authentication scheme, do nothing.
    if (authorization.Scheme != "Basic")
    {
        return;
    }

    // 4. If there are credentials that the filter understands, try to validate them.
    // 5. If the credentials are bad, set the error result.
    if (String.IsNullOrEmpty(authorization.Parameter))
    {
        context.ErrorResult = new AuthenticationFailureResult("Missing credentials", request);
        return;
    }

    Tuple<string, string> userNameAndPasword = ExtractUserNameAndPassword(authorization.Parameter);
    if (userNameAndPasword == null)
    {
        context.ErrorResult = new AuthenticationFailureResult("Invalid credentials", request);
    }

    string userName = userNameAndPasword.Item1;
    string password = userNameAndPasword.Item2;

    IPrincipal principal = await AuthenticateAsync(userName, password, cancellationToken);
    if (principal == null)
    {
        context.ErrorResult = new AuthenticationFailureResult("Invalid username or password", request);
    }

    // 6. If the credentials are valid, set principal.
    else
    {
        context.Principal = principal;
    }

}

اگر درخواست نامعتبر باشد مقدار Context.ErrorResult به IHttpActionResult  مقداردهی می شود و خطایی تولید خواهدشد.

کد زیر مربوط به تولید خطاست.

public class AuthenticationFailureResult : IHttpActionResult
{
    public AuthenticationFailureResult(string reasonPhrase, HttpRequestMessage request)
    {
        ReasonPhrase = reasonPhrase;
        Request = request;
    }

    public string ReasonPhrase { get; private set; }

    public HttpRequestMessage Request { get; private set; }

    public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        return Task.FromResult(Execute());
    }

    private HttpResponseMessage Execute()
    {
        HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
        response.RequestMessage = Request;
        response.ReasonPhrase = ReasonPhrase;
        return response;
    }
}

چند کد شناخته شده که در حین فعل و انفعالات سرور و کلاینت وجود دارند شامل

کد 101، تعویض پروتکل ها (Switching Protocols)، کد 102، در حال پردازش (Processing)، کدهای سری 200، درخواست موفق (Success)، کدهای سری 300، انتقال (Redirection)، کد 400، درخواست بد (Bad Request) که معروفترین آن  کد 404، منبع درخواستی پیدا نشد (Not Found) می باشد.و در آخر کدهای سری 500، خطای سمت سرور (Server Error) .

پیاده سازی تابع ChallengeAsync 

هدف ChallengeAsync  افزودن challenges  به پاسخ است.

public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
{
    var challenge = new AuthenticationHeaderValue("Basic");
    context.Result = new AddChallengeOnUnauthorizedResult(challenge, context.Result);
    return Task.FromResult(0);
}
فایل های ضمیمه