ASP.NET Core - JWT認證實現

chaney1992發表於2021-01-17

一、JWT結構

 JWT介紹就太多了,這裡主要關注下Jwt的結構。 

 Jwt中包含三個部分:Header(頭部).Payload(負載).Signature(簽名)

  • Header:描述 JWT 的後設資料的JSON物件,如:

    {"alg":"HS256","typ":"JWT"}
  • Payload:一個用來存放實際需要傳遞的資料的JSON 物件。如:

    "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": "admin",
        "exp": 1610877510,
        "iss": "cba",
        "aud": "cba"
    }
  • Signature:對前兩部分(Header、Payload)的簽名,防止資料篡改。
    HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

  JWT示例:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1lIjoiYWRtaW4iLCJleHAiOjE2MTA4Nzc1MTAsImlzcyI6ImNiYSIsImF1ZCI6ImNiYSJ9.O9lbZwfqRuA6vKcRCfYieA1zLkTPppdSvTc8UzwCkNw

二、ASP.NET Core 使用JTW認證

 1、新增Nuget包引用:

dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer

 2、定義一個JwtSettingDto結構,用於讀取JWT配置資訊

public class JwtSetting
{
    /// <summary>
    /// 發行者
    /// </summary>
    public string Issuer { get; set; }
    /// <summary>
    /// 受眾
    /// </summary>
    public string Audience { get; set; }
    /// <summary>
    /// 祕鑰
    /// </summary>
    public string SecretKey { get; set; }
    /// <summary>
    /// 過期時間
    /// </summary>
    public int AccessExpiration { get; set; }
    /// <summary>
    /// 重新整理時間
    /// </summary>
    public int RefreshExpiration { get; set; }
}

 3、在appsetting.json配置檔案中,新增jwt相關配置資訊

  "JWTSetting": {
    "Issuer": "cba",
    "Audience": "cba",
    "SecretKey": "123456789abcdefghi",
    "AccessExpiration": 60,
    "RefreshExpiration": 80
  }

 4、在Startup.cs 中啟用Jwt認證

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<JwtSetting>(Configuration.GetSection("JWTSetting"));
    var token = Configuration.GetSection("JWTSetting").Get<JwtSetting>();
    //JWT認證
    services.AddAuthentication(x =>
    {
        x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    }).AddJwtBearer(x =>
    {
        x.RequireHttpsMetadata = false;
        x.SaveToken = true;
        x.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(token.SecretKey)),
            ValidIssuer = token.Issuer,
            ValidAudience = token.Audience,
            ValidateIssuer = false,
            ValidateAudience = false
        };
    });
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseAuthentication();
    app.UseAuthorization();
}

 5、實現認證控制器:

  a) 新增認證Dto

public class LoginDto
{
    [Required]
    public string Username { get; set; }

    [Required]
    public string Password { get; set; }
}

  b) 實現使用者校驗服務:預設實現:當使用者密碼都等於admin通過校驗  

public interface IUserService
{
    bool IsValid(LoginDto request);
}

public class UserService : IUserService
{
    //本次,固定校驗admin
    public bool IsValid(LoginDto request)
    {
        return request.Password == request.Username && request.Username == "admin";
    }
}

  c) 實現簽發token的認證服務:  

//認證服務介面
public
interface IAuthenticateService { bool IsAuthenticated(LoginDto request, out string token); } //認證服務 public class TokenAuthenticationService : IAuthenticateService { private readonly IUserService _userService; private readonly JwtSetting _jwtSetting; public TokenAuthenticationService(IUserService userService, IOptions<JwtSetting> jwtSetting) { _userService = userService; _jwtSetting = jwtSetting.Value; } public bool IsAuthenticated(LoginDto request, out string token) { token = string.Empty; if (!_userService.IsValid(request)) return false; var claims = new[] { new Claim(ClaimTypes.Name, request.Username) }; var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSetting.SecretKey)); var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var jwtToken = new JwtSecurityToken(_jwtSetting.Issuer, _jwtSetting.Audience, claims, expires: DateTime.Now.AddMinutes(_jwtSetting.AccessExpiration), signingCredentials: credentials); token = new JwtSecurityTokenHandler().WriteToken(jwtToken); return true; } }

  d) 新增認證控制器

[Route("api/[controller]")]
[ApiController]
public class AuthenticationController : ControllerBase
{
    private IAuthenticateService _authService;

    public AuthenticationController(IAuthenticateService authService)
    {
        _authService = authService;
    }
    [AllowAnonymous]
    [HttpPost, Route("RequestToken")]
    public ActionResult RequestToken([FromBody] LoginDto request)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest("Invalid Request");
        }
        string token;
        if (_authService.IsAuthenticated(request, out token))
        {
            return Ok(token);
        }
        return BadRequest("Invalid Request");
    }
}

 6、注入服務:TokenAuthenticationService 、UserService 

services.AddScoped<IUserService, UserService>();
services.AddScoped<IAuthenticateService, TokenAuthenticationService>();

 7、新增測試控制器:

[Authorize]
[Route("api/[controller]")]
[ApiController]
public class AuditLogController : ControllerBase
{
    // GET: api/<AuditLogController>
    [HttpGet]
    public IEnumerable<string> Get()
    {
        return new string[] { "value1", "value2" };
    }
}

     到此Jwt認證在.net core中已經實現,接下來驗證下執行效果

三、驗證結果

 1、請求AuditLog介面:api/AuditLog未傳入認證資訊時:

  

  2、獲取Token:

  

 3、新增token呼叫介面:

  

四、Swagger UI新增認證

  在專案中通常都新增了Swagger UI來展示介面及基礎測試,那麼如果新增了認證後,如何在呼叫介面前新增認證資訊呢?

  在Startup中:ConfigureServices中新增Swagger設定時,新增認證設定

//註冊Swagger生成器,定義一個和多個Swagger 文件
services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "AuditLogDemo API", Version = "v1" });
    #region 啟用swagger驗證功能
    //新增一個必須的全域性安全資訊,和AddSecurityDefinition方法指定的方案名稱一致即可。
    c.AddSecurityRequirement(new OpenApiSecurityRequirement
    {
        {
            new OpenApiSecurityScheme
            {
                Reference = new OpenApiReference {
                    Type = ReferenceType.SecurityScheme,
                    Id = "Bearer"
                }
            },
            new string[] { }
        }
    });
    c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
    {
        Description = "JWT授權(資料將在請求頭中進行傳輸) 在下方輸入Bearer {token} 即可,注意兩者之間有空格",
        Name = "Authorization",//jwt預設的引數名稱
        In = ParameterLocation.Header,//jwt預設存放Authorization資訊的位置(請求頭中)
        Type = SecuritySchemeType.ApiKey,
        BearerFormat = "JWT",
        Scheme = "Bearer",              
                    
    });
    #endregion
});

  執行效果: 新增token->呼叫需認證介面

   

       

其他:

  原始碼地址:https://github.com/cwsheng/AuditLogDemo.git

相關文章