Пример #1
0
        /// <summary>
        /// 获取基于JWT的Token
        /// </summary>
        /// <param name="claims">需要在登陆的时候配置</param>
        /// <param name="permissionRequirement">在startup中定义的参数</param>
        /// <returns></returns>
        public static dynamic BuildJwtToken(Claim[] claims, PermissionRequirement permissionRequirement)
        {
            var now = DateTime.Now;
            // 实例化JwtSecurityToken
            var jwt = new JwtSecurityToken(
                issuer: permissionRequirement.Issuer,
                audience: permissionRequirement.Audience,
                claims: claims,
                notBefore: now,
                expires: now.Add(permissionRequirement.Expiration),
                signingCredentials: permissionRequirement.SigningCredentials
                );
            // 生成 Token
            var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);

            //打包返回前台
            var responseJson = new
            {
                success    = true,
                token      = encodedJwt,
                expires_in = permissionRequirement.Expiration.TotalSeconds,
                token_type = "Bearer"
            };

            return(responseJson);
        }
Пример #2
0
        // This method gets called by the runtime. Use this method to add services to the container.
        public IServiceProvider ConfigureServices(IServiceCollection services)
        {
            #region 部分服务注入 使用netcore自带方法注入
            //缓存 注入
            services.AddScoped <ICahing, MemoryCaching>();
            //reids注入
            services.AddSingleton <IRedisCacheManager, RedisCacheManger>();
            //通过注入 IHostingEnvironment 服务对象来取得Web根目录和内容根目录的物理路径
            #endregion
            #region MVC + GlobalExceptions

            //注入全局异常捕获
            services.AddMvc(
                //    o =>
                //{
                //    // 全局异常过滤
                //    o.Filters.Add(typeof(GlobalExceptionsFilter));
                //    // 全局路由权限公约
                //    o.Conventions.Insert(0, new GlobalRouteAuthorizeConvention());
                //    // 全局路由前缀,统一修改路由
                //    o.Conventions.Insert(0, new GlobalRoutePrefixFilter(new RouteAttribute(RoutePrefix.Name)));
                //}
                )
            .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
            // 取消默认驼峰
            .AddJsonOptions(options => { options.SerializerSettings.ContractResolver = new DefaultContractResolver(); });


            #endregion
            var basePath = Microsoft.DotNet.PlatformAbstractions.ApplicationEnvironment.ApplicationBasePath;
            #region 初始化数据库
            services.AddScoped <BlogCore.Model.Seed.DbSeed>();
            services.AddScoped <BlogCore.Model.Seed.MyContext>();
            #endregion
            #region AutoMapper
            //在Startup中,注入服务
            services.AddAutoMapper(typeof(Startup));//这是AutoMapper的2.0新特性
            #endregion
            #region CORS
            //跨域方法 一
            services.AddCors(c =>
            {
                //↓↓↓↓↓↓↓注意正式环境不要使用这种全开放的处理↓↓↓↓↓↓↓↓↓↓
                //c.AddPolicy("AllRequest", policy =>
                //{
                //    policy.AllowAnyOrigin()//允许任何源
                //    .AllowAnyMethod()//允许任何方法
                //    .AllowAnyHeader()//允许任何请求头
                //    .AllowCredentials();//允许cookie
                //});
                //↓↓↓↓↓↓↓注意正式环境不要使用这种全开放的处理↓↓↓↓↓↓↓↓↓↓
                //一般采用这种方法
                c.AddPolicy("LimitRequest", policy =>
                {
                    policy.WithOrigins("http://127.0.0.1:1818", "http://127.0.0.1:5000", "http://127.0.0.1:2364", "http://127.0.0.1:2365", "http://47.105.159.142:8082")
                    .AllowAnyMethod()
                    .AllowAnyHeader();
                });
            });
            //跨域方法 二 注意下边 Configure 中进行配置
            //services.AddCors();
            #endregion
            #region SwaggerUI Service
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info
                {
                    Version        = "v0.1.0",
                    Title          = "blog.core.api",
                    Description    = "框架说明文档",
                    TermsOfService = "None",
                    Contact        = new Swashbuckle.AspNetCore.Swagger.Contact
                    {
                        Name  = "Blog.Core",
                        Email = "*****@*****.**",
                        Url   = "https://www.jianshu.com/u/94102b59cc2a"
                    }
                });
                #region 读取xml信息
                var xmlPath = Path.Combine(basePath, "BlogCore.xml"); //这个就是刚刚配置的xml文件名
                c.IncludeXmlComments(xmlPath, true);                  //默认的第二个参数是false,这个是controller的注释,记得修改
                var xmlModelPath = Path.Combine(basePath, "BlogCoreModel.xml");
                c.IncludeXmlComments(xmlModelPath);
                #endregion
                #region Token绑定到ConfigureServices
                //添加header验证信息
                var security = new Dictionary <string, IEnumerable <string> > {
                    { "BlogCore", new string[] { } },
                };
                c.AddSecurityRequirement(security);
                c.AddSecurityDefinition("BlogCore", new ApiKeyScheme
                {
                    Description = "JWT授权(数据将在请求头中进行传输) 直接在下框中输入Bearer {token}(注意两者之间是一个空格)\"",
                    Name        = "Authorization", //jwt默认的参数名称
                    In          = "header",        //jwt默认存放Authorization信息的位置(请求头中)
                    Type        = "apiKey"
                });
                #endregion
            });

            #endregion
            #region Httpcontext

            // Httpcontext 注入
            services.AddSingleton <IHttpContextAccessor, HttpContextAccessor>();
            services.AddScoped <IUser, AspNetUser>();

            #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("SystemOrAdmin").Build());
            });
            #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 <AuthHelper.Policys.PermissionItem>();

            // 角色与接口的权限要求参数
            var permissionRequirement = new AuthHelper.Policys.PermissionRequirement(
                "/api/denied",                            // 拒绝授权的跳转地址(目前无用)
                permission,
                ClaimTypes.Role,                          //基于角色的授权
                audienceConfig["Issuer"],                 //发行人
                audienceConfig["Audience"],               //听众
                signingCredentials,                       //签名凭据
                expiration: TimeSpan.FromSeconds(60 * 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);
                    }
                };
            });


            //2.2【认证】、IdentityServer4 认证 (暂时忽略)
            //services.AddAuthentication("Bearer")
            //  .AddIdentityServerAuthentication(options =>
            //  {
            //      options.Authority = "http://localhost:5002";
            //      options.RequireHttpsMetadata = false;
            //      options.ApiName = "blog.core.api";
            //  });
            // 注入权限处理器

            services.AddSingleton <IAuthorizationHandler, AuthHelper.Policys.PermissionHandler>();
            services.AddSingleton(permissionRequirement);
            #endregion
            //.netcore自带的依赖注入方式
            //services.AddScoped<IRoleModulePermissionServices, RoleModulePermissionServices>();
            #endregion
            #region autofac
            //实例化autofac容器
            var bulid = new ContainerBuilder();
            //注册要通过反射创建的组件
            //bulid.RegisterType<AdvertisementServices>().As<IAdvertisementServices>();
            bulid.RegisterType <BlogRedisCacheAOP>();
            bulid.RegisterType <BlogCacheAOP>();
            bulid.RegisterType <BlogLogAOP>();//可以直接替换其他拦截器

            #region 1、服务程序集注入方式 —— 未解耦  =>没有接口层的服务层注入
            //整个程序集注入
            //注意 这个注入的是实现类层,不是接口层,不是IServices
            var assemblyServices = Assembly.Load("BlogCore.Services");
            //指定已扫描程序集中的类型注册为提供所有其实现类接口
            bulid.RegisterAssemblyTypes(assemblyServices).AsImplementedInterfaces();
            var assemblyRespository = Assembly.Load("BlogCore.Repository");
            bulid.RegisterAssemblyTypes(assemblyRespository).AsImplementedInterfaces();
            #endregion
            #region 2、程序集注入 —— 实现层级解耦 =>带有接口层的服务注入
            //获取注入绝对路径
            //var serviceDllFile = Path.Combine(basePath, "BlogCore.Services.dll");
            ////直接采用加载文件的方法
            //var assemblyServices = Assembly.LoadFrom(serviceDllFile);
            //// AOP 开关,如果想要打开指定的功能,只需要在 appsettigns.json 对应对应 true 就行。
            //var cacheType = new List<Type>();
            //if (Appsettings.app(new string[] { "AppSettings", "MemoryCachingAOP", "Enabled" }).ObjToBool())
            //{
            //    cacheType.Add(typeof(BlogCacheAOP));
            //}
            //if (Appsettings.app(new string[] { "AppSettings", "LogAOP", "Enabled" }).ObjToBool())
            //{
            //    cacheType.Add(typeof(BlogLogAOP));
            //}
            //if(Appsettings.app(new string[] { "AppSettings", "RedisCaching", "Enabled" }).ObjToBool()){
            //    cacheType.Add(typeof(BlogRedisCacheAOP));
            //}
            //bulid.RegisterAssemblyTypes(assemblyServices).
            //    AsImplementedInterfaces()
            //    .InstancePerLifetimeScope()
            //    .EnableInterfaceInterceptors()//对目标类型启用接口拦截。拦截器将被确定,通过在类或接口上截取属性, 或添加 InterceptedBy ()
            //                                  //.InterceptedBy(typeof(BlogLogAOP))
            //    .InterceptedBy(cacheType.ToArray());//允许将拦截器服务的列表分配给注册。说人话就是,将拦截器添加到要注入容器的接口或者类之上。
            //var repositoryDllFile = Path.Combine(basePath, "BlogCore.Repository.dll");
            //var assemblysRepository = Assembly.LoadFrom(repositoryDllFile);
            //bulid.RegisterAssemblyTypes(assemblysRepository).AsImplementedInterfaces();
            #endregion
            #region 3 没有接口的单独类 class注入
            #endregion
            //将service填充到autofac容器生成器
            bulid.Populate(services);
            var applicationContainer = bulid.Build();
            #endregion
            //第三方IOC接管core内置DI容器
            return(new AutofacServiceProvider(applicationContainer));
        }