.net core 2.0 jwt身份認證系統


經歷了很久,.net core 2.0 終於發布了!

之前一直用的core 1.1,升級了2.0后發現認證的機制(Auth)發生了比較大的變化,在1.1中認證配置是在Configure中完成,而在2.0中,認證配置則是在ConfigureServices中完成,剛好對調了一下。

話不多說,直接看代碼

1.ConfigureServices中的認證配置

            var audienceConfig = Configuration.GetSection("Audience");
            var symmetricKeyAsBase64 = audienceConfig["Secret"];
            var keyByteArray = Encoding.ASCII.GetBytes(symmetricKeyAsBase64);
            var signingKey = new SymmetricSecurityKey(keyByteArray);

            var tokenValidationParameters = new TokenValidationParameters
            {

                // The signing key must match!
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = signingKey,

                // Validate the JWT Issuer (iss) claim
                ValidateIssuer = true,
                ValidIssuer = audienceConfig["Issuer"],

                // Validate the JWT Audience (aud) claim
                ValidateAudience = true,
                ValidAudience = audienceConfig["Audience"],

                // Validate the token expiry
                ValidateLifetime = true,

                ClockSkew = TimeSpan.Zero
            };
            services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(o =>
            {
                //不使用https
                //o.RequireHttpsMetadata = false;
                o.TokenValidationParameters = tokenValidationParameters;
            });

貼上appsettings.json的內容

"Audience": {
    "Secret": "Y2F0Y2yhciUyMHdvbmclMFWfsaZlJTIwLm5ldA==",
    "Issuer": "test",
    "Audience": "test"
  }

2. Configure中使用認證,這里我擴展了UseAuthentication的方法。

(1)先定義一個TokenProviderOptions類

public class TokenProviderOptions
    {
        /// <summary>
        /// 請求路徑
        /// </summary>
        public string Path { get; set; } = "/Api/Token";

        public string Issuer { get; set; }

        public string Audience { get; set; }
        /// <summary>
        /// 過期時間
        /// </summary>
        public TimeSpan Expiration { get; set; } = TimeSpan.FromMinutes(5000);

        public SigningCredentials SigningCredentials { get; set; }
    }

(2)定義一個TokenProviderExtensions類擴展UseAuthentication方法

 public static class TokenProviderExtensions
    {
        public static IApplicationBuilder UseAuthentication(this IApplicationBuilder app, TokenProviderOptions options)
        {
            if (app == null)
            {
                throw new ArgumentNullException(nameof(app));
            }
            return app.UseMiddleware<TokenProviderMiddleware>(Options.Create(options));
        }
    }

至於為什么UseAuthentication為什么長這樣的,可以看https://github.com/aspnet/Security/blob/99aa3bd35dd5fbe46a93eef8a2c8ab1f9fe8d05b/src/Microsoft.AspNetCore.Authentication/AuthAppBuilderExtensions.cs源代碼

(3)寫了TokenProviderMiddleware類來代替AuthenticationMiddleware

 public class TokenProviderMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly TokenProviderOptions _options;
        private readonly IUserService _service;
        public TokenProviderMiddleware(
            RequestDelegate next,
            IOptions<TokenProviderOptions> options, IAuthenticationSchemeProvider schemes)
        {
            _next = next;
            _options = options.Value;
            Schemes = schemes;
        }
        public IAuthenticationSchemeProvider Schemes { get; set; }

        /// <summary>
        /// invoke the middleware
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public async Task Invoke(HttpContext context,IUserServcice service)
        {
       _sercice=service;
// context.Features.Set<IAuthenticationFeature>(new AuthenticationFeature { OriginalPath = context.Request.Path, OriginalPathBase = context.Request.PathBase }); //獲取默認Scheme(或者AuthorizeAttribute指定的Scheme)的AuthenticationHandler var handlers = context.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>(); foreach (var scheme in await Schemes.GetRequestHandlerSchemesAsync()) { var handler = await handlers.GetHandlerAsync(context, scheme.Name) as IAuthenticationRequestHandler; if (handler != null && await handler.HandleRequestAsync()) { return; } } var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync(); if (defaultAuthenticate != null) { var result = await context.AuthenticateAsync(defaultAuthenticate.Name); if (result?.Principal != null) { context.User = result.Principal; } } // if (!context.Request.Path.Equals(_options.Path, StringComparison.Ordinal)) { await _next(context); return; } // Request must be POST with Content-Type: application/x-www-form-urlencoded if (!context.Request.Method.Equals("POST") || !context.Request.HasFormContentType) { await ReturnBadRequest(context); return; } await GenerateAuthorizedResult(context); } /// <summary> /// 驗證結果並得到token /// </summary> /// <param name="context"></param> /// <returns></returns> private async Task GenerateAuthorizedResult(HttpContext context) { var username = context.Request.Form["username"]; var password = context.Request.Form["password"]; var identity = await GetIdentity(username, password); if (identity == null) { await ReturnBadRequest(context); return; } // Serialize and return the response context.Response.ContentType = "application/json"; await context.Response.WriteAsync(GetJwt(username)); } /// <summary> /// 驗證用戶 /// </summary> /// <param name="username"></param> /// <param name="password"></param> /// <returns></returns> private Task<ClaimsIdentity> GetIdentity(string username, string password) { var isValidated = _service.Auth(username,password); if (isValidated) { return Task.FromResult(new ClaimsIdentity(new System.Security.Principal.GenericIdentity(username, "Token"), new Claim[] { })); } return Task.FromResult<ClaimsIdentity>(null); } /// <summary> /// return the bad request (200) /// </summary> /// <param name="context"></param> /// <returns></returns> private async Task ReturnBadRequest(HttpContext context) { context.Response.StatusCode = 200; await context.Response.WriteAsync(JsonConvert.SerializeObject(new { Status = false, Message = "認證失敗" })); } /// <summary> /// get the jwt /// </summary> /// <param name="username"></param> /// <returns></returns> private string GetJwt(string username) { var now = DateTime.UtcNow; var claims = new Claim[] { new Claim(JwtRegisteredClaimNames.Sub, username), new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), new Claim(JwtRegisteredClaimNames.Iat, now.ToUniversalTime().ToString(), ClaimValueTypes.Integer64), //用戶名 new Claim(ClaimTypes.Name,username), //角色 new Claim(ClaimTypes.Role,"a") }; var jwt = new JwtSecurityToken( issuer: _options.Issuer, audience: _options.Audience, claims: claims, notBefore: now, expires: now.Add(_options.Expiration), signingCredentials:_options.SigningCredentials ); var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt); var response = new { Status=true, access_token = encodedJwt, expires_in = (int)_options.Expiration.TotalSeconds, token_type = "Bearer" }; return JsonConvert.SerializeObject(response, new JsonSerializerSettings { Formatting = Formatting.Indented }); } }

(4)最后Configure中使用認證

 var audienceConfig = Configuration.GetSection("Audience");
            var symmetricKeyAsBase64 = audienceConfig["Secret"];
            var keyByteArray = Encoding.ASCII.GetBytes(symmetricKeyAsBase64);
            var signingKey = new SymmetricSecurityKey(keyByteArray);
            var SigningCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256);
            app.UseAuthentication(new TokenProviderOptions
            {
                Audience = audienceConfig["Audience"],
                Issuer = audienceConfig["Issuer"],
                SigningCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256)
            });

3.最后的測試

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM