2017-02-04 86 views
5

我想用JWT保护ASP.NET Core Web API。此外,我想有一个选项,可以直接在控制器操作属性中使用来自令牌负载的角色。现在ASP.NET核心JWT映射角色声明ClaimsIdentity

,而我没有发现它如何与策略中使用它:

Authorize(Policy="CheckIfUserIsOfRoleX") 
ControllerAction()... 

我想最好是有使用喜欢的东西通常一个选项:

Authorize(Role="RoleX") 

其中的作用将从JWT有效载荷自动映射。

{ 
    name: "somename", 
    roles: ["RoleX", "RoleY", "RoleZ"] 
} 

那么,在ASP.NET Core中完成此操作的最简单方法是什么?有没有办法通过一些设置/映射自动工作(如果是这样,在哪里设置它?),或者我应该在验证令牌之后拦截生成的ClaimsIdentity并手动添加角色声明(如果是,请在何处/如何去做?)?

回答

-3

示例 - ASP.NET Core JWT

请考虑这是有效负载。

{ 
name:"somename", 
roles:["RoleX", "RoleY", "RoleZ"] 
} 

JWT中间件

public class Startup 
{ 
public void Configure(IApplicationBuilder app, IHostingEnvironment env,  ILoggerFactory loggerFactory) 
{ 
    var keyAsBytes = Encoding.ASCII.GetBytes("mysuperdupersecret"); 

    var options = new JwtBearerOptions 
    { 
     TokenValidationParameters = 
     { 
      IssuerSigningKey = new SymmetricSecurityKey(keyAsBytes) 
     } 
    }; 
    app.UseJwtBearerAuthentication(options); 

    app.UseMvc(); 
    } 
} 

当我让我与JWT API的请求上面创建,角色在JWT的roles权利要求中所述阵列将被自动添加作为与所述类型的权利要求http://schemas.microsoft.com/ws/2008/06/identity/claims/role给我的ClaimsIdentity。

您可以通过创建一个返回用户的声明如下简单的API方法测试:当我提出上述/claims端点调用

public class ValuesController : Controller 
{ 
[Authorize] 
[HttpGet("claims")] 
public object Claims() 
{ 
    return User.Claims.Select(c => 
    new 
    { 
     Type = c.Type, 
     Value = c.Value 
    }); 
} 
} 

所以,并通过JWT之前产生的,我会得到以下JSON返回:

[ 
{ 
"type": "name", 
"value": "someone" 
}, 
{ 
"type": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role", 
"value": "RoleX" 
}, 
{ 
"type": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role", 
"value": "RoleY" 
}, 
{ 
"type": "http://schemas.microsoft.com/ws/2008/06/identity/claims/role", 
"value": "RoleZ" 
} 
] 

如果这真的非常有趣的是当你考虑到角色传递给[Authorize]实际上将看是否有http://schemas.microsoft.com/ws/2008/06/identity/claims/role型与T索赔他对你授权的角色的价值。

这意味着我可以简单地将[Authorize(Roles = "Admin")]添加到任何API方法,并且这将确保只有负载包含声明roles的JWT包含角色数组中的Admin的值才会被授权用于该API方法。

public class ValuesController : Controller 
{ 
[Authorize(Roles = "Admin")] 
[HttpGet("ping/admin")] 
public string PingAdmin() 
{ 
    return "Pong"; 
} 
} 

现在只需用[Authorize(Roles = "Admin")],只有用户的ID令牌包含将被授权的索赔装饰MVC控制器。

确保您的JWT的roles声明包含分配给用户的一系列角色,并且您可以在控制器中使用[Authorize(Roles = "???")]。这一切都无缝工作。

+0

工作! tnx –

13

您在生成JWT时需要获得有效的声明。下面是示例代码:

登录逻辑:

private async Task<List<Claim>> GetValidClaims(ApplicationUser user) 
{ 
    IdentityOptions _options = new IdentityOptions(); 
    var claims = new List<Claim> 
     { 
      new Claim(JwtRegisteredClaimNames.Sub, user.UserName), 
      new Claim(JwtRegisteredClaimNames.Jti, await _jwtOptions.JtiGenerator()), 
      new Claim(JwtRegisteredClaimNames.Iat, ToUnixEpochDate(_jwtOptions.IssuedAt).ToString(), ClaimValueTypes.Integer64), 
      new Claim(_options.ClaimsIdentity.UserIdClaimType, user.Id.ToString()), 
      new Claim(_options.ClaimsIdentity.UserNameClaimType, user.UserName) 
     }; 
    var userClaims = await _userManager.GetClaimsAsync(user); 
    var userRoles = await _userManager.GetRolesAsync(user); 
    claims.AddRange(userClaims); 
    foreach (var userRole in userRoles) 
    { 
     claims.Add(new Claim(ClaimTypes.Role, userRole)); 
     var role = await _roleManager.FindByNameAsync(userRole); 
     if(role != null) 
     { 
      var roleClaims = await _roleManager.GetClaimsAsync(role); 
      foreach(Claim roleClaim in roleClaims) 
      { 
       claims.Add(roleClaim); 
      } 
     } 
    } 
    return claims; 
} 

Startup.cs请添加需要的政策分为:基于UserRolesRoleClaimsUserClaims表(ASP.NET身份)

[HttpPost] 
[AllowAnonymous] 
public async Task<IActionResult> Login([FromBody] ApplicationUser applicationUser) { 
    var result = await _signInManager.PasswordSignInAsync(applicationUser.UserName, applicationUser.Password, true, false); 
    if(result.Succeeded) { 
     var user = await _userManager.FindByNameAsync(applicationUser.UserName); 

     // Get valid claims and pass them into JWT 
     var claims = await GetValidClaims(user); 

     // Create the JWT security token and encode it. 
     var jwt = new JwtSecurityToken(
      issuer: _jwtOptions.Issuer, 
      audience: _jwtOptions.Audience, 
      claims: claims, 
      notBefore: _jwtOptions.NotBefore, 
      expires: _jwtOptions.Expiration, 
      signingCredentials: _jwtOptions.SigningCredentials); 
     //... 
    } else { 
     throw new ApiException('Wrong username or password', 403); 
    } 
} 

获得用户的索赔授权:

void ConfigureServices(IServiceCollection service) { 
    services.AddAuthorization(options => 
    { 
     // Here I stored necessary permissions/roles in a constant 
     foreach (var prop in typeof(ClaimPermission).GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy)) 
     { 
      options.AddPolicy(prop.GetValue(null).ToString(), policy => policy.RequireClaim(ClaimType.Permission, prop.GetValue(null).ToString())); 
     } 
    }); 
} 

我是ASP.NET的初学者,所以请让我知道你是否有更好的解决方案。

而且,我不知道在将所有声明/权限放入智威汤逊时,情况会有多糟糕。太长?性能?我是否应该将生成的JWT存储在数据库中,并稍后检查以获取有效的用户角色/索赔?

+0

这是完美的答案!大多数其他解决方案不会获得角色声明 – DIG

0

为了生成JWT令牌,我们需要AuthJwtTokenOptions辅助类

public static class AuthJwtTokenOptions 
{ 
    public const string Issuer = "SomeIssuesName"; 

    public const string Audience = "https://awesome-website.com/"; 

    private const string Key = "supersecret_secretkey!12345"; 

    public static SecurityKey GetSecurityKey() => 
     new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Key)); 
} 

账户控制器代码:

[HttpPost] 
public IActionResult GetToken([FromBody]Credentials credentials) 
{ 
    // TODO: Add here some input values validations 

    User user = _userRepository.GetUser(credentials.Email, credentials.Password); 
    if (user == null) 
     return BadRequest(); 

    ClaimsIdentity identity = GetClaimsIdentity(user); 

    return Ok(new AuthenticatedUserInfoJsonModel 
    { 
     UserId = user.Id, 
     Email = user.Email, 
     FullName = user.FullName, 
     Token = GetJwtToken(identity) 
    }); 
} 

private ClaimsIdentity GetClaimsIdentity(User user) 
{ 
    // Here we can save some values to token. 
    // For example we are storing here user id and email 
    Claim[] claims = new[] 
    { 
     new Claim(ClaimTypes.Name, user.Id.ToString()), 
     new Claim(ClaimTypes.Email, user.Email) 
    }; 
    ClaimsIdentity claimsIdentity = new ClaimsIdentity(claims, "Token"); 

    // Adding roles code 
    // Roles property is string collection but you can modify Select code if it it's not 
    claimsIdentity.AddClaims(user.Roles.Select(role => new Claim(ClaimTypes.Role, role))); 
    return claimsIdentity; 
} 

private string GetJwtToken(ClaimsIdentity identity) 
{ 
    JwtSecurityToken jwtSecurityToken = new JwtSecurityToken(
     issuer: AuthJwtTokenOptions.Issuer, 
     audience: AuthJwtTokenOptions.Audience, 
     notBefore: DateTime.UtcNow, 
     claims: identity.Claims, 
     // our token will live 1 hour, but you can change you token lifetime here 
     expires: DateTime.UtcNow.Add(TimeSpan.FromHours(1)), 
     signingCredentials: new SigningCredentials(AuthJwtTokenOptions.GetSecurityKey(), SecurityAlgorithms.HmacSha256)); 
    return new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken); 
} 

Startup.cs添加以下代码ConfigureServices(IServiceCollection services)方法之前services.AddMvc电话:

public void ConfigureServices(IServiceCollection services) 
{ 
    // Other code here… 

    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) 
     .AddJwtBearer(options => 
     { 
      options.TokenValidationParameters = new TokenValidationParameters 
      { 
       ValidateIssuer = true, 
       ValidIssuer = AuthJwtTokenOptions.Issuer, 

       ValidateAudience = true, 
       ValidAudience = AuthJwtTokenOptions.Audience, 
       ValidateLifetime = true, 

       IssuerSigningKey = AuthJwtTokenOptions.GetSecurityKey(), 
       ValidateIssuerSigningKey = true 
      }; 
     }); 

    // Other code here… 

    services.AddMvc(); 
} 

同时加上app.UseAuthentication()致电ConfigureMethodStartup.cs,然后致电app.UseMvc

public void Configure(IApplicationBuilder app, IHostingEnvironment env) 
{ 
    // Other code here… 

    app.UseAuthentication(); 
    app.UseMvc(); 
} 

现在您可以使用[Authorize(Roles = "Some_role")]属性。

要获得用户ID和电子邮件中的任何控制器,你应该做这样的

int userId = int.Parse(HttpContext.User.Claims.First(c => c.Type == ClaimTypes.Name).Value); 

string email = HttpContext.User.Claims.First(c => c.Type == ClaimTypes.Email).Value; 

userId可以retrived这样

int userId = int.Parse(HttpContext.User.Identity.Name); 

(要求类型名称ClaimTypes.Name这是因为)最好将这些代码移动到某些控制器扩展帮助程序中:

public static class ControllerExtensions 
{ 
    public static int GetUserId(this Controller controller) => 
     int.Parse(controller.HttpContext.User.Claims.First(c => c.Type == ClaimTypes.Name).Value); 

    public static string GetCurrentUserEmail(this Controller controller) => 
     controller.HttpContext.User.Claims.First(c => c.Type == ClaimTypes.Email).Value; 
} 

您添加的其他Claim也是如此。您应该只指定有效的密钥。