Пример #1
0
 // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
 public void Configure(IApplicationBuilder app, IHostingEnvironment env)
 {
     app.UseMvc();
     if (env.IsDevelopment())
     {
         app.UseDeveloperExceptionPage();
     }
     using (var serviceScope = app.ApplicationServices.GetService <IServiceScopeFactory>().CreateScope()) {
         var context = serviceScope.ServiceProvider.GetRequiredService <DomainDbContext>();
         context.Database.EnsureCreated();
     }
     QuartzRegister.Run(Container).GetAwaiter().GetResult();
 }
Пример #2
0
        public IServiceProvider ConfigureServices(IServiceCollection services)
        {
            #region 部分服务注入-netcore自带方法
            // 缓存注入
            services.AddScoped <ICaching, MemoryCaching>();
            services.AddSingleton <IMemoryCache>(factory =>
            {
                var cache = new MemoryCache(new MemoryCacheOptions());
                return(cache);
            });
            // Redis注入
            services.AddSingleton <IRedisCacheManager, RedisCacheManager>();
            // log日志注入
            services.AddSingleton <ILoggerHelper, LogHelper>();
            #endregion

            #region 初始化DB
            services.AddScoped <MyBlog.Core.Model.Models.MyContext>();
            #endregion

            // Automapper 注入
            services.AddAutoMapperSetup();

            #region CORS
            //跨域第二种方法,声明策略,记得下边app中配置
            services.AddCors(c =>
            {
                //↓↓↓↓↓↓↓注意正式环境不要使用这种全开放的处理↓↓↓↓↓↓↓↓↓↓
                c.AddPolicy("AllRequests", policy =>
                {
                    policy
                    .AllowAnyOrigin()    //允许任何源
                    .AllowAnyMethod()    //允许任何方式
                    .AllowAnyHeader()    //允许任何头
                    .AllowCredentials(); //允许cookie
                });
                //↑↑↑↑↑↑↑注意正式环境不要使用这种全开放的处理↑↑↑↑↑↑↑↑↑↑


                //一般采用这种方法
                c.AddPolicy("LimitRequests", policy =>
                {
                    // 支持多个域名端口,注意端口号后不要带/斜杆:比如localhost:8000/,是错的
                    // 注意,http://127.0.0.1:1818 和 http://localhost:1818 是不一样的,尽量写两个
                    policy
                    .WithOrigins("http://127.0.0.1:1818", "http://*****:*****@xxx.com", Url = "https://www.jianshu.com/u/94102b59cc2a"
                        }
                    });
                    // 按相对路径排序,作者:Alby
                    c.OrderActionsBy(o => o.RelativePath);
                });


                //就是这里
                var xmlPath = Path.Combine(basePath, "MyBlog.Core.Web.xml");        //这个就是刚刚配置的xml文件名
                c.IncludeXmlComments(xmlPath, true);                                //默认的第二个参数是false,这个是controller的注释,记得修改

                var xmlModelPath = Path.Combine(basePath, "MyBlog.Core.Model.xml"); //这个就是Model层的xml文件名
                c.IncludeXmlComments(xmlModelPath);

                #region Token绑定到ConfigureServices

                //添加header验证信息
                //c.OperationFilter<SwaggerHeader>();

                // 发行人
                var IssuerName = (Configuration.GetSection("Audience"))["Issuer"];
                var security   = new Dictionary <string, IEnumerable <string> > {
                    { IssuerName, new string[] { } },
                };
                c.AddSecurityRequirement(security);

                //方案名称“MyBlog.Core.Web”可自定义,上下一致即可
                c.AddSecurityDefinition(IssuerName, new ApiKeyScheme
                {
                    Description = "JWT授权(数据将在请求头中进行传输) 直接在下框中输入Bearer {token}(注意两者之间是一个空格)\"",
                    Name        = "Authorization", //jwt默认的参数名称
                    In          = "header",        //jwt默认存放Authorization信息的位置(请求头中)
                    Type        = "apiKey"
                });
                #endregion
            });

            #endregion

            #region Authorize 权限认证三步走

            //使用说明:

            //1、如果你只是简单的基于角色授权的,仅仅在 api 上配置,第一步:【1/2 简单角色授权】,第二步:配置【统一认证服务】,第三步:开启中间件

            //2、如果你是用的复杂的基于策略授权,配置权限在数据库,第一步:【3复杂策略授权】,第二步:配置【统一认证服务】,第三步:开启中间件app.UseAuthentication();

            //3、综上所述,设置权限,必须要三步走,授权 + 配置认证服务 + 开启授权中间件,只不过自定义的中间件不能验证过期时间,所以我都是用官方的。

            #region 【第一步:授权】

            #region 1、基于角色的API授权

            // 1【授权】、这个很简单,其他什么都不用做, 只需要在API层的controller上边,增加特性即可,注意,只能是角色的:
            // [Authorize(Roles = "Admin,System")]


            #endregion

            #region 2、基于策略的授权(简单版)

            // 1【授权】、这个和上边的异曲同工,好处就是不用在controller中,写多个 roles 。
            // 然后这么写 [Authorize(Policy = "Admin")]
            services.AddAuthorization(options =>
            {
                options.AddPolicy("Client", policy => policy.RequireRole("Client").Build());
                options.AddPolicy("Admin", policy => policy.RequireRole("Admin").Build());
                options.AddPolicy("SystemOrAdmin", policy => policy.RequireRole("Admin", "System"));
            });


            #endregion

            #region 【3、复杂策略授权】

            #region 参数
            //读取配置文件
            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);

            // 如果要数据库动态绑定,这里先留个空,后边处理器里动态赋值
            var permission = new List <PermissionItem>();

            // 角色与接口的权限要求参数
            var permissionRequirement = new PermissionRequirement(
                "/api/denied",                           // 拒绝授权的跳转地址(目前无用)
                permission,
                ClaimTypes.Role,                         //基于角色的授权
                audienceConfig["Issuer"],                //发行人
                audienceConfig["Audience"],              //听众
                signingCredentials,                      //签名凭据
                expiration: TimeSpan.FromSeconds(1 * 60) //接口的过期时间
                );
            #endregion

            //【授权】
            services.AddAuthorization(options =>
            {
                options.AddPolicy(Permissions.Name,
                                  policy => policy.Requirements.Add(permissionRequirement));
            });


            #endregion


            #endregion



            #region 【第二步:配置认证服务】
            // 令牌验证参数
            var tokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey         = signingKey,
                ValidateIssuer           = true,
                ValidIssuer           = audienceConfig["Issuer"],   //发行人
                ValidateAudience      = true,
                ValidAudience         = audienceConfig["Audience"], //订阅人
                ValidateLifetime      = true,
                ClockSkew             = TimeSpan.FromSeconds(30),
                RequireExpirationTime = true,
            };

            //2.1【认证】、core自带官方JWT认证
            services.AddAuthentication(x =>
            {
                x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                x.DefaultChallengeScheme    = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(o =>
            {
                o.TokenValidationParameters = tokenValidationParameters;
                o.Events = new JwtBearerEvents
                {
                    OnAuthenticationFailed = context =>
                    {
                        // 如果过期,则把<是否过期>添加到,返回头信息中
                        if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
                        {
                            context.Response.Headers.Add("Token-Expired", "true");
                        }
                        return(Task.CompletedTask);
                    }
                };
            });

            services.AddSingleton <IAuthorizationHandler, PermissionHandler>();
            services.AddSingleton(permissionRequirement);
            #endregion

            #endregion

            #region AutoFac DI
            //实例化 AutoFac  容器
            var builder = new ContainerBuilder();
            //注册要通过反射创建的组件
            //builder.RegisterType<AdvertisementServices>().As<IAdvertisementServices>();
            builder.RegisterType <LuoKiPetCacheAOP>();      //可以直接替换其他拦截器
            builder.RegisterType <LuoKiPetRedisCacheAOP>(); //可以直接替换其他拦截器
            builder.RegisterType <LuoKiPetLogAOP>();        //这样可以注入第二个

            builder.RegisterModule(new QuartzAutofacFactoryModule()
            {
                ConfigurationProvider = (provider) =>
                {
                    var properties = new NameValueCollection();
                    // 设置线程池
                    properties["quartz.threadPool.type"] = "Quartz.Simpl.SimpleThreadPool, Quartz";
                    //设置线程池的最大线程数量
                    properties["quartz.threadPool.threadCount"] = "5";
                    //设置作业中每个线程的优先级
                    properties["quartz.threadPool.threadPriority"] = ThreadPriority.Normal.ToString();
                    return(properties);
                }
            });
            builder.RegisterModule(new QuartzAutofacJobsModule(typeof(MyBlog.Core.Web.Jobs.Core.JobManager).Assembly));
            // ※※★※※ 如果你是第一次下载项目,请先F6编译,然后再F5执行,※※★※※

            #region 带有接口层的服务注入

            #region Service.dll 注入,有对应接口
            //获取项目绝对路径,请注意,这个是实现类的dll文件,不是接口 IService.dll ,注入容器当然是Activatore
            try
            {
                var servicesDllFile   = Path.Combine(basePath, "MyBlog.Core.Services.dll");
                var assemblysServices = Assembly.LoadFrom(servicesDllFile);//直接采用加载文件的方法  ※※★※※ 如果你是第一次下载项目,请先F6编译,然后再F5执行,※※★※※

                //builder.RegisterAssemblyTypes(assemblysServices).AsImplementedInterfaces();//指定已扫描程序集中的类型注册为提供所有其实现的接口。


                // AOP 开关,如果想要打开指定的功能,只需要在 appsettigns.json 对应对应 true 就行。
                var cacheType = new List <Type>();
                if (Appsettings.app(new string[] { "AppSettings", "RedisCaching", "Enabled" }).ObjToBool())
                {
                    cacheType.Add(typeof(LuoKiPetRedisCacheAOP));
                }
                if (Appsettings.app(new string[] { "AppSettings", "MemoryCachingAOP", "Enabled" }).ObjToBool())
                {
                    cacheType.Add(typeof(LuoKiPetCacheAOP));
                }
                if (Appsettings.app(new string[] { "AppSettings", "LogAOP", "Enabled" }).ObjToBool())
                {
                    cacheType.Add(typeof(LuoKiPetLogAOP));
                }

                builder.RegisterAssemblyTypes(assemblysServices)
                .AsImplementedInterfaces()
                .InstancePerLifetimeScope()
                .EnableInterfaceInterceptors()          //引用Autofac.Extras.DynamicProxy;
                                                        // 如果你想注入两个,就这么写  InterceptedBy(typeof(BlogCacheAOP), typeof(BlogLogAOP));
                                                        // 如果想使用Redis缓存,请必须开启 redis 服务,端口号我的是6319,如果不一样还是无效,否则请使用memory缓存 BlogCacheAOP
                .InterceptedBy(cacheType.ToArray());    //允许将拦截器服务的列表分配给注册。
                #endregion

                #region Repository.dll 注入,有对应接口
                var repositoryDllFile   = Path.Combine(basePath, "MyBlog.Core.Repository.dll");
                var assemblysRepository = Assembly.LoadFrom(repositoryDllFile);
                builder.RegisterAssemblyTypes(assemblysRepository).AsImplementedInterfaces();
            }
            catch (Exception ex)
            {
                throw new Exception("※※★※※ 如果你是第一次下载项目,请先对整个解决方案dotnet build(F6编译),然后再对api层 dotnet run(F5执行),\n因为解耦了,如果你是发布的模式,请检查bin文件夹是否存在Repository.dll和service.dll ※※★※※");
            }
            #endregion
            #endregion


            #region 没有接口层的服务层注入

            ////因为没有接口层,所以不能实现解耦,只能用 Load 方法。
            ////var assemblysServicesNoInterfaces = Assembly.Load("Blog.Core.Services");
            ////builder.RegisterAssemblyTypes(assemblysServicesNoInterfaces);

            #endregion

            #region 没有接口的单独类 class 注入
            ////只能注入该类中的虚方法
            //builder.RegisterAssemblyTypes(Assembly.GetAssembly(typeof(Love)))
            //    .EnableClassInterceptors()
            //    .InterceptedBy(typeof(BlogLogAOP));

            #endregion


            //将services填充到Autofac容器生成器中
            builder.Populate(services);

            //使用已进行的组件登记创建新容器
            var ApplicationContainer = builder.Build();
            QuartzRegister.UseQuartz(ApplicationContainer);
            #endregion

            return(new AutofacServiceProvider(ApplicationContainer));//第三方IOC接管 core内置DI容器
        }