Jwt 和 ASP.NET CORE 授权 AspNetRoleClaims

Posted

技术标签:

【中文标题】Jwt 和 ASP.NET CORE 授权 AspNetRoleClaims【英文标题】:Jwt and ASP.NET CORE Authorization AspNetRoleClaims 【发布时间】:2021-10-05 12:42:29 【问题描述】:

如何使用 ASP.NET CORE 授权 AspNetRoleClaims 和 Angular 实现 Jwt 令牌?

例如,我有以下默认角色(用户类型):

管理员(Gerencia) 用户 客户

拥有 viewRoles 声明的用户可以使用自定义声明添加自己的角色。

例如 工人 声明 canViewClients canViewInventory....

有权查看员工并对其进行编辑的用户可以从下拉列表中选择您可以在我的图片上看到的用户类型。

当用户选择用户类型时,我的应用程序会自动填写用户默认可以访问的声明。但是,用户始终可以取消选中或检查不同的声明,自动填充只是为了节省一些时间。这是我的复选框在视觉上的样子。

每个复选框代表一个声明。

回顾

用户类型不用于授权端点,仅用于在视觉上快速填充复选框。 每个复选框代表一个声明。

如何向我的 jwt cookie 添加声明/策略

这是我的 startup.cs 文件的重要部分。

    public class Startup
    
        public Startup(IConfiguration configuration)
        
            Configuration = configuration;
        

        public IConfiguration Configuration  get; 

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        
            services.Configure<ApplicationSettings>(Configuration.GetSection("AppSettings")); 

            services.AddTransient<DatabaseMigrator>();
            services.AddDbContext<erp_colombiaDbContext>(options => options.Usemysql(
                     Configuration.GetConnectionString("DefaultConnection"),
                     optionsBuilder => optionsBuilder.MigrationsAssembly(typeof(DesignTimeDbContextFactory).Assembly.FullName)));
            services.TryAddScoped<UserManager<Employee>>();
            services.TryAddScoped<SignInManager<Employee>>();


//Should I add roles/policies here?
            services.AddIdentityCore<Employee>(options => 
                options.SignIn.RequireConfirmedAccount = false
            ).AddRoles<Entities.Type>().AddEntityFrameworkStores<erp_colombiaDbContext>();



            //Jwt Authentication
            var key = Encoding.UTF8.GetBytes("myKey");

            services.Configure<IdentityOptions>(options =>
            
                options.Password.RequireDigit = false;
                options.Password.RequireNonAlphanumeric = false;
                options.Password.RequireLowercase = false;
                options.Password.RequireUppercase = false;
                options.Password.RequiredLength = 4;
            );

            services.AddCors();

            //Jwt Authentication

            services.AddAuthentication(x =>
            
                x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                x.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;                
            ).AddJwtBearer(x =>
            
                x.IncludeErrorDetails = true;
                x.RequireHttpsMetadata = false;
                x.TokenValidationParameters = new TokenValidationParameters
                
                    RequireSignedTokens = true,
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(key),
                    ValidateIssuer = false,
                    ValidateAudience = false,
                    ClockSkew = TimeSpan.Zero
                ;
                x.Events = new JwtBearerEvents
                
                    OnAuthenticationFailed = c =>
                    
                        // break point here
                        return Task.CompletedTask;
                    ,
                ;
            );

            services.AddAuthorization(options =>
            
            options.AddPolicy("ViewClientsPolicy",
                policy => policy.RequireClaim("View Clients"));
            );


            //services.AddControllersWithViews();
            // In production, the Angular files will be served from this directory
            services.AddSpaStaticFiles(configuration =>
            
                configuration.RootPath = "ClientApp/dist";
            );

            services.AddMvc();

            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

            services.AddScoped<IMenuService, MenuService>();


           //Service for identity user.
            services.AddScoped<IEmployeeService, EmployeeService>();
           //Service for identity role.
            services.AddScoped<ITypeService, TypeService>();

            services.AddScoped<IGeneralConfigurationService, GeneralConfigurationService>();
            services.AddScoped<IComponentLocationService, ComponentLocationService>();
            services.AddScoped<INewsService, NewsService>();
            services.AddScoped<INewsCategoryService, NewsCategoryService>();
            services.AddScoped<INewsCategoriesService, NewsCategoriesService>();
            services.AddScoped<IExternalLinksService, ExternalLinksService>();
            services.AddScoped<IClientService, ClientService>();
            services.AddScoped<ISupplierService, SupplierService>();
            services.AddScoped<IContactService, ContactService>();
            services.AddScoped<IUserTypeService, UserTypeService>();
            services.AddScoped<IComponentService, ComponentService>();
            services.AddScoped<IFamilyService, FamilyService>();

            services.AddScoped<IContractMenuService, ContractMenuService>();
            services.AddScoped<IContractService, ContractService>();

            services.AddScoped<IHolidayService, HolidayService>();
            services.AddScoped<IExpeditionService, ExpeditionService>();

            services.AddScoped<IKanbanService, KanbanService>();

        

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        
            if (env.IsDevelopment())
            
                app.UseDeveloperExceptionPage();
            
            else
            
                app.UseExceptionHandler("/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            

            app.UseHttpsRedirection();
            app.UseStaticFiles();
            if (!env.IsDevelopment())
            
                app.UseSpaStaticFiles();
            

            app.UseRouting();

            // global cors policy
            app.UseCors(x => x
                .AllowAnyOrigin()
                .AllowAnyMethod()
                .AllowAnyHeader());


            app.UseAuthentication();
            app.UseAuthorization();
            app.UseEndpoints(endpoints =>
            
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "controller/action=Index/id?");
            );


            app.UseSpa(spa =>
            
                // To learn more about options for serving an Angular SPA from ASP.NET Core,
                // see https://go.microsoft.com/fwlink/?linkid=864501

                spa.Options.SourcePath = "ClientApp";

                if (env.IsDevelopment())
                
                    spa.UseAngularCliServer(npmScript: "start");
                
            );
        
    

这是我的员工的样子

[Table("erp_empleados")]
public class Employee : IdentityUser<ulong>

    [Display(Name = "empName", ResourceType = typeof(SharedResource))]
    [Column("nombre")]
    public string Name  get; set; 

    [Display(Name = "empLastName", ResourceType = typeof(SharedResource))]
    [Column("apellido")]
    public string FamilyName  get; set; 

    [Display(Name = "empEmail", ResourceType = typeof(SharedResource))]
    [Column("correo")]
    public override string Email  get; set; 

   //And many more fields


这是我的用户角色类的样子

[Table("erp_tipo_usuario")]
public class Type : IdentityRole<ulong>



在我的 Angular 应用程序中,我有以下代码来保护我的端点。我不确定如何修改它,所以它需要索赔? 更新我现在明白我不必更改 jwt 令牌中的任何内容,声明/角色将包含在内,这就是它的工作方式。

import  Injectable  from '@angular/core';
import  ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot  from '@angular/router';

@Injectable(
  providedIn: 'root'
)
export class AuthGuard implements CanActivate 

  constructor(private router: Router)  

  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): boolean 
    if (localStorage.getItem('token') != null)
      return true;
    else 
      this.router.navigate(['/user/login']);
      return false;
    
  

也登录在我的应用程序中,我有以下代码。

[HttpPost, Route("login")]
public async Task<IActionResult> Login([FromBody] LoginModel user)

    string passowrdHashed = HashPassword(user.Password);

    var userFromDb = await _userManager.FindByNameAsync(user.UserName);
    if (userFromDb == null) 
    
        return Unauthorized();
    
    else if (user != null && userFromDb.UserName==user.UserName
        && userFromDb.PasswordHash == passowrdHashed)
    
        if (userFromDb.PasswordHash == HashPassword(_employeeService.DEFAULT_PASSWORD))
                            
            return Ok("Please change password!"); 
        
        else
        
           //*********************************************************************  
           //******      How to add Code to add ASP.NET Identity claims???????????????      *******
           //*********************************************************************
            var tokenDescriptor = new SecurityTokenDescriptor
            
                Subject = new ClaimsIdentity(new Claim[] 
                new Claim("UserID", "1")
                new Claim(ClaimTypes.Role, role.NormalizedName),
            ),
                Issuer = "jcortenbach",
                Expires = DateTime.UtcNow.AddHours(1),
                SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes("superlongKeyWithALotOfWordsToMakeItMoreSecureWhichIsGoodThankYouForReadingMySecretKey@45")), SecurityAlgorithms.HmacSha256Signature)
            ;
            var tokenHandler = new JwtSecurityTokenHandler();
            var securityToken = tokenHandler.CreateToken(tokenDescriptor);
            var token = tokenHandler.WriteToken(securityToken);
            return Ok(new  token );
        
    
    else 
    
        return Unauthorized();
    

最后,当谈到我的后端时,我认为它应该看起来像这样。例如获取客户列表代码。

[Route("api/[controller]")]
[ApiController]
public class ClientsController : ControllerBase

    private readonly ILogger<ClientsController> _logger;
    private readonly IClientService _clientService;

    public ClientsController(erp_colombiaDbContext context, ILogger<ClientsController> logger, IClientService clientService)
    
        _logger = logger;
        _clientService = clientService;
    

// GET api/clients
// **Update** I have add [Authorize(Roles = "comercial")] and comment out the     [Authorize(Policy = "ViewClientsPolicy")] and the authentication works for user role based authentication I however would like to have policy based authentication as well.
[HttpGet]
[Authorize(Roles = "comercial")]
[Authorize(Policy = "ViewClientsPolicy")]
public async Task<IEnumerable<ClientViewModel>> GetAsync()

    ClientViewModel clientViewModel;
    List<ClientViewModel> listClientViewModels = new List<ClientViewModel>();

    var clients = await _clientService.GetAllClients();

    foreach (var client in clients) 
    
        clientViewModel = new ClientViewModel();
        clientViewModel.ClientId = client.ClientId;
        clientViewModel.Active = client.Active;
        clientViewModel.Address = client.Address;
        clientViewModel.City = client.City;
        clientViewModel.ClienteName = client.ClienteName;
        clientViewModel.ComercialEmployeeId = client.ComercialEmployeeId;
        clientViewModel.Confirmed = client.Confirmed;
        clientViewModel.CountryId = client.CountryId;
        clientViewModel.CreationDate = client.CreationDate;
        clientViewModel.DANE = client.DANE;
        clientViewModel.Department = client.Department;
        clientViewModel.ElectronicBillingEmail = client.ElectronicBillingEmail;
        clientViewModel.Eliminated = client.Eliminated;
        clientViewModel.NIT = client.NIT;
        clientViewModel.PostalCode = client.PostalCode;
        clientViewModel.Phone = client.Phone;

        listClientViewModels.Add(clientViewModel);
    

    return listClientViewModels;

如果有很多拼图,我不确定下一步该做什么。非常感谢您的帮助。

问题回顾

    如何将 asp.net 核心角色添加到声明/策略中? 如何将声明/策略连接到用户? 完成 要在启动中添加什么内容才能使声明/政策发挥作用? 如何向 jwt cookie 添加声明/策略? 如何在 Angular 中使用 jwt 声明/策略 cookie? DONE在登录控制器功能中添加jwt cookie时会自动发生

【问题讨论】:

【参考方案1】:

如果您想在 Login 方法中添加一些声明到 new Claim[] 数组中的新声明。

更多细节,您可以参考以下代码:

[HttpPost, Route("login")]
public async Task<IActionResult> Login([FromBody] LoginModel user)

    string passowrdHashed = HashPassword(user.Password);

    var userFromDb = await _userManager.FindByNameAsync(user.UserName);
    if (userFromDb == null) 
    
        return Unauthorized();
    
    else if (user != null && userFromDb.UserName==user.UserName
        && userFromDb.PasswordHash == passowrdHashed)
    
        if (userFromDb.PasswordHash == HashPassword(_employeeService.DEFAULT_PASSWORD))
                            
            return Ok("Please change password!"); 
        
        else
        
           //*********************************************************************  
           //******      How to add Code to add claims???????????????      *******
           //*********************************************************************
            var tokenDescriptor = new SecurityTokenDescriptor
            
                Subject = new ClaimsIdentity(new Claim[] 
                //If you want to add cliams you could add as below:
                new Claim("UserName", "test"),
                new Claim("UserID", "1")
            ),
                Issuer = "jcortenbach",
                Expires = DateTime.UtcNow.AddHours(1),
                SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes("superlongKeyWithALotOfWordsToMakeItMoreSecureWichIsGoodThankYouForReadingMySecretKey@45")), SecurityAlgorithms.HmacSha256Signature)
            ;
            var tokenHandler = new JwtSecurityTokenHandler();
            var securityToken = tokenHandler.CreateToken(tokenDescriptor);
            var token = tokenHandler.WriteToken(securityToken);
            return Ok(new  token );
        
    
    else 
    
        return Unauthorized();
    

【讨论】:

部分是的,但是我如何在 Angular 中使用它们以及如何在 baseController 类中使用它们? 您能告诉我您使用它们是什么意思吗?通常情况下,我们会设置声明角色。然后在baskcontroller内部,我们可以使用[Authorize(Roles ="Admin")]属性对其进行授权。此外,关于如何在 Angular 中使用它,我们需要解码 jwt 令牌,然后获取声明并在客户端使用它。对于角度相关的问题。我建议你可以开始一个新的问题来解决它。对于这个问题,我建议你可以谈谈服务器端的问题。 我发现 jwt 令牌如果它与邮递员一起工作,它也适用于 Angular,因为在令牌中它说明了用户拥有的角色和声明。我能够使其适用于角色,但我希望它适用于索赔或保单,我不确定是否希望有所不同。因此管理员类型的用户可以检查用户的声明或策略。我会更新我的问题。

以上是关于Jwt 和 ASP.NET CORE 授权 AspNetRoleClaims的主要内容,如果未能解决你的问题,请参考以下文章

asp.net core 2.0 授权在身份验证(JWT)之前触发

Asp.Net Core Identity 使用 JWT 中的哪些信息来将令牌授权为有效?

为啥我在 Asp.Net CORE 中使用 JWT 获得 401 未经授权?

ASP.NET Core WebApi Jwt 基于角色的授权不起作用

Asp.Net Core 中的多个 JWT 授权机构/发行人

在 ASP .Net Core 2.2 中添加 JWT 令牌后授权不起作用