SDK 安装

下载 SDK 地址

  • 判断是否安装成功?

    Win + R => cmd => dotnet
    Options:
    -h | –help

    看到这样的结果,证明成功了。

新建项目

  • 文件 => 项目 选择.Net Core 版本和项目类型 ASP.NET Core 2.0,选择 API 的项目类型

    目录说明
    Dependencies (依赖项)
    Properties (设置文件)
    Controllers (Code)
    Program.cs (入口地址) 重要
    StartUp.cs (项目启动设置) 重要

  • 调试方法
    1、通过IIS调试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    //launthSettings.json 
    "IIS Express": {
    "commandName": "IISExpress",
    "launchBrowser": true,
    "launchUrl": "",
    "environmentVariables": {
    "ASPNETCORE_ENVIRONMENT": "Development"
    }
    },

    2、Kestrel web应用调式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //launthSettings.json 
    "项目名称": {
    "commandName": "Project",
    "launchBrowser": true,
    "launchUrl": "",
    "environmentVariables": {
    "ASPNETCORE_ENVIRONMENT": "Development"
    },
    "applicationUrl": "http://localhost:58224/"
    }
  • 核心

    1、Routing 路由

    Convention-based (按约定), attribute-based(基于路由属性配置)
    Web api 推荐使用 attribute-based


    常用的Http Method有:

    Get, 查询, Attribute: HttpGet

    POST, 创建, HttpPost

    PUT 整体修改更新 HttpPut

    PATCH 部分更新, HttpPatch

    DELETE 删除, HttpDelete


    2、内容协商 Content Negotiation

    例如: application/json, application/xml 等
    .Net Core 默认提供的是 json 格式

    1
    2
    3
    4
    5
    6
    7
    //如果想输出 xml 格式,就在 Startup.cs 配置这里:
    services.AddMvc(
    options => {
    options.ReturnHttpNotAcceptable = true;
    options.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter())
    }
    );

    3、创建Post Action

    4、Validation 验证

    5、PUT请求

Swagger

  • 引用 Nuget 包
    右键项目中的 Dependencies (依赖项) – > Manage Nuget Packags (管理Nuget程序包) –> Browse (浏览) –> Search (搜索) “Swashbuckle.AspNetCore” –> Install (安装)
  • 配置服务

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    //class Startup

    public void ConfigureServices(IServiceCollection services)
    {
    services.AddMvc(); // 注册MVC到Container

    #region Swagger
    services.AddSwaggerGen(c =>
    {
    c.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info
    {
    Version = "v0.1.0",
    Title = "MResManager API",
    Description = "框架说明文档",
    TermsOfService = "None",
    Contact = new Swashbuckle.AspNetCore.Swagger.Contact { Name = "MResManager", Email = "", Url = "" }
    });

    var basePath = PlatformServices.Default.Application.ApplicationBasePath;
    var xmlPath = Path.Combine(basePath, "MResManager.xml"); //这个就是MResManager的xml文件名
    c.IncludeXmlComments(xmlPath, true); //默认的第二个参数是false,这个是controller的注释,记得修改

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

    c.IncludeXmlComments(xmlModelPath);
    });

    #endregion
    }

  • 启动Http中间件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    //class Startup

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
    //判断是否是环境变量
    if (env.IsDevelopment())
    {
    app.UseDeveloperExceptionPage();
    }

    #region Swagger
    app.UseSwagger();
    app.UseSwaggerUI(c =>
    {
    c.SwaggerEndpoint("/swagger/v1/swagger.json", "ApiHelp V1");
    c.RoutePrefix = "";//路径配置,设置为空,表示直接访问该文件,
    //路径配置,设置为空,表示直接在根域名(localhost:8001)访问该文件,注意localhost:8001/swagger是访问不到的
    });
    #endregion

    app.UseMvc();
    }

  • 运行项目 域名后面输入/swagger,即可看到接口文档。

注意点:

  • 设置默认直接首页访问

    1
    2
    3
    4
    5
    6
    //启动Http中间件中这一段代码

    c.RoutePrefix = "";//路径配置,设置为空,表示直接访问该文件,
    //路径配置,设置为空,表示直接在根域名(localhost:8001)访问该文件,注意localhost:8001/swagger是访问不到的
    }

    launchSettings.json 的 launchUrl:“”,这样我们无论是本地开发环境,还是生产环境,都可以默认首页加载了。

  • 为接口, Model 添加注释说明

    右键项目名称 => 属性 => 生成,勾选“输出”下面的“xml文档文件”, 路径可以相对路径(../xxx/xxx.xml)。
    生成序列化程序集 => 自动 (指定的任务可执行文件“sgen.exe”未能运行。文件名或扩展名太长。)
    Errors and warnings 错误和警告 => 禁止显示警告:1701;1702;1591 (去掉Swagger警告提示)


    Xml 文件配置好了之后, 系统启动的时候,读取文件:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //配置服务这一段代码

    var basePath = PlatformServices.Default.Application.ApplicationBasePath;
    var xmlPath = Path.Combine(basePath, "MResManager.xml"); //这个就是MResManager的xml文件名
    c.IncludeXmlComments(xmlPath, true); //默认的第二个参数是false,这个是controller的注释,记得修改

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

    c.IncludeXmlComments(xmlModelPath);
    }

JWT

  • 官方解释: JWT(读作 [/dʒɒt/]),即JSON Web Tokens,是一种基于JSON的、用于在网络上声明某种主张的令牌(token),由三部分组成: 头信息(header), 消息体(payload)和签名(signature)。它是一种用于双方之间传递安全信息的表述性声明规范。JWT作为一个开放的标准(RFC 7519),定义了一种简洁的、自包含的方法,从而使通信双方实现以JSON对象的形式安全的传递信息。

JWT并不是一种只能权限验证的工具,而是一种标准化的数据传输规范。所以,只要是在系统之间需要传输简短但却需要一定安全等级的数据时,都可以使用JWT规范来传输。规范不受平台限制,所以JWT做为授权验证可以跨平台。

生成 Token 令牌

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
public class JwtHelper
{

/// <summary>
/// 颁发JWT字符串
/// </summary>
/// <param name="tokenModel"></param>
/// <returns></returns>
public static string IssueJwt(TokenModelJwt tokenModel)
{
// 自己封装的 appsettign.json 操作类,看下文
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>
{
/*
* 特别重要:
1、这里将用户的部分信息,比如 uid 存到了Claim 中,如果你想知道如何在其他地方将这个 uid从 Token 中取出来,请看下边的SerializeJwt() 方法,或者在整个解决方案,搜索这个方法,看哪里使用了!
2、你也可以研究下 HttpContext.User.Claims ,具体的你可以看看 Policys/PermissionHandler.cs 类中是如何使用的。
*/

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()}") ,
//这个就是过期时间,目前是过期1000秒,可自定义,注意JWT有自己的缓冲过期时间
new Claim (JwtRegisteredClaimNames.Exp,$"{new DateTimeOffset(DateTime.Now.AddSeconds(1000)).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;
try
{
jwtToken.Payload.TryGetValue(ClaimTypes.Role, out role);
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
var tm = new TokenModelJwt
{
Uid = (jwtToken.Id).ObjToInt(),
Role = role != null ? role.ObjToString() : "",
};
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; }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85

//Appsettings —— appsetting.json 操作类
public class Appsettings
{
static IConfiguration Configuration { get; set; }

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

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

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

Configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.Add(new JsonConfigurationSource { Path = Path, Optional = false, ReloadOnChange = true })//这样的话,可以直接读目录里的json文件,而不是 bin 文件夹下的,所以不用修改复制属性
.Build();


}

/// <summary>
/// 封装要操作的字符
/// </summary>
/// <param name="sections"></param>
/// <returns></returns>
public static string app(params string[] sections)
{
try
{
var val = string.Empty;
for (int i = 0; i < sections.Length; i++)
{
val += sections[i] + ":";
}

return Configuration[val.TrimEnd(':')];
}
catch (Exception)
{
return "";
}

}
}

//登录的时候使用
public async Task<object> GetJwtStr(string name, string pass)
{
string jwtStr = string.Empty;
bool suc = false;

// 获取用户的角色名,请暂时忽略其内部是如何获取的,可以直接用 var userRole="Admin"; 来代替更好理解。
var userRole = await _sysUserInfoServices.GetUserRoleNameStr(name, pass);
       if (userRole != null)
{
// 将用户id和角色名,作为单独的自定义变量封装进 token 字符串中。
TokenModelJwt tokenModel = new TokenModelJwt {Uid = 1, Role = userRole};
jwtStr = JwtHelper.IssueJwt(tokenModel);//登录,获取到一定规则的 Token 令牌
suc = true;
}
else
{
jwtStr = "login fail!!!";
}

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

JWT 授权认证

Swagger 已经帮我们实现了录入 Token 令牌的功能:
ConfigureServices -> AddSwaggerGen 服务中, 增加如下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#region Token绑定到ConfigureServices
//添加header验证信息
//c.OperationFilter<SwaggerHeader>();
var security = new Dictionary<string, IEnumerable<string>> { { "MResManager", new string[] { } }, };
c.AddSecurityRequirement(security);

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

运行项目,swagger/index.html 页面里看到这个Token入口: Authorize, 点击按钮,输入框输入 Bearer xxxxxxxxxxxxx
注意 Bearer 后面是一个空格,如果使用中间件 app.UseMiddleware() ,要是使用 Bearer xxxxxxxxxxxxx 传值的时候,在中间件的方法中,把Token的 “Bearer 空格” 字符给截取掉

1、API 接口授权策略

直接接口上,设置该接口对应的角色权限

1
2
3
4
5
[HttpPost]
[Authorize(Roles = "Admin")]
public void Post([FromBody]string value)
{
}

ConfigureService 中设置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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"));
});

// controller 或者 action 上,直接写策略名
[HttpGet]
[Authorize(Policy = "SystemOrAdmin")]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "value1", "value2" };
}

//官方认证配置
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,
};

});

2、自定义认证之身份验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class JwtTokenAuth
{
// 中间件一定要有一个next,将管道可以正常的走下去
private readonly RequestDelegate _next;
public JwtTokenAuth(RequestDelegate next)
{
_next = next;
}

public Task Invoke(HttpContext httpContext)
{

//检测是否包含'Authorization'请求头
if (!httpContext.Request.Headers.ContainsKey("Authorization"))
{
return _next(httpContext);
}
var tokenHeader = httpContext.Request.Headers["Authorization"].ToString().Replace("Bearer ", "");

try
{
if (tokenHeader.Length >= 128)
{
TokenModelJwt tm = JwtHelper.SerializeJwt(tokenHeader);

//授权 Claim 关键
var claimList = new List<Claim>();
var claim = new Claim(ClaimTypes.Role, tm.Role);
claimList.Add(claim);
var identity = new ClaimsIdentity(claimList);
var principal = new ClaimsPrincipal(identity);
httpContext.User = principal;
}

}
catch (Exception e)
{
Console.WriteLine($"{DateTime.Now} middleware wrong:{e.Message}");
}
return _next(httpContext);
}

}
// 这里定义一个中间件Helper,主要作用就是给当前模块的中间件取一个别名
public static class MiddlewareHelpers
{
public static IApplicationBuilder UseJwtTokenAuth(this IApplicationBuilder app)
{
return app.UseMiddleware<JwtTokenAuth>();
}
}

3、开启中间件

startup.cs -> Configure 中配置认证中间件

1
2
3
4
5
//自定义认证中间件
app.UseJwtTokenAuth(); //也可以app.UseMiddleware<JwtTokenAuth>();

//官方认证中间件
app.UseAuthentication();

JWT知识点