2015-06-11 15 views
7

Przepraszam z góry za pytanie o to, ponieważ nie mam żadnej wiedzy na temat bezpieczeństwa w ogóle, a IdentityServer w szczególności.Konfigurowanie IdentityServer za pomocą aplikacji Asp.Net MVC

Próbuję skonfigurować IdentityServer do zarządzania bezpieczeństwem dla aplikacji ASP.Net MVC.

śledzę tutorial na swojej stronie internetowej: Asp.Net MVC with IdentityServer

Jestem jednak robi coś nieco innego, że mam oddzielny projekt dla tożsamości „Server” części, co prowadzi do 2 Startup.cs plików jeden dla aplikacji i jeden dla Identity Server

dla aplikacji, plik startup.cs wygląda następująco

public class Startup 
{ 
    public void Configuration(IAppBuilder app) 
    { 
     AntiForgeryConfig.UniqueClaimTypeIdentifier = Constants.ClaimTypes.Subject; 
     JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>(); 
     app.UseCookieAuthentication(new CookieAuthenticationOptions 
     { 
      AuthenticationType = "Cookies" 
     }); 

     app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions 
     { 
      Authority = "https://localhost:44301/identity", 
      ClientId = "baseballStats", 
      Scope = "openid profile roles baseballStatsApi", 
      RedirectUri = "https://localhost:44300/", 
      ResponseType = "id_token token", 
      SignInAsAuthenticationType = "Cookies", 
      UseTokenLifetime = false, 
      Notifications = new OpenIdConnectAuthenticationNotifications 
      { 
       SecurityTokenValidated = async n => 
       { 
        var userInfoClient = new UserInfoClient(
           new Uri(n.Options.Authority + "/connect/userinfo"), 
           n.ProtocolMessage.AccessToken); 

        var userInfo = await userInfoClient.GetAsync(); 

        // create new identity and set name and role claim type 
        var nid = new ClaimsIdentity(
         n.AuthenticationTicket.Identity.AuthenticationType, 
         Constants.ClaimTypes.GivenName, 
         Constants.ClaimTypes.Role); 

        userInfo.Claims.ToList().ForEach(c => nid.AddClaim(new Claim(c.Item1, c.Item2))); 

        // keep the id_token for logout 
        nid.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken)); 

        // add access token for sample API 
        nid.AddClaim(new Claim("access_token", n.ProtocolMessage.AccessToken)); 

        // keep track of access token expiration 
        nid.AddClaim(new Claim("expires_at", DateTimeOffset.Now.AddSeconds(int.Parse(n.ProtocolMessage.ExpiresIn)).ToString())); 

        // add some other app specific claim 
        nid.AddClaim(new Claim("app_specific", "some data")); 

        n.AuthenticationTicket = new AuthenticationTicket(
         nid, 
         n.AuthenticationTicket.Properties); 
       } 
      } 
     }); 

     app.UseResourceAuthorization(new AuthorizationManager()); 

     app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions 
     { 
      Authority = "https://localhost:44301/identity", 
      RequiredScopes = new[] { "baseballStatsApi"} 
     }); 

     var config = new HttpConfiguration(); 
     config.MapHttpAttributeRoutes(); 
     app.UseWebApi(config); 
    } 
} 

dla serwera tożsamości, plik startup.cs jest

public class Startup 
{ 
    public void Configuration(IAppBuilder app) 
    { 
     app.Map("/identity", idsrvApp => 
     { 
      idsrvApp.UseIdentityServer(new IdentityServerOptions 
      { 
       SiteName = "Embedded IdentityServer", 
       SigningCertificate = LoadCertificate(), 

       Factory = InMemoryFactory.Create(
        users: Users.Get(), 
        clients: Clients.Get(), 
        scopes: Scopes.Get()) 
      }); 
     }); 
    } 

    X509Certificate2 LoadCertificate() 
    { 
     return new X509Certificate2(
      string.Format(@"{0}\bin\Configuration\idsrv3test.pfx", AppDomain.CurrentDomain.BaseDirectory), "idsrv3test"); 
    } 
} 

Ja również utworzenie Menedżer autoryzacji

public class AuthorizationManager : ResourceAuthorizationManager 
{ 
    public override Task<bool> CheckAccessAsync(ResourceAuthorizationContext context) 
    { 
     switch (context.Resource.First().Value) 
     {      
      case "Players": 
       return CheckAuthorization(context); 
      case "About": 
       return CheckAuthorization(context); 
      default: 
       return Nok(); 
     } 
    } 

    private Task<bool> CheckAuthorization(ResourceAuthorizationContext context) 
    { 
     switch(context.Action.First().Value) 
     { 
      case "Read": 
       return Eval(context.Principal.HasClaim("role", "LevelOneSubscriber")); 
      default: 
       return Nok(); 
     } 
    } 
} 

Tak na przykład, jeśli I zdefiniować metodę kontrolera, który jest ozdobiony atrybutu ResourceAuthorize, tak jak

public class HomeController : Controller 
{ 

    [ResourceAuthorize("Read", "About")] 
    public ActionResult About() 
    { 
     return View((User as ClaimsPrincipal).Claims); 
    } 
} 

Następnie kiedy po raz pierwszy spróbuję uzyskać dostęp do tej metody, nastąpi przekierowanie do domyślnej strony logowania.

Co Nie rozumiem jednak, dlaczego, kiedy zalogować się z użytkownikiem mam zdefiniowanej dla danej aplikacji (patrz poniżej),

public class Users 
{ 
    public static List<InMemoryUser> Get() 
    { 
     return new List<InMemoryUser> 
     { 
      new InMemoryUser 
      { 
       Username = "bob", 
       Password = "secret", 
       Subject = "1", 

       Claims = new[] 
       { 
        new Claim(Constants.ClaimTypes.GivenName, "Bob"), 
        new Claim(Constants.ClaimTypes.FamilyName, "Smith"), 
        new Claim(Constants.ClaimTypes.Role, "Geek"), 
        new Claim(Constants.ClaimTypes.Role, "LevelOneSubscriber") 
       } 
      } 
     }; 
    } 
} 

pojawia się błąd 403, błąd okaziciela = "insufficient_scope ".

Czy ktoś może wyjaśnić, co robię źle?

Każda kolejna próba uzyskania dostępu do metody akcji spowoduje zwrócenie tego samego błędu. Wydaje mi się, że zdefiniowany przeze mnie użytkownik ma odpowiednie roszczenia do uzyskania dostępu do tej metody. Jednak kontrola roszczeń następuje tylko raz, kiedy po raz pierwszy spróbuję uzyskać dostęp do tej metody. Po zalogowaniu otrzymuję plik cookie, a kontrola roszczeń nie jest podejmowana podczas kolejnych prób uzyskania dostępu do metody.

Jestem trochę zagubiony i byłbym wdzięczny za pomoc w oczyszczeniu tego.

Z góry dziękuję.

EDIT: oto scoles i klasy klienckie

public static class Scopes 
{ 
    public static IEnumerable<Scope> Get() 
    { 
     var scopes = new List<Scope> 
     { 
      new Scope 
      { 
       Enabled = true, 
       Name = "roles", 
       Type = ScopeType.Identity, 
       Claims = new List<ScopeClaim> 
       { 
        new ScopeClaim("role") 
       } 
      }, 
      new Scope 
      { 
       Enabled = true, 
       Name = "baseballStatsApi", 
       Description = "Access to baseball stats API", 
       Type = ScopeType.Resource, 
       Claims = new List<ScopeClaim> 
       { 
        new ScopeClaim("role") 
       } 
      } 
     }; 

     scopes.AddRange(StandardScopes.All); 

     return scopes; 
    } 
} 

i klasy Client

public static class Clients 
{ 
    public static IEnumerable<Client> Get() 
    { 
     return new[] 
     { 
      new Client 
      { 
       Enabled = true, 
       ClientName = "Baseball Stats Emporium", 
       ClientId = "baseballStats", 
       Flow = Flows.Implicit,      

       RedirectUris = new List<string> 
       { 
        "https://localhost:44300/" 
       } 
      }, 
      new Client 
      { 
       Enabled = true, 
       ClientName = "Baseball Stats API Client", 
       ClientId = "baseballStats_Api", 
       ClientSecrets = new List<ClientSecret> 
       { 
        new ClientSecret("secret".Sha256()) 
       }, 
       Flow = Flows.ClientCredentials 
      } 
     }; 
    } 
} 

Mam również utworzony atrybut niestandardowy filtr, który używam do określenia, kiedy sprawdza się roszczenia .

public class CustomFilterAttribute : ResourceAuthorizeAttribute 
{ 
    public CustomFilterAttribute(string action, params string[] resources) : base(action, resources) 
    { 
    } 

    protected override bool CheckAccess(HttpContextBase httpContext, string action, params string[] resources) 
    { 
     return base.CheckAccess(httpContext, action, resources); 
    } 
} 

Punkt przerwania trafia tylko na początkowe żądanie do adresu URL.W przypadku kolejnych żądań punkt zatrzymania atrybutu filtru nie jest trafiony, a zatem nie jest przeprowadzana kontrola. Jest to dla mnie zaskakujące, ponieważ zakładałem, że czek musi być dokonywany za każdym razem, gdy żądany jest adres URL.

+2

można dodać scopes.cs i clients.cs na pytanie? Błąd insufficiet_scope oznacza "Żądanie wymaga wyższych uprawnień niż to, które zapewnia token dostępu". – rawel

+1

Witam, dodałem klasy, o które prosiłeś. Może typ zakresu jest błędny? – Locust5304

Odpowiedz

3

Trzeba zwrócić zakresy wymaganych przez API, gdy użytkownik loguje się. Scope = "openid profile roles baseballStatsApi"

   Authority = "https://localhost:44301/identity", 

       ClientId = "baseballStats", 
       Scope = "openid profile roles baseballStatsApi", 
       ResponseType = "id_token token", 
       RedirectUri = "https://localhost:44300/", 

       SignInAsAuthenticationType = "Cookies", 
       UseTokenLifetime = false, 
+0

Cześć, dziękuję za odpowiedź. Naprawdę popełniłem błąd podczas kopiowania zakresu i klienta. Zaktualizowałem post teraz. Przepraszam za to. – Locust5304

+0

Zasięg baseballStatsApi jest wymagany przez api, ale twoja aplikacja() nie żąda tego zakresu, możesz sprawdzić modyfikację Scope = "openid profile role baseballStatsApi", – rawel

+0

Wciąż nie ma szczęścia. Wprowadziłem zmiany, które zasugerowałeś i zaktualizowałeś oryginalny post, aby odzwierciedlić stan kodu. Wciąż dostaję ten sam błąd. – Locust5304

Powiązane problemy