Identity Server 4 授权

1.生成 jwt token

  /// <summary>
    /// jwt token 
    /// </summary>
    public static class JwtHelper
    {
        //密钥 必须是32字节
        private static string SecretKey = "4046341C6E174E11B296AB4628E5AC99";
        public static string LogonCachePrefixKey = "LogonCachePrefixKey";
        

        /// <summary>
        /// 生成token
        /// </summary>
        /// <returns></returns>
        public static string GetAccessToken(User user
            , List<string> userAuthoritys
            , Dictionary<long, string> fabs
            , Dictionary<long, string> rtAreas
            , Dictionary<long, string> nonRTAreas
            , IConfiguration configuration)
        {
            string jwtIssuer = configuration["Startup:JwtSettings:Issuer"];
            string jwtAudience = configuration["Startup:JwtSettings:Audience"];
            string jwtSecretKey = configuration["Startup:JwtSettings:SecretKey"];
            string validMinutes = configuration["Startup:JwtSettings:AccessTokenValidMinutes"];
            var tokenHandler = new JwtSecurityTokenHandler();
            byte[] byteKey = Encoding.UTF8.GetBytes(jwtSecretKey);
            var authTime = DateTime.Now;//授权时间
            var expiresAt = GetAccessTokenExpiresAt(authTime,validMinutes);
            var claims = GetClaimsByuser(user
                , userAuthoritys
                , fabs
                , rtAreas
                , nonRTAreas
                , jwtIssuer, jwtAudience, authTime);
            var tokenDescripor = new SecurityTokenDescriptor
            {
                Subject = new ClaimsIdentity(claims),
                Expires = expiresAt,
                SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(byteKey), SecurityAlgorithms.HmacSha256Signature)
            };
            SecurityToken securityToken = tokenHandler.CreateToken(tokenDescripor);
            string token = tokenHandler.WriteToken(securityToken);
            return token;
        }
        private static DateTime GetAccessTokenExpiresAt(DateTime authTime, string validMinutes)
        {
            double minutes = 20;
            double.TryParse(validMinutes, out minutes);
            return authTime.AddMinutes(minutes);
        }
        private static DateTime GetRefreshTokenExpiresAt(string validMinutes)
        {
            double minutes = 24*60;
            double.TryParse(validMinutes, out minutes);
            return DateTime.Now.AddMinutes(minutes);
        }
        public static int GetRefreshTokenExpiresAtNumber(IConfiguration configuration)
        {
            int number = 24 * 60;
            int.TryParse(configuration["Startup:JwtSettings:RefreshTokenValidMinutes"], out number);
            return number;
        }
        public static string GetRefreshToken(User user, HttpContext httpContext, IConfiguration configuration,string isForMems)
        {
            string validMinutes = configuration["Startup:JwtSettings:RefreshTokenValidMinutes"];
            if (user == null) return string.Empty;
            var obj = new RefreshTokenDto()
            {
                UserName = user.UserName,
                UserID = user.UserID,
                ClientIP = httpContext.GetClientIP(),
                IsForMems = isForMems,
                ExpiresAt = GetRefreshTokenExpiresAt(validMinutes),
                LastLoginTime = user.LastLoginTime

            };
            var jsonStr = JsonConvert.SerializeObject(obj);

            return AESEncrypt(jsonStr);
        }
        public static RefreshTokenDto GetLogonUsers(HttpContext httpContext, IConfiguration configuration, User user)
        {
            var expiresAt = GetRefreshTokenExpiresAt(configuration["Startup:JwtSettings:RefreshTokenValidMinutes"]);
            return new RefreshTokenDto()
            {
                UserID = user.UserID,
                UserName = user.UserName,
                ClientIP = httpContext.GetClientIP(),
                ExpiresAt = expiresAt,
                LastLoginTime = user.LastLoginTime,
            };
        }
        public static string GetLogonUserKey(HttpContext httpContext, string userID)
        {
            return $"{LogonCachePrefixKey} - {userID} - {httpContext.GetClientIP()}";
        }
        public static RefreshTokenDto GetRefreshTokenDataByToken(string refreshToken)
        {
            if (string.IsNullOrEmpty(refreshToken) == false)
            {
                var json = AESDEncrypt(refreshToken);
                var obj = JsonConvert.DeserializeObject<RefreshTokenDto>(json);
                return obj;
            }
            return null;
        }
        public static bool CheckRefreshToken(HttpContext httpContext, string refreshToken)
        {
            var tokenData = GetRefreshTokenDataByToken(refreshToken);
            if (tokenData != null)
            {
                var clientIP = httpContext.GetClientIP();
                //验证 IP 、时间
                if(tokenData.ClientIP.Equals(clientIP) 
                    && tokenData.ExpiresAt.CompareTo(DateTime.Now) > 0)
                {
                    return true;
                }
            }
            return false;
        }
        private static IList<Claim> GetClaimsByuser(User user
            , List<string> userAuthoritys
            , Dictionary<long, string> fabs
            , Dictionary<long, string> rtAreas
            , Dictionary<long, string> nonRTAreas
            , string jwtIssuer, string jwtAudience, DateTime authTime)
        {

            string timeStamp = Datetime2Unix(authTime).ToString();
            var claims = new List<Claim>();
            if (user != null)
            {
                claims.Add(new Claim(JwtClaimTypes.Issuer, jwtIssuer));
                claims.Add(new Claim(JwtClaimTypes.Audience, jwtAudience));
                claims.Add(new Claim(JwtClaimTypes.Id, user.UserID));
                claims.Add(new Claim(JwtClaimTypes.Name, user.UserName));
                claims.Add(new Claim(JwtClaimTypes.Expiration, timeStamp));
            }            
            if (userAuthoritys != null && userAuthoritys.Count > 0)
            {
                foreach (var item in userAuthoritys)
                {
                    claims.Add(new Claim(SPCPermissions.SPCPermissionPolicyName, item));//策略授权
                }
            }
            return claims;
        }

        private static double Datetime2Unix(DateTime time)
        {
            DateTime startTime = TimeZoneInfo.ConvertTimeFromUtc(new DateTime(1970, 1, 1), TimeZoneInfo.Local);
            return (time - startTime).TotalSeconds;
        }
        //AES加密
        private static string AESEncrypt(string str)
        {
            byte[] keyArray = UTF8Encoding.UTF8.GetBytes(SecretKey);
            byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(str);
            RijndaelManaged rDel = new RijndaelManaged();
            rDel.Key = keyArray;
            rDel.Mode = CipherMode.ECB;
            rDel.Padding = PaddingMode.PKCS7;
            ICryptoTransform cTransform = rDel.CreateEncryptor();
            byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
            return Convert.ToBase64String(resultArray, 0, resultArray.Length);
        }
        //AES解密
        private static string AESDEncrypt(string str)
        {
            byte[] keyArray = UTF8Encoding.UTF8.GetBytes(SecretKey);
            byte[] toEncryptArray = Convert.FromBase64String(str);
            RijndaelManaged rDel = new RijndaelManaged();
            rDel.Key = keyArray;
            rDel.Mode = CipherMode.ECB;
            rDel.Padding = PaddingMode.PKCS7;
            ICryptoTransform cTransform = rDel.CreateDecryptor();
            byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
            return UTF8Encoding.UTF8.GetString(resultArray);
        }
    }

2.注册服务

 public class Startup
    {
        private IServiceCollection _services;
        public Startup(IConfiguration configuration, IWebHostEnvironment env)
        {
            Configuration = configuration;
            Env = env;
        }

        public IConfiguration Configuration { get; }
        public IWebHostEnvironment Env { get; }


        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
            Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
            //redis注入
            //services.AddRedisCacheSetup();

            services.Configure<CookiePolicyOptions>(options =>
            {
                options.MinimumSameSitePolicy = SameSiteMode.None;
                options.Secure = CookieSecurePolicy.SameAsRequest;
            });

            //允许跨域注入
            //services.AddCorsSetup();

            services.AddCors(option =>
            {
                option.AddPolicy(Configuration["Startup:Cors:PolicyName"], policy =>
                {
                    policy.WithOrigins(Configuration["Startup:Cors:IPs"]
                                .Split(",", StringSplitOptions.RemoveEmptyEntries)
                                .ToArray())
                    .SetIsOriginAllowedToAllowWildcardSubdomains();
                    policy.SetPreflightMaxAge(TimeSpan.FromMinutes(HttpContextExtensions.GePreflightExpireTime(Configuration)));//预检缓存最大时间
                    policy.AllowAnyHeader();
                    policy.AllowAnyMethod();
                    policy.AllowCredentials();
                });
            });
            //swagger注入
            //services.AddSwaggerAnalysisSetup();
            ConfigureSwaggerServices(services);
      
            services.AddAuthentication(b =>
            {
                b.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                b.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(a =>
            {
                //jwt token参数设置
                a.TokenValidationParameters = new TokenValidationParameters
                {
                    NameClaimType = JwtClaimTypes.Name,
                    //ClockSkew = TimeSpan.FromSeconds(10),
                    //Token颁发机构
                    ValidIssuer = Configuration["Startup:JwtSettings:Issuer"],
                    //颁发给谁
                    ValidAudience = Configuration["Startup:JwtSettings:Audience"],
                    //这里的key要进行加密
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Startup:JwtSettings:SecretKey"])),
                };
                a.Events = new JwtBearerEvents()
                {
                    OnAuthenticationFailed = context =>
                    {
                        context.Response.StatusCode = SPCJwtMiddleware.AuthenticationFailedCode;
                        return Task.CompletedTask;
                    },
                    OnForbidden = context =>
                    {
                        context.Response.StatusCode = 200;
                        context.Response.ContentType = "application/json;charset=utf-8";
                        return context.Response.WriteAsync(JsonConvert.SerializeObject(Dto.Common.SPCJsonResult.JsonErrorResult(SPCJwtMiddleware.ForbiddenCode, string.Empty)));
                    },
                    OnChallenge = context =>
                    {
                        context.HandleResponse();
                        return Task.CompletedTask;
                    }
                };
            });

            services.AddAuthorization(b =>
            {
                foreach (var item in SPCPermissions.GetStrings())
                {
                    b.AddPolicy(item, policy => policy.RequireClaim(SPCPermissions.SPCPermissionPolicyName, item));
                }

                foreach (KeyValuePair<string,string[]> item in SPCPermissions.GetKeyValues())
                {
                    b.AddPolicy(item.Key, policy => policy.RequireClaim(SPCPermissions.SPCPermissionPolicyName, item.Value));
                }
            });
            //Session
            services.AddDistributedMemoryCache();
            services.AddMemoryCache();
            // session 设置
            services.AddSession(options =>
            {
                // 设置 Session 过期时间
                options.IdleTimeout = TimeSpan.FromMinutes(SPCSessionExtensions.GetLoginExpireTime(Configuration));
                // 跨域 谷歌浏览器 session id 丢失
                options.Cookie.SameSite = SameSiteMode.None;
                options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
                options.Cookie.HttpOnly = true;
            });

            string RouteName = Appsettings.app("AppSettings", "RouteName"); ;
            services.AddControllers(o =>
            {
                // 全局异常过滤
                o.Filters.Add(typeof(GlobalExceptionsFilter));
                // 全局路由权限公约
                //o.Conventions.Insert(0, new GlobalRouteAuthorizeConvention());
                // 全局路由前缀,统一修改路由
                o.Conventions.Insert(0, new GlobalRoutePrefixFilter(new RouteAttribute(RouteName)));

            }).ConfigureApiBehaviorOptions(options =>
            {
                //FromBody
                //options.SuppressConsumesConstraintForFormFileParameters = true;
                //options.SuppressInferBindingSourcesForParameters = true;
                options.SuppressModelStateInvalidFilter = true;
                options.SuppressMapClientErrors = true;
            })
            //全局配置Json序列化处理
            .AddNewtonsoftJson(options =>
            {
                //忽略循环引用
                options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
                //不使用驼峰样式的key
                options.SerializerSettings.ContractResolver = new DefaultContractResolver();

                //设置时间格式
                options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
            });

            _services = services;

            NHSessionStorage.SetServiceProvider(services.BuildServiceProvider());
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env
            , IHostApplicationLifetime applicationLeftTime)
        {
            // 记录请求与返回数据 
            app.UseReuestResponseLog();
            // 记录ip请求
            app.UseIPLogMildd();
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            // 封装Swagger展示
            //app.UseSwaggerMildd(() => GetType().GetTypeInfo().Assembly.GetManifestResourceStream("WebAPI.index.html"));

            // CORS跨域
            app.UseCors(Configuration["Startup:Cors:PolicyName"]);
            // 跳转https
            //app.UseHttpsRedirection();
            // 使用静态文件
            app.UseStaticFiles();
            // 使用cookie
            app.UseCookiePolicy();
            // 返回错误码
            app.UseStatusCodePages();
            // Routing
            app.UseRouting();
            // 这种自定义授权中间件,可以尝试,但不推荐
            // app.UseJwtTokenAuth();
            // 先开启认证
            app.UseAuthentication();
            app.UseMiddleware<SPCJwtMiddleware>();
            // 然后是授权中间件
            app.UseAuthorization();

            // 开启异常中间件,要放到最后
            //app.UseExceptionHandlerMidd();
            // 性能分析
            app.UseMiniProfiler();



            app.UseSwagger();
            app.UseSwaggerUI(options =>
            {
                typeof(ApiGroupNames).GetFields().Skip(1).ToList().ForEach(f =>
                {
                    var info = f.GetCustomAttributes(typeof(GroupInfoAttribute), false).OfType<GroupInfoAttribute>().FirstOrDefault();
                    options.SwaggerEndpoint($"/swagger/{f.Name}/swagger.json", f.Name);
                    //options.SwaggerDoc(f.Name, new Microsoft.OpenApi.Models.OpenApiInfo
                    //{
                    //    Title = info?.Title,
                    //    Version = info?.Version,
                    //    Description = info?.Description
                    //});
                });

            });
            app.UseSession();
            applicationLeftTime.ApplicationStarted.Register(() =>
            {
                string resourcePath = Configuration["AppSettings:ResourcePath"];
                string resourceName = Configuration["AppSettings:ResourceName"];
                UtilCulture.InitialResource(resourceName, resourcePath, "en-US");
            });
            app.UseMiddleware<SPCCultureMiddleware>();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }

        private void ConfigureSwaggerServices(IServiceCollection services)
        {
            services.AddSwaggerGen(c =>
            {
                //遍历ApiGroupNames所有枚举值生成接口文档,Skip(1)是因为Enum第一个FieldInfo是内置的一个Int值  
                typeof(ApiGroupNames).GetFields().Skip(1).ToList().ForEach(f =>
                {
                    var info = f.GetCustomAttributes(typeof(GroupInfoAttribute), false).OfType<GroupInfoAttribute>().FirstOrDefault();
                    c.SwaggerDoc(f.Name, new OpenApiInfo
                    {
                        //Title = info?.Title,
                        //Version = info?.Version,
                        //Description = info?.Description
                        Title = $"{info?.Title} 接口文档——{RuntimeInformation.FrameworkDescription}",
                        Description = $"{info?.Title} " + info?.Version,
                        Contact = new OpenApiContact { Name = info?.Title, Email = "contact@fa-software.com", Url = new Uri("https://www.fa-software.com/") },
                        License = new OpenApiLicense { Name = info?.Title + " 官方简介", Url = new Uri("https://www.fa-software.com/") }
                    });
                });
                // 使用 [ApiGroup(ApiGroupNames.UI)] 进行分组
                c.DocInclusionPredicate((docName, apiDescription) =>
                {
                    if (docName == ApiGroupNames.SPC_API.ToString())
                    {
                        return true;
                    }
                    //反射拿到值
                    var actionlist = apiDescription.ActionDescriptor.EndpointMetadata.Where(x => x is ApiGroupAttribute);
                    if (actionlist.Count() > 0)
                    {
                        //判断是否包含这个分组
                        var actionfilter = actionlist.FirstOrDefault() as ApiGroupAttribute;
                        return actionfilter.GroupName.Count(x => x.ToString() == docName) > 0;
                    }
                    return false;
                });
                var basePath = Path.GetDirectoryName(typeof(Startup).Assembly.Location);
                var xmlPath = Path.Combine(basePath, "WebAPI.xml");
                c.IncludeXmlComments(xmlPath, true);

                c.AddSecurityRequirement(new OpenApiSecurityRequirement()
                    {
                        {
                            new OpenApiSecurityScheme()
                            {
                                Reference = new OpenApiReference()
                                {
                                    Id = JwtBearerDefaults.AuthenticationScheme,
                                    Type = ReferenceType.SecurityScheme
                                }
                            },
                            Array.Empty<string>()
                        }
                    });
                c.AddSecurityDefinition(JwtBearerDefaults.AuthenticationScheme, new OpenApiSecurityScheme()
                {
                    Name = "Authorization",
                    In = ParameterLocation.Header,
                    Type = SecuritySchemeType.ApiKey,
                });
            });
        }

    }

 

基于策略的授权(角色授权就是特殊的策略授权)

services.AddAuthorization(b =>
            {
//设置一个值
                foreach (var item in SPCPermissions.GetStrings())
                {
                    b.AddPolicy(item, policy => policy.RequireClaim(SPCPermissions.SPCPermissionPolicyName, item));
                }
//设置多个值或关系
                foreach (KeyValuePair<string,string[]> item in SPCPermissions.GetKeyValues())
                {
                    b.AddPolicy(item.Key, policy => policy.RequireClaim(SPCPermissions.SPCPermissionPolicyName, item.Value));//item.Value 为 string[] { "r1", "r2" }
} });

 

原理不懂请参考:https://www.cnblogs.com/wangjunwei/p/10964604.html#autoid-7-0-0

posted @ 2022-12-02 10:02  乌柒柒  阅读(37)  评论(0编辑  收藏  举报