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.
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
Ciao, ho aggiunto le classi che hai richiesto. Forse il tipo di oscilloscopio è sbagliato? – Locust5304