当前位置:坤哥网-kungge- WebAPI介绍(二):安全认证之Basic基础认证

WebAPI介绍(二):安全认证之Basic基础认证

2017/8/1 15:44:00 kungge阅读(333) 评论(0)


WebApi身份认证方式主要有4种:Form身份验证、集成Windows验证、Basic基础认证、Digest摘要认证。本次介绍Basic基础认证。


认证步骤


Basic基础认证原理是将用户关键信息生成ticket,每次请求需要登录的资源都必须加上这个ticket去服务器校验,不通过则返回401错误(Not Authorized)。具体步骤如下:

  1. 登录时服务器校验用户账号和密码,若正确则将账号和密码等信息加密生成ticket,并设置一个过期时间。

  2. 用户登录成功后,跳转页面时服务器将生成的ticket返回给前端,前端需要保存下来。

  3. 前端向服务器发起请求的时候,将保存下来的ticket同内容一起发给后台,后台接收请求之后,首先将接受到ticket进行解密校验,通过则执行正常处理并返回处理后的内容,不通过则返回401错误。


认证具体实现


以一个简单例子来说明。建立一个模拟登录页面:

@{
    ViewBag.Title = "BasicAuthenticationTest";
}
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
用户名:<input type="text" id="txtAccount" value="kungge" /><br />
密码:<input type="text" id="txtPwd" value="kungge123456" /><br />
<input type="button" id="btnLogin" value="登录" />

<script type="text/javascript">
    $(function () {
        //登录
        $("#btnLogin").on("click", function () {
            $.ajax({
                url: "/api/Commodity/Login", type: "get", data: { "account": $("#txtAccount").val(), "pwd": $("#txtPwd").val() },
                success: function (data) {
                    var result = JSON.parse(data);
                    if (result.LoginSuccess) {//登录成功
                        //alert(result.Ticket);
                        location.href = "/home/index?account=" + $("#txtAccount").val() + "&ticket=" + result.Ticket;//跳转到新的页面并带上account和ticket
                    } else {
                        alert("登录失败:用户名或密码错误!");
                    }
                },
                datatype: "json"
            });
        });
    });
</script>

在后台服务器控制器中新增一个登录方法:

/// <summary>
/// 登录
/// </summary>
/// <param name="account"></param>
/// <param name="pwd"></param>
/// <returns></returns>
[HttpGet]
public string Login(string account, string pwd)
{
    if (string.Equals(account, "kungge") && string.Equals(pwd, "kungge123456"))
    {
        FormsAuthenticationTicket faTicket = new FormsAuthenticationTicket(0,account,DateTime.Now,DateTime.Now.AddHours(2),true,string.Format($"{account}&{pwd}"),FormsAuthentication.FormsCookiePath);
        return JsonConvert.SerializeObject(new { LoginSuccess =true,Ticket=FormsAuthentication.Encrypt(faTicket) });
    }
    else
    {
        return JsonConvert.SerializeObject(new {LoginSuccess=false });
    }
}

登录时校验用户名密码,若正确则生成一个ticket并返回给前端,前端接收数据后登录成功则跳转页面并把ticket带上,下面是跳转的页面前端代码:

{
    ViewBag.Title = "Index";
}
<script src="~/Scripts/jquery-1.10.2.min.js"></script>

<input type="hidden" id="hdLoginAccount" value="@ViewBag.LoginAccount" />
<input type="hidden" id="hdLoginTicket" value="@ViewBag.LoginTicket" />

登录用户:@ViewBag.LoginAccount <br />
登录Ticket:@ViewBag.LoginTicket

<input type="button" id="btnGetCommodityList" value="获取多个商品" />

<script type="text/javascript">
    $(function () {

        //无参数:获取多个商品
        $("#btnGetCommodityList").on("click", function () {
            $.ajax({
                url: "/api/BasicAuthorizeApi/GetCommodityList", type: "get", data: "",
                beforeSend: function (XHR) {
                    //发送ajax请求之前向http的head里面加入验证信息
                    XHR.setRequestHeader('Authorization', 'BasicAuth ' + $("#hdLoginTicket").val());
                },
                success: function (data) {
                    alert(data);
                },
                datatype: "json"
            });
        });

    });
</script>

前端代码将后台服务器传过来的ticket保存下来,再次发起请求的时候则带上这个ticket,在发起ajax请求时,beforeSend中通过XHR.setRequestHeader方法向请求头中添加认证信息,如下标出的就是:

63c4b182-8bad-4783-9f24-8fd759e300e4.png


在后台服务器添加一个BaseAuthorizeAttribute特性用来在处理请求时先进行身份验证,代码如下:

 public class BaseAuthorizeAttribute:AuthorizeAttribute
{
    public override void OnAuthorization(HttpActionContext actionContext)
    {
        var authorizationHeaderValue = actionContext.Request.Headers.Authorization;//获取http请求的认证标头值
        if(actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>(true).Count>0
            || actionContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<AllowAnonymousAttribute>(true).Count > 0)//判断是否允许匿名访问
        {
            base.OnAuthorization(actionContext);
        }
        else if(authorizationHeaderValue!=null&&authorizationHeaderValue.Parameter!=null)//包含身份验证信息
        {
            if (ValidateTicket(authorizationHeaderValue.Parameter))//验证票据
            {
                base.IsAuthorized(actionContext);
            }
            else
            {
                base.HandleUnauthorizedRequest(actionContext);
            }
        }
        else//不满足则返回401未验证错误
        {
            base.HandleUnauthorizedRequest(actionContext);
        }
    }
    /// <summary>
    /// 验证票据
    /// </summary>
    /// <param name="ticket"></param>
    /// <returns></returns>
    public bool ValidateTicket(string ticket)
    {
        var userData=FormsAuthentication.Decrypt(ticket).UserData;
        string account = userData.Split('&')[0];
        string pwd = userData.Split('&')[1];
        if (account == "kungge" && pwd == "kungge123456")//实际应去数据库校验
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}

BaseAuthorizeAttribute继承AuthorizeAttribute,重写OnAuthorization校验方法。

可以在controller上添加该特性,也可以在具体的action上添加,实际开发中会建一个基础Controller,在该Controller上添加特性,然后新建的Controller都继承该基础Controller。

在后台服务器添加一个控制器BasicAuthorizeApiController,并添加BaseAuthorizeAttribute特性,新增几个方法用于测试:

[BaseAuthorize]
public class BasicAuthorizeApiController : ApiController
{
    private static IDBHelper iDbHelper = new SqlHelper();

    #region 获取信息
    /// <summary>
    /// 根据Id获取一个商品
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    [AllowAnonymous]
    [HttpGet]
    public Commodity GetCommodityById(int id)
    {
        return iDbHelper.QueryById<Commodity>(id, "GM_Commodity_001");
    }
    /// <summary>
    /// 获取多个商品
    /// </summary>
    /// <returns></returns>
    [HttpGet]
    public List<Commodity> GetCommodityList()
    {
        string sql = "select top 5 * from GM_Commodity_001";
        return iDbHelper.QueryList<Commodity>(sql);
    }
    #endregion
}

对于没有添加AllowAnonymous特性的Action,在进入该Action之前会先执行特性BaseAuthorize中的OnAuthorization方法只有通过了校验才能继续进入Action内部。而添加了AllowAnonymous特性的Action则不需要身份校验了。


分类: Web服务

发表评论 没有账号,注册评论