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 下灵活、强大的审批流程实现(含流程控制组件、流程设计器和表单设计器)
程序=数据结构+算法,而企业级的软件=数据+流程,流程往往千差万别,客户自身有时都搞不清楚,随时变化的情况更是家常便饭,抛开功能等不谈,需求变化很大程度上就是流程的变化,流程的变化会给开发工作造成很大 ...
随机推荐
- 在javascrit中怎样来刷新页面
a页面里iframe了个b页面,我想实现在b页面里一个按钮,一按就刷新a页面,也就是父页面,不是只刷新iframe里面的b页面 哦~ 请问b页面里的<input type="butto ...
- split函数的实现
split作为字符串分割函数非常有用,但在C++里面没有这个函数.自己实现一个分割函数: 1.遇到多个分隔符连在一起,则不做分割 2.()内的分隔符不起作用 3.如果只有(,没有)不影响分隔符 #in ...
- Markdown常用语法
什么是Markdown Markdown 是一种方便记忆.书写的纯文本标记语言,用户可以使用这些标记符号以最小的输入代价生成极富表现力的文档. 通过Markdown简单的语法,就可以使普通文本内容具有 ...
- Nginx中FastCGI配置优化
FastCGI: FastCGI是从CGI发展改进而来的.传统CGI接口方式的主要缺点是性能很差,因为每次HTTP服务器遇到动态程序时都需要重新启动脚本解析器来执行解析,然后结果被返回给HTTP服务器 ...
- eclipse导入项目后,java文件无法编辑的问题
新公司第一天,从svn checkout maven项目后,导入eclipse,发现文件的图标不对,如下图箭头所示,出现这个问题的原因, 是项目的的目录下没有.classpath文件,所以需要执行下m ...
- System.Linq.Dynamic的使用
项目中经常用到组合条件查询,根据用户配置的查询条件进行搜索,拼接SQL容易造成SQL注入,普通的LINQ可以用表达式树来完成,但也比较麻烦.有个System.Linq.Dynamic用起来比较方便. ...
- H5+微信支付报-1问题
1.检查发起支付的参数,全部设置为小写: WxPayData jsApiParam = new WxPayData(); jsApiParam.SetValue("appid", ...
- mvc 3 Mvc 4 使用Forms 登录验证随笔一
前言 本人虽然做 .Net 也有五年有余,可是没什么大才,总是干些打杂的活,技术很少差劲呀.以前不管是做内部管理系统,还是企业平台,保存用户登录信息用的都是Session,也许是从一开始就接触Sess ...
- MySQL 在 LIMIT 条件后注入
from:https://rateip.com/blog/sql-injections-in-mysql-limit-clause/ 此方法适用于MySQL 5.x中,在limit语句后面的注入例如: ...
- 使用PowerShell解三道测试开发笔试题
在网上看到了三道测试开发的笔试题,答案是用Python解的.这段时间正好在学PowerShell,练习一下:) 1. 验证邮箱格式 2. 获取URL的后缀名 3. 获取前一天时间或前一秒 我的解法是: ...