如何在 ASP.Net MVC 应用程序中使用来自 WCF 身份验证服务的身份验证 cookie

Posted

技术标签:

【中文标题】如何在 ASP.Net MVC 应用程序中使用来自 WCF 身份验证服务的身份验证 cookie【英文标题】:How to use authentication cookie from WCF Authentication Service in an ASP.Net MVC application 【发布时间】:2011-02-04 23:16:37 【问题描述】:

好的,我没有找到任何适合我的特定场景的文档或教程。

我有一个 ASP.Net MVC Web 应用程序,它将使用 WCF 服务进行一切,包括身份验证和角色(通过 WCF 后端的成员资格提供程序)。

setting up the authentication services 没有问题,但它没有在网络应用程序中设置 cookie。 Login method of the service 的文档表明可以连接 CreatingCookie 事件,但它对客户端没有任何影响(我也在服务端尝试过,同样没有影响)。所以我想出了如何capture the cookie。我试图在客户端上手动设置 auth cookie,但到目前为止它不起作用;由于填充,解密失败,并且客户端无法读取服务器给定的 cookie 值。

有人知道您应该如何使用 WCF 身份验证服务生成的 cookie 吗?我是否只是假设会话全部在 WCF 服务器上进行管理,并在每次页面加载时检查服务上的 IsLoggedIn()?

提前致谢。

【问题讨论】:

通读 MSDN 上的更多文档,我想我的场景不是 MS 想要的;如果您要在网站上使用会员资格,为什么要对服务进行身份验证而不是直接访问数据库,所以看起来网站必须与任何其他客户端一样处理,这意味着手动从标头并自己使用它们(创建主体、身份验证票等,并将它们分配给网站的适当部分,以便可以使用)。将进行更多调查,并希望有答案发布... 【参考方案1】:

我最近一直在尝试实现您所描述的相同功能。我已经设法让它使用以下代码:

    private readonly AuthenticationServiceClient service = new AuthenticationServiceClient();

    public void SignIn(string userName, string password, bool createPersistentCookie)
    
        using (new OperationContextScope(service.InnerChannel))
        
            // login
            service.Login(userName, password, String.Empty, createPersistentCookie);

            // Get the response header
            var responseMessageProperty = (HttpResponseMessageProperty)
                OperationContext.Current.IncomingMessageProperties[HttpResponseMessageProperty.Name];

            string encryptedCookie = responseMessageProperty.Headers.Get("Set-Cookie");

            // parse header to cookie object
            var cookieJar = new CookieContainer();
            cookieJar.SetCookies(new Uri("http://localhost:1062/"), encryptedCookie);
            Cookie cookie = cookieJar.GetCookies(new Uri("http://localhost:1062/"))[0];

            FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value);
            if (null != ticket)
            
                //string[] roles = RoleManager.GetRolesFromString(ticket.UserData); 
                HttpContext.Current.User = new GenericPrincipal(new FormsIdentity(ticket), null);
                FormsAuthentication.SetAuthCookie(HttpContext.Current.User.Identity.Name, createPersistentCookie);
            
        
    

它完全符合您对问题的评论所描述的内容。

编辑

我在此处发布此代码的服务器端部分以供参考。

public class HttpResponseMessageInspector : BehaviorExtensionElement, IDispatchMessageInspector, IServiceBehavior

    public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
    

        HttpRequestMessageProperty httpRequest = request.Properties[HttpRequestMessageProperty.Name]
        as HttpRequestMessageProperty;

        if (httpRequest != null)
        
            string cookie = httpRequest.Headers[HttpRequestHeader.Cookie];

            if (!string.IsNullOrEmpty(cookie))
            
                FormsAuthentication.Decrypt(cookie);
                FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(cookie);
                string[] roles = PrincipalHelper.GetUserRoles(authTicket);
                var principal = new BreakpointPrincipal(new BreakpointIdentity(authTicket), roles);

                HttpContext.Current.User = principal;                  
            
            // can deny request here
        

        return null;
    

【讨论】:

哇,我确实错过了一些技巧。让我看看它是如何工作的,我会回复你的。不管我真的很感激你的努力和兴趣:) 这在表面上看起来不错,但我遇到了同样的问题:System.Security.Cryptography.CryptographicException: Padding is invalid and cannot be removed。我对此错误的假设是,由于它是 2 个不同的上下文(客户端应用程序和服务应用程序),解密/加密不兼容?你是如何设置它来工作的?再次感谢您的关注和努力:) Asp.net MVC 和 WCF Host 应用程序是否在同一台机器上运行?如果不是,您必须将两个 Web.config 设置为使用相同的机器密钥。请参阅 [msdn.microsoft.com/en-us/library/ms998288.aspx] 在我的设置中,MVC 和 WCF 服务主机都在我本地机器上的 Visual Studio WebServer 中运行。所以机器钥匙是一样的。编辑:您也可以尝试将此代码添加到 WCF Host Global.cs 文件中以显式发送 cookie 以排除更多的一件事:msdn.microsoft.com/en-us/library/bb398778%28v=VS.100%29.aspx 我现在确实让它们在同一个机器上运行,但我运行的是 IIS7 而不是 cassini,所以机器密钥应该已经相同。将查看链接。谢谢 这对我有用:string encryptedTicket = cookie.Replace(String.Format("0=", FormsAuthentication.FormsCookieName), String.Empty);【参考方案2】:

这对我有用...首先设置主机的身份验证行为(这里通过代码显示,但也可以在配置中完成):

ServiceAuthorizationBehavior author = Description.Behaviors.Find<ServiceAuthorizationBehavior>();
author.ServiceAuthorizationManager = new FormCookieServiceAuthorizationManager();
author.PrincipalPermissionMode = PrincipalPermissionMode.Custom;
author.ExternalAuthorizationPolicies = new List<IAuthorizationPolicy>  new CustomAuthorizationPolicy() .AsReadOnly();

然后是辅助类

  internal class FormCookieServiceAuthorizationManager : ServiceAuthorizationManager
  
     public override bool CheckAccess(OperationContext operationContext)
     
        ParseFormsCookie(operationContext.RequestContext.RequestMessage);
        return base.CheckAccess(operationContext);
     
     private static void ParseFormsCookie(Message message)
     
        HttpRequestMessageProperty httpRequest = message.Properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;
        if (httpRequest == null) return;

        string cookie = httpRequest.Headers[HttpRequestHeader.Cookie];
        if (string.IsNullOrEmpty(cookie)) return;

        string regexp = Regex.Escape(FormsAuthentication.FormsCookieName) + "=(?<val>[^;]+)";
        var myMatch = Regex.Match(cookie, regexp);
        if (!myMatch.Success) return;

        string cookieVal = myMatch.Groups["val"].ToString();
        FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(cookieVal);
        Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity(authTicket.Name), new string[0]);
     
  
  internal class CustomAuthorizationPolicy : IAuthorizationPolicy
  
     static readonly string _id = Guid.NewGuid().ToString();
     public string Id
     
        get  return _id; 
     

     public bool Evaluate(EvaluationContext evaluationContext, ref object state)
     
        evaluationContext.Properties["Principal"] = Thread.CurrentPrincipal;
        evaluationContext.Properties["Identities"] = new List<IIdentity>  Thread.CurrentPrincipal.Identity ;
        return true;
     

     public ClaimSet Issuer
     
        get  return ClaimSet.System; 
     
  

当设置了 AspNetCompatibility 时,FormCookieServiceAuthorizationManager 稍微简单一些:

 internal class FormCookieServiceAuthorizationManager : ServiceAuthorizationManager
 
    public override bool CheckAccess(OperationContext operationContext)
    
       Thread.CurrentPrincipal = HttpContext.Current.User;
       return base.CheckAccess(operationContext);
    
 

【讨论】:

以上是关于如何在 ASP.Net MVC 应用程序中使用来自 WCF 身份验证服务的身份验证 cookie的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 IIS 7.5 压缩来自 ASP.NET MVC 的 Json 结果

在 ASP.NET MVC 4 中添加服务引用

403 禁止:Asp.net MVC

ASP.NET MVC

如何从 ASP.NET MVC 控制器的局部视图中读取值

ASP.NET MVC:如何使用 . (期间)结束