YbSoftwareFactory 代码生成插件【十三】:Web API 的安全性
ASP.NET Web API 可非常方便地创建基于 HTTP 的 Services,这些服务可以非常方便地被几乎任何形式的平台和客户端(如浏览器、Windows客户端、Android设备、IOS等)所访问,它可根据请求类型自动提供 JSON、XML 等类型的响应内容。在移动互联网逐渐成为主流的背景下,通过 Web API 对外发布基于标准、通用 HTTP 协议的服务来交换数据无疑具有非常大的优势和吸引力。本文将主要围绕 ASP.NET Web API 的安全性进行讨论。
一、Forms Authentication
Forms 认证基于凭据(Ticket)机制,凭据在登录时创建,通常会写入到 cookie 中。Forms 认证可应用在任何类型的ASP.NET 应用程序中,例如:WebForms,MVC,甚至 Web API等。默认的配置是 <authentication mode="None" />,因此为了使用Forms Authentication,通常需要在配置文件中进行配置。
尽管 Forms 认证是 Web 应用程序的首选认证方式,但从 Web API 的安全性来说,其实它并不是一个理想的解决方案,对于非浏览器的客户端来说,做个比喻,就像是穿西装戴斗笠般不搭调。
最大的问题是在ASP.NET Web API中使用 Forms 认证方式的时候,并不会返回一个 302 代码(注:302 表示重定向,Web 应用程序中会被自动导航至设定的登录页面地址),在 Web API 中使用 Forms 认证方式可参考如下代码:
1)在 WebApiConfig 文件中修改默认的代码如下:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Filters.Add(new AuthorizeAttribute());
}
}
2)修改 MVC 的 FilterConfig 默认代码如下:
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new AuthorizeAttribute());
}
}
通过上述设置,对非授权的资源进行访问时将返回一个 401 代码(注:401 表示未经授权的请求),这和在 MVC 中非授权的请求返回到登录页面的含义是一样的。
有童鞋可能会问,为什么不能重定向到登录页面呢?其实真正的问题是 Forms 认证本身,它的原理决定了它是面向 Web 应用程序的,它有 Cookie 和重定向等的支持,而 ASP.NET Web API 却是无状态的 RESTful 服务,这自然是不适合的。
说到这里,休息一下,温习下 HTTP 响应的状态代码。
HTTP 响应的状态代码为三位数字的编号,其中第 1 位定义了状态代码的类别:1 开头的代表信息类、2 开头的代表操作成功类、3 开头的代表重定向类、4 开头的代表客户端一侧的请求错误类、5 开头的代表服务器端一侧的错误类。
常见的状态代码如下:
- 200 OK
- 201 Created
- 204 No Content
- 400 Bad Request
- 401 Unauthorized
- 403 Forbidden
- 404 Not Found
- 500 Internal Server Error
二、身份(Identify)管理
1、认证(Authentication)和授权(Authorization)
1)认证的方式
身份认证的方式主要有如下三个方面:
- 根据用户知晓的东西,例如密码、PIN等
- 根据用户拥有的东西,例如证书、U盾等
- 根据用户的生物特征,例如指纹、DNA等
典型的身份认证方式可以为上述的其中一种,其中第一种使用最为普遍,如果一个应用程序需要考虑更高的安全性,需要至少使用上述列表中的两种身份认证方式。例如银行卡,本身就属于第二种认证,但在ATM上取钱时往往还需要密码,这又使用了第一种认证方式。
2)基本角色的安全
在企业级的应用中,基于角色的安全是最常见的安全模型。在.NET Framework中提供了 Identify 对象,一个 Identify 对象代表了某个用户,最重要就是这两个接口:IIdentity 和 IPrincipal,这两个接口提供了基于角色的访问控制,其中 IIdentify 代表用户的身份标识,IPrincipal 代表用户关联的身份和角色。IPrincipal 有一个 Identity 属性和 IsInRole(string) 的方法,该方法判断当前用户是否属于某个角色。
在 .NET 中,每个线程都有一个类型为 IPrincipal 的 CurrentPrincipal 属性。通常,在身份认证通过后需要创建一个 Principal 对象并赋予主线程的 CurrentPrincipal 属性,任何新创建的线程会自动创建同样的 Principal 对象。其实在.NET Framework中已经实现了两种类型的 Principal。
- GenericIdentity 和 GenericPrincipal,用于自定义的场合。
- WindowsIdentity and WindowsPrincipal,用于基于 Windows 认证的场合。
当然,你也可以自己实现符合自身要求的 Identity 和 Principal。例如在CSLA.NET中就实现了一个自定义的 Identity 和 Principal,可以设置 Serialized 特性,该 Principal 对象可以通过服务从客户端传到服务器端进行身份的认证和授权处理,以满足其 N-Tier 部署下进行身份认证和授权的需要,通过继承,甚至还可以新增必要的属性,非常方便。
3)基本声明(Claims)的安全
典型的基于声明(Claims)的 Identity 如下:
- 用户的姓名是谁
- 用户的电子邮件是什么
- 用户的性别
- 用户的年龄是多少
- 该用户被允许创建新用户
和前面提到的安全模型相比,在基于声明的安全模型中,声明的值必须来自于应用程序所信任的某个实体或机构(通常用户是/不是什么通过第三方验证,可以/不可以做什么由应用程序自身确定)。
基于声明(Claims)的安全模型往往更加贴近现实生活,可以简化某单个应用程序的身份验证逻辑,因为这些应用程序不用再重复提供诸如创建账户、密码、密码重置等机制(注:这些逻辑统一由第三方应用程序完成)。
同时声明(Claims)的安全模型也不必要求某个用户多次登录到多个应用程序,大大简化了用户的身份验证过程。
因此,声明(Claims)的安全非常适合于诸如OAuth等第三方授权的方式和云计算环境。
基于声明(Claims)的安全的实例代码如下:
static void Main(string[] args)
{
var claims = new List<Claim>()
{
new Claim(ClaimTypes.Name, "Yellbuy"),
new Claim(ClaimTypes.Email, "yb@yellbuy.com"),
new Claim(ClaimTypes.Role, "系统管理员"),
new Claim(ClaimTypes.Role, "系统操作员")
};
var id = new ClaimsIdentity(claims, "Dummy"); // Non-empty string is needed as authentication type
var principal = new ClaimsPrincipal(new[] { id });
Thread.CurrentPrincipal = principal;
CreateUser(); // Call the method that needs authorization
}
[PrincipalPermission(SecurityAction.Demand, Role = "系统管理员")] // Declarative
private static void CreateUser()
{
new PrincipalPermission(null, "NewUser").Demand(); // Imperative
Console.WriteLine(Thread.CurrentPrincipal.IsInRole("系统管理员"));
Console.WriteLine("用户已创建");
}
很明显,用户是/不是什么通常由第三方确定,因此很多第三方提供了所谓的安全令牌(Security Token)服务。目前有三种标准的令牌(Token)格式,它们是:SAML(安全断言标记语言)、SWT(简单 Web 令牌)、JWT(JSON Web 令牌)。三种令牌格式对比如下:
SAML |
SWT |
JWT |
|
表现形式 |
XML |
HTML Form encoding |
JSON |
处理方式 |
SOAP |
REST |
REST |
直接支持WIF |
是 |
否 |
否 |
协议 |
WS-Trust and WS-Federation |
OAuth 2.0 |
OAuth 2.0 |
通常的载体 |
HTTP body or URL |
HTTP Auth header (Bearer) |
HTTP Auth header (Bearer) |
支持签名 |
是,非对称密钥-X509证书 |
是, HMAC SHA-256 (使用对称密钥) |
是,支持对称密钥和非对称密钥 |
2、Basic Authentication
在Web Api中不能不提到 Basic Authentication。Basic Authentication 是 HTTP 规范的一部分,它非常的基础和简单。主要工作原理如下:
- 客户端向服务器发送资源请求。
- 假如资源请求需要进行身份认证,则服务器发送回一个 401 代码 - 未经授权响应状态码和响应头 WWW-Authenticate: Basic。这个响应报头还可以包含一个字符串,它是服务器所需要的一个有效的凭据来进行后续成功处理请求的唯一标识。
- 现在客户端发送授权请求信息,例如:WWW-Authenticate: Basic YeMfc1mgUdV2cMj0U0Kjp2C=。授权请求头值仅仅是一个用户ID和密码进行base64编码后的字符串,然后使用一个冒号进行分割,并且不以任何方式加密。
- 假如凭据有效,则服务器返回 200 响应状态码。
因为 Base Authentication 的安全性较差,但对于无 Cookie 的 Web Api 来说,应用上非常的简单和方便。
Base Authentication 最大的缺点是凭据会被浏览器缓存——直到你关闭浏览器为止。如果你已经对某个URI获得了授权,浏览器就会在授权头发送相应的凭据,这使其更容易受到跨站点请求伪造(CSRF)攻击
Base Authentication 通常需要使用HTTPS方式进行加密处理。
三、YbRapidSolution for MVC 中的 Authentication
在 YbRapidSolution for MVC 的安全解决方案中,采用了 Base Authentication 和 Form Authentication 相互补充的处理方式,使用 Attribute 方式进行请求的 Authentication 处理。Base Authentication 主要面向非Web应用的处理请求,Form Authentication 则对 MVC 的 Controller 的请求进行处理,核心代码如下:
{
//匿名用户的权限验证
AuthenticationHeaderValue authValue = request.Headers.Authorization;
//Base Authenticated 是否无效
var isNotValidatedBaseAuthenticated = authValue == null
|| string.IsNullOrWhiteSpace(authValue.Parameter)
|| string.IsNullOrWhiteSpace(authValue.Scheme)
|| authValue.Scheme.Equals(BasicAuthResponseHeaderValue);
//客户端授权标记 有效,则创建Principal并附加到HttpContext.Current.User
if (!isNotValidatedBaseAuthenticated)
{
string[] parsedHeader = ParseAuthorizationHeader(authValue.Parameter);
if (parsedHeader != null)
{
IPrincipal principal = null;
if (TryCreatePrincipal(parsedHeader[0], parsedHeader[1], out principal))
{
HttpContext.Current.User = principal;
}
}
}
//HttpContent未授权,则检查匿名用户的权限
if (!HttpContext.Current.User.Identity.IsAuthenticated)
{
string roleKey = string.Format(CacheKeyList.PERMISSION_ROOT_BY_ROLE_KEY, "EveryOne");
var permissionKeys = _cacheManager.Get(roleKey, () =>
{
var permissionsOfEveryOne = PermissionApi.GetPermissionsInRole("EveryOne");
if (permissionsOfEveryOne == null || permissionsOfEveryOne.Length == 0)
return new string[] { };
var list = permissionsOfEveryOne.Select(c => c.PermissionKey).ToArray();
return list;
});
return CheckPermission(request, permissionKeys);
}
//未设置权限Key,则任何已授权用户均可访问
if (string.IsNullOrWhiteSpace(PermissionKey)) return true;
//登录用户的权限验证
string userKey = string.Format(CacheKeyList.PERMISSION_CHILDREN_BY_USER_KEY, HttpContext.Current.User.Identity.Name);
var allowPermissionKeys = _cacheManager.Get(userKey, () =>
{
var permissions = PermissionApi.GetPermissionsForUser();
if (permissions == null || permissions.Length == 0)
return new string[] { };
var list = permissions.Select(c => c.PermissionKey).ToArray();
return list;
});
return CheckPermission(request, allowPermissionKeys);
}
private string[] ParseAuthorizationHeader(string authHeader)
{
string[] credentials = Encoding.ASCII.GetString(Convert.FromBase64String(authHeader)).Split(new[] {':'});
if (credentials.Length != 2 || string.IsNullOrEmpty(credentials[0]) || string.IsNullOrEmpty(credentials[1]))
return null;
return credentials;
}
从上述代码中可以看出,在 YbRapidSolution for MVC 首先进行 Base Authentication,如果不通过,继续进行 Form Authentication;如果 Base Authentication 通过,则创建 Form 下的 Principal 然后按 Form Authentication 的方式进行统一处理,这可以确保任何类型的客户端都能进行相应的 Authentication 处理,充分发挥出 Web Api 的特性,在为各种类型的客户端和设备提供 API 支持的同时也提供相应的安全保障。
附1:YbRapidSoluton for MVC 在线 Demo 地址:http://mvcdemo.yellbuy.com/。
附2:最新发布的 YbRapidSolution for WinForm Demo下载:运行环境-.NET 4.0。服务层部署在 Internet
上,,可直接运行;如需在本地部署,除了安装数据库外,就是修改配置文件,这里不再详述。
附3:最新发布的 YbSoftwareFactory V2 下载,运行环境-.NET 4.0。
YbSoftwareFactory 代码生成插件【十三】:Web API 的安全性的更多相关文章
- Web API 的安全性
Web API 的安全性 ASP.NET Web API 可非常方便地创建基于 HTTP 的 Services,这些服务可以非常方便地被几乎任何形式的平台和客户端(如浏览器.Windows客户端.An ...
- YbSoftwareFactory 代码生成插件【十四】:通过 DynamicLinq 简单实现 N-Tier 部署下的服务端数据库通用分页
YbSoftwareFactory 的 YbRapidSolution for WinForm 插件使用CSLA.NET作为业务层,CSLA.NET的一个强大的特性是支持 N-Tiers 部署.只需非 ...
- 使用ASP.NET Web Api构建基于REST风格的服务实战系列教程【八】——Web Api的安全性
系列导航地址http://www.cnblogs.com/fzrain/p/3490137.html 前言 这一篇文章我们主要来探讨一下Web Api的安全性,到目前为止所有的请求都是走的Http协议 ...
- Web Api的安全性
Web Api的安全性 系列导航地址http://www.cnblogs.com/fzrain/p/3490137.html 前言 这一篇文章我们主要来探讨一下Web Api的安全性,到目前为止所有的 ...
- [转]使用ASP.NET Web Api构建基于REST风格的服务实战系列教程【八】——Web Api的安全性
本文转自:http://www.cnblogs.com/fzrain/p/3552423.html 系列导航地址http://www.cnblogs.com/fzrain/p/3490137.html ...
- YbSoftwareFactory 代码生成插件【二十一】:Web Api及MVC性能提升的几个小技巧
最近在进行 YbSoftwareFactory 的流程功能升级,目前已经基本完成,现将用到的一些关于 Web Api 及 MVC 性能提升的一些小技巧进行了总结,这些技巧在使用.配置上也相当的简单,但 ...
- YbSoftwareFactory 代码生成插件【十五】:Show 一下最新的动态属性扩展功能与键值生成器功能
YbSoftwareFactory 各种插件的基础类库中又新增了两个方便易用的功能:动态属性扩展与键值生成器,本章将分别介绍这两个非常方便的组件. 一.动态属性扩展 在实际的开发过程中,你肯定会遇到数 ...
- YbSoftwareFactory 代码生成插件【二十】:DynamicObject的序列化
DynamicObject 是 .NET 4.0以来才支持的一个类,但该类在.NET 4.0下未被标记为[Serializable] Attribute,而在.NET 4.5下则被标记了[Serial ...
- YbSoftwareFactory 代码生成插件【十六】:Web 下灵活、强大的审批流程实现(含流程控制组件、流程设计器和表单设计器)
程序=数据结构+算法,而企业级的软件=数据+流程,流程往往千差万别,客户自身有时都搞不清楚,随时变化的情况更是家常便饭,抛开功能等不谈,需求变化很大程度上就是流程的变化,流程的变化会给开发工作造成很大 ...
随机推荐
- WPF整理--动态绑定到Logical Resource
“What happens if we replace aspecific resource? Would that be reflected in all objects using the res ...
- Android课程---关于下拉列表与状态栏提示的学习
activity_ui7.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout x ...
- error C2512: “Rectangle”: 没有合适的默认构造函数可用
解决办法可能为: 1.再构造一个空的显性构造函数:
- 开源代码分析之Android/iOS Hybrid JSBridge框架
Hybrid开发是现在的主流形式,对于业务快速迭代的公司尤其重要.曾将在鞋厂接触了很多关于Hybrid的理念,在这里分享一些Hybrid框架思想. Hybrid框架包括Native与H5的通信,Web ...
- Objective的字符串拼接 似乎没有Swift方便,但也可以制做一些较为方便的写法
NSString *str1 = @"字符串1"; NSString *str2 = @"字符串2"; //在同样条件下,Objective的字符串拼接 往往只 ...
- mysql_索引原理及优化
思考: 我们知道mysql最好的数据存储量级是百万级别,是的往往在百万级别或者几十万级别就会出现慢查询(我对慢查询的定义是大于1秒),几年前我所在的一个做pos机支付的联机交易的核心系统组,当时就做过 ...
- Ext.js添加子组件
Ext框架提供了很多api,对于不熟悉的人来说,api的释义有时不够明了.最近碰到了添加子组件的需求,特记录下来. 1. 例如,有一个窗体组件: 现在要为其添加一个字段“学校分类”,变成如下所示: 示 ...
- How to use groovy script on jenkins
1. Install groovy plugin 2. Add a step of groovy. (normal & systerm) 3. Execute groovy script im ...
- wordpress多站点环境设置上传附件大小
多站点环境更改上传附件大小: php.ini post_max_size = 8M upload_max_filesize = 10M 另外,后台域名管理中设置/网络设置/可以设置上传文件大小. 代码 ...
- 数据库连接池原理 与实现(动脑学院Jack老师课后自己的练习有感)
第一步: 首先创建一个数据库连接池的接口: 数据库连接池接口有两个主要的方法,其中一个getConnection(); 通过数据库连接池返回给用户封装的数据库连接对象 createConnectio ...