[原創(chuàng)] IdentityServer4 登錄成功后跳轉(zhuǎn)發(fā)生錯(cuò)誤
IdentityServer4官網(wǎng)自帶的DEMO,登錄成功后跳轉(zhuǎn)到請(qǐng)求網(wǎng)站,登錄過(guò)程是直接form提交的,我嫌棄官方的登錄不安全,字段明文發(fā)送,所以加了RSA加密,并把表單提交改為AJAX提交,于是后臺(tái)也相應(yīng)的做了代碼改動(dòng),結(jié)果大功告成的時(shí)候拿去登錄測(cè)試,出現(xiàn)以下的界面:
同時(shí),在控制臺(tái)界面中輸出以下錯(cuò)誤信息。
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"
}
}
從提示來(lái)看,是返回地址出錯(cuò)了,redirect_url為空或是太長(zhǎng),進(jìn)數(shù)據(jù)庫(kù)查看,配置是沒(méi)有問(wèn)題的,從控制臺(tái)返回的信息來(lái)看,好奇怪,地址前面多了一個(gè)amp; ,一看明顯是轉(zhuǎn)換出現(xiàn)了問(wèn)題,進(jìn)數(shù)據(jù)庫(kù)再次確認(rèn)查看,地址中不帶這個(gè)東西,打開后臺(tái)的代碼,仔細(xì)研究,如下:
/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 });
}
如上所示,紅色的代碼已被我修改成綠色那一行。去前臺(tái)登錄處console.log(url) 輸出一下看看,得到下面跳轉(zhuǎn)地址:
/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
看上去有點(diǎn)兒亂,找個(gè)在線網(wǎng)urldecode的地址轉(zhuǎn)碼一下,變成下面的會(huì)好看許多
/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 這個(gè)IDS4地址去提交,也就是在瀏覽器地址欄粘上上面的地址,回車,注意觀察控制臺(tái),報(bào)錯(cuò)信息果斷出現(xiàn)了,看來(lái)就是這里編碼轉(zhuǎn)換出錯(cuò)了,錯(cuò)誤的把&變成& ,引起程序不能正確識(shí)別URL引起的,我們替換處理一下,再在瀏覽器提交,發(fā)現(xiàn)正常跳轉(zhuǎn),至此,問(wèn)題完美解決。