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






4.62/5 (9投票s)
在本文中,我们将探讨安全问题,该问题将帮助我们使用自定义授权过滤器防止未经授权访问 Web API。
- 下载 MVC-ApiSecurity.part04 (RAR) - 6.6 MB
- 下载 MVC-ApiSecurity.part03 (RAR) - 9 MB
- 下载 MVC-ApiSecurity.part02 (RAR) - 9 MB
- 下载 MVC-ApiSecurity.part01 (RAR) - 9 MB
- 下载 db-script - 1.9 KB
引言
限制未经授权访问应用程序的特定操作/动作非常重要。当我在一个需要限制未经授权的人员执行 CRUD 操作的项目上工作时,我尝试了这一点。授权基于用户角色。
好的,让我们开始,以下是步骤。希望你会喜欢它。
目录
- SQL 数据库
- 创建新数据库
- 运行 db-script
- ASP.NET MVC 应用程序 (Web API)
- MVC,
WebApi
项目 - 安装 AngularJS
- 身份验证 &
- Authorization
- MVC,
创建新数据库
/****** 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;
}
希望这有帮助! :)