【问题标题】:Web API custom authentication filterWeb API 自定义身份验证过滤器
【发布时间】:2016-12-11 23:13:38
【问题描述】:

我正在尝试为 ASP.NET MVC 5 网站/应用程序的 Web api 控制器的一部分创建一个自定义过滤器,以检查我存储在数据库中的特定令牌的请求标头。我发现的大多数示例都包含用户凭据,然后使用身份对用户进行身份验证。不完全是我要找的。

这是我找到并正在关注的tutorial

Web API 应该只处理“外部”HTTP 调用,网站端目前将有自己的控制器(但可能会发生变化)。

如果可能,此过滤器应与已存在的身份 2 系统交互。

我所做的是然后我发送用户凭据,为用户分配一个令牌,然后我想使用该令牌来验证请求。有没有办法可以根据令牌过滤请求,或者我需要使用 Owin 身份及其令牌管理。我正在使用移动客户端(目前是 iOS,也将包括 android)来拨打电话。我可以参考任何示例或教程吗?

令牌目前是字母数字字符和符号的随机组合。

谢谢。

附:我可以在需要的地方发布代码 sn-ps 和东西。

编辑:HTTPRequest 将根据它们是否包含我们的数据库/系统中存在的令牌进行过滤。包含令牌或不存在于我们系统中的请求将收到未经授权的错误(401?)

【问题讨论】:

  • 所以token在HTTP请求头中?
  • 是的,令牌在请求头中。
  • 根据令牌过滤请求是什么意思?不是每个 HTTPReqeust 都包含令牌,还是只需要在有令牌时过滤请求?
  • HTTPRequest 将根据它们是否包含我们的数据库/系统中存在的令牌进行过滤。包含令牌或不存在于我们系统中的请求将收到未经授权的错误(401?)
  • 所以添加你的过滤器并执行数据库请求,如果令牌不存在,则重定向 HTTP 请求..

标签: c# authentication asp.net-mvc-5 asp.net-web-api2 owin


【解决方案1】:

假设您认为向每个请求发送用户名和密码并不好。请参考我下面的没有用户名和密码的实现,因为我们不会在每个请求中发送用户名和密码。

public class AuthenticationFilter : AuthorizationFilterAttribute
    {
        /// <summary>
        /// read requested header and validated
        /// </summary>
        /// <param name="actionContext"></param>
        public override void OnAuthorization(HttpActionContext actionContext)
        {
            var identity = FetchFromHeader(actionContext);

            if(identity != null)
            {
                var securityService = actionContext.ControllerContext.Configuration.DependencyResolver.GetService(typeof(ILoginService)) as ILoginService;
                if (securityService.TokenAuthentication(identity))
                {
                    CurrentThread.SetPrincipal(new GenericPrincipal(new GenericIdentity(identity), null), null, null);
                }
                else
                {
                    actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
                    return;
                }
            }
            else
            {
                actionContext.Response = new HttpResponseMessage(HttpStatusCode.BadRequest);
                return;
            }
            base.OnAuthorization(actionContext);
        }

        /// <summary>
        /// retrive header detail from the request 
        /// </summary>
        /// <param name="actionContext"></param>
        /// <returns></returns>
        private string FetchFromHeader(HttpActionContext actionContext)
        {
            string requestToken = null;

            var authRequest = actionContext.Request.Headers.Authorization;
            if (authRequest != null && !string.IsNullOrEmpty(authRequest.Scheme) && authRequest.Scheme == "Basic")
                requestToken = authRequest.Parameter;

            return requestToken;
        }
    }

【讨论】:

  • 我在 ILoginService 处出错。如何解决这个问题? @RajA
【解决方案2】:

您可以通过属性(属性注入)注入服务依赖项来使此过滤器单元可测试。对于自定义属性,我们不想通过构造函数传递依赖关系。我们希望属性易于 采用。要重写 @Raj 已经开始的内容,它可以如下所示:

   public class AuthenticationFilter : AuthorizationFilterAttribute
    {
        [Dependency]
        public ILoginService LoginService { get; set; }

        /// <summary>
        /// read requested header and validated
        /// </summary>
        /// <param name="actionContext"></param>
        public override void OnAuthorization(HttpActionContext actionContext)
        {
            var identity = FetchFromHeader(actionContext);

            if (identity != null)
            {
                if (LoginService.TokenAuthentication(identity))
                {
                    CurrentThread.SetPrincipal(new GenericPrincipal(new GenericIdentity(identity), null), null, null);

                    //IPrincipal principal = new GenericPrincipal(new GenericIdentity(identity), new string[] { "myRole" });
                    //Thread.CurrentPrincipal = principal;
                    //HttpContext.Current.User = principal;             
                }
                else
                {
                    actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
                    return;
                }
            }
            else
            {
                actionContext.Response = new HttpResponseMessage(HttpStatusCode.BadRequest);
                return;
            }
            base.OnAuthorization(actionContext);
        }

        /// <summary>
        /// retrive header detail from the request 
        /// </summary>
        /// <param name="actionContext"></param>
        /// <returns></returns>
        private string FetchFromHeader(HttpActionContext actionContext)
        {
            string requestToken = null;

            var authRequest = actionContext.Request.Headers.Authorization;
            if (authRequest != null && !string.IsNullOrEmpty(authRequest.Scheme) && authRequest.Scheme == "Basic")
                requestToken = authRequest.Parameter;

            return requestToken;
        }
    }

【讨论】: