首页 文章

.Net Claims Auth - 无法设置当前主体

提问于
浏览
7

我正在尝试使用基于声明的身份验证重新登录系统 .

到目前为止,这么好

单步执行,它似乎正确评估用户名和密码并正确创建声明主体(包括添加身份验证类型以将IsAuthenticated设置为true,per this SO question . )

但是......

不知何故,身份似乎没有正确设置在线上 . 结果,我被直接重定向回登录页面 .

守则

我在 global.asax 中有以下内容:

private void Application_PostAuthenticateRequest(object sender, EventArgs e)
{
    var currentPrincipal = ClaimsPrincipal.Current; 
    var transformer = new ClaimsTransformer(); //My own custom transformer; code below.
    var newPrincipal = transformer.Authenticate(string.Empty, currentPrincipal); // does the transformation

    // as I understand, it is proper & recommnded to set both of these
    Thread.CurrentPrincipal = newPrincipal;
    HttpContext.Current.User = newPrincipal;
}

在我的登录控制器中,我对会员数据库进行了简单的测试 . 我在调试时验证了它有 newCP 作为具有预期名称的有效,经过身份验证的标识 .

[HttpPost]
[AllowAnonymous]
public ActionResult UserLogin(LoginViewModel viewModel)
{

    var loginSuccess = Membership.ValidateUser(viewModel.UserName, viewModel.Password);

    if (loginSuccess)
    {
        // CustomApplicationIdentity puts some identity-based logic into business domain terms and uses Claims underneath. 
        //Should have done it at the IPrincipal level, but instead I created the ToAuthenticatedStandardClaimsIdentity() which returns a new authenticated ClaimsIdentity.

        var newIdentity = new CustomApplicationIdentity(viewModel.UserName);
        var cp = new ClaimsPrincipal(newIdentity.ToAuthenticatedStandardClaimsIdentity());

        var newCP = new ClaimsTransformer().Authenticate(string.Empty, cp);
        System.Web.HttpContext.Current.User = newCP;
        Thread.CurrentPrincipal = newCP;

        if (!string.IsNullOrWhiteSpace(viewModel.ReturnUrl))
        {
            return Redirect(viewModel.ReturnUrl);
        }
        return RedirectToAction("Index", "Identity");

    }
}

问题

当它重定向到Action时,我看到它再次击中 Application_PostAuthenticateRequest ,这很有道理 .

但是,尽管先前设置了主体,但现在看起来这是一个空主体(没有名称,IsAuthenticated设置为false) .

我哪里错了?

一些想法:

  • 是因为我还没有设置SessionSecurityToken吗?

  • 我是否完全错过了有关线程或正确设置上下文的内容?

  • 由于UserLogin方法在MVC中,我也尝试使用控制器上下文,但这似乎也不起作用 .

  • 有可能其他东西可能在中间乱搞这个吗?

  • 阅读:是否有一种简单的方法可以验证旧登录系统的某些部分是否未被遗留并且与我玩弄?

2 回答

  • 8

    经过大量的研究(并且涉及excellent Pluralsight Course by Dominick Baier),解决方案如下:

    重叠的大步骤/问题

    • 我没有't setting a session authentication cookie, so the redirect was being treated as a new request, which saw no cookie and didn'设置校长 .

    • 后来,当我使用会话身份验证管理器时,事实证明是Cassini(VS 's built in debug server) wasn' t加载了SessionAuthenticationManager)

    • (IIS和IIS Express就这么做了) .

    完整解决方案

    一步一步(再次,大部分内容归功于Dominick的视频):

    步骤1:向配置添加身份服务

    • 右键单击您的项目,然后选择"Add Reference..."

    • 在“框架”部分中,选择 System.IdentityModel.Services

    • 将以下内容添加到web.config:

    (下面的完整大纲,在web.config中插入该大纲中的两个部分):

    <configuration>
        <configSections>
            <section name="system.identityModel" type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
            <section name="system.identityModel.services" type="System.IdentityModel.Services.Configuration.SystemIdentityModelServicesSection, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
        </configSections>
    </configuration>
    

    步骤2:添加会话认证管理器

    (这取决于配置设置)

    在web.config的 system.webServer 部分中,添加以下行:

    <remove name="RoleManager"/> <!--Not needed anymore in my case -->
      <add name="SessionAuthenticationModule" type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
    

    步骤3:删除Global.asax中的PostAuthenticate方法

    (不再需要因为SAM,它会检测到cookie;为什么在每次请求时运行它,如果你不需要,对吧?)

    步骤4:设置Claims Transformation方法以设置身份验证cookie

    在ClaimsAuthenticationManager中添加这些行(我的名为ClaimsTransformer) . 我把它放在一个名为“EstablishSession”的单独方法中,它在已经转换后接收了我的主体:

    private void EstablishSession(ClaimsPrincipal transformedPrincipal)
    {
        var sessionToken = new SessionSecurityToken(transformedPrincipal, TimeSpan.FromHours(8));
        FederatedAuthentication.SessionAuthenticationModule.WriteSessionTokenToCookie(sessionToken);
    }
    

    因此,现在每当您转换声明时都会设置Cookie,这是有道理的,因为如果用户成功通过身份验证,您只会转换声明 .

    第5步:撕掉你的头发......

    ...想知道为什么SessionAuthenticationManager总是为空 .

    说真的,一切似乎都有效,而且你的配置是正确的,但如果它不是每个都是空的,那就是它 . 单 . 时间 .

    步骤6:将调试Web服务器切换到IIS Express

    啊,看起来Cassini(VS Debugger中的内置版)不能与SessionAuthenticationManager一起使用 .

    但是,IIS Express可以 . 将其切换到项目设置中的那个 .

    和瞧!

    现在我有一个有效的页面 .

  • -2

    我认为您需要实现SessionSecurityToken,或者在页面请求之间保持会话的东西 . 这是一种更自定义的方法:

    public static int SetAuthCookie(this HttpResponseBase responseBase, User user, bool rememberMe)
        {
            // Initialize Session Ticket
            var authTicket = new FormsAuthenticationTicket(1
                , user.Email
                , DateTime.Now
                , DateTime.Now.AddHours(30)
                , rememberMe
                , JsonConvert.SerializeObject(new {
                    Email = user.Email,
                    FirstName = user.FirstName,
                    Id = user.Id
                })
                , FormsAuthentication.FormsCookiePath);
    
            var encTicket = FormsAuthentication.Encrypt(authTicket);
            HttpCookie authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
    
            if (authTicket.IsPersistent)
                authCookie.Expires = authTicket.Expiration;
    
            responseBase.Cookies.Add(authCookie);
            return encTicket.Length;
        }
    
    
    
        public static void VerifyAuthCookie(HttpContext context)
        {
    
            HttpCookie authCookie = context.Request.Cookies[FormsAuthentication.FormsCookieName];
            if (authCookie == null)
                return;
    
            FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
            if (authTicket == null)
                return;
    
            if (authTicket.Expired)
                return;
    
            User user = !string.IsNullOrEmpty(authTicket.UserData) ? JsonConvert.DeserializeObject<User>(authTicket.UserData) : null;
            if (user == null)
                return;
    
            // Create an Identity object
            UserIdentity id = new UserIdentity(user, authTicket);
    
            // This principal will flow throughout the request.
            GenericPrincipal principal = new GenericPrincipal(id, new [] { "User" });
            context.User = principal;
    

    }

相关问题