IdentityServer4 負載均衡配置

山治先生發表於2021-11-01

  在不用到負載之前,一切都很好,但是部署多個例項之後,問題挺多的:session問題、令牌簽發後的校驗問題。

在此之前,先自查官方文件:Deployment — IdentityServer4 1.0.0 documentation

把必要的東西都配置正確,然後繼續排查。

session問題

   在需要經常需要與統一身份認證平臺進行互動的情況(比如,A站點和B站點都是統一身份認證平臺下的子系統,希望A站點登入後,在B進行登入的時候可以免登入的情形),建議不用繼續使用sessionID,如果不需要,則存放在redis,實現分散式session也是選擇。

令牌簽發後的校驗問題

  部署後啟動:

2021-11-01 12:01:50.098 [WRN] Using an in-memory repository. Keys will not be persisted to storage

然後在A站點登入之後,關閉A站點,啟動B站點進行測試是否正常可以驗證通過,會發現提示去登入,檢視日誌發現:

2021-11-01 12:01:50.276 [ERR] cookie Showing login: User is not authenticated

然後F12檢視cookie已經被刪除,講白了就是AB兩個站點並不互認,官方文件裡面寫著

IdentityServer itself is stateless and does not require server affinity - but there is data that needs to be shared between the instances.

我真想抽它鴨子的。

解決:

  因為ids4是有引用到微軟庫的一個 Microsoft.AspNetCore.DataProtection ,所以可以不用引用,你需要引用一個包:

Microsoft.AspNetCore.DataProtection.StackExchangeRedis

然後在使用:

var redis = ConnectionMultiplexer.Connect( Configuration["Redis:HostPort"]);
services.AddDataProtection()
.SetApplicationName(Configuration[
"Redis:ApplicationName"]) .PersistKeysToStackExchangeRedis( redis, "DataProtection-Keys");

就ok了,或者不想用StackExchangeRedis,就自己實現:

 public class CustomRedisXmlRepository : Microsoft.AspNetCore.DataProtection.Repositories.IXmlRepository
    {
        protected readonly IRedisCache redisCache;
        protected readonly int DBIndex;
        private readonly string key;

        public CustomRedisXmlRepository(IRedisCache redisCache, int dbIndex, string key)
        {
            this.redisCache = redisCache;
            this.DBIndex = dbIndex;
            this.key = key;
        }

        /// <inheritdoc />
        public IReadOnlyCollection<XElement> GetAllElements()
        {
            return GetAllElementsCore().ToList().AsReadOnly();
        }

        private IEnumerable<XElement> GetAllElementsCore()
        {
            foreach (var value in redisCache.GetList<string>(DBIndex,key))
            {
                yield return XElement.Parse(value);
            }
        }

        public void StoreElement(XElement element, string friendlyName)
        { 
            redisCache.AddList(DBIndex,key, element.ToString(SaveOptions.DisableFormatting));
        }
    }

擴充套件:

public static class RedisDataProtectionBuilderExtensions
    {
        private const string DataProtectionKeysName = "DataProtection-Keys";
        private const int DefaultDBIndex = 0;

        public static IDataProtectionBuilder PersistKeysToRedis(this IDataProtectionBuilder builder, IRedisCache redisCache,int dbIndex, string key)
        {
            if (builder == null)
            {
                throw new ArgumentNullException(nameof(builder));
            }
            if (redisCache == null)
            {
                throw new ArgumentNullException(nameof(IRedisCache));
            }
            return PersistKeysToRedisInternal(builder, redisCache, dbIndex, key);
        }

        public static IDataProtectionBuilder PersistKeysToRedis(this IDataProtectionBuilder builder, IRedisCache redisCache)
        {
            return PersistKeysToRedis(builder, redisCache, DefaultDBIndex, DataProtectionKeysName);
        }

        private static IDataProtectionBuilder PersistKeysToRedisInternal(IDataProtectionBuilder builder, IRedisCache redisCache, int dbIndex, string key)
        {
            builder.Services.Configure<KeyManagementOptions>(options =>
            {
                options.XmlRepository = new CustomRedisXmlRepository(redisCache, dbIndex, key);
            });
            return builder;
        }
    }

引用

 var redis = services.BuildServiceProvider().GetService<IRedisCache>(); 
 services.AddDataProtection()
         .SetApplicationName(config.RedisOptions.KeyPrefix+ "-Identityserver4-Production-Key")
         .PersistKeysToRedis(redis, Application.Configs.RedisKey.Application, config.RedisOptions.KeyPrefix + "-DataProtection-Keys");

然後重新測試,會發現token已經校驗通過:

2021-11-01 12:02:04.515 [INF] Token request validation success, {"ClientId":"zhicheng","ClientName":"**","GrantType":"authorization_code","Scopes":null,"AuthorizationCode":"****93B3","RefreshToken":"********","UserName":null,"AuthenticationContextReferenceClasses":null,"Tenant":null,"IdP":null,"Raw":{"client_id":"***","client_secret":"***REDACTED***","grant_type":"authorization_code","code":"153AF90A4B84AC5C0C8ABA1E22483A05936B6009E123AA8937015F26FD6A93B3","redirect_uri":"http://localhost:6001/Home/Index"},"$type":"TokenRequestValidationLog"}  

坐等下班!

 

相關文章