zoukankan      html  css  js  c++  java
  • abp集成IdentityServer4和单点登录

    在abp开发的系统后,需要使用这个系统作单点登录,及其他项目登录账号依靠abp开发的系统。在官方文档上只找到作为登录服务Identity Server Integration,但是host项目却无法使用登录服务生成的Token获取数据。所有的搜索结果包括abp的issue都是说去看identity server4的文档。我比较笨,文档看了还是不会。好在最后还是试出来了。


    创建登录中心项目

    1. 到官网下载一个最新的模板项目,项目类型自选(我们项目用的vue,所以我选择的vue项目,.net core3.x)。保证可以运行起来并正常登录。
    2. 右键src目录添加一个asp.net core web 空项目,在项目中添加Startup文件夹,把Startup.cs和Program.cs移动到Startup文件夹,并修改这两个文件的命名空间增加Startup。不然会有命名空间和类名冲突。
    3. 在nuget添加Abp.ZeroCore.IdentityServer4、Abp、Abp.Castle.Log4Net等引用,添加Web.Core、EntityFrameworkCore项目引用
      IdentityServernuget引用
    4. 在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());
            }
        }
    }
    
    
    1. 在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;
            }
        }
    }
    
    
    1. 修改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();
                });
            }
        }
    }
    
    
    1. 从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
          }
        ]
      }
    }
    
    

    最终项目结构如下:
    IdentityServer

    修改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项目及其他修改

    1. 按照Identity Server Integration文档修改EntityFrameworkCore项目和nuget添加引用,同时把项目因为没有引用包报错的添加引用。现在运行IdentityServer项目从connect/token中获取到token了,但是这个token还不能用。即使按照IdentityServerDemo配置了也用不了,IdentityServerDemo中实际上每个web项目都是登录中心。

    2. 修改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"
      }
    }
    
    1. 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"];
    })
    ;
    
    1. 修改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();
                //...其他代码
            }
        }
    }
    
    
    1. 修改登录方法从授权中心获取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
    原文

    参考资料

  • 相关阅读:
    Python记录_day21 模块
    python记录day_20 多继承
    python记录_day019 类的约束 异常处理 日志
    python记录_day018 md5加密
    python记录_day18 反射 判断函数与方法
    python记录_day17 类与类之间的关系
    python记录_day16 类的成员
    实验0 了解和熟悉操作系统
    IT就业·软件工程之我见
    递归下降分析程序
  • 原文地址:https://www.cnblogs.com/missile/p/12200937.html
Copyright © 2011-2022 走看看