问题描述
我有一个 ASP.NET MVC 站点、IdentityServer4 主机和一个 Web API.
I have an ASP.NET MVC site, IdentityServer4 host and a web API.
当我使用外部提供商 (Facebook) 登录 MVC 站点时,我已正常登录.从 MVC 站点,我也可以正确使用 Web API.
When I log in the MVC site, using external provider (Facebook), I'm logged in fine. From the MVC site I can also consume the web API correctly.
但是,第二天,我仍然登录到 MVC 站点,但是当我尝试访问 Web API 时,我得到一个未授权异常".
However, the next day, I'm still logged in into the MVC site, but when I then try to access the web API, I get a 'not authorized exception'.
因此,尽管我仍在 MVC 站点中登录,但我不再通过身份验证来从 MVC 站点中调用 Web API.
So although I'm still logged in in the MVC site, I'm not authenticated anymore to call a web API from within the MVC site.
我想知道如何处理这种情况,以及应如何配置 IdentityServer4.
I'm wondering how to handle this situation, and how IdentityServer4 should be configured.
- 为什么一天后我仍然登录 MVC 网站?如何配置?
- 如果我仍然登录 MVC 站点,为什么我仍然不能调用 Web API?
- 我可以同步过期时间吗?或者我应该如何处理?
MVC 应用配置如下:
The MVC application is configured like:
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies";
options.Authority = mgpIdSvrSettings.Authority;
options.RequireHttpsMetadata = false;
options.ClientId = mgpIdSvrSettings.ClientId;
options.ClientSecret = mgpIdSvrSettings.ClientSecret; // Should match the secret at IdentityServer
options.ResponseType = "code id_token"; // Use hybrid flow
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.Scope.Add("mgpApi");
options.Scope.Add("offline_access");
});
所以它使用混合流.
在 IdentityServer 中,MVC 客户端的配置如下:
In IdentityServer the MVC client is configured like:
new Client
{
EnableLocalLogin = false,
ClientId = "mgpPortal",
ClientName = "MGP Portal Site",
AllowedGrantTypes = GrantTypes.Hybrid,
// where to redirect to after login
RedirectUris = mgpPortalSite.RedirectUris,
// where to redirect to after logout
PostLogoutRedirectUris = mgpPortalSite.PostLogoutRedirectUris,
// secret for authentication
ClientSecrets = mgpPortalSite.ClientSecrets.Select(cs => new Secret(cs.Sha256())).ToList(),
AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"mgpApi"
},
AllowOfflineAccess = true,
RequireConsent = false,
},
最后是网络 API:
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(options =>
{
options.Authority = mgpIdSvrSettings.Authority;
options.RequireHttpsMetadata = false;
options.ApiName = mgpIdSvrSettings.ApiName;
options.EnableCaching = true;
options.CacheDuration = TimeSpan.FromMinutes(10);
});
推荐答案
有两种认证方式,cookie和bearer.
There are two types of authentication, cookie and bearer.
cookie 让您保持登录状态,而不记名令牌则不能.因为不记名令牌设置为在某个时间过期,不允许您更改生命周期.
Where the cookie keeps you logged in, the bearer token can't. Because the bearer token is set to expire at some point, without allowing you to change the lifetime.
访问令牌过期后访问资源 (api) 的唯一方法是让用户再次登录或使用 刷新令牌,无需用户交互.
The only way to access the resource (api) after the access token expires is to either let the user login again or request a new access token using a refresh token, without needing user interaction.
你已经配置好了:
options.Scope.Add("offline_access");
在每次登录时,请求至少会包含一个刷新令牌.将其存放在安全的地方,并在需要时使用.默认设置为一次性使用.
On each login the request will at least contain a refresh token. Store it at a safe place and use it when needed. By default it is set to one time use only.
您可以使用类似此代码的代码来更新令牌(因为您实际上并不是在刷新它,而是替换它).您需要包含IdentityModel"NuGet 包,如 IdentityServer 的示例中所示.
You can use something like this code to renew the token (as you are not actually refreshing it, but rather replacing it). You'll need to include the 'IdentityModel' NuGet package, as seen in the samples from IdentityServer.
private async Task<TokenResponse> RenewTokensAsync()
{
// Initialize the token endpoint:
var client = _httpClientFactory.CreateClient();
var disco = await client.GetDiscoveryDocumentAsync("http://localhost:5000");
if (disco.IsError) throw new Exception(disco.Error);
// Read the stored refresh token:
var rt = await HttpContext.GetTokenAsync("refresh_token");
var tokenClient = _httpClientFactory.CreateClient();
// Request a new access token:
var tokenResult = await tokenClient.RequestRefreshTokenAsync(new RefreshTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = "mvc",
ClientSecret = "secret",
RefreshToken = rt
});
if (!tokenResult.IsError)
{
var old_id_token = await HttpContext.GetTokenAsync("id_token");
var new_access_token = tokenResult.AccessToken;
var new_refresh_token = tokenResult.RefreshToken;
var expiresAt = DateTime.UtcNow + TimeSpan.FromSeconds(tokenResult.ExpiresIn);
// Save the information in the cookie
var info = await HttpContext.AuthenticateAsync("Cookies");
info.Properties.UpdateTokenValue("refresh_token", new_refresh_token);
info.Properties.UpdateTokenValue("access_token", new_access_token);
info.Properties.UpdateTokenValue("expires_at", expiresAt.ToString("o", CultureInfo.InvariantCulture));
await HttpContext.SignInAsync("Cookies", info.Principal, info.Properties);
return tokenResult;
}
return null;
}
默认情况下,刷新令牌的使用是配置作为一次性使用.请注意,当存储新刷新令牌失败并且您应该丢失它时,请求新刷新令牌的唯一方法是强制用户重新登录.
By default the refresh token usage is configured as one time use. Please note that when storing the new refresh token fails and you should lose it, then the only way to request a new refresh token is to force the user to login again.
另请注意,刷新令牌可能会过期.
Also note that the refresh token can expire.
退一步,当访问令牌过期或即将过期时,您需要使用它:
And taking it one step back, you'll need to use this when the access token expired or is about to expire:
var accessToken = await HttpContext.GetTokenAsync("access_token");
var tokenHandler = new JwtSecurityTokenHandler();
var jwtSecurityToken = tokenHandler.ReadJwtToken(accessToken);
// Depending on the lifetime of the access token.
// This is just an example. An access token may be valid
// for less than one minute.
if (jwtSecurityToken.ValidTo < DateTime.UtcNow.AddMinutes(5))
{
var responseToken = await RenewTokensAsync();
if (responseToken == null)
{
throw new Exception("Error");
}
accessToken = responseToken.AccessToken;
}
// Proceed, accessToken contains a valid token.
这篇关于仍登录 MVC 站点,但无法调用 Web API的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!