2017-02-04 81 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也是如此。您應該只指定有效的密鑰。