2016-07-22 29 views
0

我想就.NET Web API项目中的角色和作用域之间的区别做一个更清晰的描述。这比其他任何方面都更具有最佳方法的问题,我发现自己对如何最好地授权希望访问我的API的用户有点困惑。我来自.NET MVC背景,所以我熟悉角色,我想知道是否同样的方法适用于Web API框架。我很难将范围放在图片中,以及如何使用它们来允许使用特定客户端ID的用户访问。范围与访问权限类似吗?为了说明我的困惑,让我们用这个例子:.Net Web API 2,OWIN和OAuth:范围和角色。有什么区别?

Client A 
Native app: displays event calendar 
Role: Event 
User login required? No 
Allowed scopes: Read events 

Client B 
Web app: shows next upcoming event, displays registrant names 
Role: Event 
User login required? Yes 
Allowed scopes: Read events, read registrants 

Client C 
Native app: registers a person for an event 
Role: Registrant 
User login required? Yes 
Allowed scopes: Read events, read registrants, write registrants 

基本上我想知道如果我的上述使用范围的是正确的,什么是最好的办法是给予资源所有者凭证。我正在使用基于令牌的身份验证,如Taiseers tutorial中所述。下面是我目前不完全的代码片段,将采取的确认(validate)要求照顾客户端和范围:

public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) 
{ 
    ApiClient client = null; 
    string clientId = string.Empty; 
    string clientSecret = string.Empty; 

    if (!context.TryGetBasicCredentials(out clientId, out clientSecret)) 
     context.TryGetFormCredentials(out clientId, out clientSecret); 

    if (context.ClientId == null) 
    { 
     context.Validated(); 
     context.SetError("invalid_clientId", "ClientId should be sent."); 
     return Task.FromResult<object>(null); 
    } 

    using (ApiClientRepo _clientRepo = context.OwinContext.GetUserManager<ApiClientRepo>()) 
    { 
     client = _clientRepo.FindClient(context.ClientId); 
    } 

    if (client == null) 
    { 
     context.SetError("invalid_clientId", string.Format("Client '{0}' is not registered in the system.", context.ClientId)); 
     return Task.FromResult<object>(null); 
    } 

    // Validate client secret 

    if (string.IsNullOrWhiteSpace(clientSecret)) 
    { 
     context.SetError("invalid_secret", "Client secret should be sent."); 
     return Task.FromResult<object>(null); 
    } 
    else 
    { 
     WPasswordHasher passwordHasher = new WPasswordHasher(); 
     PasswordVerificationResult passwordResult = passwordHasher.VerifyHashedPassword(client.SecretHash, clientSecret); 

     if (passwordResult == PasswordVerificationResult.Failed) 
     { 
      context.SetError("invalid_secret", "Client secret is invalid."); 
      return Task.FromResult<object>(null); 
     } 
    } 

    if (!client.Active) 
    { 
     context.SetError("invalid_clientId", "Client is inactive."); 
     return Task.FromResult<object>(null); 
    } 

    context.OwinContext.Set<int>("as:clientRepoId", client.Id); 
    context.OwinContext.Set<string>("as:clientAllowedOrigin", client.AllowedOrigin); 
    context.OwinContext.Set<string>("as:clientRefreshTokenLifeTime", client.RefreshTokenLifeTime.ToString()); 

    context.Validated(); 
    return Task.FromResult<object>(null); 
} 

public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) 
{ 
    IApiUser user = null; 
    string scope = null; 

    // Get parameters sent in body 
    Dictionary<string, string> body = context.Request.GetBodyParameters(); 

    // Get API scope 
    body.TryGetValue("scope", out scope); 

    if (scope == null) 
    { 
     context.Validated(); 
     context.SetError("invalid_scope", "Invalid requested scope."); 
     return; 
    } 

    var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin"); 

    context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });  

    // At this point I got the requested scope. 
    // What should I do with it? 

    if (user == null) 
    { 
     context.SetError("invalid_grant", "The user name or password is incorrect."); 
     return; 
    }  

    // create claims identity based on user info 
    ClaimsIdentity identity = new ClaimsIdentity(context.Options.AuthenticationType); 
    identity.AddClaim(new Claim(ClaimTypes.Name, user.FirstName + " " + user.LastName)); 
    identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Username)); 
    identity.AddClaim(new Claim(ClaimTypes.Role, scope)); 

    var props = new AuthenticationProperties(new Dictionary<string, string> 
     { 
      { 
       "as:client_id", (context.ClientId == null) ? string.Empty : context.ClientId 
      }, 
      { 
       "userName", context.UserName 
      } 
     }); 

    var ticket = new AuthenticationTicket(identity, props); 
    context.Validated(ticket); 
} 

提前感谢所有的想法,建议和想法!

回答

3

在我的视角范围内定义资源。 基本上,请求的挑战是“可能客户端(=应用程序)以您的名义访问资源x”?

其中x是API提供的任何资源。 我在一个项目中使用了一个方便,其中一个作用域可以是特定于资源CRUD操作的项目。例如scope = tweets.read或tweets.create。

拥有一个作用域的令牌不会给客户端许可。该权限基于这样一个事实,即用户有权执行该操作,并让该客户端的令牌中有正确的资源范围。当然,用户权限可以基于角色,例如来宾或管理员等。

因此理论上,用户可以授予对其没有权限的范围(资源)的访问权限。

令牌的生命周期为20分钟,如果您基于访问令牌中的任何值创建了权限,则该令牌不会在令牌生存期内被撤销或更改。

+0

感谢您的解释。我明白,就你而言,你使用范围作为每个资源的权限,所以非常精细。我看了一下[Google的作用域文档](https://developers.google.com/identity/protocols/googlescopes),看起来你可以像他们那样做。那么如何验证请求是否具有资源x的适当范围?你有自定义操作过滤器来做到这一点? – Sebbo

+0

我不会说它是一个许可持续,也许是一个客户端访问用户资源的权限行为。 事实上,我已经使用自定义过滤器验证了范围,并在每个WebApi方法上定义了具有所需范围的自定义属性。 – Max

+0

啊,是的,这是有道理的。我相信我现在对Web API的作用域和角色有更清晰的了解。我在想自定义过滤器来检查所需的范围。谢谢! – Sebbo

相关问题