C# 仍然登录MVC站点,但可以';不要调用web API

C# 仍然登录MVC站点,但可以';不要调用web API,c#,asp.net-core-mvc,identityserver4,asp.net-core-webapi,C#,Asp.net Core Mvc,Identityserver4,Asp.net Core Webapi,我有一个ASP.NET MVC站点、IdentityServer4主机和一个web API 当我使用外部提供商(Facebook)登录MVC站点时,我的登录状态良好。从MVC站点,我还可以正确使用web API 然而,第二天,我仍然登录到MVC站点,但是当我尝试访问web API时,我得到了一个“未授权的异常” 因此,尽管我仍然登录到MVC站点,但我不再经过身份验证,无法从MVC站点中调用web API 我想知道如何处理这种情况,以及应该如何配置IdentityServer4 为什么一天后我仍

我有一个ASP.NET MVC站点、IdentityServer4主机和一个web API

当我使用外部提供商(Facebook)登录MVC站点时,我的登录状态良好。从MVC站点,我还可以正确使用web API

然而,第二天,我仍然登录到MVC站点,但是当我尝试访问web API时,我得到了一个“未授权的异常”

因此,尽管我仍然登录到MVC站点,但我不再经过身份验证,无法从MVC站点中调用web API

我想知道如何处理这种情况,以及应该如何配置IdentityServer4

  • 为什么一天后我仍然登录MVC站点?如何配置
  • 如果我仍然登录在MVC站点,为什么我仍然不能调用web API
  • 我可以同步过期时间吗?或者我该怎么处理
MVC应用程序的配置如下:

 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");                  
        });            
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,
},
所以它使用混合流

在IdentityServer中,MVC客户端的配置如下:

 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");                  
        });            
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,
},

有两种类型的身份验证,cookie和承载

cookie让您登录的地方,承载令牌不能。因为承载令牌设置为在某个点过期,而不允许您更改生存期

访问令牌过期后访问资源(api)的唯一方法是让用户再次登录或使用请求新的访问令牌,而无需用户交互

您已经对其进行了配置:

options.Scope.Add("offline_access");
在每次登录时,请求将至少包含一个刷新令牌。将其存放在安全的地方,并在需要时使用。默认情况下,它设置为一次性使用


您可以使用类似于此代码的代码来续订令牌(因为您实际上不是在刷新令牌,而是在替换令牌)。您需要包括'IdentityModel'NuGet包,如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;
}
private异步任务RenewTokensAsync()
{
//初始化令牌终结点:
var client=_httpClientFactory.CreateClient();
var disco=await client.GetDiscoveryDocumentAsync(“http://localhost:5000");
if(disco.IsError)抛出新异常(disco.Error);
//读取存储的刷新令牌:
var rt=await-HttpContext.GetTokenAsync(“刷新令牌”);
var tokenClient=_httpClientFactory.CreateClient();
//请求新的访问令牌:
var tokenResult=await tokenClient.RequestRefreshTokenAsync(新的RefreshTokenRequest
{
地址=disco.tokenpoint,
ClientId=“mvc”,
ClientSecret=“secret”,
RefreshToken=rt
});
如果(!tokenResult.IsError)
{
var old_id_token=wait HttpContext.GetTokenAsync(“id_token”);
var new_access_token=tokenResult.AccessToken;
var new_refresh_token=tokenResult.refreshttoken;
var expiresAt=DateTime.UtcNow+TimeSpan.FromSeconds(tokenResult.ExpiresIn);
//将信息保存在cookie中
var info=wait HttpContext.AuthenticateAsync(“Cookies”);
info.Properties.UpdateTokenValue(“刷新令牌”,新的刷新令牌);
info.Properties.UpdateTokenValue(“访问令牌”,新的访问令牌);
UpdateTokenValue(“expires_at”,expiresAt.ToString(“o”,CultureInfo.InvariantCulture));
等待HttpContext.SignInAsync(“Cookies”,info.Principal,info.Properties);
返回结果;
}
返回null;
}
默认情况下,刷新令牌使用为一次性使用。请注意,当存储新刷新令牌失败并且您应该丢失它时,请求新刷新令牌的唯一方法是强制用户再次登录

还请注意,刷新令牌可能会过期


退一步说,当访问令牌过期或即将过期时,您需要使用此选项:

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.
var-accessToken=await-HttpContext.GetTokenAsync(“访问令牌”);
var tokenHandler=new JwtSecurityTokenHandler();
var jwtSecurityToken=tokenHandler.ReadJwtToken(accessToken);
//取决于访问令牌的生存期。
//这只是一个例子。访问令牌可能有效
//不到一分钟。
if(jwtSecurityToken.ValidTo
感谢您为我指明了正确的方向。所以我需要请求一个刷新令牌。我将进一步调查什么是最好的做法,以及如何做。我想感谢你的帮助!非常感谢:)我想如果调用“var responseToken=await RenewTokensAsync();”返回null,那么我必须以编程方式注销用户?是的,如果结果为null(无论为什么),那么您无法访问资源,因此需要新的刷新令牌。唯一的方法是强制用户再次登录。@RuardvanElburg
tokenHandler.ReadJwtToken(accessToken)expires\u值存储在
时,code>看起来过多