2017-02-04 10 views
5

JWTを使用してASP.NET Core Web APIを保護したいと思います。さらに、コントローラーアクション属性で直接トークンペイロードからロールを使用するオプションがあります。私はポリシーでそれを使用するためにどのようにそれを見つけるでしたがASP.NET Core JWTマッピングロールがClaimsIdentityに申請します

今、:

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

私のような普通のものを使用するオプションを持っている方が好きでしょう:

Authorize(Role="RoleX") 

どこ役割だろうJWTペイロードから自動的にマッピングされます。

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

これで、ASP.NETコアでこれを達成する最も簡単な方法は何ですか?トークンが有効になった後に、ClaimsIdentityという代行受信を生成し、手動でロールクレームを追加する必要があります(もしそうなら、どこに/どのようにするか)。それを行う?)?

答えて

-3

サンプル - ASP.NETコア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請求におけるロールの配列を自動的にタイプとクレームとして追加されます請求項2に記載の方法。私が取得します、私は上記/claimsエンドポイントに電話をかけるときに

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

、およびJWTは、前に生成されたパス:

あなたは、ユーザーの主張を返す次のような単純なAPIメソッドを作成することによってこれをテストすることができます以下のJSONが返さ:あなたが[Authorize]へのロールを渡すと、実際トンとタイプhttp://schemas.microsoft.com/ws/2008/06/identity/claims/roleの請求があるかどうかを見ていきますことを考えると、これは本当に面白い

[ 
{ 
"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" 
} 
] 

がありますあなたが認可している役割の価値。

これは、すべてのAPIメソッドに[Authorize(Roles = "Admin")]を追加するだけで、ペイロードにロールの配列のAdminの値を含むクレームrolesが含まれているJWTのみがそのAPIメソッドに対して承認されることを保証します。

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

は今、単にそのIDトークン許可されるこれらの請求項が含まれてい [Authorize(Roles = "Admin")]とユーザーだけでMVCコントローラを飾ります。

JWTのrolesの主張には、ユーザーに割り当てられた役割の配列が含まれていることを確認してください。コントローラに[Authorize(Roles = "???")]を使用できます。すべてがシームレスに動作します。

+0

これは機能します! tnx –

13

JWTを生成するには、有効なクレームを取得する必要があります。ここではサンプルコードです:

ログインロジック:Startup.cs

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

はに必要なポリシーを追加してください: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に入れたときにどれほど最悪なのか分かりません。長すぎる?パフォーマンス?生成されたJWTをデータベースに格納し、後で有効なユーザーのロール/クレームを取得するためにチェックする必要がありますか?

+0

これは完璧な答えです!他のほとんどのソリューションでは、ロールクレームを取得できません – DIG

0

JWTトークンを生成するために、我々は

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

AuthJwtTokenOptionsヘルパークラスが必要ですアカウントコントローラコード:

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

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

services.AddMvc呼び出し前にConfigureServices(IServiceCollection services)メソッドに次のコードを追加します

app.UseAuthentication()ConfigureMethodに、Startup.csに、app.UseMvcコールの前に追加してください。

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

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

ここでは[Authorize(Roles = "Some_role")]属性を使用できます。この

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ことができるようにあなたがそれを行う必要があります任意のコントローラにユーザーIDと電子メールを取得するには

(これは型名ClaimTypes.Nameを主張する予定です)

int userId = int.Parse(HttpContext.User.Identity.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についても同じことが言えます。有効なキーを指定するだけです。

関連する問題