【问题标题】:ASP.Net MVC Custom AuthenticationASP.Net MVC 自定义身份验证
【发布时间】:2011-05-19 14:06:00
【问题描述】:

我有一个 Asp.Net MVC web 应用程序位于一个仍然主要由 delphi 管理的网站中。 安全性目前由创建 cookie 的 delphi 管理。

已决定通过提取 cookie 详细信息并将其传递给导入的 Delphi DLL 来验证 ASP.Net 应用程序中的用户身份,该 DLL 根据用户是否有效返回 true 或 false。

我的计划是使用表单身份验证,但不是将用户重定向到表单,而是调用 delphi 包装器,如果成功,则将用户重定向到原始 url。这样做的好处是,当安全性迁移到 .Net 时,身份验证框架已经存在,只是需要更改实现。

public ActionResult LogOn(SecurityCookies model, string returnUrl)
    {
        try
        {
            if (model != null)
            {
                Log.DebugFormat("User login: Id:{0}, Key:{1}", model.UserId, model.Key);
                if (authenticator.UserValid(model.UserId, model.Key, 0))
                {
                    FormsService.SignIn(model.UserId, false);
                    return Redirect(returnUrl);
                }
            }
...

请注意,SecurityCookies 是由 delphi 生成的 cookie 的自定义绑定类生成的 - 这很好用。

对 delphi dll 的调用也可以正常工作。

我必须克服的问题是,几乎所有对 .Net 应用程序的调用都是 ajax 请求。 但是,当用户未登录时,由于重定向,浏览器会进行 3 次调用: 1)原始ajax请求 2) 重定向到 ~/Account/Logon (上面的代码) 3) 重定向回原来的ajax请求

虽然跟踪发回给客户的响应,但表明第 3 步返回了正确的数据,但总体而言,该过程由于尚未确定的原因而失败。 只需单击客户端上的刷新即可,因为现在用户已通过身份验证,并且不会重定向到 ~/account/Logon。

注意我的客户端 jQuery 代码如下: $.getJSON(请求字符串,函数(数据){ //对数据做一些事情 });

有没有办法更改表单身份验证过程,以便在用户未通过身份验证时重定向到 URL,我可以运行其他代码来代替?我希望用户的浏览器完全看不到身份验证这一事实。

【问题讨论】:

  • 你能改写你最后的陈述吗?
  • 您是否考虑过将身份验证调用封装在会员提供商MSDN 中?
  • 抱歉,Shawn,现在清楚了吗?
  • @Menahem:我有,但是我不清楚在这种情况下我会在哪里进行身份验证?将它放在 Controller 方法中会很糟糕,因为缓存会绕过安全检查,而且我不热衷于将它放在 Global.asax 中的想法。正如我所看到的,自定义成员资格提供程序仍然使用 Logon.aspx 表单 - 这就是您管理更改的凭据的方式。
  • 如果您使用提供程序,您只需在 web.config 中定义对您页面的访问权限,用户将被重定向/验证,而无需在 global.asax 或控制器中输入任何代码。

标签: asp.net asp.net-mvc ajax forms-authentication


【解决方案1】:

如果您想对请求进行身份验证,可以在 global.asax.cs 中通过定义 Application_AuthenticateRequest 方法来执行此操作。在这里,您可以使用导入的 delphi dll 读取自定义 cookie 并设置 Context.User。 asp.net 中的所有授权都基于在 HttpContext 中设置的用户。 Application_AuthenticateRequest 方法的实现示例:

protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
    HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
    if(authCookie != null)
    {
        //Extract the forms authentication cookie
        FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
        // Create an Identity object
        //CustomIdentity implements System.Web.Security.IIdentity
        CustomIdentity id = GetUserIdentity(authTicket.Name);
        //CustomPrincipal implements System.Web.Security.IPrincipal
        CustomPrincipal newUser = new CustomPrincipal();
        Context.User = newUser;
    }
}

如果 cookie 无效,则不会在上下文中设置用户。

然后您可以创建一个 BaseController,您的所有控制器都将从中继承该控制器,以检查上下文中提供的用户是否经过身份验证。如果用户未通过身份验证,您可以返回 HttpUnauthorizedResult。

public class BaseController : Controller
{
    protected override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (User == null || (User != null && !User.Identity.IsAuthenticated))
        {
           filterContext.Result = new HttpUnauthorizedResult();
        }
        else
        {
            // Call the base
            base.OnActionExecuting(filterContext);
        }
    }
}

在你的 web.config 中:

<authentication mode="None"/>

因为您不希望将请求重定向到登录页面。

【讨论】:

  • 谢谢 - 这正是我所需要的!
【解决方案2】:

我将回答这个特定问题的一部分

有没有办法改变表格 身份验证过程使 当用户重定向到一个 URL 未经过身份验证,我可以运行一些 其他代码?我想要事实 该认证已经发生 对用户完全不可见 浏览器。

不,没有。我写了一个和你类似的登录系统,它是建立在表单身份验证之上的,而且没有可扩展点的事实使它成为一个完整的 PITA 来编写。

我能够覆盖内置行为的方法是使用 HttpModule 来监控重定向到表单身份验证 URL,然后拦截该操作并让我处理它。

internal static bool IsFormsAuthenticationLogin(HttpResponseBase response)
{
    if (!response.IsRequestBeingRedirected) return false;
    if (string.IsNullOrWhiteSpace(response.RedirectLocation)) return false;

    Uri formsAuthUri;
    try
    {
        formsAuthUri = new Uri(FormsAuthentication.LoginUrl);
    }
    catch (Exception ex)
    {
        throw new XifCriticalException("Could not construct formsAuthUri", ex);
    }


    Uri redirctUri;
    try
    {
        redirctUri = new Uri(response.RedirectLocation, UriKind.RelativeOrAbsolute);
    }
    catch (Exception ex)
    {
        throw new XifCriticalException("Could not construct redirctUri", ex);
    }

    try
    {
        //Check if the request was redirected by Forms Auth
        bool isFormsAuthenticationLogin = redirctUri.IsAbsoluteUri &&
                                            redirctUri.Host == formsAuthUri.Host
                                            && redirctUri.PathAndQuery.Contains(formsAuthUri.PathAndQuery); 

        return isFormsAuthenticationLogin;
    }
    catch (Exception ex)
    {
        throw new XifCriticalException("Could not construct isFormsAuthenticationLogin", ex);
    }
}

另一个注意事项,为了能够在 MVC3 中依赖此代码,您可能还需要指定应用设置

 <add key="enableSimpleMembership" value="false" />
 <add key="autoFormsAuthentication" value="false" />

【讨论】:

    【解决方案3】:

    几件事。

    首先,由于您显然处于控制器操作中,因此您应该替换

    Server.Transfer(returnUrl, true);
    

    return Redirect(returnUrl);
    

    其次,我一般更喜欢使用 ajax 调用来进行身份验证。用户应该明确地从未经身份验证的状态转移到经过身份验证的状态。

    第三,如果您必须在通常进行重定向的地方进行 ajax 调用,您可以做以下两件事之一。在您的控制器中,您可以通过调用扩展方法 IsAjaxRequest()(如果是,则返回 true)来确定它是一个 ajax 请求,如果它是一个 ajax 请求,则分支一个不同的结果。其次,如果您返回一个重定向,那么它将向客户端返回一个 HTTP 重定向,Ajax 客户端代码应该能够读取和响应(例如,通过读取位置然后对其进行 ajax 获取)。同样,我不推荐这门课程,但这是可能的。

    最后,作为一个完整的左转...您是否考虑过不理会表单身份验证,而是使用自定义的 MembershipProvider?您可以使用它通过 Delphi 验证成员资格,然后使用普通的 FormsAuth 对象设置客户端 cookie,就像在 MVC 中的示例 AccountController 中所做的那样。

    【讨论】:

    • OK 是的 - 我最初使用的是重定向 - 在我的 cmets 中提到过,但我发布的代码来自我尝试使用 Server.Transfer 以查看它是否有帮助 - 然后我意识到我仍然有重定向无论如何都要到 Account/Logon 方法来处理
    • 很抱歉,我不明白您的第二条评论如何适用于我在这里所做的事情。用户已经在 Web 服务器上进行了身份验证,只是不在 .Net 代码中。 .Net 应用程序目前仅处理少量 ajax 请求,但需要安全。
    • 它们尚未在应用程序中进行身份验证(“.net 代码”)。但对他们自己来说,这是建议而不是回答。
    • 对不起,保罗,我不是有意冒犯 - 欢迎提出建议。只是在我可以在这里做什么方面,我受到旧版应用程序的限制。
    • 了解,没有冒犯。 “第三”段应该与您相关。
    猜你喜欢
    • 1970-01-01
    • 2013-09-06
    • 1970-01-01
    • 2014-05-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多