[原创] IdentityServer4 登录成功后跳转发生错误
IdentityServer4官网自带的DEMO,登录成功后跳转到请求网站,登录过程是直接form提交的,我嫌弃官方的登录不安全,字段明文发送,所以加了RSA加密,并把表单提交改为AJAX提交,于是后台也相应的做了代码改动,结果大功告成的时候拿去登录测试,出现以下的界面:
同时,在控制台界面中输出以下错误信息。
info: WinsWEB.Controllers.AccountController[0]
用户成功登录.
info: IdentityServer4.Hosting.IdentityServerMiddleware[0]
Invoking IdentityServer endpoint: IdentityServer4.Endpoints.AuthorizeCallbackEndpoint for /connect/authorize/callback
info: Microsoft.EntityFrameworkCore.Infrastructure[10403]
Entity Framework Core 6.0.10 initialized 'ConfigurationDbContext' using provider 'Microsoft.EntityFrameworkCore.Sqlite:6.0.10' with options: MigrationsAssembly=WinsWEB
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (0ms) [Parameters=[@__clientId_0='?' (Size = 3)], CommandType='Text', CommandTimeout='30']
SELECT "c"."Id", "c"."AbsoluteRefreshTokenLifetime", "c"."AccessTokenLifetime", "c"."AccessTokenType", "c"."AllowAccessTokensViaBrowser", "c"."AllowOfflineAccess", "c"."AllowPlainTextPkce", "c"."AllowRememberConsent", "c"."AllowedIdentityTokenSigningAlgorithms", "c"."AlwaysIncludeUserClaimsInIdToken", "c"."AlwaysSendClientClaims", "c"."AuthorizationCodeLifetime", "c"."BackChannelLogoutSessionRequired", "c"."BackChannelLogoutUri", "c"."ClientClaimsPrefix", "c"."ClientId", "c"."ClientName", "c"."ClientUri", "c"."ConsentLifetime", "c"."Created", "c"."Description", "c"."DeviceCodeLifetime", "c"."EnableLocalLogin", "c"."Enabled", "c"."FrontChannelLogoutSessionRequired", "c"."FrontChannelLogoutUri", "c"."IdentityTokenLifetime", "c"."IncludeJwtId", "c"."LastAccessed", "c"."LogoUri", "c"."NonEditable", "c"."PairWiseSubjectSalt", "c"."ProtocolType", "c"."RefreshTokenExpiration", "c"."RefreshTokenUsage", "c"."RequireClientSecret", "c"."RequireConsent", "c"."RequirePkce", "c"."RequireRequestObject", "c"."SlidingRefreshTokenLifetime", "c"."UpdateAccessTokenClaimsOnRefresh", "c"."Updated", "c"."UserCodeType", "c"."UserSsoLifetime"
FROM "Clients" AS "c"
WHERE "c"."ClientId" = @__clientId_0
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (0ms) [Parameters=[@__clientId_0='?' (Size = 3)], CommandType='Text', CommandTimeout='30']
SELECT "c0"."Id", "c0"."ClientId", "c0"."Origin"
FROM "Clients" AS "c"
INNER JOIN "ClientCorsOrigins" AS "c0" ON "c"."Id" = "c0"."ClientId"
WHERE "c"."ClientId" = @__clientId_0
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (0ms) [Parameters=[@__clientId_0='?' (Size = 3)], CommandType='Text', CommandTimeout='30']
SELECT "c0"."Id", "c0"."ClientId", "c0"."GrantType"
FROM "Clients" AS "c"
INNER JOIN "ClientGrantTypes" AS "c0" ON "c"."Id" = "c0"."ClientId"
WHERE "c"."ClientId" = @__clientId_0
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (0ms) [Parameters=[@__clientId_0='?' (Size = 3)], CommandType='Text', CommandTimeout='30']
SELECT "c0"."Id", "c0"."ClientId", "c0"."Scope"
FROM "Clients" AS "c"
INNER JOIN "ClientScopes" AS "c0" ON "c"."Id" = "c0"."ClientId"
WHERE "c"."ClientId" = @__clientId_0
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (0ms) [Parameters=[@__clientId_0='?' (Size = 3)], CommandType='Text', CommandTimeout='30']
SELECT "c0"."Id", "c0"."ClientId", "c0"."Type", "c0"."Value"
FROM "Clients" AS "c"
INNER JOIN "ClientClaims" AS "c0" ON "c"."Id" = "c0"."ClientId"
WHERE "c"."ClientId" = @__clientId_0
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (0ms) [Parameters=[@__clientId_0='?' (Size = 3)], CommandType='Text', CommandTimeout='30']
SELECT "c0"."Id", "c0"."ClientId", "c0"."Created", "c0"."Description", "c0"."Expiration", "c0"."Type", "c0"."Value"
FROM "Clients" AS "c"
INNER JOIN "ClientSecrets" AS "c0" ON "c"."Id" = "c0"."ClientId"
WHERE "c"."ClientId" = @__clientId_0
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (0ms) [Parameters=[@__clientId_0='?' (Size = 3)], CommandType='Text', CommandTimeout='30']
SELECT "c0"."Id", "c0"."ClientId", "c0"."Provider"
FROM "Clients" AS "c"
INNER JOIN "ClientIdPRestrictions" AS "c0" ON "c"."Id" = "c0"."ClientId"
WHERE "c"."ClientId" = @__clientId_0
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (0ms) [Parameters=[@__clientId_0='?' (Size = 3)], CommandType='Text', CommandTimeout='30']
SELECT "c0"."Id", "c0"."ClientId", "c0"."PostLogoutRedirectUri"
FROM "Clients" AS "c"
INNER JOIN "ClientPostLogoutRedirectUris" AS "c0" ON "c"."Id" = "c0"."ClientId"
WHERE "c"."ClientId" = @__clientId_0
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (0ms) [Parameters=[@__clientId_0='?' (Size = 3)], CommandType='Text', CommandTimeout='30']
SELECT "c0"."Id", "c0"."ClientId", "c0"."Key", "c0"."Value"
FROM "Clients" AS "c"
INNER JOIN "ClientProperties" AS "c0" ON "c"."Id" = "c0"."ClientId"
WHERE "c"."ClientId" = @__clientId_0
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (0ms) [Parameters=[@__clientId_0='?' (Size = 3)], CommandType='Text', CommandTimeout='30']
SELECT "c0"."Id", "c0"."ClientId", "c0"."RedirectUri"
FROM "Clients" AS "c"
INNER JOIN "ClientRedirectUris" AS "c0" ON "c"."Id" = "c0"."ClientId"
WHERE "c"."ClientId" = @__clientId_0
fail: IdentityServer4.Validation.AuthorizeRequestValidator[0]
redirect_uri is missing or too long
{
"ClientId": "mvc",
"AllowedRedirectUris": [
"https://localhost:5002/signin-oidc"
],
"SubjectId": "63c9887e-75eb-4099-b368-9d4fd6bf8299",
"RequestedScopes": "",
"PromptMode": "",
"Raw": {
"client_id": "mvc",
"amp;redirect_uri": "https://localhost:5002/signin-oidc",
"amp;response_type": "code",
"amp;scope": "openid profile webClaims",
"amp;code_challenge": "vLk6eGikeB7TTzTUva3VQVAOi0IcCLaP93d8Wsx-85s",
"amp;code_challenge_method": "S256",
"amp;response_mode": "form_post",
"amp;nonce": "638042572307173644.YjAxY2RhODctMTY2OS00ZDNjLWJmOWQtODNjYjFkMmE0MjBiMzZkZDJjYjctYjBhOS00Yzc1LTg3ODgtMGY4NzY2NmFlMzRj",
"amp;state": "CfDJ8IuGKdJpkDxNjX0iOT5Ekdac6izbbNBVp62kcZ5xyW5PITxN5tvou4bUpXawe8aR0D4h6fUFpB_4CNYGBKbEjCdwcjqKHtdBdDT2hAnrrhhG4keG6VmQrCPoaKwdsJChplAl6jUlfwSNrJzSaFTq0NwsSwwtGMVEZyiabaFLtXBKYUIn1EP9eGpW51WhBxjycRg_WzIQVHapW12EGA3OqPHkN0R693HtrkQps-wCzC6l3QrIQOV0kBN3JbSvY1FYXXhO3JM76U6msf-L0pXIwqfg01RWeoP8jeBiXvKVHLQ20zsmB0VrqD3ZUL-AfV-fldnYIp6ocMHw1yWQUqzHmnJwCG9kVCCtz8ZMI9LENGGL2rk0YSyK1EcRmKX0t3c4Qw",
"amp;x-client-SKU": "ID_NETSTANDARD2_0",
"amp;x-client-ver": "6.10.0.0"
}
}
fail: IdentityServer4.Endpoints.AuthorizeCallbackEndpoint[0]
Request validation failed
info: IdentityServer4.Endpoints.AuthorizeCallbackEndpoint[0]
{
"ClientId": "mvc",
"AllowedRedirectUris": [
"https://localhost:5002/signin-oidc"
],
"SubjectId": "63c9887e-75eb-4099-b368-9d4fd6bf8299",
"RequestedScopes": "",
"PromptMode": "",
"Raw": {
"client_id": "mvc",
"amp;redirect_uri": "https://localhost:5002/signin-oidc",
"amp;response_type": "code",
"amp;scope": "openid profile webClaims",
"amp;code_challenge": "vLk6eGikeB7TTzTUva3VQVAOi0IcCLaP93d8Wsx-85s",
"amp;code_challenge_method": "S256",
"amp;response_mode": "form_post",
"amp;nonce": "638042572307173644.YjAxY2RhODctMTY2OS00ZDNjLWJmOWQtODNjYjFkMmE0MjBiMzZkZDJjYjctYjBhOS00Yzc1LTg3ODgtMGY4NzY2NmFlMzRj",
"amp;state": "CfDJ8IuGKdJpkDxNjX0iOT5Ekdac6izbbNBVp62kcZ5xyW5PITxN5tvou4bUpXawe8aR0D4h6fUFpB_4CNYGBKbEjCdwcjqKHtdBdDT2hAnrrhhG4keG6VmQrCPoaKwdsJChplAl6jUlfwSNrJzSaFTq0NwsSwwtGMVEZyiabaFLtXBKYUIn1EP9eGpW51WhBxjycRg_WzIQVHapW12EGA3OqPHkN0R693HtrkQps-wCzC6l3QrIQOV0kBN3JbSvY1FYXXhO3JM76U6msf-L0pXIwqfg01RWeoP8jeBiXvKVHLQ20zsmB0VrqD3ZUL-AfV-fldnYIp6ocMHw1yWQUqzHmnJwCG9kVCCtz8ZMI9LENGGL2rk0YSyK1EcRmKX0t3c4Qw",
"amp;x-client-SKU": "ID_NETSTANDARD2_0",
"amp;x-client-ver": "6.10.0.0"
}
}
从提示来看,是返回地址出错了,redirect_url为空或是太长,进数据库查看,配置是没有问题的,从控制台返回的信息来看,好奇怪,地址前面多了一个amp; ,一看明显是转换出现了问题,进数据库再次确认查看,地址中不带这个东西,打开后台的代码,仔细研究,如下:
/Account/Login 方法
....省略若干
if (result.Succeeded)
{
_logger.LogInformation("用户成功登录.");
if (context != null)
{
if (context.IsNativeClient())
{
// The client is native, so this change in how to
// return the response is for better UX for the end user.
return this.LoadingPage("Redirect", returnUrl);
}
// we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null
//return Redirect(returnUrl);
return Json(new { result = "OK", msg = $"登录成功.", returnUrl=returnUrl });
}
// request for a local page
if (Url.IsLocalUrl(returnUrl))
{
//return Redirect(returnUrl);
return Json(new { result = "OK", msg = $"登录成功.", returnUrl = returnUrl });
}
如上所示,红色的代码已被我修改成绿色那一行。去前台登录处console.log(url) 输出一下看看,得到下面跳转地址:
/connect/authorize/callback?client_id=mvc&redirect_uri=https%3A%2F%2Flocalhost%3A5002%2Fsignin-oidc&response_type=code&scope=openid%20profile%20webClaims&code_challenge=vLk6eGikeB7TTzTUva3VQVAOi0IcCLaP93d8Wsx-85s&code_challenge_method=S256&response_mode=form_post&nonce=638042572307173644.YjAxY2RhODctMTY2OS00ZDNjLWJmOWQtODNjYjFkMmE0MjBiMzZkZDJjYjctYjBhOS00Yzc1LTg3ODgtMGY4NzY2NmFlMzRj&state=CfDJ8IuGKdJpkDxNjX0iOT5Ekdac6izbbNBVp62kcZ5xyW5PITxN5tvou4bUpXawe8aR0D4h6fUFpB_4CNYGBKbEjCdwcjqKHtdBdDT2hAnrrhhG4keG6VmQrCPoaKwdsJChplAl6jUlfwSNrJzSaFTq0NwsSwwtGMVEZyiabaFLtXBKYUIn1EP9eGpW51WhBxjycRg_WzIQVHapW12EGA3OqPHkN0R693HtrkQps-wCzC6l3QrIQOV0kBN3JbSvY1FYXXhO3JM76U6msf-L0pXIwqfg01RWeoP8jeBiXvKVHLQ20zsmB0VrqD3ZUL-AfV-fldnYIp6ocMHw1yWQUqzHmnJwCG9kVCCtz8ZMI9LENGGL2rk0YSyK1EcRmKX0t3c4Qw&x-client-SKU=ID_NETSTANDARD2_0&x-client-ver=6.10.0.0
看上去有点儿乱,找个在线网urldecode的地址转码一下,变成下面的会好看许多
/connect/authorize/callback?client_id=mvc&redirect_uri=https://localhost:5002/signin-oidc&response_type=code&scope=openid profile webClaims&code_challenge=vLk6eGikeB7TTzTUva3VQVAOi0IcCLaP93d8Wsx-85s&code_challenge_method=S256&response_mode=form_post&nonce=638042572307173644.YjAxY2RhODctMTY2OS00ZDNjLWJmOWQtODNjYjFkMmE0MjBiMzZkZDJjYjctYjBhOS00Yzc1LTg3ODgtMGY4NzY2NmFlMzRj&state=CfDJ8IuGKdJpkDxNjX0iOT5Ekdac6izbbNBVp62kcZ5xyW5PITxN5tvou4bUpXawe8aR0D4h6fUFpB_4CNYGBKbEjCdwcjqKHtdBdDT2hAnrrhhG4keG6VmQrCPoaKwdsJChplAl6jUlfwSNrJzSaFTq0NwsSwwtGMVEZyiabaFLtXBKYUIn1EP9eGpW51WhBxjycRg_WzIQVHapW12EGA3OqPHkN0R693HtrkQps-wCzC6l3QrIQOV0kBN3JbSvY1FYXXhO3JM76U6msf-L0pXIwqfg01RWeoP8jeBiXvKVHLQ20zsmB0VrqD3ZUL-AfV-fldnYIp6ocMHw1yWQUqzHmnJwCG9kVCCtz8ZMI9LENGGL2rk0YSyK1EcRmKX0t3c4Qw&x-client-SKU=ID_NETSTANDARD2_0&x-client-ver=6.10.0.0
OK,我们拿上面的地址去https://localhost:5001 这个IDS4地址去提交,也就是在浏览器地址栏粘上上面的地址,回车,注意观察控制台,报错信息果断出现了,看来就是这里编码转换出错了,错误的把&变成& ,引起程序不能正确识别URL引起的,我们替换处理一下,再在浏览器提交,发现正常跳转,至此,问题完美解决。