为什么将用户重定向到登录屏幕?

时间:2018-07-08 11:00:31

标签: cookies asp.net-core .net-core identityserver4 openid-connect

我想知道为什么我的客户端重定向到身份服务器。

我采取以下步骤:

  1. 重定向到身份服务器以登录。
  2. 登录并重定向回客户端。
  3. 使用该应用。
  4. 停止使用它。
  5. 超过12小时后打开应用。
  6. 重定向到登录屏幕。
  7. 挠头。

此时,身份服务器,客户端和api已部署到共享主机提供程序,我正在使用EF来存储配置和操作。

身份服务器配置

        services.AddIdentityServer(options =>
        {
            options.Authentication.CookieSlidingExpiration = true;
            options.Authentication.CookieLifetime = TimeSpan.FromDays(1);
        }) 
            .AddDeveloperSigningCredential()
            .AddAspNetIdentity<ApplicationUser>()
            .AddConfigurationStore(options =>
            {
                options.ConfigureDbContext = builder =>
                    builder.UseSqlServer(connectionString,
                        sql => sql.MigrationsAssembly(migrationsAssembly));
            })
            .AddOperationalStore(options =>
            {
                options.ConfigureDbContext = builder =>
                    builder.UseSqlServer(connectionString,
                        sql => sql.MigrationsAssembly(migrationsAssembly));

                options.EnableTokenCleanup = true;
                options.TokenCleanupInterval = 30;
            });


        services.AddAuthentication("custom")
            .AddCookie("custom", options =>
            {
                options.Cookie.Name = "custom";
                options.ExpireTimeSpan = TimeSpan.FromDays(1);
            });

        services.AddAuthentication()
            .AddFacebook(options =>
            {
                options.AppId = "1";
                options.AppSecret = "2";
            });

Config.cs中的客户端

 new Client
 {
     ClientId = "mvc-client",
     ClientName = "Mvc Client",
     AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,

     RequireConsent = false,

     ClientSecrets =
    {
       new Secret("secret".Sha256())
    },

    RedirectUris = { $"{address}signin-oidc" },
    PostLogoutRedirectUris = { $"{address}Client/" },

    AllowedScopes = new List<string>
    {
        IdentityServerConstants.StandardScopes.OpenId,
        IdentityServerConstants.StandardScopes.Profile,
        "api"
    },

        RefreshTokenUsage = TokenUsage.OneTimeOnly,
        RefreshTokenExpiration = TokenExpiration.Sliding,
        SlidingRefreshTokenLifetime = (3600 * 24 * 30),

        IdentityTokenLifetime = (60 * 15),
        AccessTokenLifetime = 300,

        AllowOfflineAccess = true,
 }

MVC客户端配置

services.AddAuthentication(options =>
    {
        options.DefaultScheme = "Cookies";
        options.DefaultChallengeScheme = "oidc";
    })
    .AddCookie("Cookies", options =>
     {
         options.Cookie.Expiration = TimeSpan.FromDays(30);
     })
    .AddOpenIdConnect("oidc", options =>
    {
        options.SignInScheme = "Cookies";

        options.Authority = $"{_config["Server"]}";
        options.RequireHttpsMetadata = false;

        options.ClientId = "mvc-client";
        options.ClientSecret = "secret";

        options.ResponseType = "code id_token";

        options.Scope.Add("api");
        options.Scope.Add("offline_access");

        options.GetClaimsFromUserInfoEndpoint = true;
        options.SaveTokens = true;

        options.TokenValidationParameters = new TokenValidationParameters
        {
            NameClaimType = JwtClaimTypes.Name,
            RoleClaimType = JwtClaimTypes.Role,
        };
    });

令牌续订服务过滤器

var accessToken = await context.HttpContext.GetTokenAsync("access_token");
var introspectionClient = new IntrospectionClient(_ipoint, "api", "api_secret");
var response = await introspectionClient.SendAsync(new IntrospectionRequest { Token = accessToken });

if (!response.IsActive)
{
    var issuer = _config["Server"].ToLower();

    var client = new DiscoveryClient(issuer);
    client.Policy.RequireHttps = false;
    var disco = await client.GetAsync();

    if (disco.IsError) throw new Exception(disco.Error);

    var tokenClient = new TokenClient(disco.TokenEndpoint, "mvc-client", "secret");

    var rt = await context.HttpContext.GetTokenAsync("refresh_token");
    var tokenResult = await tokenClient.RequestRefreshTokenAsync(rt);

    if (!tokenResult.IsError)
    {
        var info = await context.HttpContext.AuthenticateAsync("Cookies");

        var old_id_token = await context.HttpContext.GetTokenAsync("id_token");
        var new_access_token = tokenResult.AccessToken;
        var new_refresh_token = tokenResult.RefreshToken;

        var tokens = new List<AuthenticationToken>();
        tokens.Add(new AuthenticationToken { Name = OpenIdConnectParameterNames.IdToken, Value = old_id_token });
        tokens.Add(new AuthenticationToken { Name = OpenIdConnectParameterNames.AccessToken, Value = new_access_token });
        tokens.Add(new AuthenticationToken { Name = OpenIdConnectParameterNames.RefreshToken, Value = new_refresh_token });

        var expiresAt = DateTime.UtcNow + TimeSpan.FromSeconds(tokenResult.ExpiresIn);
        tokens.Add(new AuthenticationToken { Name = "expires_at", Value = expiresAt.ToString("o", CultureInfo.InvariantCulture) });


        info.Properties.StoreTokens(tokens);
        await context.HttpContext.SignInAsync("Cookies", info.Principal, info.Properties);
    }
    else
    {
        await context.HttpContext.SignOutAsync("Cookies");
    }
}

await next();

注意:我试图在IS上覆盖的Cookie中间件不起作用。 (例如:登录后,我在开发人员工具中看不到“自定义” cookie)

所以我的逻辑是刷新令牌是30天(并且在滑动),客户端上的cookie是30天(并且在服务过滤器中更新),这意味着用户最多可以在30天后返回天,并且仍然可以使用客户端重定向到身份服务器。

我正在尝试实现尽可能少的登录,因此,如果用户正在积极使用该应用程序,我希望他永远不会重定向到登录页面。

我认为发生的事情是cookie丢失了,并且[Authorize]属性在我的过滤器之前被调用,因此发生了重定向。我不确定id_token到期是否会导致重定向,请告知我。

虽然我可能会导致此问题:

  1. 我想念一些东西,所以我在这里。
  2. 客户端应用即将进入睡眠状态,cookie丢失。我决定排除这种情况,因为在一天不使用之后,该应用程序的加载速度很快。 (但再来一次...)
  3. 必须对Cookie覆盖无效进行处理。然后,虽然操作没有在数据库中,但我并没有引起它。

1 个答案:

答案 0 :(得分:1)

我正在使用的托管服务每天凌晨4:00刷新其应用程序池,因此删除了计算机密钥。

将机器密钥存储在文件系统中是解决方案:

services.AddDataProtection()
    .SetApplicationName("Server")
    .PersistKeysToFileSystem(new DirectoryInfo(_config["MachineKeys"]));

警告:此解决方案不会使用x509证书对密钥进行加密。