文宇通用管理平台敏捷开发–.NET6后台
大家好,
1、 开发环境配置
VS2022 IIS .NET6
VSCode Nodejs ElementPlus
SqlServer数据库
2、后端服务搭建
2.1 创建解决方案
Wenyu.Framework.PortalProject
2.2 创建CoreWebApi项目
Wenyu.Framework.WebApi
3、配置Log4Net
3.1 nuget引入:
<PackageReference Include="log4net" Version="2.0.15" />
<PackageReference Include="Microsoft.Extensions.Logging.Log4Net.AspNetCore" Version="6.1.0" />
<PackageReference Include="System.Data.SqlClient" Version="4.8.5" />
3.2 定义配置文件log4net.config
CfgFiles/log4net.config
<?xml version="1.0" encoding="utf-8" ?>
<log4net>
<!-- Define some output appenders -->
<appender name="rollingAppender" type="log4net.Appender.RollingFileAppender">
<file value="log4\log.txt" />
<!--追加日志内容-->
<appendToFile value="true" />
<!--防止多线程时不能写Log,官方说线程非安全-->
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<!--可以为:Once|Size|Date|Composite-->
<!--Composite为Size和Date的组合-->
<rollingStyle value="Composite" />
<!--当备份文件时,为文件名加的后缀,这里可以作为每一天的日志分别存储不同的文件-->
<datePattern value="yyyyMMdd".txt"" />
<StaticLogFileName value="false"/>
<!--日志最大个数,都是最新的-->
<!--rollingStyle节点为Size时,只能有value个日志-->
<!--rollingStyle节点为Composite时,每天有value个日志-->
<maxSizeRollBackups value="20" />
<!--可用的单位:KB|MB|GB-->
<maximumFileSize value="3MB" />
<!--置为true,当前最新日志文件名永远为file节中的名字-->
<staticLogFileName value="true" />
<!--输出级别在INFO和ERROR之间的日志-->
<filter type="log4net.Filter.LevelRangeFilter">
<param name="LevelMin" value="ALL" />
<param name="LevelMax" value="FATAL" />
</filter>
<layout type="log4net.Layout.PatternLayout">
<!--日志输出格式:时间 日志类型 日志内容-->
<conversionPattern value="%date [%thread] %-5level %logger - %message%newline"/>
</layout>
</appender>
<!--数据库日志-->
<!--https://blog.youkuaiyun.com/liyou123456789/article/details/126264613-->
<appender name="AdoNetAppender_SqlServer" type="log4net.Appender.AdoNetAppender">
<!--日志缓存写入条数,设置为0时只要有一条就立刻写到数据库-->
<bufferSize value="0" />
<connectionType value="System.Data.SqlClient.SqlConnection,System.Data.SqlClient, Version=4.6.1.5, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<connectionString value="data source=.;initial catalog=Wenyu.Framework.DB;integrated security=false;persist security info=True;User ID=sa;Password=123456;TrustServerCertificate=True" />
<commandText value="INSERT INTO Log ([Date],[Thread],[Level],[Logger],[Message],[Exception]) VALUES (@log_date, @thread, @log_level, @logger, @message, @exception)" />
<parameter>
<parameterName value="@log_date" />
<dbType value="DateTime" />
<layout type="log4net.Layout.RawTimeStampLayout" />
</parameter>
<parameter>
<parameterName value="@thread" />
<dbType value="String" />
<size value="255" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%thread" />
</layout>
</parameter>
<parameter>
<parameterName value="@log_level" />
<dbType value="String" />
<size value="50" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%level" />
</layout>
</parameter>
<parameter>
<parameterName value="@logger" />
<dbType value="String" />
<size value="255" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%logger" />
</layout>
</parameter>
<parameter>
<parameterName value="@message" />
<dbType value="String" />
<size value="4000" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%message" />
</layout>
</parameter>
<parameter>
<parameterName value="@exception" />
<dbType value="String" />
<size value="2000" />
<layout type="log4net.Layout.ExceptionLayout" />
</parameter>
</appender>
<!-- levels: OFF > FATAL > ERROR > WARN > INFO > DEBUG > ALL -->
<root>
<priority value="ALL"/>
<!--在系统的日志筛选之后又进行筛选-->
<level value="INFO"/>
<appender-ref ref="rollingAppender" />
<appender-ref ref="AdoNetAppender_SqlServer" />
</root>
</log4net>
builder.Logging.AddLog4Net("CfgFiles/log4net.config");
3.3 创建日志表Log
数据库Wenyu.Framework.DB的日志表Log
create table Log (
Datetime timestamp(3),
Thread varchar2(255),
Log_Level varchar2(255),
Logger varchar2(255),
Message varchar2(4000)
);
4、Swagger配置
项目创建之时,启用OpenApi默认生成
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
4.1、 支持注释
4.1.1 选中CoreWebApi–属性–生成–输出–文档生成–勾选生成包含项目公共API的引用程序集
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
//支持多版本
foreach(string version in typeof(ApiVersions).GetEnumNames())
{
options.SwaggerDoc(version,new OpenApiInfo()
{
Title = $"文宇敏捷后台管理项目Api文档",
Version = version,
Description = $"通用版本的CoreApi版本--{version}"
});
}
//xml文档绝对路径
var file = Path.Combine(AppContext.BaseDirectory, $"{AppDomain.CurrentDomain.FriendlyName}.xml");
//true:显示控制器层注释
options.IncludeXmlComments(file, true);
//对action的名称进行排序,如果有多个,就可以看见效果
options.OrderActionsBy(o => o.RelativePath);
#region 支持jwt token授权
//添加安全定义
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "JWT授权(数据将在请求头中进行传输) 在下方输入Bearer {token} 即可,注意两者之间有空格",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey, //jwt默认的参数名称
BearerFormat = "JWT",
Scheme = "Bearer"
});
#endregion
//添加安全要求
options.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference=new OpenApiReference()
{
Type=ReferenceType.SecurityScheme,
Id="Bearer"
}
},
new string[]{}
}
});
});
4.2 支持版本控制
4.2.1 添加版本枚举
ApiVersions.cs
namespace Wenyu.Framework.WebApi
{
/// <summary>
/// Api版本枚举
/// </summary>
public enum ApiVersions
{
V1,
V2,
V3,
V4
}
}
4.3 支持jwt token授权
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
//支持多版本
foreach(string version in typeof(ApiVersions).GetEnumNames())
{
options.SwaggerDoc(version,new OpenApiInfo()
{
Title = $"文宇敏捷后台管理项目Api文档",
Version = version,
Description = $"通用版本的CoreApi版本--{version}"
});
}
//xml文档绝对路径
var file = Path.Combine(AppContext.BaseDirectory, $"{AppDomain.CurrentDomain.FriendlyName}.xml");
//true:显示控制器层注释
options.IncludeXmlComments(file, true);
//对action的名称进行排序,如果有多个,就可以看见效果
options.OrderActionsBy(o => o.RelativePath);
#region 支持jwt token授权
//添加安全定义
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "JWT授权(数据将在请求头中进行传输) 在下方输入Bearer {token} 即可,注意两者之间有空格",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey, //jwt默认的参数名称
BearerFormat = "JWT",
Scheme = "Bearer"
});
#endregion
//添加安全要求
options.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference=new OpenApiReference()
{
Type=ReferenceType.SecurityScheme,
Id="Bearer"
}
},
new string[]{}
}
});
});
4.4 抽离到独立项目
Wenyu.Framework.WebCore
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
</ItemGroup>
#SwaggerExtend/ApiVersions.cs
namespace Wenyu.Framework.WebCore.SwaggerExtend
{
/// <summary>
/// Api版本枚举
/// </summary>
public enum ApiVersions
{
V1,
V2,
V3,
V4
}
}
#SwaggerExtend/SwaggerExtension.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
namespace Wenyu.Framework.WebCore.SwaggerExtend
{
public static class SwaggerExtension
{
/// <summary>
/// 配置swagger的配置
/// </summary>
/// <param name="service"></param>
public static void AddSwaggerExt(this IServiceCollection service,string docName="",string docDesc= "")
{
service.AddEndpointsApiExplorer();
service.AddSwaggerGen(options =>
{
//支持多版本
foreach (string version in typeof(ApiVersions).GetEnumNames())
{
options.SwaggerDoc(version, new OpenApiInfo()
{
Title =!string.IsNullOrWhiteSpace(docName)?docName: $"文宇敏捷后台管理项目Api文档",
Version = version,
Description = !string.IsNullOrWhiteSpace(docDesc) ? docDesc : $"通用版本的CoreApi版本--{version}"
});
}
//xml文档绝对路径
var file = Path.Combine(AppContext.BaseDirectory, $"{AppDomain.CurrentDomain.FriendlyName}.xml");
//true:显示控制器层注释
options.IncludeXmlComments(file, true);
//对action的名称进行排序,如果有多个,就可以看见效果
options.OrderActionsBy(o => o.RelativePath);
#region 支持jwt token授权
//添加安全定义
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "JWT授权(数据将在请求头中进行传输) 在下方输入Bearer {token} 即可,注意两者之间有空格",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey, //jwt默认的参数名称
BearerFormat = "JWT",
Scheme = "Bearer"
});
#endregion
//添加安全要求
options.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference=new OpenApiReference()
{
Type=ReferenceType.SecurityScheme,
Id="Bearer"
}
},
new string[]{}
}
});
});
}
/// <summary>
/// 使用
/// </summary>
/// <param name="app"></param>
public static void UseSwaggerExt(this WebApplication app,string docName = "")
{
app.UseSwagger();
app.UseSwaggerUI(options =>
{
foreach (string version in typeof(ApiVersions).GetEnumNames())
{
options.SwaggerEndpoint($"/swagger/{version}/swagger.json", !string.IsNullOrWhiteSpace(docName) ? docName : $"文宇敏捷后台管理项目Api文档【{version}】版本");
}
});
}
}
}
使用
builder.Services.AddSwaggerExt();
if (app.Environment.IsDevelopment())
{
//app.UseSwaggerExt("文宇敏捷后台管理项目Api文档");
app.UseSwaggerExt();
}
5、支持跨域请求
5.1 配置跨域拓展方法
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
namespace Wenyu.Framework.WebCore.CorsExtend
{
public static class CorsExtension
{
/// <summary>
/// 配置跨域
/// </summary>
/// <param name="service"></param>
public static void AddCorsExt(this IServiceCollection service)
{
service.AddCors(options =>
{
options.AddPolicy("allCors", policy =>
{
policy.AllowAnyHeader()
.AllowAnyOrigin()
.AllowAnyMethod();
});
});
}
/// <summary>
/// 使用跨域策略生效
/// </summary>
/// <param name="app"></param>
public static void UseCorsExt(this WebApplication app)
{
app.UseCors("allCors");
}
}
}
5.2 使用跨域
builder.Services.AddCorsExt();
app.UseCorsExt();
6、配置支持EfCore
6.1 nuget引入
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.25" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.25" />
6.2 增加数据库访问层
Wenyu.Framework.DbModels
增加实体
Entities/UserEntity.cs
namespace Wenyu.Framework.DbModels.Entities
{
public class UserEntity
{
public int UserId { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
}
6.3 准备DbContext
EFCore操作数据库的核心
FrameworkDbContext.cs
using Microsoft.EntityFrameworkCore;
using Wenyu.Framework.DbModels.Entities;
namespace Wenyu.Framework.DbModels
{
public class FrameworkDbContext:DbContext
{
private readonly string _connectionString;
/// <summary>
/// 带有书库连接字符串的参数
/// </summary>
/// <param name="connectionString"></param>
public FrameworkDbContext(string connectionString) {
this._connectionString = connectionString;
}
public FrameworkDbContext(DbContextOptions<FrameworkDbContext> options)
:base(options)
{
}
public virtual DbSet<UserEntity> UserEntities { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer(this._connectionString);
}
}
/// <summary>
/// 配置数据库表映射关系
/// </summary>
/// <param name="modelBuilder"></param>
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<UserEntity>(entity =>
{
entity.ToTable("UserEntity")
.HasKey(u=>u.UserId);
});
}
}
}
6.4 使用控制台程序初始化数据
Wenyu.Framework.InitDatabase
using Wenyu.Framework.DbModels;
namespace Wenyu.Framework.InitDatabase
{
internal class Program
{
static void Main(string[] args)
{
string connString = "Data Source=.;Initial Catalog=Wenyu.Framework.DB;User ID=sa;Password=123456";
using(FrameworkDbContext context=new FrameworkDbContext(connString))
{
//根据数据库连接字符串的配置,如果不存在就不操作
context.Database.EnsureDeleted();
//根据数据库连接字符串的配置,如果存在就不创建
context.Database.EnsureCreated();
}
Console.WriteLine("数据库创建成功");
}
}
}
6.5 依赖注入DbContext
#region DbContext
var connString = builder.Configuration.GetConnectionString("Default");
builder.Services.AddDbContext<FrameworkDbContext>(optionsBuilder =>
{
optionsBuilder.UseSqlServer(connString);
});
#endregion
7 构建业务逻辑层
7.1 为支持IOC容器,这创建的业务逻辑层有抽象层和具体实现层
7.2 抽象层
Wenyu.Framework.IBusinessServices
7.3 具体实现层
Wenyu.Framework.BusinessServices
7.4 创建基础接口(通用接口)
IBaseService ,增加分页实体:PagingData
7.4.1 IBaseService.cs
using System.Data.SqlClient;
using System.Linq.Expressions;
namespace Wenyu.Framework.IBusinessServices
{
public interface IBaseService
{
#region 查询
/// <summary>
/// 主键查询
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="id"></param>
/// <returns></returns>
public T Find<T>(int id) where T : class;
/// <summary>
/// 不应该暴露给上端使用者,尽量少用
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
/// [Obsolete("尽量避免使用,使用带表达式目录树的代替")]
public IQueryable<T> Set<T>() where T : class;
/// <summary>
/// 根据表达式目录树条件查询
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="funcWhere"></param>
/// <returns></returns>
public IQueryable<T> Query<T>(Expression<Func<T,bool>> funcWhere) where T : class;
/// <summary>
/// 分页查询
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="S"></typeparam>
/// <param name="funcWhere"></param>
/// <param name="pageSize"></param>
/// <param name="pageIndex"></param>
/// <param name="funcOrderBy"></param>
/// <param name="isAsc"></param>
/// <returns></returns>
public PagingData<T> QueryPage<T,S>(Expression<Func<T, bool>> funcWhere,
int pageSize,int pageIndex,Expression<Func<T,S>> funcOrderBy,bool isAsc=true) where T : class;
#endregion
#region 添加
/// <summary>
/// 即时保存,不需要再Commit
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="t"></param>
/// <returns></returns>
public T Insert<T>(T t) where T : class;
/// <summary>
/// 新增集合
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="tList"></param>
/// <returns></returns>
public IEnumerable<T> Insert<T>(IEnumerable<T> tList) where T : class;
#endregion
#region 修改
/// <summary>
/// 修改一个对象
/// 如果已经在Context,只能再封装一个
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="t"></param>
/// <returns></returns>
public int Update<T>(T t) where T : class;
/// <summary>
/// 修改一个集合
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="t"></param>
/// <returns></returns>
public int Update<T>(IEnumerable<T> tList) where T : class;
#endregion
#region 删除
/// <summary>
/// 删除一个对象
/// 先附加再删除
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="t"></param>
/// <returns></returns>
public int Delete<T>(T t) where T : class;
/// <summary>
/// 根据主键删除记录
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="id"></param>
/// <returns></returns>
public int Delete<T>(int id) where T : class;
/// <summary>
/// 删除一个对象集合
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="tList"></param>
/// <returns></returns>
public int Delete<T>(IEnumerable<T> tList) where T : class;
#endregion
#region Other
/// <summary>
/// 执行sql语句,返回IQueryable
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="sql"></param>
/// <param name="parameters"></param>
/// <returns></returns>
public IQueryable<T> ExcuteQuery<T>(string sql, SqlParameter[] parameters) where T : class;
/// <summary>
/// 执行sql语句
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="sql"></param>
/// <param name="parameters"></param>
/// <returns></returns>
public void Excute<T>(string sql, SqlParameter[] parameters) where T : class;
#endregion
#region 伪代码
#endregion
}
}
7.4.2 BaseService.cs
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.25" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.25" />
</ItemGroup>
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;
using System.Data.SqlClient;
using System.Linq.Expressions;
using Wenyu.Framework.IBusinessServices;
namespace Wenyu.Framework.BusinessServices
{
public abstract class BaseService: IBaseService
{
protected DbContext Context { get; set; }
/// <summary>
/// 构造函数注入
/// </summary>
/// <param name="context"></param>
public BaseService(DbContext context)
{
Context=context;
}
#region Query 查询
public IQueryable<T> Set<T>() where T : class
{
return this.Context.Set<T>();
}
public T Find<T>(int id) where T : class
{
return this.Context.Set<T>().Find(id);
}
public IQueryable<T> Query<T>(Expression<Func<T, bool>> funcWhere) where T : class
{
return this.Context.Set<T>().Where<T>(funcWhere);
}
public PagingData<T> QueryPage<T, S>(Expression<Func<T, bool>> funcWhere, int pageSize, int pageIndex, Expression<Func<T, S>> funcOrderBy, bool isAsc = true) where T : class
{
var list=Set<T>();
if (funcWhere != null)
{
list=list.Where(funcWhere);
}
if (isAsc)
{
list = list.OrderBy(funcOrderBy);
}
else
{
list = list.OrderByDescending(funcOrderBy);
}
PagingData<T> result = new PagingData<T>()
{
DataList = list.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList(),
RecordCount = list.Count(),
PageIndex=pageIndex,
PageSize=pageSize,
};
return result;
}
#endregion
#region Insert 添加
public T Insert<T>(T t) where T : class
{
this.Context.Set<T>().Add(t);
this.Commit();
return t;
}
public IEnumerable<T> Insert<T>(IEnumerable<T> tList) where T : class
{
this.Context.Set<T>().AddRange(tList);
this.Commit();
return tList;
}
#endregion
#region Update 修改
public int Update<T>(T t) where T : class
{
this.Context.Set<T>().Attach(t);
this.Context.Entry<T>(t).State = EntityState.Modified;
return this.Commit();
}
public int Update<T>(IEnumerable<T> tList) where T : class
{
foreach (var t in tList)
{
this.Context.Set<T>().Attach(t);
this.Context.Entry<T>(t).State=EntityState.Modified;
}
return this.Commit ();
}
#endregion
#region Delete 删除
public int Delete<T>(T t) where T : class
{
if (t == null) throw new ArgumentNullException("this is null");
this.Context.Set<T>().Attach(t);
this.Context.Set<T>().Remove(t);
return this.Commit();
}
public int Delete<T>(int id) where T : class
{
T t=this.Find<T>(id);
if (t == null) throw new ArgumentNullException("this is null");
this.Context.Set<T>().Remove(t);
return this.Commit();
}
public int Delete<T>(IEnumerable<T> tList) where T : class
{
foreach(var t in tList)
{
this.Context.Set<T>().Attach(t);
}
this.Context.Set<T>().RemoveRange(tList);
return this.Commit();
}
#endregion
#region Other
public void Excute<T>(string sql, SqlParameter[] parameters) where T : class
{
IDbContextTransaction transaction = null;
try
{
transaction = this.Context.Database.BeginTransaction();
this.Context.Database.ExecuteSqlRaw(sql, parameters);
transaction.Commit();
}catch (Exception ex)
{
if(transaction != null)
transaction.Rollback();
throw;
}
}
public IQueryable<T> ExcuteQuery<T>(string sql, SqlParameter[] parameters) where T : class
{
return this.Context.Set<T>().FromSqlRaw(sql, parameters);
}
#endregion
protected int Commit()
{
return this.Context.SaveChanges();
}
#region 伪代码
#endregion
public virtual void Dispose()
{
if(this.Context != null)
{
this.Context.Dispose();
}
}
}
}
7.4.3 PagingData.cs
namespace Wenyu.Framework.IBusinessServices
{
public class PagingData<T> where T : class
{
public int RecordCount { get; set; }
public int PageIndex { get; set; }
public int PageSize { get; set; }
public List<T>? DataList { get; set; }
public string? SearchString { get; set; }
}
}
7.4.4 IUserService.cs
public interface IUserService:IBaseService
{
}
7.4.4 UserService.cs
using Wenyu.Framework.DbModels;
using Wenyu.Framework.IBusinessServices;
namespace Wenyu.Framework.BusinessServices
{
public class UserService : BaseService, IUserService
{
public UserService(FrameworkDbContext context) : base(context)
{
}
}
}
7.4.5 配置依赖注入服务
Program.cs
"ConnectionStrings": {
"Default": "Data Source=.;Initial Catalog=Wenyu.Framework.DB;User ID=sa;Password=123456"
}
var connString = builder.Configuration.GetConnectionString("Default");
builder.Services.AddDbContext<FrameworkDbContext>(optionsBuilder =>
{
optionsBuilder.UseSqlServer(connString);
});
builder.Services.AddTransient<IUserService, UserService>();
8、 autofac支持
8.1 引入nuget包
<ItemGroup>
<PackageReference Include="Autofac" Version="7.1.0" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="8.0.0" />
</ItemGroup>
8.2 添加autofac模块化配置代码
创建一个测试服务
namespace WorldSystem.Api.Common.Services
{
public class TodayTestService
{
public string ShowTime()
{
string time = DateTime.Now.ToString();
Console.WriteLine(time);
return time;
}
}
}
在autofac模块配置中注册服务
using Autofac;
using System.Reflection;
using WorldSystem.Api.Common.Services;
namespace WorldSystem.Api.Configs
{
public class AutofacModuleConfig:Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
// 批量注册服务
//Assembly interfaces = Assembly.Load("Interfaces");
//Assembly services = Assembly.Load("Services");
//builder.RegisterAssemblyTypes(interfaces, services).AsImplementedInterfaces();
// 注册服务
builder.RegisterType<TodayTestService>();
}
}
}
8.3 替换aspnetcore自带容器
在Program.cs上添加如下代码:
#region 替换为autofac依赖注入容器
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureContainer<ContainerBuilder>(containerBuilder =>
{
containerBuilder.RegisterModule(new AutofacModuleConfig());
});
#endregion
9 Autofac支持Aop
两种方式:
1 通过接口实现Aop拓展,拓展后,实现该接口的所有方法都会支持AOP拓展功能
2 通过类实现Aop拓展,拓展后,定义为虚方法的实现方法支持AOP拓展
二者比较:通过类实现更加灵活,可以选择行支持AOP
9.1 nuget包引入
<ItemGroup>
<PackageReference Include="Castle.Core" Version="5.1.1" />
<PackageReference Include="Autofac.Extras.DynamicProxy" Version="7.1.0" />
</ItemGroup>
9.2 拓展AOP写日志
创建项目:Wenyu.Framework.AutofacAOP
AOPExtend/CustomLogInterceptor.cs
using Castle.DynamicProxy;
using Microsoft.Extensions.Logging;
namespace Wenyu.Framework.AutofacAOP.AOPExtend
{
public class CustomLogInterceptor : IInterceptor //nuget:Castle.Core
{
private readonly ILogger<CustomLogInterceptor> _logger;
public CustomLogInterceptor(ILogger<CustomLogInterceptor> logger)
{
this._logger = logger;
}
public void Intercept(IInvocation invocation)
{
_logger.LogInformation($"===================={invocation.Method.Name}执行前=================");
invocation.Proceed();//继续往后执行
_logger.LogInformation($"===================={invocation.Method.Name}执行后=================");
}
}
}
9.3 通过接口实现Aop拓展
using Autofac.Extras.DynamicProxy;
using Wenyu.Framework.AutofacAOP.AOPExtend;
namespace Wenyu.Framework.IBusinessServices
{
[Intercept(typeof(CustomLogInterceptor))] //nuget:Autofac.Extras.DynamicProxy
public interface ITestService
{
public void ShowName();
}
}
builder.RegisterType<TestService>().As<ITestService>()
.EnableInterfaceInterceptors(); //nuget:Autofac.Extras.DynamicProxy
9.4 通过类实现Aop拓展
using Autofac.Extras.DynamicProxy;
using Wenyu.Framework.AutofacAOP.AOPExtend;
using Wenyu.Framework.DbModels;
using Wenyu.Framework.IBusinessServices;
namespace Wenyu.Framework.BusinessServices
{
[Intercept(typeof(CustomLogInterceptor))] //nuget:Autofac.Extras.DynamicProxy
public class TestService :ITestService
{
public TestService(FrameworkDbContext context)
{
}
//非virtual的无法进入Aop代理
public void ShowName()
{
}
//virtual的能有Aop代理
public virtual void ShowAndName()
{
}
}
}
builder.RegisterType<TestService>().As<ITestService>()
//.EnableInterfaceInterceptors(); //nuget:Autofac.Extras.DynamicProxy
.EnableClassInterceptors(); //nuget:Autofac.Extras.DynamicProxy
10、 Api返回统一格式
10.1 定义通用的返回结果类ApiResult.cs
ApiResult.cs
namespace Wenyu.Framework.Common
{
/// <summary>
/// api json 通用返回格式
/// </summary>
public class ApiResult
{
/// <summary>
/// 是否正常返回
/// </summary>
public bool Success { get; set; }
/// <summary>
/// 消息
/// </summary>
public string? Message { get; set; }
}
public class ApiDataResult<T>: ApiResult
{
/// <summary>
/// 结果集数据
/// </summary>
public T? Data { get; set; }
/// <summary>
/// 冗余结果
/// </summary>
public object? OValue { get; set; }
}
}
10.2 使用方法
[HttpGet]
public IActionResult GetUsers()
{
var list= _userService.Set<UserEntity>().AsQueryable().ToList();
return new JsonResult(new ApiDataResult<List<UserEntity>>
{
Success = true,
Message = "成功",
Data = list
}) ;
}
11、AutoMapper支持
11.1 安装AutoMapper库
在ASP.NET Core项目中,使用NuGet包管理器安装AutoMapper库,或者在Package Manager Console中运行以下命令:
Install-Package AutoMapper
<ItemGroup>
<PackageReference Include="AutoMapper" Version="12.0.1" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
</ItemGroup>
11.2 创建映射配置
在项目的根目录下创建一个映射配置类,该类将定义对象之间的映射关系。例如,以下是一个简单的映射配置类:
using Wenyu.Framework.DbModels.Entities;
using Wenyu.Framework.ModelDto;
namespace Wenyu.Framework.WebCore.AutoMapperExtend
{
public class WebApiAutoMapperProfile:AutoMapper.Profile
{
public WebApiAutoMapperProfile()
{
CreateMap<UserEntity, UserDto>()
.ForMember(c=>c.UserId,s=>s.MapFrom(x=>x.UserId))
.ForMember(c => c.Name, s => s.MapFrom(x => x.Name))
.ReverseMap();
//CreateMap<UserInfoDto, LoginDto>().ReverseMap();
//CreateMap<UserInfo, UserInfoDto>().ReverseMap();
//CreateMap<UserInfoInput, UserInfo>();
}
}
}
11.3 在启动代码中注册映射配置
在ASP.NET Core的启动代码(通常是Startup.cs文件)中注册映射配置。在Startup类的构造函数中添加以下代码:
#region Automapper
builder.Services.AddAutoMapper(typeof(WebApiAutoMapperProfile));
#endregion
或者
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
// Add other services here...
services.AddAutoMapper(typeof(WebApiAutoMapperProfile);
}
在上面的代码中,我们使用AddAutoMapper方法将映射配置注册到服务容器中。
11.4 BusinessServices业务层中使用
现在可以在需要的地方使用AutoMapper进行数据转换。例如,在业务服务中可以使用以下代码将领域模型转换为视图模型:
using AutoMapper;
using SqlSugar;
using WorldSystem.Api.Common.Db;
using WorldSystem.Api.Models.Entities;
using WorldSystem.Api.Models.Services.User.Dto;
namespace WorldSystem.Api.Models.Services.User
{
public class UserInfoService : IUserInfoService
{
private readonly ISqlSugarClient _db;
private readonly IMapper _mapper;
public UserInfoService(ISqlSugarClient db ,IMapper mapper)
{
this._db = db;
this._mapper = mapper;
}
public async Task<UserInfoDto> CheckLogin(LoginDto loginDto)
{
UserInfo userInfo = await _db.Queryable<UserInfo>().FirstAsync(p => p.QQ.Equals(loginDto.QQ) && p.Password.Equals(loginDto.Password));
UserInfoDto userInfoDto=_mapper.Map<UserInfoDto>(userInfo);
return userInfoDto;
}
}
}
11.5 Controllers控制器中使用
private readonly IMapper _mapper;
public TestController(ILogger<TestController> logger, IUserService userService,IMapper mapper)
{
_logger = logger;
_userService = userService;
this._mapper = mapper;
}
[HttpGet]
public IActionResult GetUsersDto()
{
var userList = _userService.Set<UserEntity>().AsQueryable().ToList();
var list =_mapper.Map<List<UserDto>>(userList);
return new JsonResult(new ApiDataResult<List<UserDto>>
{
Success = true,
Message = "成功",
Data = list
});
}
12 基于角色权限系统功能实现
12.1 基础类
12.2 枚举类型
namespace Wenyu.Framework.Common.EnumTypes
{
public enum UserTypeEnum
{
/// <summary>
/// 管理员
/// </summary>
Administrator=1,
/// <summary>
/// 普通用户
/// </summary>
GeneralUser=2
}
}
namespace Wenyu.Framework.Common.EnumTypes
{
public enum StatusEnum
{
/// <summary>
/// 正常
/// </summary>
Normal=0,
/// <summary>
/// 冻结
/// </summary>
Frozen=1,
/// <summary>
/// 已删除
/// </summary>
Delete=2
}
}
12.3 权限实体
using Wenyu.Framework.Common.EnumTypes;
namespace Wenyu.Framework.DbModels.Entities
{
/// <summary>
/// 基础类
/// </summary>
public abstract class BaseEntity
{
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreateTime { get; set; } = DateTime.Now;
/// <summary>
/// 修改时间
/// </summary>
public DateTime ModifyTime { get; set; }
/// <summary>
/// 状态 0正常 1冻结 2 删除
/// </summary>
public StatusEnum Status { get; set; }
}
}
using Wenyu.Framework.Common.EnumTypes;
namespace Wenyu.Framework.DbModels.Entities
{
/// <summary>
/// 用户信息
/// </summary>
public class UserEntity:BaseEntity
{
/// <summary>
/// 用户唯一id
/// </summary>
public int UserId { get; set; }
/// <summary>
/// 用户名
/// </summary>
public string? UserName { get; set; }
/// <summary>
/// 姓名
/// </summary>
public string? Name { get; set; }
/// <summary>
/// 密码
/// </summary>
public string? Password { get; set; }
/// <summary>
/// 年龄
/// </summary>
public int Age { get; set; }
/// <summary>
/// 用户类型-UserTypeEnum
/// 1:管理员 系统默认生成
/// 2:普通用户 添加的或者注册的用户都为普通用户
/// </summary>
public UserTypeEnum UserType { get; set; }
/// <summary>
/// 电话
/// </summary>
public string? Phone { get; set; }
/// <summary>
/// 移动电话
/// </summary>
public string? Mobile { get; set; }
/// <summary>
/// 性别 0男 1女
/// </summary>
public int Sex { get; set; }
/// <summary>
/// 微信
/// </summary>
public string? Wechat { get; set; }
/// <summary>
/// QQ
/// </summary>
public string? QQ { get; set; }
/// <summary>
/// 邮箱
/// </summary>
public string? Email { get; set; }
/// <summary>
/// 头像
/// </summary>
public string? ImageUrl { get; set; }
/// <summary>
/// 最后登录时间
/// </summary>
public DateTime? LastLoginTime { get; set; }
}
}
namespace Wenyu.Framework.DbModels.Entities
{
/// <summary>
/// 角色信息
/// </summary>
public class RoleEntity:BaseEntity
{
public int RoleId { get; set; }
public int RoleName { get; set; }
}
}
namespace Wenyu.Framework.DbModels.Entities
{
/// <summary>
/// 菜单信息
/// </summary>
public class MenuEntity:BaseEntity
{
public Guid Id { get; set; }
public Guid? ParentId { get; set; }
public string? MenuText { get; set; }
public int MenuType { get; set; }
public string? Icon { get; set; }
public string? WebUrlName { get; set; }
public string? WebUrl { get; set; }
public string? VueFilePath { get; set; }
public bool IsLeafNode { get; set; }
public int OrderBy { get; set; }
}
}
namespace Wenyu.Framework.DbModels.Entities
{
/// <summary>
/// 用户角色关系信息
/// </summary>
public class UserRoleMapEntity:BaseEntity
{
public int Id { get; set; }
public int UserId { get; set; }
public int RoleId { get; set; }
}
}
namespace Wenyu.Framework.DbModels.Entities
{
/// <summary>
/// 角色菜单映射信息
/// </summary>
public class RoleMenuMapEntity:BaseEntity
{
public int Id { get; set; }
public int RoleId { get; set; }
public int MenuId { get; set; }
}
}
namespace Wenyu.Framework.DbModels.Entities
{
/// <summary>
/// 日志信息
/// </summary>
public class SystemLog
{
public int Id { get; set; }
public DateTime? Date { get; set; }
public string? Thread { get; set; }
public string? Level { get; set; }
public string? Logger { get; set; }
public string? Message { get; set; }
public string? Exception { get; set; }
}
}
using Microsoft.EntityFrameworkCore;
using Wenyu.Framework.DbModels.Entities;
namespace Wenyu.Framework.DbModels
{
public class FrameworkDbContext:DbContext
{
private readonly string _connectionString;
/// <summary>
/// 带有书库连接字符串的参数
/// </summary>
/// <param name="connectionString"></param>
public FrameworkDbContext(string connectionString) {
this._connectionString = connectionString;
}
public FrameworkDbContext(DbContextOptions<FrameworkDbContext> options)
:base(options)
{
}
public virtual DbSet<UserEntity> UserEntities { get; set; }
public virtual DbSet<MenuEntity> MenuEntities { get; set; }
public virtual DbSet<RoleEntity> RoleEntities { get; set; }
public virtual DbSet<UserRoleMapEntity> UserRoleMapEntities { get; set; }
public virtual DbSet<RoleMenuMapEntity> RoleMenuMapEntities { get; set; }
public virtual DbSet<SystemLog> SystemLogs { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer(this._connectionString);
}
}
/// <summary>
/// 配置数据库表映射关系
/// </summary>
/// <param name="modelBuilder"></param>
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<UserEntity>(entity =>
{
entity.ToTable("UserEntity")
.HasKey(u=>u.UserId);
});
modelBuilder.Entity<RoleEntity>(entity =>
{
entity.ToTable("RoleEntity")
.HasKey(u => u.RoleId);
});
modelBuilder.Entity<MenuEntity>(entity =>
{
entity.ToTable("MenuEntity")
.HasKey(u => u.Id);
});
modelBuilder.Entity<UserRoleMapEntity>(entity =>
{
entity.ToTable("UserRoleMapEntity")
.HasKey(u => u.Id);
});
modelBuilder.Entity<RoleMenuMapEntity>(entity =>
{
entity.ToTable("RoleMenuMapEntity")
.HasKey(u => u.Id);
});
modelBuilder.Entity<SystemLog>(entity =>
{
entity.ToTable("SystemLog")
.HasKey(u => u.Id);
});
}
}
}
12.4 定义业务逻辑服务类
using Wenyu.Framework.DbModels.Entities;
using Wenyu.Framework.ModelDto;
namespace Wenyu.Framework.IBusinessServices
{
public interface IUserService:IBaseService
{
public UserEntity Login(LoginReq loginReq);
}
}
using Wenyu.Framework.Common.Helpers;
using Wenyu.Framework.DbModels;
using Wenyu.Framework.DbModels.Entities;
using Wenyu.Framework.IBusinessServices;
using Wenyu.Framework.ModelDto;
namespace Wenyu.Framework.BusinessServices
{
public class UserService : BaseService, IUserService
{
public UserService(FrameworkDbContext context) : base(context)
{
}
/// <summary>
/// 登录
/// </summary>
/// <param name="loginReq"></param>
/// <returns></returns>
public UserEntity? Login(LoginReq loginReq)
{
UserEntity? userEntity= this.Query<UserEntity>(x => x.UserName == loginReq.UserName && x.Password == Md5Helper.Encrypt(loginReq.Password)).FirstOrDefault();
return userEntity;
}
}
}
12.5 在控制器类中的使用业务逻辑服务类
using AutoMapper;
using Microsoft.AspNetCore.Mvc;
using Wenyu.Framework.BusinessServices;
using Wenyu.Framework.Common;
using Wenyu.Framework.Common.EnumTypes;
using Wenyu.Framework.Common.Helpers;
using Wenyu.Framework.DbModels.Entities;
using Wenyu.Framework.IBusinessServices;
using Wenyu.Framework.ModelDto;
using Wenyu.Framework.WebCore.SwaggerExtend;
using Wenyu.Framework.WebCore.ToolServices;
namespace Wenyu.Framework.WebApi.Controllers
{
/// <summary>
/// 用户相关的api控制器
/// </summary>
[ApiController]
[Route("api/[controller]")]
[ApiExplorerSettings(IgnoreApi = false,GroupName =nameof(ApiVersions.V1))]
public class UserController : ControllerBase
{
private readonly ILogger<UserController> _logger;
private readonly IUserService _userService;
private readonly JwtToolService _jwtToolService;
private readonly IMapper _mapper;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="logger"></param>
/// <param name="userService"></param>
/// <param name="mapper"></param>
public UserController(ILogger<UserController> logger,
IUserService userService,
JwtToolService jwtToolService,
IMapper mapper)
{
_logger = logger;
this._userService = userService;
this._jwtToolService = jwtToolService;
this._mapper = mapper;
}
/// <summary>
/// 获取用户的分页列表
/// </summary>
/// <returns></returns>
[HttpGet]
[Route("{pageIndex:int}/{pageSize:int}")]
[Route("{pageIndex:int}/{pageSize:int}/{searchString}")]
public async Task<JsonResult> GetUsersPagedAsync(int pageIndex,int pageSize,string? searchString=null)
{
PagingData<UserEntity> paging = _userService
.QueryPage<UserEntity, DateTime>(!string.IsNullOrWhiteSpace(searchString) ? c => c.Name.Contains(searchString) : a=>true,
pageSize, pageIndex, c => c.CreateTime, false);
PagingData<UserDto> pagingResult = _mapper.Map<PagingData<UserDto>>(paging);
JsonResult result= new JsonResult(new ApiDataResult<PagingData<UserDto>>
{
Success = true,
Message = "成功",
Data = pagingResult
});
return await Task.FromResult(result);
}
/// <summary>
/// 新增用户
/// </summary>
/// <param name="addUserDto"></param>
/// <returns></returns>
[HttpPost]
public async Task<JsonResult> AddUserAsync([FromBody] AddUserDto addUserDto)
{
UserEntity userEntity=_mapper.Map<UserEntity>(addUserDto);
userEntity.Password = Md5Helper.Encrypt(addUserDto.Password);
userEntity.Status = addUserDto.IsEnable? StatusEnum.Normal: StatusEnum.Frozen;
userEntity.UserType = UserTypeEnum.GeneralUser;
UserEntity user=_userService.Insert(userEntity);
ApiDataResult<UserEntity>? apiResult = null;
if (user.UserId <= 0)
{
string token=_jwtToolService.GetToken( userEntity);
apiResult = new ApiDataResult<UserEntity>
{
Success = false,
Message = "失败",
Data = null
};
}
else
{
new ApiDataResult<UserEntity>
{
Success = true,
Message = "成功",
Data = userEntity
};
}
return await Task.FromResult(new JsonResult(apiResult));
}
/// <summary>
/// 登录
/// </summary>
/// <param name="loginReq"></param>
/// <returns></returns>
[Route("login")]
[HttpPost]
public async Task<JsonResult> LoginAsync([FromBody] LoginReq loginReq)
{
UserEntity? userEntity = _userService.Login(loginReq);
ApiDataResult<string>? apiResult = null;
if (userEntity==null)
{
apiResult = new ApiDataResult<string>
{
Success = false,
Message = "登录失败",
};
}
else
{
string token = _jwtToolService.GetToken(userEntity);
apiResult = new ApiDataResult<string>
{
Success = true,
Message = "登录成功",
Data = token
};
}
return await Task.FromResult(new JsonResult(apiResult));
}
}
}
13、JwtToken权限实现
13.1 ToolServices/JwtTokenOption.cs
"JwtTokenOption": {
"Audiece": "http://localhost:6787",
"Issuer": "http://localhost:6787",
"SecurityKey": "gg45454454545454544545454"
}
namespace Wenyu.Framework.WebCore.ToolServices
{
public class JwtTokenOption
{
public string Audiece { get; set; }
public string Issuer { get; set; }
public string SecurityKey { get; set; }
}
}
13.2 ToolServices/JwtToolService.cs
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Wenyu.Framework.DbModels.Entities;
namespace Wenyu.Framework.WebCore.ToolServices
{
public class JwtToolService
{
private readonly JwtTokenOption _jwtOption;
public JwtToolService(IOptionsMonitor<JwtTokenOption> optionsMonitor)
{
_jwtOption = optionsMonitor.CurrentValue;
}
public string GetToken(UserEntity user)
{
//有效载荷
var claims = new[]
{
new Claim(ClaimTypes.Name,user.Name),
new Claim(ClaimTypes.Role,"admin"),
new Claim(ClaimTypes.Role,"teacher"),
new Claim("UserId",user.UserId.ToString()),
new Claim("UserName",user.UserName),
//new Claim("Name",user.Name.ToString()),
new Claim("UserType",user.UserType.ToString()),
};
//加密需要加密key
SymmetricSecurityKey key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtOption.SecurityKey));
SigningCredentials creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
JwtSecurityToken token = new JwtSecurityToken(
issuer: _jwtOption.Issuer,
audience: _jwtOption.Audiece,
claims: claims,
//expires: DateTime.Now.AddMinutes(10),//10分钟过期
expires: DateTime.Now.AddSeconds(20),//20s过期
signingCredentials: creds
);
string returnToken = new JwtSecurityTokenHandler().WriteToken(token);
return returnToken;
}
}
}
13.3 鉴权
13.3.1 基于JWT鉴权
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using Wenyu.Framework.WebCore.ToolServices;
namespace Wenyu.Framework.WebCore.AuthorizationExtend
{
public static class AuthorizationExtension
{
/// <summary>
/// 配置鉴权和授权
/// </summary>
/// <param name="service"></param>
public static void AddAuthorizationExt(this WebApplicationBuilder builder)
{
JwtTokenOption jwtTokenOption = new JwtTokenOption();
builder.Configuration.Bind("JwtTokenOption", jwtTokenOption);
builder.Services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) //nuget :Microsoft.AspNetCore.Authentication.JwtBearer
.AddJwtBearer(options => //nuget :Microsoft.AspNetCore.Authentication.JwtBearer
{
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true, //是否验证Issuer
ValidateAudience = true,//是否验证Audience
ValidateLifetime = true,//是否验证失效时间
ValidateIssuerSigningKey = true,//是否验证SecurityKey
ValidIssuer = jwtTokenOption.Issuer,
ValidAudience = jwtTokenOption.Audiece,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtTokenOption.SecurityKey)),
ClockSkew = TimeSpan.FromSeconds(0) //token过期后默认300s是有效的,0马上失效
//AudienceValidator = (m, n, z) =>
//{
// //这里可以写自己定义的验证逻辑
// //return m.
// return true;
//},
//LifetimeValidator = (notBefore,expires,securityToken,validationParameters) =>
//{
// //这里可以写自己定义的验证逻辑
// return true;
//}
};
//options.Events = new JwtBearerEvents
//{
// OnAuthenticationFailed = (context) =>
// {
// return Task.CompletedTask;
// },
// OnChallenge = (context) =>
// {
// return Task.CompletedTask;
// },
// OnForbidden = (context) =>
// {
// return Task.CompletedTask;
// },
// OnTokenValidated = (context) =>
// {
// return Task.CompletedTask;
// },
//};
});
builder.Services.AddAuthorization(config =>
{
config.AddPolicy("MenuPolicy", (pollyBuider) =>
{
pollyBuider.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
.AddRequirements(new MenuAuthorizationRequirement());
});
});
builder.Services.AddTransient<IAuthorizationHandler, MenuAuthorizationHandler>();
}
/// <summary>
/// 使用鉴权和授权
/// </summary>
/// <param name="app"></param>
public static void UseAuthorizationExt(this WebApplication app)
{
app.UseAuthentication();//鉴权,将请求携带的信息(token,session,cookies)解析到HttpContext.User中
app.UseAuthorization();//授权,根据用户信息检查当前用户是否有权请求当前资源
}
}
}
Program.cs
#region Authentication和Authorization 鉴权和授权配置
builder.AddAuthorizationExt();
#endregion
app.UseAuthorizationExt();
13.4 基于角色授权
/// <summary>
/// 基于角色的需要鉴权和授权,只要是其中一个角色就能通过
/// </summary>
/// <returns></returns>
[HttpGet]
[Authorize(AuthenticationSchemes =JwtBearerDefaults.AuthenticationScheme,Roles ="admin,teacher")]
public IActionResult GetTest1()
{
System.Security.Claims.ClaimsPrincipal user = HttpContext.User;
return new JsonResult(new ApiResult
{
Success = true,
Message = "成功",
});
}
/// <summary>
/// 基于角色的需要鉴权和授权,必须是所有角色都有才能通过
/// </summary>
/// <returns></returns>
[HttpGet]
[Authorize(AuthenticationSchemes =JwtBearerDefaults.AuthenticationScheme,Roles ="teacher")]
[Authorize(AuthenticationSchemes =JwtBearerDefaults.AuthenticationScheme,Roles ="admin")]
public IActionResult GetTest2()
{
System.Security.Claims.ClaimsPrincipal user = HttpContext.User;
return new JsonResult(new ApiResult
{
Success = true,
Message = "成功",
});
}
13.5 基于策略授权
关键配置
builder.Services.AddAuthorization(config =>
{
config.AddPolicy("MenuPolicy", (pollyBuider) =>
{
pollyBuider.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
.AddRequirements(new MenuAuthorizationRequirement());
});
});
builder.Services.AddTransient<IAuthorizationHandler, MenuAuthorizationHandler>();
AuthorizationExtend/MenuAuthorizationHandler
using Microsoft.AspNetCore.Authorization;
namespace Wenyu.Framework.WebCore.AuthorizationExtend
{
public class MenuAuthorizationHandler : AuthorizationHandler<MenuAuthorizationRequirement>
{
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, MenuAuthorizationRequirement requirement)
{
if (context.User.Claims == null||context.User.Claims.Count()<=0)
{
context?.Fail();
}else if (context.User.IsInRole("admin"))
{
context?.Succeed(requirement);
}
await Task.CompletedTask;
}
}
}
AuthorizationExtend/MenuAuthorizationRequirement.cs
using Microsoft.AspNetCore.Authorization;
namespace Wenyu.Framework.WebCore.AuthorizationExtend
{
public class MenuAuthorizationRequirement : IAuthorizationRequirement
{
}
}
14 工具服务类
区别于业务逻辑类,与技术有关,一般放在基础设施层,
不抽离的话也可以放在应用层
14.1 Md5Helper.cs
Helpers/Md5Helper.cs
using System.Security.Cryptography;
using System.Text;
namespace Wenyu.Framework.Common.Helpers
{
public class Md5Helper
{
public static string Encrypt(string str)
{
string pwd = String.Empty;
MD5 md5 = MD5.Create();
// 编码UTF8/Unicode
byte[] s = md5.ComputeHash(Encoding.UTF8.GetBytes(str));
// 转换成字符串
for (int i = 0; i < s.Length; i++)
{
//格式后的字符是小写的字母
//如果使用大写(X)则格式后的字符是大写字符
pwd = pwd + s[i].ToString("X");
}
return pwd;
}
}
}