JWT身份验证教程

  1. 通过 NuGet 安装 Microsoft.AspNetCore.Authentication.JwtBearer

  1. 打开 Startup.cs 在 ConfigureServices 添加如下代码

    services.AddScoped<IUserService, UserService>();
    
    //将appsettings.json中的jwtsettings部分读取到jwtsettings中,给其他地方用
    services.Configure<JwtSettings>(config.GetSection("JwtSettings"));
    //由于初始化的时候我们就需要用,所有用bind方法读取配置
    var jwtSettings = new JwtSettings();
    config.Bind("JwtSettings", jwtSettings);
    
    //添加身份验证
    services.AddAuthentication(option =>
    {
        //认证middleware配置
        option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        option.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    }).AddJwtBearer(o =>
    {
        //jwt token参数设置
        o.TokenValidationParameters = new TokenValidationParameters
        {
            NameClaimType = JwtClaimTypes.Name,
            RoleClaimType = JwtClaimTypes.Role,
            //token颁发机构
            ValidIssuer = jwtSettings.Issuer,
            //颁发给谁
            ValidAudience = jwtSettings.Audience,
            //这里的key要加密
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.SecretKey))
        };
    });
  2. Configure 配置认证中间件

    app.UseAuthentication();//认证中间件
  3. 创建登录用实体类,也可以直接使用 Identity Server 的,这里图个方便

    public class LoginInput
    {
    
        public string Username { get; set; }
    
        public string Password { get; set; }
    }
  4. 创建一个UserService类用于验证用户凭据和返回JWT令牌

    文件的顶部包含一个定义用户服务的接口,下面是实现该接口的具体用户服务类。

    身份验证成功后,该Authenticate()方法将使用JwtSecurityTokenHandler该类生成JWT(JSON Web令牌),该类将生成使用存储在appsettings.json中的密钥进行数字签名的令牌。

    标准中注册的声明 (建议但不强制使用) :

    • iss: jwt签发者
    • sub: jwt所面向的用户
    • aud: 接收jwt的一方
    • exp: jwt的过期时间,这个过期时间必须要大于签发时间
    • nbf: 定义在什么时间之前,该jwt都是不可用的.
    • iat: jwt的签发时间
    • jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
    public interface IUserService
       {
           Task<AuthenticateResponse> Authenticate(LoginDto model);
       }
       
       public class UserService : IUserService
       {
           private readonly UserManager<IdentityUser> _userManager;
       
           public JwtSettings _JwtSettings { get; }
       
           public UserService(UserManager<IdentityUser> userManager, IOptions<JwtSettings> jwtSettings)
           {
               this._userManager = userManager;
               this._JwtSettings = jwtSettings.Value;
           }
       
           /// <summary>
           /// 进行身份验证
           /// </summary>
           /// <param name="model">LoginDto模型</param>
           /// <returns>AuthenticateResponse</returns>
           public async Task<AuthenticateResponse> Authenticate(LoginDto model)
           {
               var user = await _userManager.FindByNameAsync(model.UserName);
       
               // 没用用户或密码错误返回null
               if (user == null && !await _userManager.CheckPasswordAsync(user, model.Password)) return null;
       
               // 验证成功 生成jwt令牌
               var token = generateJwtToken(user);
       
               return new AuthenticateResponse(user, token);
           }
       
           //获取Jwt Token
           private string generateJwtToken(IdentityUser user)
           {
               var tokenHandler = new JwtSecurityTokenHandler();
               var tokenDescriptor = new SecurityTokenDescriptor
               {
                   Subject = new ClaimsIdentity(new[] {
                       new Claim(JwtClaimTypes.Audience, _JwtSettings.Audience),
                       new Claim(JwtClaimTypes.Issuer, _JwtSettings.Issuer),
                       new Claim(JwtClaimTypes.Name, user.UserName),
                       new Claim("id", user.Id.ToString())
                   }),
                   Expires = DateTime.UtcNow.AddDays(1),//有效日期
                   SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_JwtSettings.SecretKey)), SecurityAlgorithms.HmacSha256Signature)
               };
               var token = tokenHandler.CreateToken(tokenDescriptor);
               return tokenHandler.WriteToken(token);
           }
       }
  5. 添加登录控制器

    [Route("api/[controller]")]
    [ApiController]
    public class UserController : ControllerBase
    {
        private readonly IUserService userService;
       
        public UserController(IUserService userService)
        {
            this.userService = userService;
        }
        
        /// <summary> 
        /// 实现用户登录 post: /api/user/Login/model
        /// </summary>
        /// <param name="model">LoginInput</param>
        /// <returns></returns>
        [HttpPost]
        [Route("Login")]
        [AllowAnonymous]
        public async Task<IActionResult> Login(LoginInput model)
        {
            var response = await userService.Authenticate(model);
       
            if (response == null)
                return BadRequest(new { message = "用户名或密码错误" });
       
            return Ok(response);
        }
    }
  6. appsettings.json

    "JwtSettings": {
      "Issuer": "https://localhost:44336",
      "Audience": "https://localhost:44336",
      "SecretKey": "Hello-key----------"//注意 必须达到16位
    }