ASP.NET Core JWT 和声明

2023-11-30

我对 ASP.NET Core 和声明中的 JWT 身份验证有疑问,因为我不知道我是否正确理解了所有内容。

当我在 ASP.NET 中创建 JWT 令牌时,我添加了一些声明,其中一些可以自定义。当带有 JWT 令牌的请求从客户端发送到 API 时会发生什么。 User.Claims 如何填写?它是否使用从 JWT 读取的声明?

我想创建一个自定义身份提供程序(不想使用 ASP.NET 提供的),并使用我自己的用户数据、角色等表。我不想将实现策略所需的所有重要数据存储在JWT 令牌(令牌中存储的信息量很重要,也很安全)。是否可以在 JWT 令牌中仅存储基本声明(如用户 ID、姓名等),然后重新获取其他所需的数据 DB/缓存?除此之外,我想使用[授权]的标准机制和策略机制。

如何使这一切发挥作用:自定义用户身份 + JWT + 标准 ASP.NET 基于策略的授权 + 对每个请求从 DB/Cache 获取的声明?如何实现这一目标?


ASP网络核心

第一步是编写配置 Jwt 身份验证的方法:

// Configure authentication with JWT (Json Web Token).
public void ConfigureJwtAuthService(IServiceCollection services)
{
  // Enable the use of an [Authorize(AuthenticationSchemes = 
  // JwtBearerDefaults.AuthenticationScheme)]
  // attribute on methods and classes to protect.
  services.AddAuthentication().AddJwtBearer(cfg =>
  {
    cfg.RequireHttpsMetadata = false;
    cfg.SaveToken = true;
    cfg.TokenValidationParameters = new TokenValidationParameters()
    {
      IssuerSigningKey = JwtController.SecurityKey,
      ValidAudience = JwtController.Audience,
      ValidIssuer = JwtController.Issuer,
      // When receiving a token, check that we've signed it.
      ValidateIssuerSigningKey = true,
      // When receiving a token, check that it is still valid.
      ValidateLifetime = true,
      // This defines the maximum allowable clock skew when validating 
      // the lifetime. As we're creating the tokens locally and validating
      // them on the same machines which should have synchronised time,
      // this can be set to zero.
      ClockSkew = TimeSpan.FromMinutes(0)
    };
  });
}

现在里面配置服务()的方法启动.cs,我们可以调用配置JwtAuthService()配置 Jwt 身份验证的方法。

这是完整的启动.cs:

using System;
using Autofac;
using ExpertCodeBlogWebApp.Controllers;
using ExpertCodeBlogWebApp.Domain;
using ExpertCodeBlogWebApp.Domain.Interfaces;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.SpaServices.Webpack;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

using Microsoft.IdentityModel.Tokens;

namespace ExpertCodeBlogWebApp
{
  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 IServiceProvider ConfigureServices(IServiceCollection services)
  {
    services.AddMvc();

    // Configure jwt autenticazione 
    ConfigureJwtAuthService(services);

    // Repositories
    services.AddScoped<IUserRepository, UserRepository>();

    // Create the Autofac container builder for dependency injection
    var builder = new ContainerBuilder();

    // Add any Autofac modules or registrations. 
    builder.RegisterModule(new AutofacModule());

    // Return ServiceProvider
    var serviceProvider = services.BuildServiceProvider();
    return serviceProvider;
  }

  // Configure authentication with JWT (Json Web Token).
  public void ConfigureJwtAuthService(IServiceCollection services)
  {
    // Enable the use of an [Authorize(AuthenticationSchemes = 
    // JwtBearerDefaults.AuthenticationScheme)]
    // attribute on methods and classes to protect.
    services.AddAuthentication().AddJwtBearer(cfg =>
    {
      cfg.RequireHttpsMetadata = false;
      cfg.SaveToken = true;

      cfg.TokenValidationParameters = new TokenValidationParameters()
      {
        IssuerSigningKey = JwtController.SecurityKey,
        ValidAudience = JwtController.Audience,
        ValidIssuer = JwtController.Issuer,
        // When receiving a token, check that we've signed it.
        ValidateIssuerSigningKey = true,
        // When receiving a token, check that it is still valid.
        ValidateLifetime = true,
        // This defines the maximum allowable clock skew when validating 
        // the lifetime.
        // As we're creating the tokens locally and validating them on the 
        // same machines which should have synchronised time, this can be 
        // set to zero.
        ClockSkew = TimeSpan.FromMinutes(0)
      };
    });
  }

  // This method gets called by the runtime. Use this method to configure 
  // the HTTP request pipeline.
  public void Configure(IApplicationBuilder app, IHostingEnvironment env)
  {
    if (env.IsDevelopment())
    {
      app.UseDeveloperExceptionPage();
      app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions
      {
        HotModuleReplacement = true
      });
    }
    else
    {
      app.UseExceptionHandler("/Home/Error");
    }

    app.UseStaticFiles();

    app.UseMvc(routes =>
    {
      routes.MapRoute(
      name: "default",
      template: "{controller=Home}/{action=Index}/{id?}");

      routes.MapSpaFallbackRoute(
        name: "spa-fallback",
        defaults: new { controller = "Home", action = "Index" });
      });
    }
  }

  // For dependency injection.
  public class AutofacModule : Module
  {
    // Dependency Injection with Autofact
    protected override void Load(ContainerBuilder builder)
    {
      builder.RegisterType<UserRepository>().As<IUserRepository>()
        .SingleInstance();
    }
  }
}

The JwtController.cs

using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Principal;
using System.Text;
using System.Threading.Tasks;
using AutoMapper;
using ExpertCodeBlogWebApp.Domain;
using ExpertCodeBlogWebApp.Domain.Interfaces;
using ExpertCodeBlogWebApp.Domain.Models;
using ExpertCodeBlogWebApp.ViewModels;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;

namespace ExpertCodeBlogWebApp.Controllers
{

[Route("api/[controller]")]
public class JwtController : Controller
{
  #region Private Members
  // JWT-related members
  private TimeSpan TokenExpiration;
  private SigningCredentials SigningCredentials;
  // EF and Identity members, available through DI
  private MyDbContext DbContext;
  private IUserRepository _userRepository;
  private readonly ILogger _logger;
  #endregion Private Members

  #region Static Members
  private static readonly string PrivateKey = "my_PrivateKey";
  public static readonly SymmetricSecurityKey SecurityKey = 
    new SymmetricSecurityKey(Encoding.ASCII.GetBytes(PrivateKey));
  public static readonly string Issuer = "my_Issuer";
  public static readonly string Audience = "my_Audience";
  #endregion Static Members

  #region Constructor
  // I have used Autofac in the Startup.cs for dependency injection)
  public JwtController(
    MyDbContext dbContext,
    IUserRepository userRepository,
    ILogger<JwtController> logger)
  {
    _logger = logger;
    _userRepository = userRepository;
    // Instantiate JWT-related members
    TokenExpiration = TimeSpan.FromMinutes(10);
    SigningCredentials = new SigningCredentials(SecurityKey, 
      SecurityAlgorithms.HmacSha256);
    // Instantiate through Dependency Injection with Autofact
    DbContext = dbContext;
  }
  #endregion Constructor

  #region Public Methods 
  // Manages the request for a new authentication or the refresh of an 
  // already established one
  [HttpPost("token")]
  public async Task<IActionResult> 
    Authentication([FromBody]JwtRequestViewModel jwt)
  {
    if (ModelState.IsValid)
    {
      string grantType = jwt.GrantType; 
      if (grantType == "password")
      {
        string userName = jwt.UserName;
        string password = jwt.Password;

        // Password check required
        var user = await 
          _userRepository.GetUserInfoWithCheckPwd(userName, password);

        // Check if user is expired (check the ExpireDate property)
        if (UserExpired(user))
          return BadRequest($"Account of {user.Name} expired!");

        if (UserEnabled(user))
          return await GenerateToken(user);
        else
          return BadRequest("User name or password invalid.");
      }
    }
    else if (grantType == "refresh_token")
    {
      string userName = jwt.UserName;

      // Refresh token (no password check required)
      var user = await _userRepository.GetUserInfoByName(userName);

      // Check if user is expired (check the ExpireDate property)
      if (UserExpired(user))
        return BadRequest($"Account of {user.Name} expired!");

      string token = jwt.Token;
      if (token == user.Token)
      {
        // Generate token and send it via a json-formatted string
        return await GenerateToken(user);
      }
      else
      {
        return BadRequest("User token invalid.");
      }
    }
    else
      return BadRequest("Authentication type invalid.");
  }
  else
    return BadRequest("Request invalid.");
  }
  #endregion Public Methods

  #region Private Methods
  private bool UserExpired(Users utente)
  {
    if (utente != null)
      return utente.ExpireDate.CompareTo(DateTime.Now) < 0;
    return true;
  }

  private bool UserEnabled(Users utente)
  {
    if (utente != null)
      return utente.Enabled == true;
    return false;
  }

  private JsonSerializerSettings DefaultJsonSettings
  {
    get
    {
      return new JsonSerializerSettings()
      {
        Formatting = Formatting.Indented
      };
    }
  }

  private async Task<IActionResult> GenerateToken(Users user)
  {
    try
    {
      if (user != null)
      {
        var handler = new JwtSecurityTokenHandler();
        DateTime newTokenExpiration = DateTime.Now.Add(TokenExpiration);

        ClaimsIdentity identity = new ClaimsIdentity(
          new GenericIdentity(user.Name, "TokenAuth"),
          new[] { new Claim("ID", user.Id.ToString())}
        );

        var securityToken = handler.CreateToken(new SecurityTokenDescriptor
        {
          Issuer = JwtController.Issuer,
          Audience = JwtController.Audience,
          SigningCredentials = SigningCredentials,
          Subject = identity,
          Expires = newTokenExpiration
        });
        string encodedToken = handler.WriteToken(securityToken);

        // Update token data on database
        await _userRepository.UpdateTokenData(user.Name, encodedToken, 
          newTokenExpiration);
        // Build the json response 
        // (I use Automapper to maps an object into another object)
        var jwtResponse = Mapper.Map<JwtResponseViewModel>(user);
        jwtResponse.AccessToken = encodedToken;
        jwtResponse.Expiration = (int)TokenExpiration.TotalSeconds;
        return Ok(jwtResponse);
      }
      return NotFound();
      }
      catch(Exception e)
      {
        return BadRequest(e.Message);
      }
    }
    #endregion
  }
}

在我的项目中我使用 Angular。通过 Angular 调用 JwtController 方法:

login(userName: string, password: string)
{
  return this.getLoginEndpoint(userName, password)
    .map((response: Response) => this.processLoginResponse(response));
}

getLoginEndpoint(userName: string, password: string): Observable<Response> 
{
  // Body
  // JwtRequest is a model class that I use to send info to the controller
  let jwt = new JwtRequest(); 
  jwt.GrantType = "password";
  jwt.UserName = userName;
  jwt.Password = password;
  jwt.ClientId = "my_Issuer";
  // Post requiest (I use getAuthHeader that attach to the header the
  // authentication token, but it can also be omitted because it is ignored
  // by the JwtController
  return this.http.post(this.loginUrl, JSON.stringify(jwt), 
    this.getAuthHeader(true))
}

protected getAuthHeader(includeJsonContentType?: boolean): RequestOptions
{
  // Hera I use this.authService.accessToken  that is a my service where
  // I have store the token received from the server
  let headers = new Headers({
    'Authorization': 'Bearer ' + this.authService.accessToken });

  if (includeJsonContentType)
    headers.append("Content-Type", "application/json");

  headers.append("Accept", `application/vnd.iman.v01+json, 
    application/json, text/plain, */*`);
  headers.append("App-Version", "01");

  return new RequestOptions({ headers: headers });
}

private processLoginResponse(response: Response)
{
  // process the response..
}

在您希望仅由经过身份验证的用户访问的控制器类(或方法)上(不在您的 JwtController 上,因为它的方法必须由所有用户访问),您可以设置:

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]

要从 Angular 调用需要身份验证的控制器方法,您需要将令牌附加到标头中获取验证头() method.

我希望这篇文章可以帮助你。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ASP.NET Core JWT 和声明 的相关文章

随机推荐

  • PHP 日期“最后一个工作日”输出错误的月份

    我正在尝试获取last weekday使用下面的代码显示当前月份的数据 我期望它的行为就像所有其他运算符一样last friday of并输出本月最后一个工作日 而是输出上个月的最后一个工作日 我知道我可以通过使用来纠正这个问题modify
  • 使用线程执行shell命令c++

    是否可以通过创建线程来执行 shell 命令 即不使用 popen system 或任何 fork exec 变体 为什么或者为什么不 为了提供更多上下文 我正在运行一个多线程程序 其中一个线程需要执行 shell 脚本 目前它使用 pop
  • CDataColumn 的“值”内的条件语句

    有谁知道如何在 CGridView 列字段中显示条件值 我在数据库中有一个性别 M 或 F 的条目 在 CGridView 中 但是我想分别显示 男 和 女 而不是 M 或 F 欢迎任何想法 我发现虽然不允许使用完整的 if else 语法
  • 在 MVC3 中将 ListBox 与模型绑定

    我的模型是 public class SiteConfig public SiteConfig public int IdSiteConfig get set public string Name get set public byte S
  • 在审核表单中添加自定义字段

    我期待在审核表单中创建自定义字段 电子邮件 ID 和一个下拉列表 我已经尝试过这个 但没有保存数据 仅保存字段 应用程序 代码 核心 法师 评论 模型 Mysql4 Review php protected function afterSa
  • 如何使用与用户定义函数同名的 MATLAB 工具箱函数

    我遇到了问题findpeaks函数 该函数位于信号处理工具箱中 并且该程序还有它的另一个版本 用户定义函数 我需要调用信号处理工具箱中的 on 而不是用户定义的函数 而且由于多种原因我无法重命名用户定义的函数 谁能帮我调用工具箱函数 MAT
  • 解析正数和负数的字符串,Javascript

    所以我正在研究 d3 中制作的标签云示例 http www jasondavies com wordcloud http 3A 2F 2Fsearch twitter com 2Fsearch json 3Frpp 3D100 26q 3D
  • 使用 jQuery 动态创建链接

    我正在尝试使用 jQuery 在 blockquote 元素中创建一个链接 我现在正处于这个阶段 var pullQuote span pull quote each function var this this hrefLink http
  • 如何找到 Redshift 中访问次数最多的表?

    我们正在将实时数据传输到 Redshift 瓶颈是没有可以并发运行的表加载 目前 我们每 15 分钟运行超过 1000 个负载 但我们希望根据用户使用这些表的频率来减少这个数字 请建议我们如何在 Redshift 中获取此信息 This v
  • 获取奇数长度回文

    我试图找到最长的奇数长度回文 但我编写的代码没有给我完整的回文 只是其中的一部分 任何帮助都会很棒 def get odd palindrome at s index str int gt str gt get odd palindrome
  • 使用 firestore 安全规则中的替换功能

    我正在努力应对 Firestore 安全规则 我想检查需要替换功能的值 即电子邮件地址 我可以在一般安全文档中找到一些文档 但这似乎不适用于 Firestore 例如这有效 allow write if resource data memb
  • 如何获取/跟踪 ASP.NET 传出响应文本

    我的服务器似乎有时会向网络客户端返回错误的 html 我在 VS 2012 中使用 asp net 4 在 IIS Express 上进行调试 为了调试这个问题 我想跟踪asp net发送的html in the Global asax P
  • 如何从回历日期转换为公历日期,反之亦然

    我正在寻找一种方法来计算例如从现在到特定日期有多少天 并且我可以用它来确定我是否处于特定时间段 例如 Muharram 5 天 我已经找了 10 多个小时了 我发现的最好的东西是 HijrahDate 库 java time chrono
  • 更新 conda 后如何从终端提示符中删除 (base)

    更新 miniconda3 后 每当我打开终端时 它都会在我的用户名和主机前面显示 base 在这个回答帖子里https askubuntu com a 1113206 315699建议使用 conda config set changep
  • 在 Python 中格式化驱动器

    我正在尝试制作一个 Python 3 3 程序来格式化 Windows 中的驱动器 例如 SD 卡或 USB 记忆棒 我在这里尝试了代码 http bytes com topic python answers 537724 formatti
  • 从 Silverlight 4 调用 HTTPS-WCF 服务时出现安全错误

    我创建了一个简单的 WCF 服务 我希望可以通过 https 访问它 WCF 服务使用 UserNamePasswordValidator customBinding 和 UserNameOverTransport 作为身份验证模式 我在
  • IPython模块

    我有一些具有冗余功能的 IPython 脚本 我想将常见功能重构为一个模块 并将该模块包含在现有脚本中 问题是它不能成为 python 模块 因为代码使用 Ipython 的语言扩展 等 是否可以制作一个具有 IPython 代码的模块并将
  • 如何使用 django-mptt 创建嵌套评论系统

    我正在开发一个简单的博客 并尝试为每个帖子实现一个嵌套评论系统 我创建了评论模型 它通过 Django 管理页面运行良好 我不知道如何创建用于发布新评论和回复的表单 这是我到目前为止所拥有的 模型 py class Post models
  • Flutter Firebase.signInWithGoogle 方法未找到

    我正在尝试在我的 Flutter 应用程序中添加 Google 身份验证 但是我的Android studio找不到该方法登录Google under Firebase身份验证班级 我的意思是说当我写 FirebaseAuth 时 登录Go
  • ASP.NET Core JWT 和声明

    我对 ASP NET Core 和声明中的 JWT 身份验证有疑问 因为我不知道我是否正确理解了所有内容 当我在 ASP NET 中创建 JWT 令牌时 我添加了一些声明 其中一些可以自定义 当带有 JWT 令牌的请求从客户端发送到 API