Привет! Я делаю basic-авторизацию в WCF. Я в этом новичок и мои попытки основаны на серии статей http://leastprivilege.com/2008/01/11/http-...t-part-0-intro/ Я использую привязку webHttpBinding и HTTPS включен Главная идея - реализовать интерфейс IHttpModule следующим образом - Когда пользователь отправляет запрос, модуль проверяет присутствует ли в запросе заголовок Authorization
- Если Authorization присутствует, модуль извлекает его значение, декодирует логин и пароль из Base64 и проверяет совпадение
- Если Authorization отсутствует, модуль возвращает код 401 с заголовком WWW-Authenticate
Вот моя реализация модуля: Код | using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Management; using System.Text;
namespace MyProj_A { public class MyHTTPModule : IHttpModule {
void IHttpModule.Dispose() { }
void IHttpModule.Init(HttpApplication context) { context.BeginRequest += Context_BeginRequest; context.AuthenticateRequest += OnEnter; context.EndRequest += OnLeave; }
private void Context_BeginRequest(object sender, EventArgs e) { HttpContext context = HttpContext.Current; context.Response.Write("BeginRequest"); }
void OnEnter(object sender, EventArgs e) { HttpContext context = HttpContext.Current; if (IsHeaderPresent()) { if (!AuthenticateUser()) { DenyAccess(); } } else { // if anonymous requests are not allowed - end the request DenyAccess(); } }
bool IsHeaderPresent() { return HttpContext.Current.Request.Headers["Authorization"] != null; }
bool AuthenticateUser() { string username = "", password = ""; string authHeader = HttpContext.Current.Request.Headers["Authorization"]; if (authHeader != null && authHeader.StartsWith("Basic")) { // extract credentials from header string[] credentials = ExtractCredentials(authHeader); username = credentials[0]; password = credentials[1]; if (username.CompareTo("tikskit") == 0 && password.CompareTo("") == 0) { return true; } else { return false; } } else { return false; } }
private static void DenyAccess() { HttpContext context = HttpContext.Current;
context.Response.StatusCode = 401; context.Response.End(); }
void OnLeave(object sender, EventArgs e) { // check if module is enabled if (HttpContext.Current.Response.StatusCode == 401) { SendAuthenticationHeader(); } }
private void SendAuthenticationHeader() { HttpContext context = HttpContext.Current;
context.Response.StatusCode = 401; context.Response.AddHeader( "WWW-Authenticate", "Basic realm=\"yo-ho-ho\"" );
} }
}
|
И вот мой web.config Код | <?xml version="1.0"?> <configuration>
<appSettings> <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" /> </appSettings> <system.web> <compilation debug="true" targetFramework="4.5.2" /> <httpRuntime targetFramework="4.5.2"/> <customErrors mode="Off" /> </system.web>
<system.serviceModel> <behaviors>
<endpointBehaviors> <behavior name="webBehavior"> <webHttp automaticFormatSelectionEnabled="false"/> </behavior> </endpointBehaviors>
<serviceBehaviors> <behavior name="Default" > <serviceMetadata httpGetEnabled="false" /> <serviceMetadata httpsGetEnabled="false"/> <serviceAuthenticationManager authenticationSchemes="Basic"/> <serviceCredentials> </serviceCredentials>
</behavior> </serviceBehaviors>
</behaviors>
<bindings> <webHttpBinding> <binding name="MyBinding"> <security mode="TransportCredentialOnly"> <transport clientCredentialType="Basic"/> </security> </binding> </webHttpBinding> </bindings>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
<services> <service name="MyProj_A.Service1"> <endpoint address="" binding="webHttpBinding" contract="MyProj_A.IService1" behaviorConfiguration="webBehavior"/> <host> <baseAddresses> <add baseAddress="http://localhost/" /> </baseAddresses> </host> </service> </services> <diagnostics> <endToEndTracing activityTracing="false" messageFlowTracing="true" propagateActivity="true"></endToEndTracing> </diagnostics> </system.serviceModel>
<system.webServer> <modules runAllManagedModulesForAllRequests="true"> <add name="MyHTTPModule" type="MyProj_A.MyHTTPModule,MyProj-A"/> </modules>
<directoryBrowse enabled="false"/> </system.webServer>
</configuration>
|
Я компилирую и публикую проект под IIS 7.5 на удаленном компьютере и подключаюсь к нему из Visual Studio через remote debugger, предварительно расставив брейкпойнты в Context_BeginRequest, OnEnter и OnLeave. Потом я отправляю запрос к моей WCF из браузера, используя URL. И вот что происходит: - После того, как я жму в браузере Enter, срабатывает Context_BeginRequest
- Там я могу видеть из VS, что заголовок Authorization в запросе отсутствует
- Срабатывает OnEnter, где в конце концов ответу присваивается код 401
- Срабатывает OnLeave, где в ответ добавляется заголовок WWW-Authenticate
- В браузере отображается стандартное окно запроса пароля и логина
- Ввожу в этом окне логин и пароль и жму ОК
- Срабатывает Context_BeginRequest, где я вижу, что заголовок Authorization присутствует с значением "Basic <логин и пароль в Base64>"
- На этот раз OnEnter не срабатывает
- Выполнение попадает в OnLeave, где значение HttpContext.Current.Response.StatusCode уже почему-то равно 401
У меня следующие вопросы: - Почему OnEnter не срабатывает во второй раз, когда браузер присылает запрос с Authorization в заголовках? Вот тут сказано, что AuthenticateRequest срабатывает тогда, когда пользователь уже прошел аутентификацию. Тогда я не понимаю, почему OnEnter срабатывает в первый раз, когда никто еще не вводил никакого пароля? У меня каким-то образом включена анонимная аутентификация? Я в IIS удалил все модули аутентификации, кроме BasicAuthenticationModule! Манипуляции с <authentication mode="Forms"/> в <system.web> тоже ничего не дали
- Почему, когда браузер присылает запрос с Authorization, в OnLeave код ответа уже установлен 401? Что и где это устанавливает?
- Как мне правильно настроить мой web.config и IIS и как правильно реализовать Basic-аутентификацию?
Спасибо!
|