Подтвердить что ты не робот

Thread.CurrentPrincipal не установлен в службе WCF, вызванной с помощью WebGet

У меня есть веб-сайт, размещенный в IIS, который использует проверку подлинности Windows и предоставляет веб-службы WCF.

Я настраиваю эту службу с помощью поведения конечной точки:

<serviceAuthorization principalPermissionMode ="UseAspNetRoles" 
                   roleProviderName="MyRoleProvider"/>

и привязка:

 <security mode="TransportCredentialOnly">
    <transport clientCredentialType="Ntlm" />
 </security>

При вызове службы Thread.CurrentPrincipal устанавливается в RolePrincipal с идентификатором клиента и ролями, предоставленными сконфигурированным поставщиком.

Все хорошо с миром.

Теперь я добавил несколько дополнительных WCF-сервисов, которые используются REST-ful Ajax-вызовами: Factory="System.ServiceModel.Activation.WebScriptServiceHostFactory" в файле svc, WebGet в контракте на обслуживание и атрибут AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed) в реализации службы.

Я также добавляю следующее заклинание в web.config, как рекомендовано в MSDN:

<system.serviceModel>
    ...
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
    ...
</system.serviceModel>

Моя служба Ajax почти работает так, как я ее хочу. Когда он вызывается, HttpContext.Current.User устанавливается в RolePrincipal с ролями, которые я ожидаю. Но Thread.CurrentPrincipal остается установленным не прошедшим проверку подлинности GenericPrincipal.

Поэтому мне нужно добавить строку кода в каждый из моих методов:

Thread.CurrentPrincipal = HttpContext.Current.User

Есть ли какое-либо заклинание в файле конфигурации, который я могу использовать для автоматического набора Thread.CurrentPrincipal, например, для нормальной службы SOAP?

UPDATE Вот блог от кого-то, у кого была такая же проблема, и решил его, внедряя пользовательские поведения. Наверняка есть способ сделать это из коробки?

ОБНОВЛЕНИЕ 2 Возвращаясь назад, чтобы добавить щедрость к этому, поскольку он снова прослушивает меня в новом проекте, используя службу WCF WebGet с поддержкой .NET 3.5.

Я экспериментировал с несколькими параметрами, включая настройку mainPermissionMode = "None", но ничего не работает. Вот что происходит:

  • Я перехожу к URL-адресу WebGet, который вызывает мою службу: http://myserver/MyService.svc/...

  • Я установил точку останова в Global.asax "Application_AuthorizeRequest". Когда эта точка останова ударяется, оба параметра "HttpContext.Current.User" и "Thread.CurrentPrincipal" были установлены в "RolePrincipal", который использует мой настроенный ASP.NET RoleProvider. Это поведение, которое я хочу.

  • У меня есть вторая точка останова, когда вызывается мой метод OperationContract службы. Когда эта точка останова ударяется, HttpContext.Current.User все еще ссылается на мою RolePrincipal, но Thread.CurrentPrincipal был изменен на GenericPrincipal. Aaargh.

Я видел предложения реализовать пользовательскую IAuthorizationPolicy и будет изучать это, если я не найду лучшего решения, но почему следует ли мне реализовать пользовательскую политику для использования существующих полномочий авторизации ASP.NET? Если у меня есть mainPermissionMode = "UseAspNetRoles", конечно, WCF должен знать, что мне нужно?

4b9b3361

Ответ 1

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

Как мы сохраняем Thread.CurrentPrincipal и HttpContext.Current.User в Sync

Мы написали HttpModule под названием "AuthenticationModule", который наследует от IHtppModule.

Затем мы присоединяемся к событию HttpApplication.AuthenticateRequest, которое происходит очень рано в жизненном цикле запроса.

В нашем обработчике событий AuthenticateRequest мы реализуем наши требования к конкретным приложениям, включая установку Thread.CurrentPrincipal и, при необходимости, и текущего пользователя контекста. Таким образом, вы реализуете этот код только один раз для всего своего приложения, и если он изменится (например, если вы реализуете пользовательский принцип IIDentity), у вас есть только одно место для его изменения. (Не дублируйте этот код в каждом сервисном методе.)

public class AuthenticationModule : IHttpModule
{
    public void Dispose() { return; }

    public void Init(HttpApplication app)
    {
        app.AuthenticateRequest += new EventHandler(app_AuthenticateRequest);
    }

    void app_AuthenticateRequest(object sender, EventArgs e)
    {
        HttpApplication app = (HttpApplication)sender;

        // This is what you were asking for, but hey you 
        // could change this behavior easily.
        Thread.CurrentPrincipal = app.Context.User;
    }
}

На самом деле это немного сложнее, поскольку мы реализуем пользовательский IIdentity, создаем экземпляр GenericPrincipal, а затем назначаем его как app.Context.User, так и Thread.CurrentPrincipal; но выше вы просите.

Не забудьте зарегистрировать новый HttpModule в web.config!

Для интегрированных пулов приложений:

<system.webServer>
    <modules>
      <add name="AuthenticationModule" type="YourNameSpace.AuthenticationModule" preCondition="integratedMode" />
    </modules>
</system.webServer>

Для старых классических пулов приложений вы должны поместить их в <system.web><httpModules></httpModules></system.web>

Возможно, вам потребуется сыграть с тем, что входит в этот обработчик событий AuthenticationRequest и/или порядок, с которым вы регистрируете обработчик. Поскольку наш пользователь полностью настраивается, он может отличаться от того, что вам нужно. Мы фактически захватываем файл cookie для проверки подлинности форм, расшифровываем его и т.д.... вам может понадобиться выполнить ping некоторые встроенные методы для WindowsAuthentication.

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