【问题标题】:Integration Test Web Api With [Authorize]使用 [Authorize] 集成测试 Web Api
【发布时间】:2013-08-04 22:18:42
【问题描述】:

所以我在 [Authorize] 标签上找到了一些启发我的点点滴滴,但没有任何东西可以解决我的问题。

我的情况是我有 Web Api 方法,我想使用 RestSharp 进行集成测试。但是 RestSharp 正在获取我的登录页面,而不是调用结果。

[Authorize]
public Item GetItem([FromBody] int id) 
{
   return service.GetItem(id);
}

该产品使用自定义登录系统,而我真正想要的是一种仅在集成测试中禁用 [Authorize] 标志的方法。但是我读到您可以允许匿名用户并且它会“禁用”徽章,因此在解决方案中,我有一个集成测试项目,并且在该项目中我有一个 App.config 文件。在我放的那个文件中:

 <location>
  <system.web>
   <authorization>
     <allow users="?"/>
    </authorization>
  </system.web>
 </location>

但这似乎也不起作用。任何关于发生了什么、为什么它不工作以及可以做些什么来使它工作的解释将不胜感激。

我试图设置一个 Thread.CurrentPrincipal 但这不起作用(也许我做错了 - 你能在代码中设置“任何东西”来获得授权吗?)。如果有帮助的话,身份验证会在 httpmodule 中处理。

【问题讨论】:

    标签: c# asp.net-web-api web-config integration-testing authorize-attribute


    【解决方案1】:

    我意识到这个问题是关于在 webapi 端点从 RestSharp 触发“真实”请求,所以这个建议并不立即适用于 OPs 场景。但是:

    我正在使用内存中的 Web Api 测试,使用 HttpConfigurationHttpServerHttpMessageInvoker(我相信很像 Badri's suggestion)。通过这种方式,我不需要打开侦听器或端口,因为我可以在内存中测试完整的堆栈(端到端测试)——在构建服务器、Heroku 实例等上非常方便。

    使用内存测试,您可以通过以下方式设置 Thread.CurrentPrincipal.. 我的测试基类上有一个助手,如下所示:

    protected void AuthentateRequest()
    {
        Thread.CurrentPrincipal = new AuthenticatedPrincipal(Thread.CurrentPrincipal);
    }
    

    哪个使用这个:

    public class AuthenticatedPrincipal : IPrincipal
    {
        private readonly IPrincipal _principalToWrap;
        private readonly IIdentity _identityToWrap;
    
        public AuthenticatedPrincipal(IPrincipal principalToWrap)
        {
            _principalToWrap = principalToWrap;
            _identityToWrap = new AuthenticatedIdentity(principalToWrap.Identity);
        }
    
        public bool IsInRole(string role)
        { return _principalToWrap.IsInRole(role); }
    
        public IIdentity Identity
        {
            get { return _identityToWrap; }
            private set { throw new NotSupportedException(); }
        }
    }
    
    public class AuthenticatedIdentity : IIdentity
    {
        private readonly IIdentity _identityToWrap;
    
        public AuthenticatedIdentity(IIdentity identityToWrap)
        {
            _identityToWrap = identityToWrap;
        }
    
        public string Name
        {
            get { return _identityToWrap.Name; }
            private set { throw new NotSupportedException(); }
        }
        public string AuthenticationType
        {
            get { return _identityToWrap.AuthenticationType; }
            private set { throw new NotSupportedException(); }
        }
        public bool IsAuthenticated
        {
            get { return true; }
            private set { throw new NotSupportedException(); }
        }
    }
    

    手动存根IPrincipal 似乎有点过头了,但我尝试使用我的mocking framework 并且它在我的一些测试运行程序中爆炸了(Resharper 和 TeamCity,但不是 NCrunch - 我认为关于在 AppDomains 上序列化的一些东西) .

    这将在ApiController 操作方法中设置Thread.CurrentPrincipal,从而欺骗AuthorizeAttribute 使其相信您已通过身份验证。

    【讨论】:

    【解决方案2】:

    这里是你应该如何设置Thread.CurrentPrincipal。将这样的消息处理程序添加到您的 Web API 项目中,并将处理程序添加到 WebApiConfig.csRegister 方法中,如下所示:config.MessageHandlers.Add(new MyTestHandler());

    public class MyTestHandler : DelegatingHandler
    {
        protected override async Task<HttpResponseMessage> SendAsync(
                                     HttpRequestMessage request,
                                         CancellationToken cancellationToken)
        {
            var local = request.Properties["MS_IsLocal"] as Lazy<bool>;
            bool isLocal = local != null && local.Value;
    
            if (isLocal)
            {
                if (request.Headers.GetValues("X-Testing").First().Equals("true"))
                {
                    var dummyPrincipal = new GenericPrincipal(
                                            new GenericIdentity("dummy", "dummy"),
                                              new[] { "myrole1" });
    
                    Thread.CurrentPrincipal = dummyPrincipal;
    
                    if (HttpContext.Current != null)
                        HttpContext.Current.User = dummyPrincipal;
                }
            }
    
            return await base.SendAsync(request, cancellationToken);
        }
    }
    

    此处理程序设置一个经过身份验证的主体,以使您的所有[Authorize] 开心。这种方法存在风险因素。仅用于测试,您应该将此处理程序插入 Web API 管道。如果您将此处理程序插入生产代码中的管道(有意或无意),它基本上会破坏您的身份验证机制。为了在一定程度上降低风险(希望 API 不在本地访问),我检查以确保访问是本地的,并且有一个标头 X-Testing,其值为 true

    从 RestSharp 添加自定义标头。

    var request = new RestRequest(...);
    request.AddHeader("X-Testing", "true");
    

    顺便说一句,对于集成测试,我更愿意使用内存托管,而不是网络托管。这样,Web API 在同一个测试项目中运行,你可以用它做任何你想做的事情,而不必担心在生产中破坏某些东西。有关内存托管的更多信息,请参阅 thisthis

    【讨论】:

      【解决方案3】:

      为您的RestClient 设置身份验证器:

      RestClient.Authenticator = new HttpBasicAuthenticator(username, password);
      

      使用您的自定义登录系统实际接受的身份验证器...基本、NTLM、OAuth、简单...

      http://restsharp.org/ 示例的第二行中有所记录

      【讨论】:

      • 这实际上适用于 Web API 的常规登录系统,可能是您的自定义登录系统正在做其他事情......您的登录系统是否接受基本身份验证?您是否尝试过将其他身份验证器与 restsharp 一起使用?还有其他的,例如 NTLM、Simple、OAuth ...
      • 如果有帮助...我正在使用 FedAuth 登录。
      • 所以你有令牌供你的用户登录..看看这篇文章,因为他们使用 OAuth 身份验证传递秘密和令牌,而不是 stackoverflow.com/questions/8321034/…
      猜你喜欢
      • 2016-09-10
      • 2014-02-15
      • 1970-01-01
      • 2019-03-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多