更多 abp 文章, 请到专栏查看 ↑
创建项目
创建一个 AspNetCore Web Api
默认的空项目
空项目只用来学习, 真实项目建议使用 ABP CLI
安装 2 个必要的包
Volo.Abp.AspNetCore.Mvc
: web 相关的核心包
Volo.Abp.Autofac
: 目前 .Net 默认的依赖注入 功能少, 所以 abp 必须依赖 autofac 才能运行, 这是单独的包, 未来有替代品只需再装一个包,就可以无缝的迁移
<PackageReference Include="Volo.Abp.AspNetCore.Mvc" Version="8.1.3" />
<PackageReference Include="Volo.Abp.Autofac" Version="8.1.3" />
swagger
<PackageReference Include="Volo.Abp.Swashbuckle" Version="8.1.3" />
JWT 和 多租户
<PackageReference Include="Volo.Abp.AspNetCore.Authentication.JwtBearer" Version="8.1.3" />
<PackageReference Include="Volo.Abp.AspNetCore.MultiTenancy" Version="8.1.3" />
Program.cs
比较简单 只是把原生 AspNetCore 的入口
改为 AddApplicationAsync
( 注入各种服务 ) 和 InitializeApplicationAsync
(配置请求管道)
两个和原来的功能一样. 只是做了封装. 全写到了 AppModule
中
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseAutofac(); // 注册 autofac
await builder.AddApplicationAsync<AppModule>(); // 加载入口 Module
var app = builder.Build();
await app.InitializeApplicationAsync(); // 管道相关
await app.RunAsync();
AppModule.cs
使用 abp 的模块, 安装后 一定要在 顶部的
DependsOn
中 添加,
注意名称, 有的包名字比较像, 或者是某个包只多一个单词.
写错的话. 会运行不正常, 报错 缺少一些各种各样的东西
AbpModule 有几个生命周期 常用的就 2 个
ConfigureServices
和 OnApplicationInitialization
JWT
登录授权的相关功能 , ABP CLI 创建的项目用的 openiddict
授权中心,
国内的各种 admin 开源模版好像不太爱用这种, 都是普通的 JWT.
abp 也封装了 Volo.Abp.AspNetCore.Authentication.JwtBearer
模块
直接用 AddAuthentication
和 AddAbpJwtBearer
去配置相关参数,
将 app.UseJwtTokenMiddleware()
加入管道
只要登录成功, ICurrentUser
对象就会有相关数据
( 原理是 从 HttpContext.User
中取出来, 赋值到 ICurrentUser
)
多租户
和 jwt 相关的还有多租户, 开启 AbpMultiTenancyOptions.IsEnabled
和 配置默认数据AbpDefaultTenantStoreOptions
每次请求进来, 都会从 ITenantStore
中 查询租户信息 , ICurrentTenant
就会有相关数据, 之后 abp 的内部都是从这个对象中获取 租户ID (比如 查询数据库)
ITenantStore
默认的实现是 DefaultTenantStore
从内存对象中取数据, 用的 TryRegister
注入优先级比较低 , 只有在空的时候才会注入, 所以我们可以继承接口去替换实现,
在模块 Volo.Abp.TenantManagement
(租户增删改查) 中就用的这种替换方式, 使用 EF Core 来查询数据库中的租户信息
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Modularity;
using Volo.Abp;
using Volo.Abp.Autofac;
using Volo.Abp.Swashbuckle;
using Microsoft.OpenApi.Models;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using Volo.Abp.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.JsonWebTokens;
using Volo.Abp.MultiTenancy;
using Volo.Abp.AspNetCore.MultiTenancy;
using Volo.Abp.MultiTenancy.ConfigurationStore;
namespace abpJwtDemo;
[DependsOn(
typeof(AbpAspNetCoreMvcModule)// asp.net core
,typeof(AbpAutofacModule)// autofac
,typeof(AbpSwashbuckleModule)// swagger
,typeof(AbpAspNetCoreAuthenticationJwtBearerModule) // jwt
,typeof(AbpAspNetCoreMultiTenancyModule)// 多租户
)]
public class AppModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
// 控制器继承 ApplicationService ( 命名空间: Volo.Abp.Application.Services )
// 就可以使用 abp 的 `自动 api 控制器`, 按照约定使用, 可以省去很多模板代码
PreConfigure<AbpAspNetCoreMvcOptions>(options =>
{
options
.ConventionalControllers
.Create(typeof(AppModule).Assembly);
});
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
var services = context.Services;
var configuration = services.GetConfiguration();
// JWT
services.AddAbpSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo { Title = "Test API", Version = "v1" });
options.DocInclusionPredicate((docName, description) => true);
options.CustomSchemaIds(type => type.FullName);
//添加 Jwt 验证设置,添加请求头信息
options.AddSecurityRequirement(new OpenApiSecurityRequirement()
{
{
new OpenApiSecurityScheme()
{
Reference = new OpenApiReference()
{
Id = "Bearer",
Type = ReferenceType.SecurityScheme
}
},
new List<string>()
}
});
//放置授权按钮
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme()
{
Name = "Authorization", //jwt默认的参数名称
In = ParameterLocation.Header, //jwt默认存放Authorization信息的位置(请求头中)
Type = SecuritySchemeType.ApiKey,
BearerFormat = JwtConstants.TokenType,
Scheme = "Bearer",
Description = "Value Bearer {token}",
});
});
/*
"Jwt": {
"SecretKey": "a3eCdqjwyAJTLZH3rweILjRj8tpwMKka",
"Issuer": "WebAppIssuer",
"Audience": "WebAppAudience"
}
*/
//注册 JWT
services.AddAuthentication(options => { options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; })
.AddAbpJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = false, //是否验证Issuer
ValidateAudience = false, //是否验证Audience
ValidateIssuerSigningKey = false, //是否验证SecurityKey
RequireExpirationTime = true, // 要求过期时间
ValidateLifetime = true, //是否验证失效时间
ValidAudience = configuration["Jwt:Audience"], //订阅人Audience
ValidIssuer = configuration["Jwt:Issuer"], //发行人Issuer
IssuerSigningKey =
new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["Jwt:SecretKey"])), //SecurityKey
ClockSkew = TimeSpan.FromSeconds(30), //过期时间容错值,解决服务器端时间不同步问题(秒)
};
});
// 开启 多租户
Configure<AbpMultiTenancyOptions>(options => { options.IsEnabled = true; });
// 配置租户信息
// 由于当前没有数据库相关, 所以会调用默认的内存数据," Dependency(TryRegister = true) DefaultTenantStore"
// abp 中 efcore 租户管理 在 Volo.Abp.TenantManagement 模块中,
// 里面会实现 ITenantStore 数据库相关操作, 还有 页面 和 web api 接口
Configure<AbpDefaultTenantStoreOptions>(o =>
{
o.Tenants =
[
new TenantConfiguration()
{
Id = new Guid("6b66c229b8fd8680de60e1944d51e9d9"),
IsActive = true,
Name = "xiaoming",
NormalizedName = "xiaoming"
}
];
});
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
var app = context.GetApplicationBuilder();
var env = context.GetEnvironment();
if (env.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
// JWT
app.UseJwtTokenMiddleware();
// Swagger
app.UseStaticFiles();
app.UseSwagger();
app.UseAbpSwaggerUI(options => { options.SwaggerEndpoint("/swagger/v1/swagger.json", "Test API"); });
// 授权验证
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
// 从几种多租户 ID 的获取方式中提取 ID,
// 使用 ITenantStore 查询租户完整信息 并设置到 ICurrentTenant
app.UseMultiTenancy();
// controller
app.UseConfiguredEndpoints();
}
}
JWT 登录
登录唯一要注意的是 设置 Claim 的 key
.
只有对应的 key 才能自动赋值到 ICurrentUser
对象上 ,
不对应的 key 需要自己封装方法, 到 HttpContext.User
中去查询
(
相关的 key 在 Volo.Abp.Security.Claims.AbpClaimTypes
对象,
.Net 也有对应的 key 在 System.Security.Claims.ClaimTypes
)
AbpClaimTypes.TenantId
比较关键, abp 会从这里获取 租户ID
去查具体租户信息,
还有几种优先级比较低的获取 租户ID 的方式, 不太常用, 就不介绍了
CurrentUser
QueryString
Route
Header ( key 的名字默认是 `__tenant`)
Cookie
AbpMultiTenancyModule
中会将 CurrentUserTenantResolveContributor
插入到第一个, 所以优先级最高.
AbpClaimTypes.UserId
和 AbpClaimTypes.TenantId
的值是 GUID
,
在设置时, 必须是符合格式的 GUID 字符串, 否则登录后拿到的 用户ID 和 租户ID 是 null ,
租户ID 必须是数据源中存在的租户. 不存在会报错
相关示例
using Volo.Abp.Security.Claims;
using System.Security.Claims;
Claim[] claims = new[]
{
//new Claim(ClaimTypes.Name, "u_admin"), //HttpContext.User.Identity.Name
//new Claim(ClaimTypes.Role, "r_admin"), //HttpContext.User.IsInRole("r_admin")
//new Claim(JwtRegisteredClaimNames.Jti, "admin"),
// abp 提供的 AbpClaimTypes 有和 System.Security.Claims.ClaimTypes 等价的, 例如: ClaimTypes.Role
// .Net 会自动验证 , 失败这返回 401 未授权, 成功会把 key value 放到 HttpContext.User 中
// 同时 abp 也会把属于 AbpClaimTypes 的 key 放到 ICurrentUser
new Claim(AbpClaimTypes.UserId, "6b66c229b8fd8680de60e1944d51e9d3"),
new Claim(AbpClaimTypes.UserName, "xiaoming"),
// 数据源中要存在, 否则报错
new Claim(AbpClaimTypes.TenantId, "6b66c229b8fd8680de60e1944d51e9d9"),
// 非 abp 规定的 key 到 ICurrentPrincipalAccessor 去查 (等价于 HttpContext.User),
// _currentPrincipalAccessor.Principal.Claims
// 可以给 ICurrentUser 写 扩展函数, 封装一下. 调用更方便, 例如: CurrentUser.GetMyKey2()
new Claim("mykey", "qweqweqe"),
new Claim("mykey2", "qweqweqe2"),
new Claim("mykey3", "qweqweqe3"),
new Claim(ClaimTypes.Role, "qweqweqe3"),
};
// 正常生成 token 返回给前端
如果你用的是 ABP CLI 创建的项目, openiddict 授权中心登录默认是用 angular 实现的.( 前后端分离 )
如果你是 React 可以用 react-oidc-context 来登录
import { WebStorageStateStore } from "oidc-client-ts";
import { AuthProviderProps } from "react-oidc-context";
// 版本号:
/*
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-oidc-context": "^3.1.0",
"oidc-client-ts": "^3.0.1",
*/
const config = {
oAuth:{
authority: "https://localhost:44354/", // 授权服务器的地址
redirect_uri: "http://localhost:5173", // 前端项目的地址 ( 授权成功后, 会跳转到这个地址并携带 url 参数)
client_id: "MyAdmin_React_App", // 非常重要: 需要在数据库配置好相关信息, 数据相同才能成功授权并跳转到前端地址
response_type: "code",
scope: "offline_access MyAdmin", // offline_access 会返回 Access Token 和 Refresh Token
}
}
export const oidcConfig: AuthProviderProps = {
...config.oAuth,
onSigninCallback: () => {
// 授权成功后, 把 url 参数去掉
window.history.replaceState({}, document.title, window.location.pathname);
},
userStore: new WebStorageStateStore({ store: window.localStorage }), // 持久化到 localStorage
};
// 使用
// 配置正确后, 调用相关 登录 / 退出登录 的方法即可
/*
import { AuthProvider } from "react-oidc-context";
ReactDOM.render(
<AuthProvider {...oidcConfig}>
<App />
</AuthProvider>,
document.getElementById("app")
);
*/
本站资源均来自互联网,仅供研究学习,禁止违法使用和商用,产生法律纠纷本站概不负责!如果侵犯了您的权益请与我们联系!
转载请注明出处: 免费源码网-免费的源码资源网站 » 一起学习 ABP (1) 项目入口
发表评论 取消回复