首页 文章

ASP.NET核心JWT和声明

提问于
浏览
1

我有一个关于ASP.NET核心和声明中的JWT身份验证的问题,因为我不知道我是否正确地获取了所有内容 .

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

我想创建一个自定义身份提供者(不想使用ASP.NET提供的这个),我自己的表用于用户数据,角色等 . 我不想存储完成策略所需的所有重要数据JWT令牌(存储在令牌事务中的信息量以及安全事项) . 是否可以在JWT令牌中仅存储基本声明(如用户ID,名称等),然后重新获取其他所需的数据DB / Cache?与此同时,我想使用[授权]和政策机制的标准机制 .

如何使这一切工作:自定义用户身份JWT标准基于ASP.NET策略的授权声明在每次请求时从DB / Cache获取?怎么做到这一点?

2 回答

  • 2

    Asp Net Core

    第一步是编写配置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)
        };
      });
    }
    

    现在在 Startup.csConfigureServices() 方法内,我们可以调用 ConfigureJwtAuthService() 方法来配置Jwt身份验证 .

    这是完整的 Startup.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();
        }
      }
    }
    

    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调用需要身份验证的控制器方法,您需要使用 getAuthHeader() 方法将令牌附加到标头中 .

    我希望这篇文章可以帮到你 .

  • 0

    是的,它使用存储在jwt令牌中的claism查看httpcontext对象,查看创建令牌时存储在令牌中的声明

    这个链接也有帮助https://joonasw.net/view/adding-custom-claims-aspnet-core-2

相关问题