17 KiB
title, date, author, top_img
| title | date | author | top_img |
|---|---|---|---|
| ASP.Net | 2021-03-23 10:30:31 | 文永达 | https://gcore.jsdelivr.net/gh/volantis-x/cdn-wallpaper/abstract/67239FBB-E15D-4F4F-8EE8-0F1C9F3C4E7C.jpeg |
ASP.NET Core
Web API 项目初始化搭建
首先打开Visual Studio 2022,然后选择创建新项目
之后筛选下拉框选择如红框标注
起一个项目名称及选择项目位置,下一步
框架选择.Net 6.0(长期支持)
选择启用Docker,为了之后可以部署到Docker容器
启用OpenAPI支持是为了可以输出Swagger接口文档,但如果使用Furion框架的话,需要勾掉
顶级语句是无需在Program.cs中显式包含Main方法,可以使用顶级语句功能最大程度地减少必须编写的代码
点击创建即可
集成Furion框架
在NuGet包管理器中搜索Furion
选择安装的项目,然后安装即可
可能遇到的问题
包降级
将提示的NuGet包升级到 前者的版本即可,比如图内的 Swashbuckle.AspNetCore 原有的版本是 6.2.3 那么升级到 6.5.0即可
部署到Docker
安装.Net SDK 6.0环境
sudo rpm -Uvh https://packages.microsoft.com/config/centos/7/packages-microsoft-prod.rpm
sudo yum install dotnet-sdk-6.0
dotnet --info
Visual Studio添加Docker支持
Linux下构建Docker镜像
docker image build -f ./XiaodaERP/Dockerfile -t aspnetcore .
docker images
运行Docker镜像
docker run --name=aspnetcore -p 9001:80 -d aspnetcore
docker ps
cd /usr/local/jenkins_home/workspace/XiaodaERP_NetCore
echo $PWD
docker image build -f ./XiaodaERP/Dockerfile -t xiaodaerp/netcore .
docker images
docker run --name xiaodaerp/netcore -p 7274:80 -d xiaodaerp/netcore
顶级语句配置Program.cs
取消默认JSON首字母小写命名
builder.Services.AddControllers().AddJsonOptions(options => {
options.JsonSerializerOptions.PropertyNamingPolicy = null;
});
Json序列化时忽略属性为null的值
builder.Services.AddControllers().AddJsonOptions(options => {
options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
});
Json序列化时日期类型格式化输出
builder.Services.AddControllers().AddJsonOptions(options =>
{
options.JsonSerializerOptions.Converters.Add(new SystemTextJsonDateTimeJsonConverter("yyyy-MM-dd HH:mm:ss"));
});
集成Furion框架
var builder = WebApplication.CreateBuilder(args).Inject();
builder.Services.AddControllers().AddInject();
app.UseInject();
使用Autofac自动注入Service
通过NuGet包管理器 安装NuGet包
Autofac
Autofac.Extensions.DependencyInjection
Autofac.Extras.DynamicProxy
新建ServiceAutofac.cs类
using System.Reflection;
namespace XiaodaERP
{
public class ServiceAutofac
{
/// <summary>
/// 获取程序集名称
/// </summary>
/// <returns></returns>
public static string GetAssemblyName()
{
return Assembly.GetExecutingAssembly().GetName().Name;
}
}
}
Program.cs配置
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
builder.Host.ConfigureContainer<ContainerBuilder>(builder =>
{
Assembly assembly = Assembly.Load(ServiceAutofac.GetAssemblyName());//注入Service程序集 可以是其他程序集
builder.RegisterAssemblyTypes(assembly)
.AsImplementedInterfaces()
.InstancePerDependency();
});
注入Entity Framework Core 6 DbContext上下文
builder.Services.AddDbContext<OracleDbContext>(options =>
options.UseOracle(builder.Configuration.GetConnectionString("OracleDbContext")));
builder.Services.AddDbContext<SqlServerDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("SqlServerDbContext")));
使用JWT进行授权与认证
安装NuGet包
Microsoft.AspNetCore.Authentication.JwtBearer
appsettings.json配置文件中配置
"Authentication": {
"SecretKey": "nadjhfgkadshgoihfkajhkjdhsfaidkuahfhdksjaghidshyaukfhdjks",
"Issuer": "www.xiaoda",
"Audience": "www.xiaoda"
}
Program.cs顶级语句配置
// 使用Autofac自动注入Service
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
builder.Host.ConfigureContainer<ContainerBuilder>(builder =>
{
Assembly assembly = Assembly.Load(ServiceAutofac.GetAssemblyName());//注入Service程序集 可以是其他程序集
builder.RegisterAssemblyTypes(assembly)
.AsImplementedInterfaces()
.InstancePerDependency();
// 在IOC容器中注入
// 用于Jwt的各种操作
builder.RegisterType<JwtSecurityTokenHandler>().InstancePerLifetimeScope();
// 支持泛型存入Jwt
builder.RegisterType<TokenHelper>().InstancePerLifetimeScope();
});
//JWT认证
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
{
//取出私钥
var secretByte = Encoding.UTF8.GetBytes(builder.Configuration["Authentication:SecretKey"]);
options.TokenValidationParameters = new TokenValidationParameters()
{
//验证发布者
ValidateIssuer = true,
ValidIssuer = builder.Configuration["Authentication:Issuer"],
//验证接受者
ValidateAudience = true,
ValidAudience = builder.Configuration["Authentication:Audience"],
//验证是否过期
ValidateLifetime = true,
//验证私钥
IssuerSigningKey = new SymmetricSecurityKey(secretByte)
};
});
// 顺序不能颠倒
// 你是谁 授权
app.UseAuthentication();
// 你可以干什么 验证
app.UseAuthorization();
新建TokenHelper.cs工具类
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Reflection;
using System.Security.Claims;
using System.Text;
using XiaodaERP.Models;
namespace XiaodaERP.Utils
{
public class TokenHelper
{
private readonly IConfiguration _configuration;
private readonly JwtSecurityTokenHandler _jwtSecurityTokenHandler;
public TokenHelper(IConfiguration configuration, JwtSecurityTokenHandler jwtSecurityTokenHandler)
{
this._configuration = configuration;
this._jwtSecurityTokenHandler = jwtSecurityTokenHandler;
}
public static string? Token { get; set; }
// 生成Token
public string CreateJwtToken<T>(T user)
{
// 生成JWT
// Header,选择签名算法
var signingAlogorithm = SecurityAlgorithms.HmacSha256;
// Payload,存放用户信息,放用户ID,用户名
var claimList = this.CreateClaimList(user);
//Signature
//取出私钥并以utf8编码字节输出
var secretByte = Encoding.UTF8.GetBytes(_configuration["Authentication:SecretKey"]);
//使用非对称算法对私钥进行加密
var signingKey = new SymmetricSecurityKey(secretByte);
//使用HmacSha256来验证加密后的私钥生成数字签名
var signingCredentials = new SigningCredentials(signingKey, signingAlogorithm);
//生成Token
var Token = new JwtSecurityToken(
issuer: _configuration["Authentication:Issuer"], //发布者
audience: _configuration["Authentication:Audience"], //接收者
claims: claimList, //存放的用户信息
notBefore: DateTime.UtcNow, //发布时间
expires: DateTime.UtcNow.AddMinutes(30), //有效期设置为1天
signingCredentials //数字签名
);
//生成字符串token
var TokenStr = new JwtSecurityTokenHandler().WriteToken(Token);
return TokenStr;
}
// 获取Token Payload信息
public T GetToken<T>(string token)
{
Type t = typeof(T);
object obj = Activator.CreateInstance(t);
var b = _jwtSecurityTokenHandler.ReadJwtToken(token);
foreach (var item in b.Claims)
{
PropertyInfo propertyInfo = t.GetProperty(item.Type);
if (propertyInfo != null && propertyInfo.CanRead)
{
propertyInfo.SetValue(obj, item.Value, null);
}
}
return (T)obj;
}
// 根据类生成Token 断言列表
private List<Claim> CreateClaimList<T>(T authUser)
{
var Class = typeof(T);
List<Claim> claimList = new();
foreach (var item in Class.GetProperties())
{
// 不将PassWord放入Token中
if (item.Name == "PassWord")
{
continue;
}
// 将UserName属性名重命名为username存入Token中
if (item.Name == "UserName")
{
claimList.Add(new Claim("username", Convert.ToString(item.GetValue(authUser))));
continue;
}
claimList.Add(new Claim(item.Name, Convert.ToString(item.GetValue(authUser))));
}
return claimList;
}
}
}
在登录方法中加入
public ViewUser Login(string UserName, string PassWord)
{
var res = _sqlServerDbContext.Users.Include(user => user.Role).FirstOrDefault(x => x.UserName == UserName);
if (res != null)
{
if (res.PassWord == Md5Encoding(PassWord))
{
// 生成JWT
var TokenStr = _tokenHelper.CreateJwtToken(res);
var config = new MapperConfiguration(cfg => cfg.CreateMap<User, ViewUser>()
.ForMember(d => d.username, opt => opt.MapFrom(src => src.UserName))
.AfterMap((src, des) => des.Roles = new Role[1] { src.Role })
.AfterMap((src, des) => des.Token = "bearer " + TokenStr) // 需要加上bearer
.AfterMap((src, des) => des.HomePath = "/dashboard/analysis")
.AfterMap((src, des) => des.password = null));
var mapper = config.CreateMapper();
return mapper.Map<ViewUser>(res);
}
}
return null;
}
WebAPI 需要认证的加上[Authorize]注解,注意登录不能加
[AuthFilter]
[HttpPost(Name = "login")]
public ResultUtil Login(ViewUser viewUser) =>
ResultUtil.ok(_userService.Login(viewUser.username, viewUser.password));
// 需要认证的API
[Authorize]
[AuthFilter]
[HttpGet(Name = "getUserInfo")]
public ResultUtil GetUserInfo()
{
Token = HttpContext.Request.Headers["Authorization"];
Token = Token.Split(" ")[1];
TokenHelper.Token = Token;
ViewUser us = _tokenHelper.GetToken<ViewUser>(Token);
return ResultUtil.ok(us);
}
访问登录接口
访问需要认证的接口,需要把Token放在请求头中,如果不携带Token,访问则报401
请求头Key 为 Authorization
访问成功
面向切面编程(AOP)
三大拦截器
认证拦截器 AuthorizeAttribute
方法拦截器 ActionFilterAttribute
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.IdentityModel.Tokens.Jwt;
using Castle.Core.Internal;
using XiaodaERP.Models;
using XiaodaERP.Utils;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
namespace XiaodaERP.Attributes
{
public class AuthFilter : ActionFilterAttribute
{
//private readonly TokenHelper _tokenHelper;
//public AuthFilter(TokenHelper tokenHelper)
//{
// this._tokenHelper = tokenHelper;
//}
private readonly SqlServerDbContext _sqlServerDbContext;
public AuthFilter(SqlServerDbContext sqlServerDbContext)
{
this._sqlServerDbContext = sqlServerDbContext;
}
private SysActionLog sysActionLog = new()
{
ActionId = Guid.NewGuid().ToString().Replace("-", "").ToUpper()
};
public override void OnActionExecuting(ActionExecutingContext context)
{
var descriptor = context.ActionDescriptor as ControllerActionDescriptor;
string param = string.Empty;
string globalParam = string.Empty;
var jsonSetting = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore };
foreach (var arg in context.ActionArguments)
{
string value = Newtonsoft.Json.JsonConvert.SerializeObject(arg.Value, Formatting.None, jsonSetting);
param += $"{arg.Key} : {value} \r\n";
globalParam += value;
}
// 方法名
Console.WriteLine(descriptor.ActionName);
// 参数值拼接
Console.WriteLine(globalParam);
// 参数名 与 值
Console.WriteLine(param);
sysActionLog.ActionName = descriptor.ActionName;
sysActionLog.RequestParams = param;
}
public override void OnActionExecuted(ActionExecutedContext context)
{
// 获取请求Host
Console.WriteLine(context.HttpContext.Request.Host);
sysActionLog.RequestHost = context.HttpContext.Request.Host.ToString();
// 获取请求方法
Console.WriteLine(context.HttpContext.Request.Method);
sysActionLog.RequestMethod = context.HttpContext.Request.Method;
// 获取请求Url
Console.WriteLine(context.HttpContext.Request.Path);
sysActionLog.RequestPath = context.HttpContext.Request.Path.ToString();
// 获取应答返回状态码
Console.WriteLine(context.HttpContext.Response.StatusCode);
if (context.HttpContext.Request.Path.Equals("/api/User/login"))
{
sysActionLog.ActionTime = DateTime.Now;
}
else
{
string Token = context.HttpContext.Request.Headers["Authorization"];
// Token失效
if (Token.IsNullOrEmpty())
{
}
else
{
Token = Token.Split(" ")[1];
TokenHelper.Token = Token;
ViewUser us = new TokenHelper(new JwtSecurityTokenHandler()).GetToken<ViewUser>(Token);
Console.WriteLine(us.UserId);
sysActionLog.ActionUserId = us.UserId;
Console.WriteLine(us.username);
sysActionLog.ActionUserName = us.username;
}
sysActionLog.ActionTime = DateTime.Now;
}
_sqlServerDbContext.SysActionLogs.Add(sysActionLog);
_sqlServerDbContext.SaveChanges();
}
}
}
接口上使用
//[AuthFilter]
[TypeFilter(typeof(AuthFilter))]
[HttpPost(Name = "login")]
public ResultUtil Login(ViewUser viewUser) =>
ResultUtil.ok(_userService.Login(viewUser.username, viewUser.password));
[Authorize]
//[AuthFilter] // 注解为拦截器类名
[TypeFilter(typeof(AuthFilter))] // 因为主键中使用了构造器依赖注入,所以需要使用TypeFilter,并需要在顶级语句中注入 AuthFilter
[HttpGet(Name = "getUserInfo")]
public ResultUtil GetUserInfo()
{
Token = HttpContext.Request.Headers["Authorization"];
Token = Token.Split(" ")[1];
TokenHelper.Token = Token;
ViewUser us = _tokenHelper.GetToken<ViewUser>(Token);
return ResultUtil.ok(us);
}
顶级语句中注入
builder.Services.AddScoped<AuthFilter>();
异常拦截器 ExceptionFilterAttribute












