ASP.NET Web API 可以使用多种不同的方式来实现安全性。'接受' 的方式来处理的身份验证是使用任一 IIS 的内置的安全性 (ie.依靠 HttpContext 和 Windows 安全通过 IIS 身份验证) 或使用 Web Api 消息语义可以滚您自己的 Web API 里面。如果你把你自己的身份验证的推荐的方法是创建 MessageHandler,然后添加一个筛选器与授权。AFAIK、 Web API 本身不会船与任何身份验证处理程序,所以你差不多要滚你自己如果你想在 IIS 中承载。
不管怎样,在我的应用程序的客户之一,我们需要基于用户凭据的业务层的自定义用户身份验证,客户端明确请求客户端一侧的要求基本身份验证。基本身份验证是很容易和支持的任何 Web 客户端,但它是不安全的并要求使用 SSL,以保持编码 (不加密) 凭据有些安全从简单的攻击。在这种情况下该应用程序在内部网络上运行,因此,风险因素是低。
筛选器仅吗?
当我看着在外 ASP.NET 自定义登录安全实施的各种选项时,我发现的第一件事是授权筛选器。授权筛选器是很容易的方式,审查请求,确定用户是否有访问权限然后去或与来异常退出。
筛选器是不是充满了对 HTTP 请求管理器返回结果-通常来说那是 Web API 中 MessageHandlers-但基本身份验证是一种简单的协议,需要几行代码来实现,所以我往前走,在筛选器中实现整个议定书。因为此应用程序中我们有授权的特定方式有一种类型的 auth 发生了,有一点需要使用一种更复杂的实现。在我下一步的对比度后我还实现消息处理程序基于基本身份验证实现,因此,您可以轻松地比较两个如果希望。
在 ASP.NET Web API 授权筛选器
授权筛选器从 AuthorizationFilterAttribute 的类继承,并通常重写 OnAuthorization() 方法,其中应处理的授权任务。筛选器不应允许通过的请求,如果是有效的授权,抛出 UnauthorizedException(),如果它未能验证用户,或返回新的自定义 HttpResponseMessage。
这里是我带了的有点笼统授权筛选器版本:
- /// <summary>
- /// Generic Basic Authentication filter that checks for basic authentication
- /// headers and challenges for authentication if no authentication is provided
- /// Sets the Thread Principle with a GenericAuthenticationPrincipal.
- ///
- /// You can override the OnAuthorize method for custom auth logic that
- /// might be application specific.
- /// </summary>
- /// <remarks>Always remember that Basic Authentication passes username and passwords
- /// from client to server in plain text, so make sure SSL is used with basic auth
- /// to encode the Authorization header on all requests (not just the login).
- /// </remarks>
- [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
- public class BasicAuthenticationFilter : AuthorizationFilterAttribute
- {
- bool Active = true;
-
- public BasicAuthenticationFilter()
- { }
-
- /// <summary>
- /// Overriden constructor to allow explicit disabling of this
- /// filter's behavior. Pass false to disable (same as no filter
- /// but declarative)
- /// </summary>
- /// <param name="active"></param>
- public BasicAuthenticationFilter(bool active)
- {
- Active = active;
- }
-
-
- /// <summary>
- /// Override to Web API filter method to handle Basic Auth check
- /// </summary>
- /// <param name="actionContext"></param>
- public override void OnAuthorization(HttpActionContext actionContext)
- {
- if (Active)
- {
- var identity = ParseAuthorizationHeader(actionContext);
- if (identity == null)
- {
- Challenge(actionContext);
- return;
- }
-
-
- if (!OnAuthorizeUser(identity.Name, identity.Password, actionContext))
- {
- Challenge(actionContext);
- return;
- }
-
- var principal = new GenericPrincipal(identity, null);
-
- Thread.CurrentPrincipal = principal;
-
- // inside of ASP.NET this is required
- //if (HttpContext.Current != null)
- // HttpContext.Current.User = principal;
-
- base.OnAuthorization(actionContext);
- }
- }
-
- /// <summary>
- /// Base implementation for user authentication - you probably will
- /// want to override this method for application specific logic.
- ///
- /// The base implementation merely checks for username and password
- /// present and set the Thread principal.
- ///
- /// Override this method if you want to customize Authentication
- /// and store user data as needed in a Thread Principle or other
- /// Request specific storage.
- /// </summary>
- /// <param name="username"></param>
- /// <param name="password"></param>
- /// <param name="actionContext"></param>
- /// <returns></returns>
- protected virtual bool OnAuthorizeUser(string username, string password, HttpActionContext actionContext)
- {
- if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
- return false;
-
- return true;
- }
-
- /// <summary>
- /// Parses the Authorization header and creates user credentials
- /// </summary>
- /// <param name="actionContext"></param>
- protected virtual BasicAuthenticationIdentity ParseAuthorizationHeader(HttpActionContext actionContext)
- {
- string authHeader = null;
- var auth = actionContext.Request.Headers.Authorization;
- if (auth != null && auth.Scheme == "Basic")
- authHeader = auth.Parameter;
-
- if (string.IsNullOrEmpty(authHeader))
- return null;
-
- authHeader = Encoding.Default.GetString(Convert.FromBase64String(authHeader));
-
- var tokens = authHeader.Split(':');
- if (tokens.Length < 2)
- return null;
-
- return new BasicAuthenticationIdentity(tokens[0],tokens[1]);
- }
-
-
- /// <summary>
- /// Send the Authentication Challenge request
- /// </summary>
- /// <param name="message"></param>
- /// <param name="actionContext"></param>
- void Challenge(HttpActionContext actionContext)
- {
- var host = actionContext.Request.RequestUri.DnsSafeHost;
- actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
- actionContext.Response.Headers.Add("WWW-Authenticate", string.Format("Basic realm=\"{0}\"", host));
- }
-
- }
此代码依赖于自定义扩展带有密码的标准 GenericIdentity 的 BasicAuthenticationIdentity 类:
- public class BasicAuthenticationIdentity : GenericIdentity
- {
- public BasicAuthenticationIdentity(string name, string password)
- : base(name,"Basic")
- {
- this.Password = password;
- }
-
- /// <summary>
- /// Basic Auth Password for custom authentication
- /// </summary>
- public string Password { get; set; }
- }
筛选器的执行是很直向前和处理的几个不同的步骤:
- 解析为 BasicAuthenticationIdentity 的凭据,如果可用
- 如果不使用凭据被发现授权面临的挑战 (401 响应)
- 如果凭据被发现授权基于的凭据的用户
- 如果凭据有效,则设置 ThreadPrinicipal (或 HttpContext.User)
基本身份验证是-基本
基本身份验证通常回降至的原因之一是它是-基本。它是非常容易实现,因为旅行的线路上的数据只是一个用户名称和密码编码为 base64 字符串分隔:。
username:password
整件事然后是 base64 编码:
dXNlcm5hbWU6cGFzc3dvcmQ =
从客户端的入站授权标头,发送一个用户名和密码,然后看起来像这样:
授权: 基本 dXNlcm5hbWU6cGFzc3dvcmQ =
因为它是最基本也是相当不安全。还记得在真实的世界方案中使用 SSL 与基本身份验证访问 Api,尽量减少暴露的纯文本用户名和密码 !
授权
在筛选器中的 ParseAuthorizationHeader() 方法然后分解授权标头和重组入 BasicAuthenticationIdentity,只需保存的用户名和密码以便授权过程可以确定该用户和密码组合是否有效的用户名和密码。
筛选器包含一个简单的 OnAuthorize() 方法,可以在子类中重写。此方法只是应返回 true 或 false,并应实施以确定用户是否被授权或不必要的任何业务逻辑。您可以验证对业务对象,或考虑到你有一个用户名和密码与工作,您可以对本地或域甚至验证的 Windows 帐户。
默认实现只是检查存在授权标头,则返回 true。
这里是专门的 BasicAuthenticationFilter,验证用户使用的业务对象的示例:
- public class MyBasicAuthenticationFilter : BasicAuthenticationFilter
- {
-
- public MyBasicAuthenticationFilter()
- { }
-
- public MyBasicAuthenticationFilter(bool active) : base(active)
- { }
-
-
- protected override bool OnAuthorizeUser(string username, string password, HttpActionContext actionContext)
- {
- var userBus = new BusUser();
-
- var user = userBus.AuthenticateAndLoad(username, password);
- if (user == null)
- return false;
-
- return true;
- }
- }
若要使用筛选器,现在你可以简单地将属性添加到一个控制器您想要应用它:
- [MyBasicAuthenticationFilter]
- public class QueueController : ApiController
或全球范围内可以将其应用在 Web API 配置:
- GlobalConfiguration.Configuration.Filters.Add(new MyBasicAuthenticationFilter());
另外还可以对个别的方法来启用或禁用身份验证功能应用的筛选器属性。
- [MarvelPressAuthorizationFilter(false)]
- [GET("Queue")]
- public IEnumerable<QueueMessageItem> GetRecentMessages(string type = null)
一般我喜欢第一种方法,因为在我的应用程序的大部分有哪里不适用于身份验证的安全的至少一节。此筛选器可能并不重要,但如果您使用的令牌基于安全像你可能有一个需要访问未经身份验证的登录 API。如果你需要保留单个方法 (如登录方法!) 从射击身份验证可以使用最后的方法并添加一个属性与活动的 = false 参数。
这效果很好,和它是完全自足。此外好这种简单的实现的是你有一定的控制,并适用。可以将它分配给全局筛选器以火针对每个请求或个别控制器和甚至个别操作方法。MessageHandler 这是大大你有 MessageHandler 和一个筛选器来决定应用消息处理程序的位置之间协调的更多地参与。
我们需要一个消息处理程序吗?
大多数其他的例子,我看涉及的消息处理程序,是有点更多地参与设立并与其进行交互。MessageHandlers 在 WebAPI 中的,基本上是 pre-和后允许操作在途中在出去的路上响应请求的请求筛选器。要有效地建立起一个身份验证的消息处理程序是有点更多的工作,比我有上面的代码。MessageHandlers 也是完全异步,您需要处理的任务 (或异步/等待至少) 在您的代码,会增加一些复杂性。
身份验证的消息处理程序通常只会有应付检查身份验证的 HTTP 标头中的信息,以及如果不有火挑战请求。授权是留给其他机制-像一个筛选器。该处理程序然后设置可以稍后签一个主体。该处理程序还必须检查以确定是否要挑战客户端的响应输出。所以你会有使用消息处理程序的实现两个折执行: 消息处理程序加上 AuthorizationFilter,来验证用户。
供参考我还写了一篇博客文章有关基本身份验证 MessageHandler。它是有点更多地参与,但大部分的代码是相似的只是有点更分散-你可以为你自己看看和拣你从两个实现。
我确实认为,如果您要构建一种通用身份验证机制,是普遍可用,然后 MessageHandler 有意义的。例如您可以组合多个消息处理程序和身份验证方案。
但是对于简单的使用案例在你使用非常应用特定的自定义登录方案-你不会在乎其他安全实现,所以筛选器确实是有道理,因为它能让所有的代码逻辑上一起管理身份验证和授权。
这是一个很好和简单和自包含的解决方案,是很容易地重用,现在已经使用上了几个项目。我希望你们中的一些发现这也非常有用。
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请
点击举报。