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 ...
随机推荐
- MYGUI3.2改造——完善windows下的文字显示
MYGUI 3.2 是支持中文显示的——往“MyGUI_Fonts.xml”文件里添加上中文的编码范围即可,这个网上有很多. 能显示中文后,再添加中文的自动换行,这个网上有:http://blog.s ...
- c#数字图像处理(十二)图像的腐蚀与膨胀
背景知识 腐蚀与膨胀基本原理:就是用一个特定的结构元素来与待处理图像按像素做逻辑操作:可以理解成拿一个带孔的网格板(结构元素矩阵中元素为1的为孔)盖住图像的某一部分,然后按照各种不同的观察方式来确定操 ...
- 简单的在jsp页面操作mysql
---恢复内容开始--- 上一篇讲了在DOS界面下操作mysql 现在我们来说说怎么在jsp页面中操作mysql 要用jsp页面操作mysql需要jdbc(不是非要jdbc,还有其他的) 下载地址:w ...
- CGI fastCgi php-fpm PHP-CGI 辨析
CGI fastCgi php-fpm PHP-CGI 辨析 LNMP环境中的nginx是不支持php的,需要通过fastcgi插件来处理有关php的请求.而php需要php-fpm这个组件提供该功能 ...
- Java错误:找不到类文件或者未加载主类
使用java命令执行.class文件时,java只会查找环境变量CLASSPATH中的目录,并会不查找当前目录,所以只要把当前目录”."加入到CLASSPATH中就可以了.
- 推荐一本学习Groovy的书籍Groovy程序设计!
有朋友公司在用groovy开发,于是推荐我学习一下,搜到了这本书: 花了一个月时间读完了这本书!写的很棒,几乎没有废话,全书都是很重要的知识点和很好的讲解,确实像封面说的那样,使用的好可以提高开发效率 ...
- 如何快速安装kafka-manager
1.下载kafka-manager git clone https://github.com/yahoo/kafka-manager.git 2.配置sbt镜像仓库 因为kafka-manager是雅 ...
- Head First设计模式——状态模式
糖果机 如下糖果机工作状态图,我们对这个状态图进行编码实现糖果机的工作过程 这个状态图的每个圆圈代表一个状态,可以看到有4个状态同时又4个动作,分别是:“投入1元钱”.“退回1元钱”.“转动曲柄”.“ ...
- 批量解析域名为IP地址的python脚本
脚本如下: #!/usr/bin/env python #coding:utf- import os,sys from socket import gethostbyname DOMAIN= &quo ...
- java设计模式2————工厂模式
1.工厂模式介绍: 1.1.实现了创建者与调用者的分离 1.2.详细分类: 简单工厂模式 工厂方法模式 抽象工厂模式 1.3.所遵循的OOP原则: 开闭原则:对扩展开放,对修改关闭 依赖倒转原则:面向 ...