2015-06-11 91 views
7

我很抱歉提出這個問題,因爲我幾乎沒有安全知識,特別是IdentityServer。使用Asp.Net MVC應用程序設置IdentityServer

我正在嘗試設置IdentityServer以管理Asp.Net MVC應用程序的安全性。

我下面在其網站上的教程:Asp.Net MVC with IdentityServer

不過,我做的事情在略有不同我有身份「服務器」部分單獨的項目,這導致2個Startup.cs文件,一個用於應用程序,一個用於身份服務器

對於應用程序時,startup.cs文件看起來像這樣

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

對於身份服務器,該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"); 
    } 
} 

我還設置了一個授權管理

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

因此,舉例來說,如果我定義了裝飾與ResourceAuthorize屬性,像這樣

public class HomeController : Controller 
{ 

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

然後,控制器的方法,當我第一次嘗試訪問此方法時,我將被重定向到默認登錄頁面。

我不明白不過,就是爲什麼當我用我已經爲應用程序定義的用戶登錄(見下文),

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

我得到一個403錯誤,承載錯誤=「insufficient_scope 」。

任何人都可以解釋我做錯了什麼嗎?

任何後續嘗試訪問操作方法都將返回相同的錯誤。在我看來,我定義的用戶具有能夠訪問此方法的正確聲明。但是,索賠檢查只發生一次,當我第一次嘗試訪問此方法。登錄後,我得到一個cookie,並且在後續嘗試訪問該方法期間不會進行索賠檢查。

我有點失落,並希望有一些幫助清除這個。

在此先感謝。

編輯:這裏是scoles和客戶端類

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

而且

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

我也創建了我用它來確定何時要求檢查,自定義過濾器屬性,客戶端類。

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

該斷點僅在最初的url請求中被命中。在隨後的請求中,過濾器屬性斷點未命中,因此不會發生檢查。這對我來說很令人驚訝,因爲我認爲每次請求url都必須進行檢查。

+2

你可以添加scopes.cs和clients.cs的問題? insufficiet_scope錯誤意味着「請求需要比訪問令牌提供的更高權限」。 – rawel

+1

嗨,我添加了你要求的課程。也許範圍類型是錯誤的? – Locust5304

回答

3

您需要申請由API,當用戶登錄所需要的範圍英寸 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

嗨,謝謝你的回答。我實際上在複製Scope和Client時犯了一個錯誤。我現在更新了這個帖子。對於那個很抱歉。 – Locust5304

+0

baseballStatsApi作用域是您的api所需要的,但您的應用程序()不請求該作用域,您能否檢查修改Scope =「openid配置文件角色baseballStatsApi」, – rawel

+0

仍然沒有運氣。我已經完成了您建議的更改並更新了原始帖子以反映代碼的狀態。我仍然得到同樣的錯誤。 – Locust5304

相關問題