2015-01-29 1 views
6

У меня есть контроллер, где я вставляю служебный интерфейс в конструктор. У службы есть интерфейсы, введенные в его конструктор. Контейнер IoC (Unity) должен использовать информацию о пользователе при построении одного из классов, которые он возвращает для данного интерфейса.ASP.NET MVC Аутентификация перед созданным контроллером

Что происходит, заключается в том, что контроллер создается до того, как оценивается атрибут [Authorize], и ​​пользователь аутентифицируется. Это заставляет Unity выполнять инъекцию зависимостей и использовать информацию о пользователе до того, как они вошли в систему. Ни одна из этих проблем не была проблемой при использовании интегрированной проверки подлинности Windows, но теперь мы используем OpenID Connect для Azure AD, а информация о пользователе isn ' t до тех пор, пока они не войдут в систему (что происходит ПОСЛЕ ПОСТАНОВЛЕНИЯ контроллера).

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

Вот упрощенный пример того, что у меня есть ...

Контроллер:

[Authorize] 
public class MyController : Controller 
{ 
    private readonly IMyService myService; 

    public MyController(IMyService myService) 
    { 
     this.myService = myService; 
    } 

    // ... 
} 

конфигурации Unity:

public class UnityBootstrap : IUnityBootstrap 
{ 
    public IUnityContainer Configure(IUnityContainer container) 
    { 
     // ... 

     return container 
      .RegisterType<ISomeClass, SomeClass>() 
      .RegisterType<IMyService>(new InjectionFactory(c => 
      { 
       // gather info about the user here 
       // e.g. 
       var currentUser = c.Resolve<IPrincipal>(); 
       var staff = c.Resolve<IStaffRepository>().GetBySamAccountName(currentUser.Identity.Name); 
       return new MyService(staff); 
      })); 
    } 
} 

Owin Startup (Startup.Auth.cs):

public void ConfigureAuth(IAppBuilder app) 
{ 
    app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType); 

    app.UseCookieAuthentication(new CookieAuthenticationOptions()); 

    app.UseOpenIdConnectAuthentication(
     new OpenIdConnectAuthenticationOptions 
     { 
      ClientId = this.clientID, 
      Authority = this.authority, 
      PostLogoutRedirectUri = this.postLogoutRedirectUri, 
      Notifications = new OpenIdConnectAuthenticationNotifications 
      { 
       RedirectToIdentityProvider = context => 
       { 
        context.ProtocolMessage.DomainHint = this.domainHint; 
        return Task.FromResult(0); 
       }, 
       AuthorizationCodeReceived = context => 
       { 
        var code = context.Code; 

        var credential = new ClientCredential(this.clientID, this.appKey.Key); 
        var userObjectID = context.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value; 
        var authContext = new AuthenticationContext(this.authority, new NaiveSessionCache(userObjectID)); 
        var result = authContext.AcquireTokenByAuthorizationCode(code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, this.graphUrl); 
        AzureAdGraphAuthenticationHelper.Token = result.AccessToken; 
        return Task.FromResult(0); 
       } 
      } 
     }); 
} 
+0

Я споткнулся это тоже, хотя и без какой-либо из DI материала. Нужен userId в конструкторе базового контроллера и был удивлен, увидев, что конструктор вызван до того, как пользователь «AuthorizedAttribute» переместил пользователя. –

+1

Объяснение, почему именно так http://stackoverflow.com/a/4462767/10245 –

ответ

4

Af тер, не найдя ничего, что конкретно касается моей проблемы в Интернете, я решил копать в ASP.NET MVC 5 application lifecycle. Я обнаружил, что могу создать пользовательский IControllerFactory (или наследовать от DefaultControllerFactory), где я мог бы определить метод CreateController.

Во время CreateController я проверяю, проверен ли пользователь. Если они есть, я просто позволяю DefaultControllerFactory создавать контроллер, как обычно.

Если пользователь не прошел аутентификацию, я создаю свой (очень) простой контроллер «Auth» вместо запрошенного контроллера (тот, у которого много уровней зависимостей), при этом RequestContext остается неизменным.

Контроллер Auth будет создан без проблем, поскольку он не имеет зависимостей. Примечание. Никакое действие не выполняется на контроллере Auth. После создания Auth-контроллера глобальный атрибут AuthorizeAttribute запускается и пользователь должен аутентифицироваться (через OpenID Connect to Azure AD и ADFS).

После входа в систему они перенаправляются обратно в мое приложение с исходным RequestContext, все еще находясь в такте. CusomControllerFactory видит пользователя как аутентифицированный и создается запрошенный контроллер.

Этот метод отлично подходит для меня, поскольку мои контроллеры имеют большую цепочку зависимостей, в которую вводится (т. Е. Контроллер зависит от ISomeService, который зависит от многих ISomeRepository, ISomeHelper, ISomethingEles ...), и ни одна из зависимостей не разрешена до тех пор, пока пользователь не будет вошел в систему.

Я все еще определенно открыт для слушания других (более элегантных) идей о том, как добиться того, что я задавал в своем первоначальном вопросе.


CustomControllerFactory.CS

public class CustomControllerFactory : DefaultControllerFactory 
{ 
    public override IController CreateController(RequestContext requestContext, string controllerName) 
    { 
     var user = HttpContext.Current.User; 
     if (user.Identity.IsAuthenticated) 
     { 
      return base.CreateController(requestContext, controllerName); 
     } 

     var routeValues = requestContext.RouteData.Values; 
     routeValues["action"] = "PreAuth"; 
     return base.CreateController(requestContext, "Auth"); 
    } 
} 

Global.asax.cs

public class MvcApplication : HttpApplication 
{ 
    protected void Application_Start() 
    { 
     // ... 
     ControllerBuilder.Current.SetControllerFactory(typeof(CustomControllerFactory)); 
    } 

    // ... 
} 

AuthController.cs

public class AuthController : Controller 
{ 
    public ActionResult PreAuth() 
    { 
     return null; 
    } 
}