23

我有一个AngularJS客户端应用程序使用javascript(而不是coffeescript或打字稿)Oauth2来验证WebAPI 2应用程序使用最新的Identity 2。我应用程序中的所有软件都是最新的,基于this example。我的客户端浏览器目标是IE9及以上。如何在使用AngularJS,WebAPI 2和Oauth 2时将授权信息发回我的客户端应用程序?

请注意,我对上面的示例做了一些小修改,因为我没有对使用转换发送到服务器的所有数据进行urlen编码。相反,我用urlencode只是在下面的身份验证方法:

user.authenticate = function (userName, password, rememberMe, successCallback, errorCallback) { 
    var config = { 
     method: 'POST', 
     url: '/Token', 
     headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, 
     data: 'grant_type=password&username=' + encodeURIComponent(userName) + '&password=' + encodeURIComponent(password), 
    }; 

我与VS2013更新2和服务器上开发,我使用C#,最新的实体框架和SQL Server 2012的

登录我的客户端向WebAPI调用/ Token方法并传递用户标识和密码。 WebAPI然后用一个令牌给我存储的客户端回应。对于每个请求的WebAPI令牌被发回,并验证:

$http.defaults.headers.common.Authorization = 'Bearer ' + user.data.bearerToken; 

这个作品非常好,到目前为止,但因为它代表应用程序无法告诉分配有不同角色的用户之间的差异。

某些WebAPI方法只能由具有特定角色的用户执行。我想调整我的前端AngularJS应用程序的菜单,以便只有当用户具有此角色时,才会显示相应的链接。我意识到这不会阻止用户检查HTML和发布,但我不关心这一点,因为我仍然会有方法修饰来限制用户不在角色中执行操作的能力。

有人可以给我的,我怎么能做到这一点使用只是产品上面我在这个问题加上JavaScript的网页令牌何况,如果他们帮助把解决方案的最新套装提到一个例子。从我理解的角色是由索赔处理,但我不明白如何添加这些并将它们发回给客户与令牌。我在互联网上做了大量的研究,但是我还没有找到任何好的例子,因为我认为大多数情况都是非常新的,没有多少人有机会探索SPA如何使用这些最新的软件组件。

当回答这个问题时,请注意,我不是在寻找一个可以告诉社区如何在服务器上设置角色的答案,或者是一个解释在服务器上提供角色检查有多重要的答案。 。我想几乎每个人都知道这一点。我真正认为将使用的是一些非常详细的技术建议,包括示例代码和解释。为了保持答案的重点,如果不满足这种需求的答案不作为建议答案发布,它可能对每个人都有帮助。

预先感谢您。

+0

你能解释一下这个“角色是由索赔来处理吗?声明是角色的替代方法,但如果您仍想使用角色,请添加声明,声明的类型为[ClaimTypes.Role](http://msdn.microsoft.com/zh-cn/library/system.security.claims.claimtypes .role(v = vs.110).aspx) – LostInComputer

+0

我认为这可能有所帮助:http://msdn.microsoft.com/en-us/library/hh545448.aspx –

回答

21

简短的回答你的问题是ApplicationOAuthProvider.CreateProperties方法。它为你默认创建和下WebApi2 /供应商/ ApplicationOAuthProvider.cs发现,在默认情况下只发送userName

//WepApi2/Providers/ApplicationOAuthProvider.cs 
public static AuthenticationProperties CreateProperties(string userName) 
{ 
    IDictionary<string, string> data = new Dictionary<string, string> 
    { 
     { "userName", userName } 
    }; 
    return new AuthenticationProperties(data); 
} 

我会做以下更新(如果我需要发送更多的用户数据以后):

public static AuthenticationProperties CreateProperties(string userName, ClaimsIdentity oAuthIdentity) 
{ 
    IDictionary<string, string> data = new Dictionary<string, string> 
    { 
     { "userName", userName}, 
     { "roles",string.Join(",",oAuthIdentity.Claims.Where(c=> c.Type == ClaimTypes.Role).Select(c => c.Value).ToArray())} 

    }; 
    return new AuthenticationProperties(data); 
} 

如果你还没有到的WebAPI项目发生重大变化,ApplicationOAuthProvider.CreateProperties只在两个地方引用,只需更新调用代码传递oAuthIdentityuser.UserName一起,你会得到用户角色一起发送小时访问令牌响应:

{ 
"access_token": "ZpxAZyYuvCaWgShUz0c_XDLFqpbC0-DIeXl_tuFbr11G-5hzBzSUxFNwNPahsasBD9t6mDDJGHcuEqdvtBT4kDNQXFcjWYvFP7U2Y0EvLS3yejdSvUrh2v1N7Ntz80WKe5G_wy2t11eT0l48dgdyak8lYcl3Nx8D0cgwlQm-pePIanYZatdPFP9q5jzhD-_k9SF-ARTHgf0ePnbvhLBi1MCYQjvfgPKlbBHt0M5qjwGAeFg1IhSVj0gb4g9QTXoiPhRmxGBmjOpGgzxXixavmrpM7cCBFLoR3DCGnIJo6pwT-6VArxlB8-ZyyOZqh_6gGtptd0lIu8iJRUIGwO9HFNkROdoE9T4buwLnhPpWpy9geBjPVwsB1K3xnbch26YbklhxIHVybBxeIVXd17QTw_LjlQ5TJdqpAYfiZ5B9Nx2AFYYYe3--aemh4y1XOIvN", 
"token_type": "bearer", 
"expires_in": 1209599, 
"userName": "MK", 
"roles": "Admin,Public", 
".issued": "Fri, 23 May 2014 17:36:54 GMT", 
".expires": "Fri, 06 Jun 2014 17:36:54 GMT" 
} 

现在你有角色可用,您可以用角条件指令显示/根据用户角色隐藏的动作。

如果您需要更多的解释,请让我知道。

编辑:

装饰你的控制器方法与Authorize属性是有效的,因为HttpContext.Current.User.Identity实际上是ClaimsIdentity。但不会在应用程序内硬编码的安全逻辑,我更喜欢使用使用RoleManagerClaimsAuthorizationManager

public ActionResult Secure() 
{ 
    if(!ClaimsPrincipalPermission.CheckAccess("resource", "action")) 
    return new HttpUnauthorizedResult(); 

    ViewBag.Message = "You are allowed to perform action on resource."; 
    return View(); 
} 

角色创建:

RoleManager roleManger = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>()); 
roleManager.Create(new IdentityRole() { Name = "Admin" }); 

角色分配使用UserManager

userManager.AddToRole(user.Id, "Admin"); 
+0

谢谢。我会检查你的答案并更新评论。 –

+0

MK。 - 谢谢你的建议。我检查了上面的代码示例,并且工作正常。然而,我有一个问题,虽然我的用户被分配了角色,并且他们适合身份表,但这些角色并不存在于声明中。因此,当我检查已分配角色的用户时,我发现他们没有任何要求。在上述情况下,您的用户被分配了Admin和Public角色,您是如何分配角色Admin的?你是否也用一个检查装饰你的控制器方法以确保用户在Admin角色中? –

+0

@SamanthaJ角色存在于'ClaimsIdentity'中,我使用修改的'UserManager',我更新了答案以使用默认的asp.net标识2.您也可以直接使用'user.Roles'查询角色。 至于角色检查,我更喜欢使用'ClaimsAuthorizationManager' http://msdn.microsoft.com/en-us/library/system.security.claims.claimsauthorizationmanager.checkaccess(v=vs.110).aspx –

0

我有一个非常类似于您的场景,但不使用令牌进行身份验证,而是使用Identity Server(Thinktecture)来处理我的身份验证。我的应用程序重定向到身份服务器进行身份验证,并返回一些非常基本的声明(用户名和电子邮件)。只要有人试图首先浏览页面,就会发生这种情况。一旦用户通过身份验证并重定向到我的应用程序,我会再次调用服务器以获取用户的权限。这些权限存储在一个安全服务(AngularJS)中,该服务也暴露了一个“hasPermissions”方法。然后,我使用ng-if来决定是否要显示页面的某些部分 - 包括菜单项。事情到这种效果:

var service = { 
    currentUser: ..., 
    isAuthenticated: function() { 
     return ...; 
    }, 
    checkAccess: function(permission) { 
     return service.isAuthenticated() ? 
      !!(service.currentUser.permissions.indexOf(permission) > -1) : false; 
    } 
} 

请记住,所有的所有的权限和HTML元素的人谁决定打开发工具按钮,并采取偷看可见。在执行任何操作之前,您必须在服务器端执行相同的检查。我们有一个基于this的自定义授权属性,该属性检查用户是否拥有执行MVC/WebAPI操作的必要权限,以便在执行任何需要提升的任何事情之前执行简单案例或在Action或HTTP资源中实际检查它们特权。

如果你希望客户端看不到你的网站的任何HTML元素或某一部分,您可以将您的模板,以一个MVC的行动,将身份验证,然后返回HTML模板或重定向到另一个页面(不是SPA境界)并在服务器返回响应之前让它们在服务器上进行身份验证。

+0

感谢您的建议,但我正在寻找索赔基于角色的解决方案更紧密地集成角色和声明。 –

+0

角色是索赔。 mithun_daa说的是,您需要向客户提供索赔(或者在这个例子中,这些索赔给您的权限),以便根据 –

+2

@Martin采取行动 - 这是向客户提供这些索赔的机制我正在寻找更多信息。最新版本的Identity,WebAPI和Oauth2发生了很大变化。我甚至观看了Thinktekture架构师的视频,他提到他不能再推荐将自己的产品用于新的开发,因为微软现在拥有更好的解决方案。看起来好像事情不断发生变化,我发现没有什么好的例子可以说明最好的方法。我希望基于最新版本的软件提供一个很好的例子。谢谢 –

2

有两种方式我看到你可以接近你的问题。

  1. 包括通过散列或一个简单的字符串令牌中的“角色”的信息作为附加你是一个生成令牌,然后可以解密其上的角。

  2. 看来你想使用ASP.NET Identity系统并在那里存储和检索角色信息。如果是这种情况,您可以通过this post注意“初始化数据库以创建管理员角色和管理员用户”部分。

IMO,#1会给你更多的灵活性,你如何存储和使用您的用户数据#2你是以下微软的IdentityUser,尽管它看起来魔术有时,它往往张贴限制,你需要花了解它如何在幕后工作,并使其适用于您的项目。

要更多地了解你所创建的项目的WebAPI中挑选“个人用户帐户”,你可以去http://www.asp.net/visual-studio/overview/2013/creating-web-projects-in-visual-studio#indauth

0

这里另外一个答案:

I ñApplicationOAuthProvider.cs

public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) 
     { 
      var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>(); 

      ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password); 


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

只需添加自定义标题!

context.OwinContext.Response.Headers.Add("Roles", userManager.GetRoles(user.Id).ToArray()); 
相关问题