ASP.NET MVC - HTTP-запрос аутентификации - PullRequest
27 голосов
/ 22 октября 2009

Можно ли заставить мое приложение запрашивать имя пользователя и пароль перед отображением представления? Как и в Twitter API, чтобы получить информацию о вашей учетной записи:

http://twitter.com/account/verify_credentials.xml

Так что перед рендерингом просмотра || Файл, который просит вас ввести имя пользователя и пароль, я думаю, что это сделано непосредственно на сервере, так как запрос curl основан на имени пользователя: пароль, а также, как это:

curl -u user:password http://twitter.com/account/verify_credentials.xml

Поскольку я пытаюсь создать API, следуя той же структуре, я хотел бы знать, как я могу это сделать на ASP.NET MVC C #. Я уже использовал это на рубиновых рельсах, и это довольно просто, как:

before_filter :authenticate

def authenticate
    authenticate_or_request_with_http_basic do |username, password|
    username == "foo" && password == "bar"
end

Я не думаю, что фильтр [Authorize] такой же, так как я считаю, что это просто перенаправление, и он перенаправляет вас на внутренний контроллер учетных записей, который основан на базе данных учетных записей, в этом случае я буду использовать другую базу данных, в частности, из веб-службы, и выполню проверку после предоставления информации. Но мне нужно действие, чтобы потребовать от пользователя и передать учетные данные по его запросу.

Заранее спасибо


UPDATE:

На самом деле, чтобы запросить страницу, которая требует этой аутентификации (т. Е. Twitter) Я должен был бы заявить об этом по его запросу

request.Credentials = new NetworkCredential("username", "password");

И это будет отражать это имя пользователя и пароль.

Итак, это точно то же самое, но с другой стороны, если можно предоставить информацию для запроса аутентификации по запросу, как я могу потребовать эту аутентификацию по запросу вместо этого?

Так что каждый раз, когда кто-то пытается сделать запрос на мое приложение, например:

http://myapplication/clients/verify_credentials

он должен запросить имя пользователя и пароль с этим запросом сервера таким образом, чтобы получить информацию о curl, например, это было бы так

curl -u user:password http://myapplication/clients/verify_credentials

Ответы [ 4 ]

47 голосов
/ 22 октября 2009

Ну, чтобы потребовать базовую аутентификацию, вам нужно вернуть код состояния 401. Но это приведет к тому, что текущий модуль аутентификации выполнит свой неавторизованный обработчик по умолчанию (для аутентификации с помощью форм это означает перенаправление на страницу входа).

Я написал ActionFilterAttribte, чтобы посмотреть, смогу ли я получить желаемое поведение, если в 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();
       }
   }
}

И действие контроллера:

[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));
}

Это действие успешно печатает имя пользователя и пароль, которые я ввожу. Но я действительно сомневаюсь, что это лучший способ сделать это. У вас нет выбора, кроме как спрашивать имя пользователя и пароль таким образом?

3 голосов
/ 08 декабря 2014

Я изменил ответ çağdaş, чтобы поместить всю логику в свой собственный атрибут ActionFilter.

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();
    }
}

Может использоваться для базовой проверки подлинности всего контроллера:

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

или конкретный ActionResult:

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

ПРИМЕЧАНИЕ : Приведенная выше реализация требует, чтобы разработчик вручную вставил имя пользователя и пароль в качестве обязательных параметров ActionFilter, но его можно легко расширить, чтобы он поддерживал любой механизм авторизации (MembershipProvider, ASP.NET Identity, пользовательская база пользователей во внешней СУБД или в файле и т. д.) путем удаления пользовательского конструктора и соответствующего изменения блока IF метода OnActionExecuting.

Для получения дополнительной информации, вы также можете прочитать это сообщение Я написал в своем блоге.

3 голосов
/ 22 октября 2009

Вы действительно хотите создать сервис, а не веб-приложение, основываясь на том, что я прочитал. Я предполагаю здесь, но я думаю, что вы выбрали ASP.NET MVC, чтобы использовать преимущества маршрутизации и построения URL-адреса так, как вы хотите? Поправь меня, если я ошибаюсь.

По моему мнению, лучший способ решить вашу проблему - это создать веб-сервисы RESTful с WCF, если вы возвращаете данные. Эта статья должна помочь вам начать работу, если вы хотите пойти по этому пути.

В противном случае вам нужно будет пойти дальше вверх по стеку для обработки запроса и его аутентификации. Если это так, я могу помочь с предоставлением дополнительной информации и кода.

2 голосов
/ 09 апреля 2012

Вот способ, который работал для меня. Это немного трудоемкий процесс, но он заставит IIS и MVC3 вести себя намного больше, как все другие системы аутентификации Basic Http, например, Apache ...

Шаг 1.

Убедитесь, что для IIS установлена ​​«Базовая аутентификация».

(Пример: Панель управления -> Программы и компоненты -> Включение или отключение функций Windows)

* В настоящее время я использую Windows 7 и не уверен в точном пути. [GOOGLE: установка базовой аутентификации в IIS] должна приблизить вас.

Шаг 2.

Убедитесь, что на вашем сайте включена базовая аутентификация. Если вам нужно было установить это на предыдущем шаге, вам необходимо убедиться, что вы перезапустили службу IIS и все пулы приложений фактически вышли из строя.

Шаг 3.

(Примечание: я использую MVC3 и считаю, что это должно работать в большинстве моделей, включая ASP.Net, без особых хлопот.)
В ваш проект вам нужно будет добавить следующие классы:

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;
  }  
}

Шаг 3а. [Править]

Вот разные библиотеки, которые вы будете "использовать"

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

Просто хотел бросить их. Я ненавижу, когда люди их пропускают. :)

Шаг 4.

Добавьте следующее в вашу веб-конфигурацию. Обратите внимание, что я включаю окружающую структуру, например, тег «конфигурации» ... Это просто дорожная карта, если у вас уже есть тег «конфигурации», не добавляйте другой или IIS расстраивается из-за вас.

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

Обратите внимание, что пространство имен в {Namespace} .ServiceModule - это пространство имен, в которое вы помещаете классы из шага 3.

... и это все.

...