65.9K
CodeProject 正在变化。 阅读更多。
Home

ASP.NET Web API 2 和 AngularJS 中的授权过滤器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.62/5 (9投票s)

2016 年 7 月 3 日

CPOL

2分钟阅读

viewsIcon

41356

downloadIcon

452

在本文中,我们将探讨安全问题,该问题将帮助我们使用自定义授权过滤器防止未经授权访问 Web API。

引言

限制未经授权访问应用程序的特定操作/动作非常重要。当我在一个需要限制未经授权的人员执行 CRUD 操作的项目上工作时,我尝试了这一点。授权基于用户角色。

好的,让我们开始,以下是步骤。希望你会喜欢它。

目录

  • SQL 数据库
    • 创建新数据库
    • 运行 db-script
  • ASP.NET MVC 应用程序 (Web API)
    • MVC,WebApi 项目
    • 安装 AngularJS
    • 身份验证 &
    • Authorization

创建新数据库

/****** Object:  Database [ApiSecurity]    Script Date: 7/3/2016 11:50:11 AM ******/
CREATE DATABASE [ApiSecurity] ON  PRIMARY 
( NAME = N'ApiSecurity', _
FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQL\DATA\ApiSecurity.mdf' , _
SIZE = 3072KB , MAXSIZE = UNLIMITED, FILEGROWTH = 1024KB )
 LOG ON 
( NAME = N'ApiSecurity_log', _
FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQL\DATA\ApiSecurity_log.ldf' , _
SIZE = 1024KB , MAXSIZE = 2048GB , FILEGROWTH = 10%)
GO

创建数据库后,让我们下载并运行脚本。让我们创建一个新的 MVC 应用程序。

MVC 应用程序

nuget 包安装程序安装用于客户端脚本的 AngularJS。

首先,我们需要登录进行身份验证检查。

身份验证与授权

  • 身份验证:用户的身份
  • 授权:允许执行操作

成功登录(身份验证)后,我们可以访问获取客户链接以显示所有客户,前提是我们在数据库中拥有读取权限。

在我们的数据库表中,我们限制了管理员查看客户列表的访问权限(CanRead 为“False”)。

从数据库获取数据时,如果登录用户的角色是管理员,结果将显示 401 响应消息。

Using the Code

这是我们的 API 控制器,它通过在 CRUD 方法的顶部使用 [BasicAuthorization] 属性来限制。

[RoutePrefix("api/Customer")]
public class CustomerController : ApiController
{
    private CustomersMgt objCust = null;

    //Get
    [BasicAuthorization, HttpGet, Route("GetCustomers/{pageNumber:int}/{pageSize:int}")]
    public IHttpActionResult GetCustomers(int pageNumber, int pageSize)
    {
        objCust = new CustomersMgt();
        return Json(objCust.GetCustomer(pageNumber, pageSize));
    }

    //Post
    [BasicAuthorization, HttpPost, Route("SaveCustomer")]
    public IHttpActionResult SaveCustomer(Customer model)
    {
        objCust = new CustomersMgt();
        return Json(objCust.SaveCustomer(model));
    }

    //Put
    [BasicAuthorization, HttpPut, Route("UpdateCustomer")]
    public IHttpActionResult UpdateCustomer(Customer model)
    {
        objCust = new CustomersMgt();
        return Json(objCust.UpdateCustomer(model));
    }

    //Delete
    [BasicAuthorization, HttpDelete, Route("DeleteCustomer/{CustomerID:int}")]
    public IHttpActionResult DeleteCustomer(int CustomerID)
    {
        objCust = new CustomersMgt();
        return Json(objCust.DeleteCustomer(CustomerID));
    }
}

以下是我们custom属性的代码片段,该属性使用System.Web.Http.Filters继承自AuthorizationFilterAttribute 。如果只想针对方法,则以 or 运算符删除 AttributeTargets Class 目标属性。

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class BasicAuthorization : AuthorizationFilterAttribute
{
    private const string _authorizedToken = "AuthorizedToken";
    private const string _userAgent = "User-Agent";

    private UserAuthorizations objAuth = null;

    public override void OnAuthorization(HttpActionContext filterContext)
    {
        string authorizedToken = string.Empty;
        string userAgent = string.Empty;

        try
        {
            var headerToken = filterContext.Request.Headers.SingleOrDefault
                                  (x => x.Key == _authorizedToken);
            if (headerToken.Key != null)
            {
                authorizedToken = Convert.ToString(headerToken.Value.SingleOrDefault());
                userAgent = Convert.ToString(filterContext.Request.Headers.UserAgent);
                if (!IsAuthorize(authorizedToken, userAgent))
                {
                    filterContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
                    return;
                }
            }
            else
            {
                filterContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden);
                return;
            }
        }
        catch (Exception)
        {
            filterContext.Response = new HttpResponseMessage(HttpStatusCode.Forbidden);
            return;
        }

        base.OnAuthorization(filterContext);
    }

    private bool IsAuthorize(string authorizedToken, string userAgent)
    {
        objAuth = new UserAuthorizations();
        bool result = false;
        try
        {
            result = objAuth.ValidateToken(authorizedToken, userAgent);
        }
        catch (Exception)
        {
            result = false;
        }
        return result;
    }
}

在此,OnAuthorization 是从继承类重写的方法,当进程请求授权时调用,filterContext 是使用 System.Web.Http.Filters.AuthorizationFilterAttribute 封装信息的参数。

在本节中,通过发送禁止 (403) 和未经授权 (401) 的响应代码来处理异常。

令牌生成脚本

以下是客户端令牌生成的 angularJS 脚本,在处理每个请求时,首先生成此令牌并与请求标头一起发送以进行验证。

AppSecurity.controller('tokenCtrl', 
['$scope', '$http', 'crudService', '$sessionStorage',
function ($scope, $http, crudService, $sessionStorage) {

    //Token Generate ClientEnd
    $scope.tokenManager = {
        generateSecurityToken: function (methodtype) {
            var model = {
                username: $sessionStorage.loggeduser,
                key: methodtype,
                ip: $sessionStorage.loggedip,
                userAgent: navigator.userAgent.replace(/ \.NET.+;/, '')
            };

            var message = [model.username, model.ip, model.userAgent].join(':');
            var hash = CryptoJS.HmacSHA256(message, model.key);

            var token = CryptoJS.enc.Base64.stringify(hash);
            var tokenId = [model.username, model.key].join(':');
            var tokenGenerated = CryptoJS.enc.Utf8.parse([token, tokenId].join(':'));

            return CryptoJS.enc.Base64.stringify(tokenGenerated);
        },
    };
}]);

该令牌由 Base64 编码生成,其中哈希具有消息正文和加密密钥,在此应用程序中,我们使用 crud 类型作为密钥。

服务器令牌生成

客户端令牌的生成方式,我们需要以相同的方式重新生成令牌,这将比较并验证请求是否是假的?

public string generateToken(string userid, string methodtype, string ip, string userAgent)
{
    string message = string.Join(":", new string[] { userid, ip, userAgent });
    string key = methodtype ?? "";

    var encoding = new System.Text.ASCIIEncoding();

    byte[] keyByte = encoding.GetBytes(key);
    byte[] messageBytes = encoding.GetBytes(message);

    using (var hmacsha256 = new HMACSHA256(keyByte))
    {
        byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
        return Convert.ToBase64String(hashmessage);
    }
}

验证令牌

这部分代码将分两步比较并验证请求,首先比较令牌,然后验证从数据库访问 CRUD 操作的授权。

public bool ValidateToken(string authorizedToken, string userAgent)
{
    bool result = false;
    try
    {
        string key = Encoding.UTF8.GetString(Convert.FromBase64String(authorizedToken));
        string[] parts = key.Split(new char[] { ':' });
        if (parts.Length == 3)
        {
            objModel = new tokenModel()
            {
                clientToken = parts[0],
                userid = parts[1],
                methodtype = parts[2],
                ip = HostService.GetIP()
            };

            //compare token
            string serverToken = generateToken(objModel.userid, objModel.methodtype, 
                                 objModel.ip, userAgent);
            if (objModel.clientToken == serverToken)
            {
                result = ValidateAuthorization(objModel.userid, objModel.methodtype);
            }
        }
    }
    catch (Exception e)
    {
        e.ToString();
    }
    return result;
}

Authorization

此代码示例将验证每个操作的数据库访问权限。

public bool ValidateAuthorization(string userid, string methodtype)
{
    bool IsValid = false;
    if (userid != null)
    {
        using (_ctx = new ApiSecurityEntities())
        {
            if (_ctx.UserAuthentications.Any(u => u.LoginID == userid && u.StatusID == 1))
            {
                switch (methodtype)
                {
                    case "get":
                        IsValid = (from u in _ctx.UserAuthentications
                                    join r in _ctx.UserRoles on u.RoleID equals r.RoleID
                                    where u.LoginID == userid && u.StatusID == 1 && r.CanRead == true
                                    select u).Any();
                        break;
                    case "post":
                        IsValid = (from u in _ctx.UserAuthentications
                                    join r in _ctx.UserRoles on u.RoleID equals r.RoleID
                                    where u.LoginID == userid && u.StatusID == 1 && r.CanCreate == true
                                    select u).Any();
                        break;
                    case "put":
                        IsValid = (from u in _ctx.UserAuthentications
                                    join r in _ctx.UserRoles on u.RoleID equals r.RoleID
                                    where u.LoginID == userid && u.StatusID == 1 && r.CanUpdate == true
                                    select u).Any();
                        break;
                    case "delete":
                        IsValid = (from u in _ctx.UserAuthentications
                                    join r in _ctx.UserRoles on u.RoleID equals r.RoleID
                                    where u.LoginID == userid && u.StatusID == 1 && r.CanDelete == true
                                    select u).Any();
                        break;
                    default:
                        IsValid = false;
                        break;
                }
            }
        }
    }
    return IsValid;
}

希望这有帮助! :)

© . All rights reserved.