ASP.NET MVC - HTTP Authentication Prompt

后端 未结 4 2079
轮回少年
轮回少年 2020-11-28 04:44

Is it possible to make my application ask for username and password prompting for it before render a view? Just like on twitter API to get information about your account:

相关标签:
4条回答
  • 2020-11-28 05:24

    Well, to require basic authentication you need to return 401 status code. But doing that will cause the current authentication module to execute its default unauthorized handler (for forms authentication, this means redirecting to login page).

    I wrote an ActionFilterAttribte to see if I can get the behaviour you want when there's no authentication module installed in web.config.

    public class RequireBasicAuthentication : ActionFilterAttribute {
       public override void OnActionExecuting(ActionExecutingContext filterContext) {
           var req = filterContext.HttpContext.Request;
           if (String.IsNullOrEmpty(req.Headers["Authorization"])) {
               var res = filterContext.HttpContext.Response;
               res.StatusCode = 401;
               res.AddHeader("WWW-Authenticate", "Basic realm=\"Twitter\"");
               res.End();
           }
       }
    }
    

    And the controller action :

    [RequireBasicAuthentication]
    public ActionResult Index() {
        var cred = System.Text.ASCIIEncoding.ASCII
                .GetString(Convert.FromBase64String(
                Request.Headers["Authorization"].Substring(6)))
                .Split(':');
        var user = new { Name = cred[0], Pass = cred[1] };
        return Content(String.Format("user:{0}, password:{1}", 
            user.Name, user.Pass));
    }
    

    That action successfully prints the username and password I enter. But I really doubt that's the best way to do this. Do you have no choice except asking for username and password this way?

    0 讨论(0)
  • 2020-11-28 05:32

    You really want to create a service and not a web application, based on what I have read. I am guessing here, but I think you picked ASP.NET MVC to take advantage of the routing and building the URL's the way you want? Correct me if I am wrong.

    In my opinion the best way to solve the problem you are having is to build RESTful web services with WCF if you are returning data. This article should help you get started if you want to go this route.

    Otherwise, you will need to go further up the stack for handling the request and authenticating it. If this is the case, I can help with providing more info and code.

    0 讨论(0)
  • 2020-11-28 05:34

    I modified the çağdaş answer to put the whole logic inside my custom ActionFilter attribute.

    public class BasicAuthenticationAttribute : ActionFilterAttribute
    {
        public string BasicRealm { get; set; }
        protected string Username { get; set; }
        protected string Password { get; set; }
    
        public BasicAuthenticationAttribute(string username, string password)
        {
            this.Username = username;
            this.Password = password;
        }
    
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var req = filterContext.HttpContext.Request;
            var auth = req.Headers["Authorization"];
            if (!String.IsNullOrEmpty(auth))
            {
                var cred = System.Text.ASCIIEncoding.ASCII.GetString(Convert.FromBase64String(auth.Substring(6))).Split(':');
                var user = new { Name = cred[0], Pass = cred[1] };
                if (user.Name == Username && user.Pass == Password) return;
            }
            var res = filterContext.HttpContext.Response;
            res.StatusCode = 401;
            res.AddHeader("WWW-Authenticate", String.Format("Basic realm=\"{0}\"", BasicRealm ?? "Ryadel"));
            res.End();
        }
    }
    

    It can be used to put under Basic Authentication a whole controller:

    [BasicAuthenticationAttribute("your-username", "your-password", 
        BasicRealm = "your-realm")]
    public class HomeController : BaseController
    {
       ...
    }
    

    or a specific ActionResult:

    public class HomeController : BaseController
    {
        [BasicAuthenticationAttribute("your-username", "your-password", 
            BasicRealm = "your-realm")]
        public ActionResult Index() 
        {
            ...
        }
    }
    

    NOTE: The above implementation requires the developer to manually insert the username and password as ActionFilter required parameters but can be easily extended to make it support any authorization mechanism (MembershipProvider, ASP.NET Identity, custom userbase on an external DBMS or file, etc.) by removing the custom constructor and modifying the OnActionExecuting method IF block accordingly.

    For additional info, you can also read this post I wrote on my blog.

    0 讨论(0)
  • 2020-11-28 05:36

    Here's the way that has worked for me. It's a little foot work but it will make IIS and MVC3 behave a lot more like all the other Basic Http authentication systems, like Apache...

    Step 1.

    Make sure "Basic Authentication" is installed for IIS.

    ( Example: Control Panel -> Programs and Features -> Turn Windows features on or off )

    *I'm using Windows 7 at the moment and am not sure the exact path. [GOOGLE: installing basic authentication in IIS] should get you close.

    Step 2.

    Make sure Basic Authentication is enabled under your site. If you had to install this in the previous step you need to make sure you reset the IIS service and that all the app pools actually went down.

    Step 3.

    (Note: I am using MVC3, and feel this should work in most models, including ASP.Net, without a lot of fuss.)
    In your project you will need to add the following classes:

    public class ServicePrincipal : IPrincipal { // This answers the "What am I allowed to do" question
    
      // In real life, this guy will contain all your user info
      // and you can put what ever you like and retrieve it 
      // later via the HttpContext, on your application side.
      // Some fun with casting will be required.
    
      public static IPrincipal Default { 
        get {
          return new ServicePrincipal {
            Identity = new ServiceIdentity {
              AuthenticationType = "Test",
              IsAuthenticated = true,
              Name = "Basic"
            }
          };
        }
      }
    
      public IIdentity Identity { get; set; } 
    
      public bool IsInRole(string role) {
        // If you want to use role based authorization
        // e.g. [Authorize(Roles = "CoolPeople")]
        // This is the place to do it and you can do
        // anything from load info from a db or flat file
        // or simple case statement...though that would 
        // be silly.
        return true;
      }
    }
    
    public class ServiceIdentity : IIdentity { // This answers the "Who Am I" Question
      public string AuthenticationType { get; set; }
    
      public bool IsAuthenticated { get; set; }
    
      public string Name { get; set; }
    }
    
    
    public class ServiceModule : IHttpModule { // This is the module for IIS
      public void Init(HttpApplication context) {
        context.AuthenticateRequest += this.BasicAuthenticationRequest;
      }
    
      public void BasicAuthenticationRequest(object sender, EventArgs e) {
        HttpApplication app = sender as HttpApplication;
    
        if( !ServiceProvider.Authenticate(app.Context) ) {
          // Total FAIL!
        }
      }
    
      public void Dispose() {
        // Clean up the mess, if needed.
      }
    
    }
    
    public class ServiceProvider {
    
      public static bool Authenticate( HttpContext context ) {
        // For the example we are going to create a nothing user
        // say he is awesome, pass him along through and be done.
        // The heavy lifting of the auth process will go here 
        // in the real world.
    
        HttpContext.Current.User = ServicePrincipal.Default;
        return true;
      }  
    }
    

    Step 3a. [edit]

    Here's the different libs you'll be "using"

    using System.Security.Principal;
    using System.Web;
    

    Just wanted to throw those in. I hate it when folks leave them out. :)

    Step 4.

    Add the following to your web config. Please note I am including the surrounding structure, for example the "configuration" tag... It's just a road map, if you already have a "configuration" tag don't add the other or IIS gets upset with you.

    <configuration>
      <system.webServer>
        <modules runAllManagedModulesForAllRequests="true">
          <add name="ServiceCredentialModule" type="{Namespace}.ServiceModule"/>
        </modules>
      </system.webServer>
    <configuration>
    

    Please note that the Namespace in {Namespace}.ServiceModule is the Namespace you put the classes from Step 3 into.

    ...and that's pretty much it.

    0 讨论(0)
提交回复
热议问题