【笔记】ASP.NET Core 2.2 Web API —— 学习笔记

当年刚接触 ASP.NET Core 2.2 时,留下的学习笔记。现在把它挪到 CSDN,也是对过去学习 ASP.NET Core 痕迹进行记录。

VS 2019 + ASP.NET Core 2.2 + sqlSugarCore (ORM)

1. 仓储模式 + 服务 + 抽象接口

1.1 新建asp.net core 2.2 WebApi项目

nmmking.Core.Api
nmmking.Core.Common
nmmking.Core.IRepository
nmmking.Core.IServices
nmmking.Core.Model
nmmking.Core.Repository
nmmking.Core.Services
nmmking.Core.Test

2.nmmking.Core.Model 使用 Sqlsuger Code first

2.1用 nuget 安装 sqlSugarCore ,新建 model 类

namespace nmmking.Core.Model.Models
{
    public class User
    {
        [SugarColumn(IsNullable =false,IsPrimaryKey =true,IsIdentity =true)]
        public int Id { get; set; }
        [SugarColumn(Length =50)]
        public string Name { get; set; }
        [SugarColumn(Length =50,IsNullable =true)]
        public string Password { get; set; }
    }
}

2.2 新建IDbContext接口

namespace nmmking.Core.Model
{
    public interface IDbContext
    {
        /// <summary>
        /// 操作数据库对象
        /// </summary>
        SqlSugarClient db { get; }

        /// <summary>
        /// 创建数据表
        /// </summary>
        /// <param name="Backup">是否备份</param>
        /// <param name="StringDefaultLength">string类型映射的长度</param>
        /// <param name="types">要创建的数据表</param>
        void CreateTable(bool Backup = false, int StringDefaultLength = 50, params Type[] types);

        /// <summary>
        /// 创建表
        /// </summary>
        /// <param name="Backup">是否备份</param>
        /// <param name="StringDefaultLength">string类型映射的长度</param>
        //public void CreateAllTable(bool Backup = false, int StringDefaultLength = 50);
    }
}

2.3 新建MyContext类

public class MyContext: IDbContext
    {

        public SqlSugarClient db
        {
            get
            {
                return new SqlSugarClient(new ConnectionConfig()
                {
                    ConnectionString = "server=.;database=nmmkingCoreDb;uid=sa;pwd=123456;",
                    DbType = DbType.SqlServer,//数据库类型
                    IsAutoCloseConnection = true,//自动释放数据务,如果存在事务,在事务结束后释放
                    InitKeyType = InitKeyType.Attribute,//从实体特性中读取主键自增列信息
                });
            }
        }

        //public static SqlSugarClient GetInstance()
        //{
        //    db = new SqlSugarClient(new ConnectionConfig()
        //    {
        //        ConnectionString = "server=.;database=StudentDb;uid=sa;pwd=123456;",
        //        DbType = DbType.SqlServer,//设置数据库类型
        //        IsAutoCloseConnection = true,//自动释放数据库,如果存在事务,在事务结束之后释放。
        //        InitKeyType = InitKeyType.Attribute//从实体特性中读取主键自增列信息   
        //    });

        //    //db.Aop.OnLogExecuting = (sql, pars) =>
        //    //{
        //    //    Console.WriteLine(sql + "\r\n" + db.Utilities.SerializeObject
        //    //        (pars.ToDictionary(it => it.ParameterName, it => it.Value)));
        //    //    Console.WriteLine();
        //    //};

        //    return db;
        //}

        public void CreateTable(bool Backup = false, int StringDefaultLength = 50, params Type[] types)
        {
            //设置varchar的默认长度
            db.CodeFirst.SetStringDefaultLength(StringDefaultLength);
            db.DbMaintenance.CreateDatabase();

            //创建表
            if (Backup)
            {
                db.CodeFirst.BackupTable().InitTables(types);
            }
            else
            {
                db.CodeFirst.InitTables(types);
            }
        }

        public SimpleClient<User> userDb { get { return new SimpleClient<User>(db); } }

        //public SqlSugarClient db => throw new NotImplementedException();
    }

2.4 使用

namespace nmmking.Core.Test
{
    class Program
    {
        static void Main(string[] args)
        {
            MyContext context = new MyContext();
            context.CreateTable(false, 50, typeof(User));

            //AppDbContext context = new AppDbContext();
            //context.CreateTable(false, 50, typeof(Students), typeof(Schools));

            Console.WriteLine("Hello World!");
        }
    }
}

3. 仓储层

3.1 IBaseRepository

namespace nmmking.Core.IRepository
{
    public interface IBaseRepository<T> where T:class
    {
        Task<T> QueryById(object id, bool IsUseCache = false); //根据id查询

        Task<List<T>> GetEntities(Expression<Func<T, bool>> whereLambda); //根据查询条件查询

        Task<bool> Add(T entity);

        Task<bool> DeleteById(object Id);

        Task<bool> Update(T entity);
    }
}

3.2 BaseRepository

namespace nmmking.Core.Repository
{
    public class BaseRepository<T> : IBaseRepository<T> where T : class, new()
    {
        private readonly IDbContext _dbContext;

        public BaseRepository(IDbContext dbContext)
        {
            this._dbContext = dbContext;
        }

        // 增加单条数据
        public async Task<bool> Add(T entity)
        {
            return await _dbContext.db.Insertable<T>(entity).ExecuteCommandAsync() > 0;
        }

        // 根据id删除单条数据
        public async Task<bool> DeleteById(object id)
        {
            return await _dbContext.db.Deleteable<T>(id).ExecuteCommandHasChangeAsync();
        }

        // 查询数据列表
        public async Task<List<T>> GetEntities(Expression<Func<T, bool>> whereExpression)
        {
            return await _dbContext.db.Queryable<T>().WhereIF(whereExpression != null, whereExpression).ToListAsync();
        }

        // 根据Id查询一条数据
        public async Task<T> QueryById(object id, bool IsUseCache =false)
        {
            return await _dbContext.db.Queryable<T>().WithCacheIF(IsUseCache).In(id).SingleAsync();
        }

        // 更新实体数据
        public Task<bool> Update(T entity)
        {
            var i = await Task.Run(() => dbcontext.Updateable(model).ExecuteCommand());
            return i > 0;
        }
    }
}

4.读取 appsettings.json 文件

4.1 nuget 安装 Microsoft.Extensions.Configuration.Binder,Microsoft.Extensions.Configuration.Json。

4.2 新建 Appsettings 类

namespace nmmking.Core.Common.Helper
{
    /// <summary>
    /// appsettings.json操作类
    /// </summary>
    public class Appsettings
    {
        static IConfiguration Configuration { get; set; }
        static string contentPath { get; set; }


        static Appsettings()
        {
            //如果你把配置文件 是 根据环境变量来分开了,可以这样写
            //Path = $"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json";

            Configuration = new ConfigurationBuilder()
               .Add(new JsonConfigurationSource { Path = "appsettings.json", ReloadOnChange = true })//请注意要把当前appsetting.json 文件->右键->属性->复制到输出目录->始终复制
               .Build();
        }

        /// <summary>
        /// 封装要操作的字符
        /// </summary>
        /// <param name="sections">节点配置</param>
        /// <returns></returns>
        public static string app(params string[] sections)
        {
            try
            {

                if (sections.Any())
                {
                    return Configuration[string.Join(":", sections)];
                }
            }
            catch (Exception) { }

            return "";
        }

        /// <summary>
        /// 递归获取配置信息数组
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="sections"></param>
        /// <returns></returns>
        public static List<T> app<T>(params string[] sections)
        {
            List<T> list = new List<T>();
            // 引用 Microsoft.Extensions.Configuration.Binder 包
            Configuration.Bind(string.Join(":", sections), list);
            return list;
        }
    }
}

4.3 使用

string iss = Appsettings.app(new string[] { "Audience", "Issuer" });
string aud = Appsettings.app(new string[] { "Audience", "Audience" }); 

5.使用Cors跨域

5.1 允许所有域名访问接口
在Startup.cs文件的ConfigureServices方法添加:

public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

            services.AddCors(c => 
            {
                c.AddPolicy("LimitRequests", policy => 
                {
                    policy
                    .AllowAnyOrigin()
                    .AllowAnyHeader()
                    .AllowAnyMethod();
                });
            });

            services.AddTransient(typeof(IBaseRepository<>), typeof(BaseRepository<>));

            services.AddTransient<IUserService, UserService>();

            services.AddTransient<IDbContext, MyContext>();
        }
在Startup.cs文件的 Configure 方法 添加:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        app.UseCors("LimitRequests");
        app.UseMvc();
}

运行:http://localhost:61576/api/values

测试一下,打开百度地址:https://www.baidu.com/,按F12,在Console输入:

$.get("http://localhost:61576/api/values",function(data,status){
	console.log(data);
});

5.2 限制域的访问

            services.AddCors(c => 
            {
                c.AddPolicy("LimitRequests", policy => 
                {
                    policy
                    //.AllowAnyOrigin()
                    .WithOrigins(new string[]{ "http://localhost:8881" })
                    //.WithOrigins(Appsettings.app(new string[] { "Startup", "Cors", "IPs" }).Split(','))
                    .AllowAnyHeader()
                    .AllowAnyMethod();
                });
            });

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            app.UseCors("LimitRequests");
            app.UseMvc();
        }

5.3 跨域访问

下载 nginx ,http://nginx.org/en/download.html,配置 nginx。

新建 index.html

<html>
<head>
<script src="https://s3.pstatp.com/cdn/expire-1-M/jquery/3.3.1/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
	$.get("http://localhost:61576/api/values",function(data,status){
		alert(data);
		console.log(data);
	});
});
</script>
</head>

<body>
<p>If you click on me, I will disappear.</p>
</body>

</html> 

出现跨域问题

修改访问地址

            services.AddCors(c => 
            {
                c.AddPolicy("LimitRequests", policy => 
                {
                    policy
                    //.AllowAnyOrigin()
                    .WithOrigins(new string[]{ "http://localhost:8889" })
                    //.WithOrigins(Appsettings.app(new string[] { "Startup", "Cors", "IPs" }).Split(','))
                    .AllowAnyHeader()
                    .AllowAnyMethod();
                });
            });
刷新一下index.html,弹出界面,访问成功

6.使用Log4Net日志处理

安装

注册服务

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                 .UseStartup<Startup>()
                    .ConfigureLogging((hostingContext, builder) =>
                    {
                        //过滤掉系统默认的一些日志
                        builder.AddFilter("System", LogLevel.Error);
                        builder.AddFilter("Microsoft", LogLevel.Error);
                        //builder.AddFilter();

                        //可配置文件
                        var path = Path.Combine(Directory.GetCurrentDirectory(),
                            "Log4net.config");
                        builder.AddLog4Net(path);
                    });

配置文件,下面文件配置了error和info两种级别的日志

<?xml version="1.0" encoding="utf-8"?>
<log4net>
	<!-- 将日志以回滚文件的形式写到文件中 -->
	<!-- 按日期切分日志文件,并将日期作为日志文件的名字 -->
	<!--Error-->
	<appender name="ErrorLog" type="log4net.Appender.RollingFileAppender">
		<file value="Log/"/>
		<appendToFile value="true" />
		<rollingStyle value="Date" />
		<!--日期的格式,每天换一个文件记录,如不设置则永远只记录一天的日志,需设置-->
		<datePattern value="&quot;GlobalExceptionLogs_&quot;yyyyMMdd&quot;.log&quot;" />
		<!--日志文件名是否为静态-->
		<StaticLogFileName value="false"/>
		<!--多线程时采用最小锁定-->
		<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
		<!--布局(向用户显示最后经过格式化的输出信息)-->
		<layout type="log4net.Layout.PatternLayout">
			<conversionPattern value="%date| %-5level %newline%message%newline--------------------------------%newline" />
		</layout>
		<filter type="log4net.Filter.LevelRangeFilter">
			<levelMin value="ERROR" />
			<levelMax value="FATAL" />
		</filter>
	</appender>
	<!--Error-->

	<!--Info-->
	<appender name="InfoLog" type="log4net.Appender.RollingFileAppender">
		<!--定义文件存放位置-->
		<file value="Log/"/>
		<appendToFile value="true" />
		<rollingStyle value="Date" />
		<!--日志文件名是否为静态-->
		<StaticLogFileName value="false"/>
		<!--日期的格式,每天换一个文件记录,如不设置则永远只记录一天的日志,需设置-->
		<datePattern value="&quot;GlobalInfoLogs_&quot;yyyyMMdd&quot;.log&quot;" />
		<!--多线程时采用最小锁定-->
		<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
		<!--布局(向用户显示最后经过格式化的输出信息)-->
		<layout type="log4net.Layout.PatternLayout">
			<conversionPattern value="%date| %-5level %newline%message%newline--------------------------------%newline" />
		</layout>
		<filter type="log4net.Filter.LevelRangeFilter">
			<levelMin value="DEBUG" />
			<levelMax value="WARN" />
		</filter>
	</appender>
	<!--Info-->

	<root>
		<!-- 控制级别,由低到高:ALL|DEBUG|INFO|WARN|ERROR|FATAL|OFF -->
		<!-- 比如定义级别为INFO,则INFO级别向下的级别,比如DEBUG日志将不会被记录 -->
		<!-- 如果没有定义LEVEL的值,则缺省为DEBUG -->
		<level value="ALL" />
		<!-- 按日期切分日志文件,并将日期作为日志文件的名字 -->
		<appender-ref ref="ErrorLog" />
		<appender-ref ref="InfoLog" />
	</root>
</log4net>

使用

[Produces("application/json")]
    [Route("api/Values")]
    [AllowAnonymous]
    public class ValuesController : ControllerBase
    {
        private readonly IUserService _userService;
        private readonly ILogger<ValuesController> _logger;

        public ValuesController(IUserService userService,ILogger<ValuesController> logger)
        {
            this._userService = userService;
            this._logger = logger;
        }

        // GET api/values
        [HttpGet]
        public ActionResult<IEnumerable<string>> Get()
        {
            _logger.LogError("this is an error");
            return new string[] { "value1", "value2" };
        }
}

7.JWT基于策略的授权

安装 Microsoft.IdentityModel.Tokens 和 System.IdentityModel.Tokens.Jwt

新建Jwt操作类

 public class JwtHelper
    {

        /// <summary>
        /// 颁发JWT字符串
        /// </summary>
        /// <param name="tokenModel"></param>
        /// <returns></returns>
        public static string IssueJWT(TokenModelJWT tokenModel)
        {
            var dateTime = DateTime.UtcNow;

            string iss = Appsettings.app(new string[] { "Audience", "Issuer" });
            string aud = Appsettings.app(new string[] { "Audience", "Audience" });
            string secret = Appsettings.app(new string[] { "Audience", "Secret" });

            //var claims = new Claim[] //old
            var claims = new List<Claim>
                {
                    //下边为Claim的默认配置
                new Claim(JwtRegisteredClaimNames.Jti, tokenModel.Uid.ToString()),
                new Claim(JwtRegisteredClaimNames.Iat, $"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}"),
                new Claim(JwtRegisteredClaimNames.Nbf,$"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}") ,
                //这个就是过期时间,目前是过期100秒,可自定义,注意JWT有自己的缓冲过期时间
                new Claim (JwtRegisteredClaimNames.Exp,$"{new DateTimeOffset(DateTime.Now.AddSeconds(100)).ToUnixTimeSeconds()}"),
                new Claim(JwtRegisteredClaimNames.Iss,iss),
                new Claim(JwtRegisteredClaimNames.Aud,aud),

                //new Claim(ClaimTypes.Role,tokenModel.Role),//为了解决一个用户多个角色(比如:Admin,System),用下边的方法
               };

            // 可以将一个用户的多个角色全部赋予;
            // 作者:DX 提供技术支持;
            claims.AddRange(tokenModel.Role.Split(',').Select(s => new Claim(ClaimTypes.Role, s)));



            //秘钥 (SymmetricSecurityKey 对安全性的要求,密钥的长度太短会报出异常)
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret));
            var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

            var jwt = new JwtSecurityToken(
                issuer: iss,
                claims: claims,
                signingCredentials: creds);

            var jwtHandler = new JwtSecurityTokenHandler();
            var encodedJwt = jwtHandler.WriteToken(jwt);

            return encodedJwt;
        }

        /// <summary>
        /// 解析
        /// </summary>
        /// <param name="jwtStr"></param>
        /// <returns></returns>
        public static TokenModelJWT SerializeJWT(string jwtStr)
        {
            var jwtHandler = new JwtSecurityTokenHandler();
            JwtSecurityToken jwtToken = jwtHandler.ReadJwtToken(jwtStr);
            object role = new object(); ;
            try
            {
                jwtToken.Payload.TryGetValue(ClaimTypes.Role, out role);
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }
            var tm = new TokenModelJWT
            {
                Uid = Convert.ToInt32(jwtToken.Id),
                Role = role != null ? role.ToString() : "",
            };
            return tm;
        }
    }

    /// <summary>
    /// 令牌
    /// </summary>
    public class TokenModelJWT
    {
        /// <summary>
        /// Id
        /// </summary>
        public long Uid { get; set; }
        /// <summary>
        /// 角色
        /// </summary>
        public string Role { get; set; }
        /// <summary>
        /// 职能
        /// </summary>
        public string Work { get; set; }

    }

配置文件appsettings.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "Audience": {
    "Secret": "sdfsdfsrty45634kkhllghtdgdfss345t678fs", //至少要16位
    "Issuer": "nmmking.Core",
    "Audience": "yika"
  },
  "Startup": {
    "Cors": {
      "IPs": "http://127.0.0.1:2364,http://localhost:2364,http://localhost:8080,http://localhost:8021,http://localhost:1818"
    }
  },
    "AllowedHosts": "*"
  }
在Startup.cs文件注册和配置到管道

            #region 基于策略的授权(简单版)
            // 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 【认证】
            //读取配置文件
            var audienceConfig = Configuration.GetSection("Audience");
            var symmetricKeyAsBase64 = audienceConfig["Secret"];
            var keyByteArray = Encoding.ASCII.GetBytes(symmetricKeyAsBase64);
            var signingKey = new SymmetricSecurityKey(keyByteArray);

            //2.1【认证】
            services.AddAuthentication(x =>
            {
                x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
             .AddJwtBearer(o =>
             {
                 o.TokenValidationParameters = new TokenValidationParameters
                 {
                     ValidateIssuerSigningKey = true,
                     IssuerSigningKey = signingKey,
                     ValidateIssuer = true,
                     ValidIssuer = audienceConfig["Issuer"],//发行人
                     ValidateAudience = true,
                     ValidAudience = audienceConfig["Audience"],//订阅人
                     ValidateLifetime = true,
                     ClockSkew = TimeSpan.Zero,
                     RequireExpirationTime = true,
                 };

             });
            #endregion
app.UseAuthentication();

接口 GetJWTToken

    [Produces("application/json")]
    [Route("api/[controller]/[action]")]
    public class ValuesController : ControllerBase
    {
    }
  /// <summary>
        /// 登录接口:随便输入字符,获取token,然后添加 Authoritarian
        /// </summary>
        /// <param name="name"></param>
        /// <param name="pass"></param>
        /// <returns></returns>
        [HttpGet]
        public async Task<object> GetJWTToken(string name, string pass)
        {
            string jwtStr = string.Empty;
            bool suc = false;
            //这里就是用户登陆以后,通过数据库去调取数据,分配权限的操作
            //这里直接写死了


            if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(pass))
            {
                return new JsonResult(new
                {
                    Status = false,
                    message = "用户名或密码不能为空"
                });
            }

            TokenModelJWT tokenModel = new TokenModelJWT();
            tokenModel.Uid = 1;
            tokenModel.Role = name;

            jwtStr = JwtHelper.IssueJWT(tokenModel);
            suc = true;


            return Ok(new
            {
                success = suc,
                token = jwtStr
            });
        }

测试1:测试 Get 

        /// <summary>
        /// 这个也需要认证,只不过登录即可,不一定是Admin
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpGet]
        [Authorize]
        public ActionResult<string> Get()
        {
            return "value";
        }
token 逾期,访问 Get 接口, 结果提示没有权限(未授权),如下图所示:

用户重新登录获取 token (这里没有指定的用户,只要登录就行),如下图所示:

 ​​​​​​再次访问 Get 接口,访问成功如下图所示:

接口2:

        /// <summary>
        /// 这个需要认证,角色必须是Admin,其他的不需要
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        [Authorize(Policy = "SystemOrAdmin")]
        public ActionResult<IEnumerable<string>> GetData()
        {
            _logger.LogError("this is an error");
            return new string[] { "value1", "value2" };
        }

随意的用户登录获取 token,如下所示

 登录的用户不是 Admin,结果是拒绝访问

 如果是admin用户,请求参数和请求头如下:

请求成功 

 

8.Swagger和JWT设置

nuget 安装 Microsoft.OpenApi ,Swashbuckle.AspNetCore,Swashbuckle.AspNetCore.Filters。

在Startup.cs设置:

private const string ApiName = "nmmking.Core.Api";
private readonly string version = "V1";
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseCors("LimitRequests");

            //注册异常处理中间件
            app.UseMiddleware<GlobalExceptionMiddleware>();

            #region Swagger
            app.UseSwagger();
            app.UseSwaggerUI(c =>
            {
                c.SwaggerEndpoint($"/swagger/{version}/swagger.json", $"{ApiName} {version}");
                //c.IndexStream = () => GetType().GetTypeInfo().Assembly.GetManifestResourceStream("index.html");
                c.RoutePrefix = "";
            });
            #endregion
            // 开启权限
            app.UseAuthentication();

            app.UseMvc();
        }
        public IServiceProvider ConfigureServices(IServiceCollection services)
        {
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

            services.AddCors(c => 
            {
                c.AddPolicy("LimitRequests", policy => 
                {
                    policy
                    //.AllowAnyOrigin()
                    .WithOrigins(new string[]{ "http://localhost:8881" })
                    //.WithOrigins(Appsettings.app(new string[] { "Startup", "Cors", "IPs" }).Split(','))
                    .AllowAnyHeader()
                    .AllowAnyMethod();
                });
            });

            #region Swagger UI Service

            var basePath = Microsoft.DotNet.PlatformAbstractions.ApplicationEnvironment.ApplicationBasePath;
            services.AddSwaggerGen(c =>
            {
                //遍历出全部的版本,做文档信息展示
                c.SwaggerDoc(version, new OpenApiInfo
                {
                    Version = version,
                    Title = $"{ApiName} 接口文档——{RuntimeInformation.FrameworkDescription}",
                    Description = $"{ApiName} HTTP API " + version,
                    Contact = new OpenApiContact { Name = ApiName, Email = "nmmking.Core@xxx.com", Url = new Uri("http地址") },
                    License = new OpenApiLicense { Name = ApiName + " 官方文档", Url = new Uri("http地址") }
                });
                c.OrderActionsBy(o => o.RelativePath);


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

                var xmlPath2 = Path.Combine(basePath, "nmmking.Core.Model.xml");
                c.IncludeXmlComments(xmlPath2, true);

                #region Token绑定到ConfigureServices

                // 开启加权小锁
                c.OperationFilter<AddResponseHeadersFilter>();
                c.OperationFilter<AppendAuthorizeToSummaryOperationFilter>();

                // 在header中添加token,传递到后台
                c.OperationFilter<SecurityRequirementsOperationFilter>();

                // Jwt Bearer 认证,必须是 oauth2
                c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
                {
                    Description = "JWT授权(数据将在请求头中进行传输) 直接在下框中输入Bearer {token}(注意两者之间是一个空格)\"",
                    Name = "Authorization",//jwt默认的参数名称
                    In = ParameterLocation.Header,//jwt默认存放Authorization信息的位置(请求头中)
                    Type = SecuritySchemeType.ApiKey
                });
                #endregion
            });

            #endregion

            #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 permissionRequirement = new PermissionRequirement(
                ClaimTypes.Role,//基于角色的授权
                audienceConfig["Issuer"],//发行人
                audienceConfig["Audience"],//听众
                signingCredentials,//签名凭据
                expiration: TimeSpan.FromSeconds(60 * 60)//接口的过期时间
                );

            // 授权
            services.AddAuthorization(options =>
            {
                // 权限变量配置
                options.AddPolicy("Permission",
                         policy => policy.Requirements.Add(permissionRequirement));
            });

            // 令牌验证参数
            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,
            };

            services.AddAuthentication("Bearer")
             .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(permissionRequirement);

            #endregion

            return services.BuildDynamicProxyProvider();
        }

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夜飞鼠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值