2015-06-11 18 views
7

Mi scuso in anticipo per averlo chiesto, poiché in quel momento non conosco la sicurezza in generale e IdentityServer in particolare.Configurazione di IdentityServer con Asp.Net MVC Application

Sto cercando di configurare IdentityServer per gestire la sicurezza per un'applicazione ASP.Net MVC.

sto seguendo il tutorial sul loro sito web: Asp.Net MVC with IdentityServer

Tuttavia, sto facendo qualcosa di leggermente diverso in quanto ho un progetto separato per la parte Identity "Server", che porta a 2 file Startup.cs, uno per l'applicazione e uno per l'identità del server

per l'applicazione, il file Startup.cs assomiglia a questo

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

per il server di identità, il file è startup.cs

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

io sono anche la creazione di un Gestione autorizzazioni

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

Così, per esempio, se io definisco un metodo di controllo che è decorato con l'attributo ResourceAuthorize, in questo modo

public class HomeController : Controller 
{ 

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

Poi, quando cerco di accedere a questo metodo, verrà reindirizzato alla pagina di accesso predefinita.

Quello che non capisco però, è per questo che quando faccio il login con l'utente che ho definito per l'applicazione (vedi sotto),

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

ottengo un errore 403, errore portatore = "insufficient_scope ".

Qualcuno può spiegare cosa sto facendo male?

Qualsiasi tentativo successivo di accedere al metodo di azione restituirà lo stesso errore. Mi sembra che l'utente che ho definito abbia le affermazioni corrette per poter accedere a questo metodo. Tuttavia, il controllo delle attestazioni avviene solo una volta, quando cerco di accedere a questo metodo. Dopo l'accesso ottengo un cookie e il controllo delle richieste non viene effettuato durante i successivi tentativi di accesso al metodo.

Sono un po 'perso, e gradirei un po' di aiuto per chiarire questo.

Grazie in anticipo.

EDIT: ecco i scoles e le classi client

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

e la classe 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 
      } 
     }; 
    } 
} 

Ho anche creato un attributo filtro personalizzato che io uso per determinare quando rivendicazioni controllo viene effettuato .

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

Il punto di interruzione viene raggiunto solo nella richiesta iniziale all'URL.Nelle richieste successive, il punto di interruzione dell'attributo filtro non viene colpito e quindi non si verifica alcun controllo. Ciò mi sorprende dal momento che ho assunto che l'assegno debba essere effettuato ogni volta che viene richiesta l'url.

+2

si può aggiungere scopes.cs e clients.cs alla domanda? L'errore insufficiet_scope significa "La richiesta richiede privilegi più elevati di quelli forniti dal token di accesso." – rawel

+1

Ciao, ho aggiunto le classi che hai richiesto. Forse il tipo di oscilloscopio è sbagliato? – Locust5304

risposta

3

È necessario richiedere gli scopi richiesti dal api quando l'utente accede a. 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

Ciao, grazie per la tua risposta. In realtà ho fatto un errore nel copiare Scope e Client. Ho aggiornato il post ora. Mi dispiace per quello – Locust5304

+0

baseballStatsApi è richiesto dall'api, ma la tua applicazione() non richiede quell'ambito, puoi controllare modificando Scope = "openid profile roles baseballStatsApi", – rawel

+0

Ancora senza fortuna. Ho apportato le modifiche suggerite e aggiornato il post originale per riflettere lo stato del codice. Ho ancora lo stesso errore. – Locust5304