【问题标题】:Jwt and ASP.NET CORE Authorization AspNetRoleClaimsJwt 和 ASP.NET CORE 授权 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;
}

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

问题回顾

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

【问题讨论】:

    标签: angular asp.net-core jwt


    【解决方案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,因为在令牌中它说明了用户拥有的角色和声明。我能够使其适用于角色,但我希望它适用于索赔或保单,我不确定是否希望有所不同。因此管理员类型的用户可以检查用户的声明或策略。我会更新我的问题。
    猜你喜欢
    • 1970-01-01
    • 2018-08-19
    • 1970-01-01
    • 2019-03-18
    • 2016-12-11
    • 2020-08-08
    • 2020-07-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多