IdentityServer4 资源服务器 Token 验证大全(Jwt 和 Reference )

强制实施 HTTPS 在 ASP.NET Core 
System.IdentityModel.Tokens.Jwt

JWT(JSON Web Token) 结构
JWT由3部分组成:标头(Header)、有效载荷(Payload)和签名(Signature)。在传输的时候,会将JWT的3部分分别进行Base64编码后用.进行连接形成最终传输的字符串

标头(Header)
JWT头是一个描述JWT元数据的JSON对象,alg属性表示签名使用的算法,默认为HMAC SHA256(写为HS256);typ属性表示令牌的类型,JWT令牌统一写为JWT。最后,使用Base64 URL算法将上述JSON对象转换为字符串保存。
*

{
  "alg": "HS256",
  "type": "JWT"
}

*
*
有效载荷(Payload)
是JWT的主体内容部分,是一个JSON对象,包含需要传递的数据。 JWT指定七个默认字段供选择

iss:发行人
exp:到期时间
sub:主题
aud:用户
nbf:在此之前不可用
iat:发布时间
jti:JWT ID用于标识该JWT

*
签名(Signature)
签名哈希部分是对上面两部分数据签名,需要使用base64编码后的header和payload数据,通过指定的算法生成哈希,以确保数据不会被篡改。

{
  "sub": "123456",
  "name": "admin",
  "admin": true
}

*
管理 NuGet 程序包(N)...

Microsoft.AspNetCore.Authentication.JwtBearer

JwtSecurityToken:
JwtSecurityTokenHandler:创建、校验token,返回ClaimsPrincipal
      CanReadToken():确定字符串是否是格式良好的Json Web令牌(JWT)
      ReadJwtToken(string token):token字符串转为JwtSecurityToken对象
      ValidateToken(string token、TokenValidationParameters parameter,out SecurityToken validatedToken):校验token,返回ClaimsIdentity
TokenValidationParameters:
SecurityKey:

本文章介绍的是资源服务器(api接口)验证 Token 是否合法

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Reflection;
using System.IO;
using AutoMapper;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Microsoft.AspNetCore.Mvc;
using IdentityModel;
using log4net;
using log4net.Config;
using log4net.Repository;
using System.Net.Mime;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc.Versioning;
using Microsoft.IdentityModel.Tokens;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using System.Threading.Tasks;
using System.Text;
using IdentityServer4.AccessTokenValidation;
using IdentityModel.AspNetCore.OAuth2Introspection;
using IdentityModel.Client;
using Microsoft.AspNetCore.Http;

namespace MobileNurse.WebAPI
{
    public class Startup
    {
        public Startup(IConfiguration configuration, IWebHostEnvironment env)
        {
            Configuration = configuration;

            var builder = new ConfigurationBuilder()
               .SetBasePath(env.ContentRootPath)
               .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
               .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
               .AddEnvironmentVariables();
            Configuration = builder.Build();
        }

        public IConfiguration Configuration { get; }
        public static ILoggerRepository loggerRepository { get; set; }

        readonly string AllowSpecificOrigins = "AllowSpecificOrigins";

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            string dbType = Configuration["DbSql:DbType"];
            switch (dbType)
            {
                case "SqlServer":
                    //services.AddSingleton(Configuration.GetConnectionString("DefaultConnection"));
                    services.AddSingleton(Configuration["DbSql:SqlServerConnection"]);
                    break;
                case "Oracle":
                    services.AddSingleton(Configuration["DbSql:OracleConnection"]);
                    break;
                case "MySql":
                    services.AddSingleton(Configuration["DbSql:MySqlConnection"]);
                    break;
                default:
                    break;
            }
            services.AddSingleton<IDbConnection, SqlConnection>();

            #region 认证    第 1 步
            services.AddAuthentication(config =>
            {
                config.DefaultScheme = "Bearer";
            })
            .AddIdentityServerAuthentication(options =>
            {
                options.Authority = Configuration["IdentityAuthentication:Authority"];
                options.ApiName = Configuration["IdentityAuthentication:ApiName"];
                options.ApiSecret = Configuration["IdentityAuthentication:ApiSecret"];
                options.RequireHttpsMetadata = true;
                options.JwtBearerEvents = new JwtBearerEvents
                {
                    OnAuthenticationFailed = context =>
                    {
                        //Token expired
                        if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                            context.Response.Headers.Add("Token-Expired", "true");

                        context.Response.ContentType = "application/json";
                        context.Response.WriteAsync(JsonConvert.SerializeObject(new { code = "401", msg = "Unauthorized" }));
                        return Task.CompletedTask;
                    }
                };
                options.OAuth2IntrospectionEvents = new OAuth2IntrospectionEvents
                {
                    OnAuthenticationFailed = context =>
                    {
                        //Token expired
                        if (context.Error.GetType() == typeof(SecurityTokenExpiredException))
                            context.Response.Headers.Add("Token-Expired", "true");

                        context.Response.ContentType = "application/json";
                        context.Response.WriteAsync(JsonConvert.SerializeObject(new { code = "401", msg = "Unauthorized" }));
                        return Task.CompletedTask;
                    }
                };
                //options.EnableCaching = true;
                //options.CacheDuration = TimeSpan.FromMinutes(10);
            });

            #region 经测试可以使用(OK)    第 1 步
            services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
                .AddIdentityServerAuthentication(options =>
                {
                    options.Authority = Configuration["IdentityAuthentication:Authority"];
                    options.ApiName = Configuration["IdentityAuthentication:ApiName"];
                    options.ApiSecret = Configuration["IdentityAuthentication:ApiSecret"];
                    options.RequireHttpsMetadata = true;
                    options.JwtBearerEvents = new JwtBearerEvents { };
                    options.OAuth2IntrospectionEvents = new OAuth2IntrospectionEvents { };
                    //options.EnableCaching = true;
                    //options.CacheDuration = TimeSpan.FromMinutes(10);
                });

            services.AddAuthentication(OAuth2IntrospectionDefaults.AuthenticationScheme)
            .AddOAuth2Introspection(options =>
            {
                options.Authority = Configuration["IdentityAuthentication:Authority"];
                options.ClientId = Configuration["IdentityAuthentication:ApiName"];
                options.ClientSecret = Configuration["IdentityAuthentication:ApiSecret"];
                //options.EnableCaching = true;
                //options.CacheDuration = TimeSpan.FromMinutes(10);
                options.Events = new OAuth2IntrospectionEvents
                {
                    OnAuthenticationFailed = context =>
                    {
                        // 错误访问
                        var tokenType = context.Options.TokenTypeHint;
                        if (context.Error.GetType() == typeof(SecurityTokenExpiredException))
                            context.Response.Headers.Add("Token-Expired", "true");

                        context.Response.ContentType = "application/json";
                        //context.Response.StatusCode = StatusCodes.Status200OK;
                        //context.Response.StatusCode = StatusCodes.Status401Unauthorized;
                        context.Response.WriteAsync(JsonConvert.SerializeObject(new { code = "401", msg = "Unauthorized" }));
                        return Task.CompletedTask;
                    },
                    OnTokenValidated = context =>
                    {
                        // 正常访问
                        var token = context.SecurityToken;
                        return Task.CompletedTask;
                    },
                };
            });

            #endregion

            #region

            services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
                .AddIdentityServerAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme,
                jwtOptions =>
                {
                    // jwt bearer options
                    jwtOptions.Authority = Configuration["IdentityAuthentication:Authority"];
                    jwtOptions.RequireHttpsMetadata = true;
                    jwtOptions.Events = new JwtBearerEvents
                    {
                        OnAuthenticationFailed = context =>
                        {
                            //Token expired
                            if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                                context.Response.Headers.Add("Token-Expired", "true");

                            context.Response.ContentType = "application/json";
                            //context.Response.StatusCode = StatusCodes.Status200OK;
                            //context.Response.StatusCode = StatusCodes.Status401Unauthorized;
                            context.Response.WriteAsync(JsonConvert.SerializeObject(new { code = "401", msg = "Unauthorized" }));
                            return Task.CompletedTask;
                        },
                        OnTokenValidated = context =>
                        {
                            context.Options.TokenValidationParameters = new TokenValidationParameters
                            {
                                ValidateIssuer = true,
                                ValidateAudience = true,
                                ValidateLifetime = true,  //是否验证超时(失效)时间  当设置exp和nbf时有效 
                                ValidateIssuerSigningKey = true,  //是否验证密钥
                                ValidAudience = "http://localhost:49999",//Audience
                                ValidIssuer = "http://localhost:49998",//Issuer,这两项和登陆时颁发的一致
                                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("123456888jdijxhelloworldprefect")),     //拿到SecurityKey
                        //缓冲过期时间,总的有效时间等于这个时间加上jwt的过期时间,如果不配置,默认是5分钟                                                                                                            //注意这是缓冲过期时间,总的有效时间等于这个时间加上jwt的过期时间,如果不配置,默认是5分钟
                                ClockSkew = TimeSpan.FromSeconds(30)   //设置过期时间
                            };
                            return Task.CompletedTask;
                        }
                    };
                },
                referenceOptions =>
                {
                    // oauth2 introspection options
                    referenceOptions.Authority = Configuration["IdentityAuthentication:Authority"];
                    referenceOptions.ClientId = Configuration["IdentityAuthentication:ApiName"];
                    referenceOptions.ClientSecret = Configuration["IdentityAuthentication:ApiSecret"];
                    //referenceOptions.EnableCaching = true;
                    //referenceOptions.CacheDuration = TimeSpan.FromMinutes(10);
                    referenceOptions.Events = new IdentityModel.AspNetCore.OAuth2Introspection.OAuth2IntrospectionEvents
                    {
                        OnAuthenticationFailed = context =>
                        {
                            string a = "";
                            if (context.Error.GetType() == typeof(SecurityTokenExpiredException))
                                context.Response.Headers.Add("Token-Expired", "true");

                            context.Response.ContentType = "application/json";
                            //context.Response.StatusCode = StatusCodes.Status200OK;
                            //context.Response.StatusCode = StatusCodes.Status401Unauthorized;
                            context.Response.WriteAsync(JsonConvert.SerializeObject(new { code = "401", msg = "Unauthorized" }));
                            return Task.CompletedTask;
                        },
                        OnTokenValidated = context =>
                        {
                            return Task.CompletedTask;
                        }
                    };
                });
            #endregion

            #region Jwt 类型

            services.AddAuthentication("Bearer")
            .AddJwtBearer("Bearer", options =>
            {
                options.Authority = Configuration["IdentityAuthentication:Authority"];
                options.RequireHttpsMetadata = true;
                options.Audience = Configuration["IdentityAuthentication:ApiName"];
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateLifetime = true,//是否验证失效时间
                    ClockSkew = TimeSpan.FromSeconds(30),
                };
                options.Events = new JwtBearerEvents
                {
                    OnAuthenticationFailed = context =>
                    {
                        //Token expired
                        if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                            context.Response.Headers.Add("Token-Expired", "true");

                        context.Response.ContentType = "application/json";
                        context.Response.WriteAsync(JsonConvert.SerializeObject(new { code = "401", msg = "Unauthorized" }));
                        return Task.CompletedTask;
                    }
                };
            });
            #endregion

            #endregion

            services.AddControllers(options =>
            {
                //第 2 步
                options.Filters.Add<QDAuthorizeAttribute>();
                //options.Filters.Add<PermissionAttribute>();
                options.Filters.Add<ClientIpCheckFilter>();
                options.Filters.Add(typeof(CustomExceptionFilter));
                options.Filters.Add<ValidateModelAttribute>();
                options.Filters.Add<ResultFilterAttribute>();
                options.Filters.Add<HttpResponseExceptionFilter>();
                //options.UseCentralRoutePrefix(new RouteAttribute("api/v0.1/[controller]/[action]"));
                //options.UseCentralRoutePrefix(new RouteAttribute($"api/v{Configuration["AppSettings:ApiVersion"]}/[controller]/[action]"));
                //options.UseCentralRoutePrefix(new RouteAttribute($"api/v{Configuration["AppSettings:ApiVersion"]}/[controller]"));
                //options.UseCentralRoutePrefix(new RouteAttribute($"api/v{Configuration["AppSettings:ApiVersion"]}"));
                options.UseCentralRoutePrefix(new RouteAttribute($"api"));
                options.MaxModelValidationErrors = 50;
            })
            .ConfigureApiBehaviorOptions(options =>
            {
                options.InvalidModelStateResponseFactory = context =>
                {
                    var result = new BadRequestObjectResult(context.ModelState);
                    result.ContentTypes.Add(MediaTypeNames.Application.Json);
                    result.ContentTypes.Add(MediaTypeNames.Application.Xml);
                    return result;
                };

                //options.SuppressConsumesConstraintForFormFileParameters = true;
                //options.SuppressInferBindingSourcesForParameters = true;
                //options.SuppressModelStateInvalidFilter = true;
                //options.SuppressMapClientErrors = true;
                //options.ClientErrorMapping[404].Link = "https://httpstatuses.com/404";
            })
            .AddNewtonsoftJson(options =>
            {
                options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
                options.SerializerSettings.ContractResolver = new DefaultContractResolver();
                options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
            });

            #region 仓储注入
            foreach (var item in GetAssemblyName("MobileNurse.Repository"))
                services.AddTransient(item.Key, item.Value);
            #endregion

            services.AddApiVersioning(options =>
            {
                options.ReportApiVersions = true;
                options.AssumeDefaultVersionWhenUnspecified = true;
                options.DefaultApiVersion = new ApiVersion(0, 1);
            });

            #region Swagger
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" });
            });
            #endregion

            #region 跨域访问
            var urls = Configuration["AppSettings:Cores"].Split(',');
            services.AddCors(options =>
            {
                options.AddPolicy(AllowSpecificOrigins, builder => { builder.WithOrigins(urls); });
            });
            #endregion

            services.AddSignalR(options =>
            {
                options.KeepAliveInterval = TimeSpan.FromSeconds(5);
            });

            services.AddAutoMapper(typeof(AutoMapperInit));
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                //app.UseHsts();
            }

            //app.UseHttpsRedirection();
            app.UseStaticFiles();
            //app.UseMiddleware<AdminSafeListMiddleware>(Configuration["AdminSafeList"]);
            app.UseStatusCodePages();

            app.UseCors(AllowSpecificOrigins);

            app.UseSwagger();
            app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); });

            app.UseRouting();
            app.UseAuthentication();
            app.UseAuthorization();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
                endpoints.MapHub<Chat>("/chathub");
            });
            app.UseApiVersioning();

        }

        private Dictionary<Type, Type> GetAssemblyName(string assemblyName)
        {
            var result = new Dictionary<Type, Type>();
            if (!String.IsNullOrEmpty(assemblyName))
            {
                Assembly assembly = Assembly.Load(assemblyName);
                List<Type> ts = assembly.GetTypes().ToList();
                foreach (var item in ts.Where(x => x.IsClass && !x.IsAbstract && !x.IsGenericType))
                    result.Add(item.GetInterfaces().FirstOrDefault(x => !x.Name.StartsWith("IRepository")), item);
            }
            return result;
        }
    }
}

*
*
*
禁用 HTTPS 请求方式
Properties/launchSettings.json(推荐)
在我们项目的Properties/launchSettings.json文件中,把 "sslPort": 44383 修改为 "sslPort": 0
*
UseHttpsRedirection 方式(貌似有问题)
app.UseHttpsRedirection() 为应用添加了重定向HTTP请求到HTTPS请求的中间件。如果使用HTTP,那么就注释掉这行代码。
*
*
*
*
*

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
JWT(JSON Web Token)是一种用于身份验证和授权的开放标准(RFC 7519)。它是一种轻量级的安全传输方式,用于在网络应用间传递声明信息。JWT由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)。 头部包含了关于令牌的元数据和加密算法的信息,通常由两部分组成:令牌类型(即JWT)和所使用的签名算法(如HMAC SHA256或RSA)。 载荷是JWT的主要内容,包含了一些声明信息,如用户ID、角色、权限等。载荷可以自定义,但建议只包含一些非敏感的信息,因为JWT是可解码的。 签名是对头部和载荷进行加密生成的,用于验证JWT的真实性和完整性。签名需要使用头部中指定的算法和密钥进行生成,接收方可以通过验证签名来确保JWT没有被篡改。 生成JWT token的过程如下: 1. 创建一个包含所需声明信息的JSON对象。 2. 使用Base64编码头部和载荷,形成两个字符串。 3. 将两个字符串用点号连接起来,形成一个未签名的JWT。 4. 使用指定的算法和密钥对未签名的JWT进行签名,生成签名字符串。 5. 将签名字符串添加到未签名的JWT末尾,形成最终的JWT token验证JWT token的过程如下: 1. 将接收到的JWT token按点号分割为头部、载荷和签名三部分。 2. 使用相同的算法和密钥对头部和载荷进行签名,生成一个新的签名字符串。 3. 将新生成的签名字符串与接收到的签名进行比较,如果相同,则说明JWT token是有效的。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值