abp集成IdentityServer4和单点登录
在abp开发的系统后,需要使用这个系统作单点登录,及其他项目登录账号依靠abp开发的系统。在官方文档上只找到作为登录服务Identity Server Integration,但是host项目却无法使用登录服务生成的Token获取数据。所有的搜索结果包括abp的issue都是说去看identity server4的文档。我比较笨,文档看了还是不会。好在最后还是试出来了。
创建登录中心项目
- 到官网下载一个最新的模板项目,项目类型自选(我们项目用的vue,所以我选择的vue项目,.net core3.x)。保证可以运行起来并正常登录。
- 右键src目录添加一个asp.net core web 空项目,在项目中添加Startup文件夹,把Startup.cs和Program.cs移动到Startup文件夹,并修改这两个文件的命名空间增加Startup。不然会有命名空间和类名冲突。
- 在nuget添加Abp.ZeroCore.IdentityServer4、Abp、Abp.Castle.Log4Net等引用,添加Web.Core、EntityFrameworkCore项目引用

- 在Startup文件加新增xxxModule文件,初始化登录中心项目,因为这个项目要用到abp的模块所以要添加module
using Abp.Ids4;
using Abp.Ids4.Configuration;
using Abp.Modules;
using Abp.Reflection.Extensions;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
namespace Abp.Ids4.Server.Startup
{
[DependsOn(
typeof(Ids4WebCoreModule))]
public class AbpIds4ServerModule: AbpModule
{
private readonly IWebHostEnvironment _env;
private readonly IConfigurationRoot _appConfiguration;
public AbpIds4ServerModule(IWebHostEnvironment env)
{
_env = env;
_appConfiguration = env.GetAppConfiguration();
}
public override void Initialize()
{
IocManager.RegisterAssemblyByConvention(typeof(AbpIds4ServerModule).GetAssembly());
}
}
}
- 在Startup文件加新增AuthConfigurer.cs文件,你也可以直接从IdentityServerDemo项目复制文件过来,但是记得修改命名空间
using System;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Abp.Authorization;
using Abp.Ids4;
using Abp.Runtime.Security;
using IdentityServer4.Models;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Logging;
using Microsoft.IdentityModel.Tokens;
namespace Abp.Ids4.Server.Startup
{
public static class AuthConfigurer
{
/// <summary>
/// Configures the specified application.
/// </summary>
/// <param name="app">The application.</param>
/// <param name="configuration">The configuration.</param>
public static void Configure(IServiceCollection services, IConfiguration configuration)
{
var authenticationBuilder = services.AddAuthentication();
if (bool.Parse(configuration["Authentication:JwtBearer:IsEnabled"]))
{
authenticationBuilder.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
// The signing key must match!
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(configuration["Authentication:JwtBearer:SecurityKey"])),
// Validate the JWT Issuer (iss) claim
ValidateIssuer = true,
ValidIssuer = configuration["Authentication:JwtBearer:Issuer"],
// Validate the JWT Audience (aud) claim
ValidateAudience = true,
ValidAudience = configuration["Authentication:JwtBearer:Audience"],
// Validate the token expiry
ValidateLifetime = true,
// If you want to allow a certain amount of clock drift, set that here
ClockSkew = TimeSpan.Zero
};
options.Events = new JwtBearerEvents
{
OnMessageReceived = QueryStringTokenResolver
};
});
}
IdentityModelEventSource.ShowPII = true;
authenticationBuilder.AddIdentityServerAuthentication("Bearer", options =>
{
options.Authority = configuration["IdentityServer:Authority"];
options.ApiName = configuration["IdentityServer:ApiName"];
options.ApiSecret = configuration["IdentityServer:ApiSecret"];
options.RequireHttpsMetadata = false;
});
}
/* This method is needed to authorize SignalR javascript client.
* SignalR can not send authorization header. So, we are getting it from query string as an encrypted text. */
private static Task QueryStringTokenResolver(MessageReceivedContext context)
{
if (!context.HttpContext.Request.Path.HasValue ||
!context.HttpContext.Request.Path.Value.StartsWith("/signalr"))
{
//We are just looking for signalr clients
return Task.CompletedTask;
}
var qsAuthToken = context.HttpContext.Request.Query["enc_auth_token"].FirstOrDefault();
if (qsAuthToken == null)
{
//Cookie value does not matches to querystring value
return Task.CompletedTask;
}
//Set auth token from cookie
context.Token = SimpleStringCipher.Instance.Decrypt(qsAuthToken, AppConsts.DefaultPassPhrase);
return Task.CompletedTask;
}
}
}
- 修改Startup文件,因为有部分文件在Web.Core项目中,但是还没有添加进来,所以现在编译会报错,先忽略
using System;
using Abp.AspNetCore;
using Abp.AspNetCore.Mvc.Antiforgery;
using Abp.Castle.Logging.Log4Net;
using Abp.Dependency;
using Abp.Ids4.Configuration;
using Abp.Ids4.Identity;
using Abp.Ids4.Web.Core.IdentityServer;
using Abp.Json;
using Castle.Facilities.Logging;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Newtonsoft.Json.Serialization;
namespace Abp.Ids4.Server.Startup
{
public class Startup
{
private readonly IConfigurationRoot _appConfiguration;
public Startup(IWebHostEnvironment env)
{
_appConfiguration = env.GetAppConfiguration();
}
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(
options =>
{
options.Filters.Add(new AbpAutoValidateAntiforgeryTokenAttribute());
}
).AddNewtonsoftJson(options =>
{
options.SerializerSettings.ContractResolver = new AbpMvcContractResolver(IocManager.Instance)
{
NamingStrategy = new CamelCaseNamingStrategy()
};
});
IdentityRegistrar.Register(services);
IdentityServerRegistrar.Register(services, _appConfiguration);
AuthConfigurer.Configure(services, _appConfiguration);
// Configure Abp and Dependency Injection
return services.AddAbp<AbpIds4ServerModule>(
// Configure Log4Net logging
options => options.IocManager.IocContainer.AddFacility<LoggingFacility>(
f => f.UseAbpLog4Net().WithConfig("log4net.config")
)
);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseAbp(); //Initializes ABP framework.
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
if (bool.Parse(_appConfiguration["IdentityServer:IsEnabled"]))
{
app.UseJwtTokenMiddleware();
app.UseIdentityServer();
}
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
}
}
}
- 从Web.Core项目中复制appsettings.json和log4net.config到IdentityServer项目,在appsettings.json文件中增加IdentityServer4配置
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"Default": "Server=localhost\\sqlexpress; Database=Ids4Db; Trusted_Connection=True;"
},
"Authentication": {
"Facebook": {
"IsEnabled": "false",
"AppId": "",
"AppSecret": ""
},
"Google": {
"IsEnabled": "false",
"ClientId": "",
"ClientSecret": ""
},
"JwtBearer": {
"IsEnabled": "false",
"SecurityKey": "Ids4_C421AAEE0D126E5C",
"Issuer": "Ids4",
"Audience": "Ids4"
}
},
"IdentityServer": {
"IsEnabled": "true",
"Authority": "http://localhost:5000",
"ApiName": "default-api",
"ApiSecret": "secret",
"Clients": [
{
"ClientId": "client",
"AllowedGrantTypes": [
"password",
"client_credentials"
],
"ClientSecrets": [
{
"Value": "def2e777-5d42-4edc-a84a-30136c340e13"
}
],
"AllowedScopes": [
"default-api",
"openid",
"profile",
"email"
]
},
{
"ClientId": "mvc_implicit",
"ClientName": "MVC Client",
"AllowedGrantTypes": [ "implicit" ],
"RedirectUris": [
"http://localhost:5002/signin-oidc"
],
"PostLogoutRedirectUris": [
"http://localhost:5002/signout-callback-oidc"
],
"AllowedScopes": [
"openid",
"profile",
"email",
"default-api"
],
"AllowAccessTokensViaBrowser": true
}
]
}
}
最终项目结构如下:

修改Web.Core项目
从IdentityServerDemo项目复制IdentityServer目录和文件到xxx.Web.Core项目,修改文件中的命名空间和当前项目对应。修改IdentityServerRegistrar文件中的dbcontext,把直接引用dbcontext实例改成引用接口,如下:
public static void Register(IServiceCollection services, IConfigurationRoot configuration)
{
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryIdentityResources(IdentityServerConfig.GetIdentityResources())
.AddInMemoryApiResources(IdentityServerConfig.GetApiResources())
.AddInMemoryClients(IdentityServerConfig.GetClients(configuration))
-- .AddAbpPersistedGrants<IdentityServerDemoDbContext>()
++ .AddAbpPersistedGrants<IAbpPersistedGrantDbContext>()
.AddAbpIdentityServer<User>();
}
EntityFrameworkCore项目及其他修改
按照Identity Server Integration文档修改EntityFrameworkCore项目和nuget添加引用,同时把项目因为没有引用包报错的添加引用。现在运行IdentityServer项目从connect/token中获取到token了,但是这个token还不能用。即使按照IdentityServerDemo配置了也用不了,IdentityServerDemo中实际上每个web项目都是登录中心。
修改Web.Host项目的appsettings.json
{
"ConnectionStrings": {
"Default": "Server=localhost\\sqlexpress; Database=Ids4Db; Trusted_Connection=True;"
},
"App": {
"ServerRootAddress": "http://localhost:21022/",
"ClientRootAddress": "http://localhost:8080/",
"CorsOrigins": "http://localhost:4200,http://localhost:8080,http://localhost:8081,http://localhost:3000"
},
"Authentication": {
"JwtBearer": {
"IsEnabled": "true",
"SecurityKey": "Ids4_C421AAEE0D126E5C",
"Issuer": "Ids4",
"Audience": "Ids4"
}
},
"IdentityServer": {
"IsEnabled": "true",
"Authority": "http://localhost:5000",
"ApiName": "default-api",
"ApiSecret": "secret",
"ClientId": "client",
// no interactive user, use the clientid/secret for authentication
"AllowedGrantTypes": "password",
// secret for authentication
"ClientSecret": "def2e777-5d42-4edc-a84a-30136c340e13",
// scopes that client has access to
"AllowedScopes": "default-api"
}
}
- Web.Host项目在AuthConfigurer.cs文件的Configure方法中增加如下代码
var authenticationBuilder = services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme);
IdentityModelEventSource.ShowPII = true;
authenticationBuilder
// .AddIdentityServerAuthentication(JwtBearerDefaults.AuthenticationScheme, options =>
//{
// options.Authority = configuration["IdentityServer:Authority"];
// options.ApiName = configuration["IdentityServer:ApiName"];
// options.ApiSecret = configuration["IdentityServer:ApiSecret"];
// //options.Audience = configuration["IdentityServer:ApiName"];
// options.RequireHttpsMetadata = false;
//})
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
{
options.Authority = configuration["IdentityServer:Authority"];
options.RequireHttpsMetadata = false;
options.Audience = configuration["IdentityServer:ApiName"];
})
;
- 修改Web.Host项目中的Startup类
using System;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Castle.Facilities.Logging;
using Abp.AspNetCore;
using Abp.AspNetCore.Mvc.Antiforgery;
using Abp.Castle.Logging.Log4Net;
using Abp.Extensions;
using Abp.Ids4.Configuration;
using Abp.Ids4.Identity;
using Abp.AspNetCore.SignalR.Hubs;
using Abp.Dependency;
using Abp.Json;
using Microsoft.OpenApi.Models;
using Newtonsoft.Json.Serialization;
using Abp.Ids4.Web.Core.IdentityServer;
namespace Abp.Ids4.Web.Host.Startup
{
public class Startup
{
private const string _defaultCorsPolicyName = "localhost";
private readonly IConfigurationRoot _appConfiguration;
public Startup(IWebHostEnvironment env)
{
_appConfiguration = env.GetAppConfiguration();
}
public IServiceProvider ConfigureServices(IServiceCollection services)
{
//MVC
services.AddControllersWithViews(
options =>
{
options.Filters.Add(new AbpAutoValidateAntiforgeryTokenAttribute());
}
).AddNewtonsoftJson(options =>
{
options.SerializerSettings.ContractResolver = new AbpMvcContractResolver(IocManager.Instance)
{
NamingStrategy = new CamelCaseNamingStrategy()
};
});
IdentityRegistrar.Register(services);
AuthConfigurer.Configure(services, _appConfiguration);
//其他代码
//...
}
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
app.UseAbp(options => { options.UseAbpRequestLocalization = false; }); // Initializes ABP framework.
app.UseCors(_defaultCorsPolicyName); // Enable CORS!
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
//app.UseJwtTokenMiddleware();
if (bool.Parse(_appConfiguration["IdentityServer:IsEnabled"]))
{
app.UseJwtTokenMiddleware();
}
app.UseAbpRequestLocalization();
//...其他代码
}
}
}
- 修改登录方法从授权中心获取token,修改Web.Core项目TokenAuthController.cs的Authenticate方法
public async Task<AuthenticateResultModel> Authenticate([FromBody] AuthenticateModel model)
{
var loginResult = await GetLoginResultAsync(
model.UserNameOrEmailAddress,
model.Password,
GetTenancyNameOrNull()
);
if (loginResult.Result != AbpLoginResultType.Success)
{
throw new UserFriendlyException("登录失败");
}
//var accessToken = CreateAccessToken(CreateJwtClaims(loginResult.Identity));
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync(_appConfiguration["IdentityServer:Authority"]);
if (disco.IsError)
{
throw new UserFriendlyException(disco.Error);
}
var tokenResponse = await client.RequestPasswordTokenAsync(new PasswordTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = _appConfiguration["IdentityServer:ClientId"],
ClientSecret = _appConfiguration["IdentityServer:ClientSecret"],
UserName = model.UserNameOrEmailAddress,
Password = model.Password,
Scope = _appConfiguration["IdentityServer:AllowedScopes"],
});
if (tokenResponse.IsError)
{
throw new UserFriendlyException(tokenResponse.Error);
}
var accessToken = tokenResponse.AccessToken;
return new AuthenticateResultModel
{
AccessToken = accessToken,
EncryptedAccessToken = GetEncryptedAccessToken(accessToken),
ExpireInSeconds = (int)_configuration.Expiration.TotalSeconds,
UserId = loginResult.User.Id
};
}
至此host项目的登录获取的token就是从登录中心获取的了,其他客户端的对接按照使用Identity Server 4建立Authorization Server配置就可以了
源码:https://gitee.com/XiaoShenXiana/abp_ids4.git
原文
参考资料
abp集成IdentityServer4和单点登录的更多相关文章
- ABP集成WIF实现单点登录
ABP集成WIF实现单点登录 参考 ojlovecd写了三篇关于WIF文章. 使用WIF实现单点登录Part III —— 正式实战 使用WIF的一些开源示例. https://github.com/ ...
- IdentityServer4实现单点登录统一认证
什么是单点登录统一认证:假如某公司旗下有10个网站(比如各种管理网站:人事系统啊,财务系统啊,业绩系统啊等),我是该公司一管理员或者用户,按照传统网站模式是这样:我打开A网站 输入账号密码 然后进入到 ...
- spring security集成cas实现单点登录
spring security集成cas 0.配置本地ssl连接 操作记录如下: =====================1.创建证书文件thekeystore ,并导出为thekeystore.c ...
- Spring Security 集成CAS实现单点登录
参考:http://elim.iteye.com/blog/2270446 众所周知,Cas是对单点登录的一种实现.本文假设读者已经了解了Cas的原理及其使用,这些内容在本文将不会讨论.Cas有Ser ...
- 基于IdentityServer4的单点登录——IdentityServer
1.新建项目并添加引用 新建一个asp .net core 2.0的项目引用IdentityServer4.AspNetIdentity 2.定义资源 新建Config.cs文件,定义Api资源与Id ...
- 基于IdentityServer4的单点登录——项目基本结构与流程
组成 IdentityServer,Api和Client(客户端,asp .net core)本文以官方demo:https://github.com/IdentityServer/IdentityS ...
- Spring Boot 集成 JWT 实现单点登录授权
使用步骤如下:1. 添加Gradle依赖: dependencies { implementation 'com.auth0:java-jwt:3.3.0' implementation('org.s ...
- 基于IdentityServer4的单点登录——Client
以MvcClient项目为例 1.新建项目并添加引用 新建一个asp .net core 2.0的项目引用IdentityModel 2.配置 比之前的控制台客户端多这个步骤,需要配置这个客户端的Cl ...
- 基于IdentityServer4的单点登录——Api
1.新建项目并添加引用 新建一个asp .net core 2.0的项目引用IdentityServer4.AccessTokenValidation 2.配置 将Api与IdentityServer ...
随机推荐
- [集训]Trominoes,钩子公式运用
题意 用这四种骨牌密铺n*m的正方形矩阵,可以不选,求方案数.n*m<=1E8.多组询问. 思考 用如上的表达难以进行计算,尝试转化为一种新的组合解释. 若从右上角开始填起,我们强制要求里面的轮 ...
- [SDOI2011]染色(树链剖分)
[SDOI2011]染色(luogu) Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段 ...
- linux--->redis php扩展安装
阿里云centos6.9下 redis php扩展安装 下载phpredis wget http://pecl.php.net/get/redis-3.1.0.tgz 或 wget https://g ...
- ActiveMQ 快速入门教程系列 第一章 点对点消息实现
ActiveMQ 开发包下载及运行环境搭建 主页:http://activemq.apache.org/目前最新版本:5.11.1开发包及源码下载地址:http://activemq.apache.o ...
- js之split()和join()的用法
说明 这几天写了一个简单的vue项目,需要截取字符串,一时间想到了正则表达式,还折腾了还一会儿,最后上网查了一下,使用split()再简单不过了,也顺便回忆一下join().可见我有多菜,哈哈,学了这 ...
- 实验17:NAT
实验14-1:静态NAT 配置 Ø 实验目的通过本实验可以掌握(1)静态NAT 的特征(2)静态NAT 基本配置和调试 Ø 拓扑结构 实验步骤n 步骤1:配置路由器R1 提供NAT ...
- CCF_ 201409-3_字符串匹配
水. #include<cstdio> #include<iostream> #include<cstring> using namespace std; int ...
- 一口气说出 9种 分布式ID生成方式,面试官有点懵了
整理了一些Java方面的架构.面试资料(微服务.集群.分布式.中间件等),有需要的小伙伴可以关注公众号[程序员内点事],无套路自行领取 本文作者:程序员内点事 原文链接:https://mp.weix ...
- 【原创】(二)Linux进程调度器-CPU负载
背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...
- ASP.NET Core 2.2 WebApi 系列【九】使用SignalR (作者:tenghao510 ) 学习及内容补充
原文地址: ASP.NET Core 2.2 WebApi 系列[九]使用SignalR 今天,看到了大牛的这篇博文, 发了一下评论, 我很惊喜, 没想到他很快就回复了我, 而且通过QQ帮助了S ...