ASP.NET Core 2中基于Rediris OpenID 2.0的RP认证问题求助
解决ASP.NET Core 2对接Rediris OpenID 2.0身份服务器的认证请求问题
首先要明确:ASP.NET Core的AddOpenIdConnect中间件是为OpenID Connect 1.0设计的,而Rediris的PAPI框架遵循的是OpenID Authentication 2.0规范,这两个协议核心逻辑不兼容,所以你之前的配置无效是必然的。我们需要手动实现OpenID 2.0的认证流程,才能匹配Rediris的要求。
核心解决方案步骤
1. 移除无效的AddOpenIdConnect配置
直接删掉Startup里的AddOpenIdConnect代码块,它无法生成符合OpenID 2.0要求的认证参数,继续使用只会增加不必要的调试成本。
2. 手动构造OpenID 2.0认证请求
你需要生成一个自动提交的POST表单,向Rediris的端点发送指定参数。在你的登录控制器动作里,完成以下操作:
- 生成唯一
nonce(防止重放攻击) - 取出之前通过Smart Mode获取的
assoc_handle、mac_key等关联信息 - 构造包含所有必填参数的POST表单
示例控制器代码:
public IActionResult Login() { // 替换为你实际获取的关联句柄 var assocHandle = "{HMAC-SHA1}{5a3294ef}{A8uNlg==}"; var nonce = Guid.NewGuid().ToString(); // 生成符合要求的return_to地址,确保在realm范围内 var returnUrl = Url.Action("LoginCallback", "Account", new { nonce = nonce }, Request.Scheme); var realm = $"{Request.Scheme}://{Request.Host}/"; // 构建OpenID 2.0标准参数 var formParams = new Dictionary<string, string> { {"openid.ns", "http://specs.openid.net/auth/2.0"}, {"openid.ns.sreg", "http://openid.net/extensions/sreg/1.1"}, {"openid.sreg.required", "nickname,email"}, {"openid.sreg.optional", "fullname"}, {"openid.realm", realm}, {"openid.mode", "checkid_setup"}, {"openid.return_to", returnUrl}, {"openid.identity", "http://specs.openid.net/auth/2.0/identifier_select"}, {"openid.claimed_id", "http://yo.rediris.es/"}, {"openid.assoc_handle", assocHandle} }; // 生成自动提交的HTML表单,让浏览器自动发送POST请求 var html = $@" <html> <body onload='document.forms[0].submit()'> <form method='post' action='https://yo.rediris.es/papoid-server.php'> {string.Join("\n", formParams.Select(kv => $"<input type='hidden' name='{kv.Key}' value='{HttpUtility.HtmlEncode(kv.Value)}' />"))} </form> </body> </html>"; return Content(html, "text/html"); }
3. 处理回调(return_to端点)
创建LoginCallback动作,接收Rediris返回的响应,完成签名验证和用户信息提取:
[HttpGet] public async Task<IActionResult> LoginCallback(string nonce) { // 提取所有请求参数 var requestParams = Request.Query.ToDictionary(kv => kv.Key, kv => kv.Value.ToString()); // 1. 检查是否为认证成功响应 if (requestParams.TryGetValue("openid.mode", out var mode) && mode != "id_res") { // 处理取消或错误情况 return RedirectToAction("Login"); } // 2. 验证nonce有效性(需提前将生成的nonce存入Session或缓存) var storedNonce = HttpContext.Session.GetString("OpenIdNonce"); if (storedNonce != nonce) { return BadRequest("无效的Nonce,可能存在重放攻击"); } HttpContext.Session.Remove("OpenIdNonce"); // 3. 使用关联时获取的mac_key验证响应签名 var macKey = "YOUR_STORED_MAC_KEY"; // 替换为实际的mac_key if (!ValidateOpenIdSignature(requestParams, macKey)) { return BadRequest("响应签名验证失败"); } // 4. 提取Simple Registration扩展的用户信息 var userClaims = new List<Claim>(); if (requestParams.TryGetValue("openid.sreg.nickname", out var nickname)) userClaims.Add(new Claim(ClaimTypes.Name, nickname)); if (requestParams.TryGetValue("openid.sreg.email", out var email)) userClaims.Add(new Claim(ClaimTypes.Email, email)); if (requestParams.TryGetValue("openid.sreg.fullname", out var fullname)) userClaims.Add(new Claim("fullname", fullname)); // 5. 创建认证票据并完成登录 var claimsIdentity = new ClaimsIdentity(userClaims, "OpenId2"); var authProperties = new AuthenticationProperties(); await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity), authProperties); return RedirectToAction("Index", "Home"); } // 实现OpenID 2.0的HMAC-SHA1签名验证逻辑 private bool ValidateOpenIdSignature(Dictionary<string, string> parameters, string macKey) { // 按照规范构建签名基字符串 var signedParams = parameters["openid.signed"].Split(','); var baseString = string.Join("\n", signedParams.Select(p => $"{p}:{parameters[$"openid.{p}"]}")); // 计算HMAC-SHA1签名并对比 var hmac = new HMACSHA1(Encoding.ASCII.GetBytes(macKey)); var hash = hmac.ComputeHash(Encoding.ASCII.GetBytes(baseString)); var computedSignature = Convert.ToBase64String(hash); return parameters["openid.sig"] == computedSignature; }
关键注意事项
- Realm与Return_to一致性:
openid.realm必须是return_to的父域名/路径,Rediris会严格校验这一点,确保你的realm是http://localhost:5002/,return_to是该域名下的合法路径。 - 关联信息存储:
assoc_handle、mac_key、expires_in需要存入缓存或数据库,确保认证请求时使用未过期的有效关联。 - Nonce管理:生成的nonce要存入Session或缓存,回调时验证后立即删除,避免被重复使用。
内容的提问来源于stack exchange,提问作者Francisco Reyes
相关产品推荐
相关产品推荐

