简体   繁体   English

ASP.Net MVC自定义身份验证

[英]ASP.Net MVC Custom Authentication

I have an Asp.Net MVC webapplication sitting inside a website still largely managed by delphi. 我有一个Asp.Net MVC webapplication坐在一个仍然主要由delphi管理的网站中。 Security is currently managed by delphi which creates cookies. 安全性目前由delphi管理,它创建cookie。

It has been decided to authenticate users within the ASP.Net application by extracting the cookie details and passing them to an imported Delphi DLL which returns true or false depending on whether the user is valid. 已经决定通过提取cookie详细信息并将它们传递给导入的Delphi DLL来验证ASP.Net应用程序中的用户,该DLL根据用户是否有效返回true或false。

My plan was to use Forms authentication, but instead of redirecting the user to a form instead call the delphi wrapper and if successful, redirect the user to the original url. 我的计划是使用Forms身份验证,但不是将用户重定向到表单而是调用delphi包装器,如果成功,则将用户重定向到原始URL。 This gives the benefit that when security is migrated to .Net, the authentication framework will already exist, just the implementation will need to change. 这样做的好处是,当安全性迁移到.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);
                }
            }
...

Note that the SecurityCookies are generated by a custom binding class from the delphi generated cookie - this works well. 请注意,SecurityCookies是由delphi生成的cookie中的自定义绑定类生成的 - 这很有效。

The call to the delphi dll also works ok. 对delphi dll的调用也可以。

The issue I have to overcome is that nearly all of the calls to the .Net application are ajax requests. 我必须克服的问题是几乎所有对.Net应用程序的调用都是ajax请求。 However when the user is not logged in, the browser makes 3 calls due to the redirects: 1) Original ajax request 2) Redirect to ~/Account/Logon (code above) 3) Redirect back to original ajax request 但是,当用户未登录时,浏览器会因重定向而进行3次调用:1)原始ajax请求2)重定向到〜/ Account / Logon(上面的代码)3)重定向回原来的ajax请求

Although tracking the responses posted back to the client, show that Step 3 returns the correct data, overall the process fails for an as yet undetermined reason. 虽然跟踪回发给客户端的响应,但显示步骤3返回正确的数据,整个过程因尚未确定的原因而失败。 Simply clicking refresh on the client works because now the user is authenticated and the redirect to ~/account/Logon doesn't occur. 只需单击客户端上的刷新即可,因为现在用户已通过身份验证,并且不会重定向到〜/ account / Logon。

Note my client jQuery code is as follows: $.getJSON(requestString, function (data) { //do something with the data }); 注意我的客户端jQuery代码如下:$ .getJSON(requestString,function(data){//对数据执行某些操作});

Is there a way of changing the Forms Authentication process so that instead of redirecting to a Url when the User is not authenticated, I can run some other code instead? 有没有办法更改窗体身份验证过程,以便在用户未经过身份验证时,而不是重定向到Url,我可以运行其他一些代码? I'd like the fact that authentication has taken place to be completely invisible to the User's browser. 我希望身份验证已经发生在用户的浏览器中完全不可见。

If you want to authenticate the request, the place to do this is in global.asax.cs by defining the Application_AuthenticateRequest method. 如果要对请求进行身份验证,则可以通过定义Application_AuthenticateRequest方法在global.asax.cs中执行此操作。 Here you can read out the custom cookies with the imported delphi dll and set the Context.User. 在这里,您可以使用导入的delphi dll读出自定义cookie并设置Context.User。 All authorization in asp.net is based on the user that is set in the HttpContext. asp.net中的所有授权都基于HttpContext中设置的用户。 An example of an implementation of the Application_AuthenticateRequest method: 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;
    }
}

If the cookie is not valid then you won't set a user in the context. 如果cookie无效,那么您将不会在上下文中设置用户。

You can then create a BaseController which all your controllers will inherit from that checks if the user that is provided in the context is authenticated. 然后,您可以创建一个BaseController,如果上下文中提供的用户经过身份验证,则所有控制器都将从该检查继承。 If the user is not authenticated you can return a HttpUnauthorizedResult. 如果用户未经过身份验证,则可以返回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);
        }
    }
}

And in your web.config: 在你的web.config中:

<authentication mode="None"/>

because you don't want the request to be redirected to a login page. 因为您不希望将请求重定向到登录页面。

I'm going to answer a part of this specific question 我将回答这个具体问题的一部分

Is there a way of changing the Forms Authentication process so that instead of redirecting to a Url when the User is not authenticated, I can run some other code instead? 有没有办法更改窗体身份验证过程,以便在用户未经过身份验证时,而不是重定向到Url,我可以运行其他一些代码? I'd like the fact that authentication has taken place to be completely invisible to the User's browser. 我希望身份验证已经发生在用户的浏览器中完全不可见。

No there is not. 不,那里没有。 I wrote a login system that is similar to yours being that it is built ontop of forms authentication and the fact there is no extensibility point made it a complete PITA to write. 我编写了一个类似于你的登录系统,因为它是在表单身份验证的基础上构建的,而事实上没有可扩展性点使它成为一个完整的PITA来编写。

The way I was able to override the built in behavior was using a HttpModule that monitors for redirects to the forms auth URL and then to intercept that action and lets me handle it. 我能够覆盖内置行为的方式是使用HttpModule监视重定向到表单auth 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);
    }
}

One other note, to be able to rely on this code in MVC3 you may also need to specify the app settings 另外需要注意的是,为了能够在MVC3中依赖此代码,您可能还需要指定应用程序设置

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

Couple things. 几件事。

First, since you're apparently in a controller action, you should replace 首先,因为你显然是在控制器动作中,所以你应该更换

Server.Transfer(returnUrl, true);

with

return Redirect(returnUrl);

Second, I generally prefer to not use ajax calls to do authentication. 其次,我通常更喜欢使用ajax调用来进行身份验证。 Users should explicitly move from unauthenticated to authenticated states. 用户应明确地从未经身份验证的状态转移到经过身份验证的状态。

Third, if you must do ajax calls where you would normally do redirects, you can do one of two things. 第三,如果你必须在通常进行重定向的地方进行ajax调用,你可以做两件事之一。 In your controller, you can determine it's an ajax request by calling the extension method IsAjaxRequest() (returns true if it is) and branching a different result if it is an ajax request. 在您的控制器中,您可以通过调用扩展方法IsAjaxRequest()来确定它是一个ajax请求(如果是,则返回true)并且如果它是ajax请求则分支不同的结果。 Second, if you return a Redirect, then it will return to the client a HTTP redirect, which the Ajax client code should be able to read and respond to (eg by reading the Location and then doing an ajax fetch on that). 其次,如果你返回一个Redirect,那么它将向客户端返回一个HTTP重定向,Ajax客户端代码应该能够读取并响应(例如,通过读取Location然后对其进行ajax获取)。 Again, I don't recommend this course, but it's possible. 同样,我不推荐这门课程,但这是可能的。

Finally, as a complete left turn... have you considered leaving forms auth alone and instead using a custom MembershipProvider? 最后,作为一个完整的左转...您是否考虑过单独使用表单auth而不是使用自定义MembershipProvider? You can use that to validate membership via Delphi and then set the client cookies using the normal FormsAuth objects like they do in the sample AccountController in MVC. 您可以使用它来通过Delphi验证成员资格,然后使用普通的FormsAuth对象设置客户端cookie,就像它们在MVC中的示例AccountController中一样。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM