ASP.NET Web API 2 使用 AuthorizationFilter(授权过滤器)实现 Basic 认证
Ø 前言
在 Web 项目中授权认证方式有很多种,本文主要讲述基于 Basic 的认证方式。这是一种比较简单、常见的认证方式,主要是将请求的用户名和密码进行加密后返回给调用方,比较适合采用用户名、密码授权的项目中,比如:网站系统、后台管理系统、以及前后端分离的 APP 应用等。
1. 首先,来看一下基于 Basic 认证的请求模式
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 调用失败
2) Account/Login 调用成功
3) Customer/Get 调用失败(票据过期)
4) Customer/Get 调用失败(无票据)
5) Customer/Get 调用失败(错误的票据)
6) Customer/Get 调用成功
Ø 总结
1) 关于 Basic 认证的方式,服务端可能存在不同的处理方式,比如:有将请求用户名+密码进行 base64 编码的方式,也有将票据 MD5 加密等。
2) 但无论使用哪种方式,都是将票据经过不同的处理后传递给调用方,调用方再请求其他需授权的接口时,在将票据“带过来”,完成身份认证。
3) 最后简单分析下 Basic 认证的优缺点:
1. 优点
1) 简单,无论是调用方还是服务端实现起来都比较简单。
2. 缺点
1) 存在安全隐患,因为会比较频繁的向服务端提供用户名和密码来换取票据,容易被其他程序截取或破解。比较适合一些内部系统,或者安全要求比较低的项目。
2) 只适合以用户名+密码的访问方式,所有的调用者首先必须有用户名、密码,这样就说明只能是在同一套系统或框架中使用。
4) 好了 Basic 认证就先讨论到这里,如有不对之处,欢迎指正,感激不尽,嘿嘿~~
ASP.NET Web API 2 使用 AuthorizationFilter(授权过滤器)实现 Basic 认证的更多相关文章
- ASP.NET Web API身份验证和授权
英语原文地址:http://www.asp.net/web-api/overview/security/authentication-and-authorization-in-aspnet-web-a ...
- Web Api 过滤器之 AuthorizationFilter 验证过滤器
该过滤器是最先执行的过滤器,即使把它放在最后 API [MyActionFilter] [MyExceptionFilter] [MyAuthorize] public void Get() { Tr ...
- ASP.NET Web API 安全验证之摘要(Digest)认证
在基本认证的方式中,主要的安全问题来自于用户信息的明文传输,而在摘要认证中,主要通过一些手段避免了此问题,大大增加了安全性. 1.客户端匿名的方式请求 (无认证) HTTP/ Unauthorized ...
- ASP.NET Web API 2 使用 DelegatingHandler(委托处理程序)实现签名认证
Ø 前言 在前一篇ASP.NET Web API 2 使用 AuthorizationFilter(授权过滤器)实现 Basic 认证文章中实现了采用 Basic 认证的方式,但是这种方式存在安全隐 ...
- ASP.NET Web API 2 过滤器
Ø 前言 我们知道 ASP.NET Web API 过滤器,也是属于消息处理机制中的一部分.正因如此,我们经常使用它来完成对请求的授权验证.参数验证,以及请求的 Log 记录,程序异常捕获等. 1. ...
- 用Middleware给ASP.NET Core Web API添加自己的授权验证
Web API,是一个能让前后端分离.解放前后端生产力的好东西.不过大部分公司应该都没能做到完全的前后端分离.API的实现方式有很 多,可以用ASP.NET Core.也可以用ASP.NET Web ...
- [转]用Middleware给ASP.NET Core Web API添加自己的授权验证
本文转自:http://www.cnblogs.com/catcher1994/p/6021046.html Web API,是一个能让前后端分离.解放前后端生产力的好东西.不过大部分公司应该都没能做 ...
- ASP.NET Web API自身对CORS的支持: CORS授权检验的实施
通过<EnableCorsAttribute特性背后的故事>我们知道:由CorsPolicyProvider提供的CorsPolicy表示目标Action采用的资源授权策略,ASP.NET ...
- 新作《ASP.NET Web API 2框架揭秘》正式出版
我觉得大部分人都是“眼球动物“,他们关注的往往都是目光所及的东西.对于很多软件从业者来说,他们对看得见(具有UI界面)的应用抱有极大的热忱,但是对背后支撑整个应用的服务却显得较为冷漠.如果我们将整个“ ...
随机推荐
- HZNU ACM一日游 2019.3.17 【2,4,6-三硝基甲苯(TNT)】
Travel Diary 早上8:00到HG,听说hjc20032003在等我. 然后他竟然鸽我...最后还是勉强在8:30坐上去偏僻的HZNU的地铁. 到文新,然后带上fjl,打滴滴,一行人来到了H ...
- POJ-3687 Labeling Balls(拓扑)
不一样的拓扑排序 给定一些标记为1到n的数, 求出满足a < b 的序列, 如果有多个输出, 按先标签1往前的位置, 然后按标签2往前的位置, 对于每个标签, 位置都尽量往前. 因为位置要往前, ...
- 10张图带你深入理解Docker容器和镜像
http://dockone.io/article/783 [编者的话]本文用图文并茂的方式介绍了容器.镜像的区别和Docker每个命令后面的技术细节,能够很好的帮助读者深入理解Docker. Doc ...
- MySQL使用普通用户访问返回ERROR 1698 (28000): Access denied for user 'root'@'localhost'
这个问题最开始查资料都说要改密码,密码不对.其实不是这个样子都. 解决方法 修改/etc/mysql/my.cnf,添加以下内容 [mysqld] skip-grant-tables 重启mysql服 ...
- A1131. Subway Map (30)
In the big cities, the subway systems always look so complex to the visitors. To give you some sense ...
- 第一个java程序的编译
import java.io.*; public class EmployeeTest{ public static void main(String args[]){ /* 使用构造器创建两个对象 ...
- PHP ACCESS-CONTROL-ALLOW-ORIGIN ,设置跨域头
php里允许别的域名跨域访问,先解释下跨域 跨域,指的是浏览器不能执行其他网站的脚本.它是由浏览器的同源策略造成的,是浏览器对javascript施加的安全限制. 所谓同源是指,域名,协议,端口均相同 ...
- C# 数据库批量插入数据之 —— SqlBulkCopy、表值参数
创建了一个用来测试的Student表: CREATE TABLE [dbo].[Student]( [ID] [int] PRIMARY KEY NOT NULL, ) NULL, ) NULL, [ ...
- Oracle基础--创建临时表空间/表空间/创建用户/授权
总结:创建用户一般分四步: 第一步:创建临时表空间(创建用户之前要创建"临时表空间",若不创建则默认的临时表空间为temp.) SQL> CREATE TEMPORARY T ...
- MySQL复制相关参数详解
MySQL复制相关参数详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.复制相关系统变量 1>.server_id 是必须设置在master和每个slave上的唯一标 ...