2017-02-04 16 views
5

Tôi muốn bảo vệ ASP.NET Core Web API bằng JWT. Ngoài ra, tôi muốn có một tùy chọn sử dụng các vai trò từ tải trọng thẻ trực tiếp trong các thuộc tính của các hành động của trình điều khiển.Tuyên bố vai trò bản đồ JWT của ASP.NET Core tới ClaimsIdentity

Bây giờ, khi tôi đã tìm thấy nó ra làm thế nào để sử dụng nó với chính sách:

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

Tôi muốn tốt hơn để có một tùy chọn để sử dụng một cái gì đó thông thường như:

Authorize(Role="RoleX") 

nơi Role sẽ được tự động ánh xạ từ tải trọng JWT.

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

Vì vậy, cách dễ nhất để thực hiện điều này trong ASP.NET Core là gì? Có cách nào để tự động làm việc này thông qua một số cài đặt/ánh xạ (nếu có, vị trí đặt nó?) Hoặc tôi nên, sau khi mã thông báo được xác thực, hãy chặn thế hệ ClaimsIdentity và thêm xác nhận quyền sở hữu vai trò (nếu có, ở đâu/như thế nào làm việc đó đi?)?

Trả lời

-3

Mẫu - ASP.NET Core JWT

Hãy xem xét đây là tải trọng.

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

JWT Middleware

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

Khi tôi thực hiện một yêu cầu API của tôi với JWT đã tạo ở trên, các mảng của vai trò trong roles tuyên bố trong JWT sẽ tự động được thêm vào như là tuyên bố với các loại http://schemas.microsoft.com/ws/2008/06/identity/claims/role vào ClaimsIdentity của tôi.

Bạn có thể kiểm tra điều này bằng cách tạo ra các phương pháp API đơn giản sau đây trả về tuyên bố của người dùng:

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

Vì vậy, khi tôi thực hiện một cuộc gọi đến /claims endpoint trên, và vượt qua JWT tạo ra trước đây, tôi sẽ nhận được JSON sau trả về:

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

đâu này được thực sự thú vị là khi bạn xem xét rằng qua Vai trò đến [Authorize] thực sự sẽ xem xét liệu có là khiếu nại về loại http://schemas.microsoft.com/ws/2008/06/identity/claims/role với t anh ta coi trọng vai trò bạn đang cho phép. Điều này có nghĩa là tôi có thể chỉ cần thêm [Authorize(Roles = "Admin")] vào bất kỳ phương thức API nào và điều đó sẽ đảm bảo chỉ JWT nơi tải trọng chứa yêu cầu roles chứa giá trị của Quản trị viên trong dãy vai trò sẽ được ủy quyền cho phương thức API đó.

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

Bây giờ chỉ đơn giản là trang trí các bộ điều khiển MVC với [Authorize(Roles = "Admin")] và chỉ những người dùng có ID Mã chứa những tuyên bố sẽ được ủy quyền.

Đảm bảo yêu cầu roles của JWT của bạn chứa một loạt vai trò được gán cho người dùng và bạn có thể sử dụng [Authorize(Roles = "???")] trong bộ điều khiển của mình. Tất cả đều hoạt động liên tục.

+0

hoạt động! tnx –

13

Bạn cần nhận được xác nhận quyền sở hữu hợp lệ khi tạo JWT.Dưới đây là mã ví dụ:

Login logic: tuyên bố sử dụng

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

Nhận dựa UserRoles, RoleClaimsUserClaims bảng (ASP.NET Identity):

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

Trong Startup.cs xin vui lòng thêm các chính sách cần thiết vào ủy quyền:

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

Tôi là người mới bắt đầu trong ASP.NET, vì vậy hãy cho tôi biết nếu bạn có giải pháp tốt hơn.

Và, tôi không biết làm thế nào tồi tệ nhất khi tôi đặt tất cả các xác nhận quyền/quyền vào JWT. Quá lâu? Hiệu suất ? Tôi có nên lưu trữ JWT được tạo trong cơ sở dữ liệu và kiểm tra sau này để nhận vai trò/xác nhận quyền sở hữu của người dùng hợp lệ không?

+0

Đây là câu trả lời hoàn hảo! Hầu hết các giải pháp khác không nhận được xác nhận quyền sở hữu vai trò – DIG

0

Đối tạo JWT Tokens chúng tôi sẽ cần AuthJwtTokenOptions helper class

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

Account mã điều khiển:

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

Trong Startup.cs add sau mã để ConfigureServices(IServiceCollection services) phương pháp trước services.AddMvc gọi:

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

Đồng thời thêm app.UseAuthentication() gọi tới ConfigureMethod của Startup.cs trước khi gọi app.UseMvc.

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

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

Bây giờ bạn có thể sử dụng các thuộc tính [Authorize(Roles = "Some_role")].

Để có được user id và email trong bất kỳ điều khiển bạn nên làm điều đó như thế này

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; 

Cũng userId có thể được retrived theo cách này (điều này là do tuyên bố loại tên ClaimTypes.Name)

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

Tốt hơn là chuyển mã như vậy sang một số người trợ giúp trình điều khiển mở rộng:

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

Điều này cũng đúng với bất kỳ một số khác Claim mà bạn đã thêm. Bạn chỉ nên chỉ định khóa hợp lệ.

Các vấn đề liên quan