ASP.NET Web API 2 使用 AuthorizationFilter(授权过滤器)实现 Basic 认证

Ø  前言

Web 项目中授权认证方式有很多种,本文主要讲述基于 Basic 的认证方式。这是一种比较简单、常见的认证方式,主要是将请求的用户名和密码进行加密后返回给调用方,比较适合采用用户名、密码授权的项目中,比如:网站系统、后台管理系统、以及前后端分离的 APP 应用等。

 

1.   首先,来看一下基于 Basic 认证的请求模式

clip_image002[4]

 

2.   具体实现步骤

1)   首先,新建一个授权过滤器(RequestAuthorizeAttribute),可以继承于 System.Web.Http.AuthorizationFilterAttribute,或者 System.Web.Http.AuthorizeAttribute,因为 AuthorizeAttribute 也是 AuthorizationFilterAttribute 的派生类。

/// <summary>

/// 请求授权特性。

/// </summary>

public class RequestAuthorizeAttribute : System.Web.Http.AuthorizeAttribute

{

    public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)

    {

        //首先检查 Action Controller 是否允许匿名访问

        if (actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Count > 0

            || actionContext.ControllerContext.ControllerDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Count > 0)

        {

            base.OnAuthorization(actionContext);

        }

        else

        {   //不允许匿名访问

            var authorization = actionContext.Request.Headers.Authorization;

            if (authorization != null)

            {

                if ("Basic".Equals(authorization.Scheme, StringComparison.CurrentCultureIgnoreCase)

                    && !string.IsNullOrEmpty(authorization.Parameter))

                {

                    try

                    {

                        var ticket = System.Web.Security.FormsAuthentication.Decrypt(authorization.Parameter);

                        string[] array = ticket.UserData.Split('&');    //获取加密前的用户数据(用户名和密码)

                        //登录时:已经验证了用户名和密码,所以这里只需要验证票据是否过期

                        if (ticket.Expired)

                        {

                            //也可以使用 actionContext.ControllerContext.Request,与 actionContext.Request 是同一实例。

                            actionContext.Response = actionContext.ControllerContext.Request.CreateErrorResponse(

                                System.Net.HttpStatusCode.BadRequest, "身份票据已过期");

                        }

                        else

                        {

                            base.IsAuthorized(actionContext);

                        }

                    }

                    catch (Exception ex)

                    {

                        actionContext.Response = actionContext.ControllerContext.Request.CreateErrorResponse(

                            System.Net.HttpStatusCode.Unauthorized, string.Format("授权鉴定异常,{0}", ex.Message));

                    }

                }

                else { HandleUnauthorizedRequest(actionContext); }

            }

            else { HandleUnauthorizedRequest(actionContext); }

        }

    }

}

1.   在授权过滤器中,首先检查当前 Action Controller 是否允许匿名访问。如果允许则跳过授权验证,否则需要验证传递的票据以及是否过期。

 

2)   新建一个控制器(用于验证登录用户名和密码)

/// <summary>

/// 账户控制器。

/// </summary>

[RequestAuthorize]

[RoutePrefix("api/account")]

public class AccountController : ApiController

{

    /// <summary>

    /// 登录。

    /// </summary>

    [Route("login"), HttpPost]

    [AllowAnonymous]

    public object Login(Newtonsoft.Json.Linq.JObject jObj)

    {

        //两种方式获取 JObject 中的值

        string userName = jObj.GetValue("UserName").ToObject<string>();

        Newtonsoft.Json.Linq.JToken token = null;

        string password = null;

        if (jObj.TryGetValue("Password", out token))

            password = token.ToObject<string>();

        else

            throw new System.ArgumentException("获取密码失败");   //异常将被异常过滤器捕获

 

        //模拟用户数据(应该是从数据库获取)

        var userData = new

        {

            UserId = 1,

            UserName = "aibaincheng",

            Password = "abc123" //可以将密码加密后保存至数据库(以提高安全性)

        };

        if (userData.UserName == userData.UserName && userData.Password != password)

            return new { Msg = "用户名或密码错误" };

        else

        {

            // Forms 身份验证加密

            FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(

                1,

                userName,

                DateTime.Now,

                DateTime.Now.AddMinutes(3), //票据3分钟后失效

                true,

                string.Format("{0}&{1}", userName, password),

                FormsAuthentication.FormsCookiePath);

            string ticketStr = FormsAuthentication.Encrypt(ticket);

            return new { UserId = userData.UserId, Expire = DateTime.Now.AddMinutes(3), Ticket = ticketStr };

        }

    }

}

1.   在控制器上加了 RequestAuthorize 特性,并在 Login Action 上也加了 AllowAnonymous 特性,这样最终还是以 Action 的为准,表示该 Action 可以匿名访问(跳过了授权验证)。

2.   拿到请求的用户名和密码,与数据库中的用户名密码进行匹配。如果匹配成功则生成加密的票据返回,否则返回错误信息。

 

3)   再新建另一个控制器(用于授权测试)

/// <summary>

/// 客户控制器。

/// </summary>

[RequestAuthorize]

[RoutePrefix("api/customer")]

public class CustomerController : ApiController

{

    [Route("get"), HttpGet]

    public object Get(int id)

    {

        //这里完成查询客户的操作...

        return new { Code = 200, Data = new { CustomerId = id, CustomerName = "客户A", Address = "上海市杨浦区" } };

    }

}

1.   同样在控制器上加了 RequestAuthorize 特性,表示控制器中的所有 Action 方法都将采用 RequestAuthorize 过滤器完成授权验证。

2.   因为该接口需要授权访问,所以在调用该接口时,必须将 Ticket(票据)传递给服务器端,否则不能访问该接口。

 

3.   模拟客户端调用

1)   Account/Login 调用失败

clip_image004[4]

2)   Account/Login 调用成功

clip_image006[4]

3)   Customer/Get 调用失败(票据过期)

clip_image008[4]

4)   Customer/Get 调用失败(无票据)

clip_image010[4]

5)   Customer/Get 调用失败(错误的票据)

clip_image012[4]

6)   Customer/Get 调用成功

clip_image014[4]

 

Ø  总结

1)   关于 Basic 认证的方式,服务端可能存在不同的处理方式,比如:有将请求用户名+密码进行 base64 编码的方式,也有将票据 MD5 加密等。

2)   但无论使用哪种方式,都是将票据经过不同的处理后传递给调用方,调用方再请求其他需授权的接口时,在将票据带过来,完成身份认证。

3)   最后简单分析下 Basic 认证的优缺点:

1.   优点

1)   简单,无论是调用方还是服务端实现起来都比较简单。

2.   缺点

1)   存在安全隐患,因为会比较频繁的向服务端提供用户名和密码来换取票据,容易被其他程序截取或破解。比较适合一些内部系统,或者安全要求比较低的项目。

2)   只适合以用户名+密码的访问方式,所有的调用者首先必须有用户名、密码,这样就说明只能是在同一套系统或框架中使用。

4)   好了 Basic 认证就先讨论到这里,如有不对之处,欢迎指正,感激不尽,嘿嘿~~

转载于:https://www.cnblogs.com/abeam/p/8698019.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值