如何在Owin Startup中传递值从OnRedirectToIdentityProvider到OnAuthenticationFailed
解决Owin OIDC中OnRedirectToIdentityProvider与OnAuthenticationFailed间传递数据的问题
核心原因说明
OnRedirectToIdentityProvider和OnAuthenticationFailed属于两次独立的请求上下文:重定向到身份提供商是一次请求,回调回来是全新的请求,所以直接通过Add.Environment或setParameter无法跨请求传递数据,需要用跨请求的存储或协议参数嵌入的方式实现。
可行解决方案
方案1:加密嵌入OIDC的State参数传递数据
OIDC的State参数本身用于CSRF防护,我们可以在其中附加加密后的自定义数据,回调时解析获取:
public void Configuration(IAppBuilder app) { app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions { // 其他基础配置... OnRedirectToIdentityProvider = context => { // 定义要传递的自定义数据 var customPayload = new { Env = Environment.GetEnvironmentVariable("APP_ENV"), TraceId = Guid.NewGuid().ToString() }; var jsonPayload = JsonConvert.SerializeObject(customPayload); var encryptedPayload = ProtectData(jsonPayload); // 加密防止篡改 // 把自定义数据附加到原有State后 var originalState = context.ProtocolMessage.State; context.ProtocolMessage.State = $"{originalState}|{encryptedPayload}"; return Task.CompletedTask; }, OnAuthenticationFailed = context => { // 拆分State并解析自定义数据 var stateSegments = context.ProtocolMessage.State.Split('|'); if (stateSegments.Length == 2) { var encryptedPayload = stateSegments[1]; var jsonPayload = UnprotectData(encryptedPayload); var customPayload = JsonConvert.DeserializeObject<dynamic>(jsonPayload); // 访问自定义数据 string env = customPayload.Env; string traceId = customPayload.TraceId; } // 错误处理逻辑 context.HandleResponse(); context.Response.Redirect("/Error?msg=" + Uri.EscapeDataString(context.Exception.Message)); return Task.CompletedTask; } }); } // 基于DataProtection的加密解密辅助方法 private string ProtectData(string rawData) { var protector = new DpapiDataProtectionProvider("YourOidcAppName") .CreateProtector("OidcCustomData"); return protector.Protect(rawData); } private string UnprotectData(string protectedData) { var protector = new DpapiDataProtectionProvider("YourOidcAppName") .CreateProtector("OidcCustomData"); return protector.Unprotect(protectedData); }
方案2:使用会话/分布式缓存存储数据
如果应用已启用会话或有分布式缓存(如Redis),可以用唯一请求ID作为键存储数据,通过State传递ID来关联:
public void Configuration(IAppBuilder app) { // 先启用会话(未启用时添加) app.UseSession(); app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions { // 其他基础配置... OnRedirectToIdentityProvider = context => { var requestId = Guid.NewGuid().ToString(); // 存储自定义数据到会话 context.OwinContext.Session[$"OidcData_{requestId}"] = new { Env = Environment.GetEnvironmentVariable("APP_ENV"), Timestamp = DateTime.UtcNow }; // 将requestId附加到State var originalState = context.ProtocolMessage.State; context.ProtocolMessage.State = $"{originalState}|{requestId}"; return Task.CompletedTask; }, OnAuthenticationFailed = context => { var stateSegments = context.ProtocolMessage.State.Split('|'); if (stateSegments.Length == 2) { var requestId = stateSegments[1]; var customData = context.OwinContext.Session[$"OidcData_{requestId}"]; // 访问数据后清理会话存储 context.OwinContext.Session.Remove($"OidcData_{requestId}"); } // 错误处理逻辑 context.HandleResponse(); context.Response.Redirect("/Error"); return Task.CompletedTask; } }); }
原方法失效的原因
setParameter是给发送到身份提供商的协议消息添加参数,这些参数不会被身份提供商主动带回你的应用,因此回调时无法获取。Add.Environment是给当前请求的Owin上下文添加数据,但重定向后原上下文已销毁,回调请求是全新的上下文,无法访问之前的数据。
内容的提问来源于stack exchange,提问作者user464291
相关产品推荐
相关产品推荐

