tldr 。我有一个仅具有外部登录的ASP.NET门户(没有本地身份)。用户不断每3-4分钟自动登录一次,迫使门户对外部服务进行重新认证
MVC5/OWIN/IDENTITY3
我们有一个中央身份验证服务器,该服务器可与修改的O-Auth2工作流一起使用。基本上,一旦用户登录,将从该服务器中检索所有用户/个人资料/授权详细信息。
请注意,没有局部身份的概念,因为我们没有永久存储上述数据,除了在用户登录的当前身份中作为索赔。
为了实现此目的,我们创建了一个自定义中间件,该中间件对外部系统(类似于Google Auth Katana实现)
对外部系统进行身份验证该系统的工作正常,除了它似乎每180秒左右"忘记"用户...
本质上,发生的事情是,即使'.aspnet.applicationcookie'已发送回门户,门户也将用户视为未登录并将用户重定向到外部服务器。
。startup.auth.cs
相关部分 // Commented out since we do NOT want local identity
//app.CreatePerOwinContext(ApplicationDbContext.Create);
//app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
//app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
var cookieOptions = new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
SlidingExpiration = true,
ExpireTimeSpan = new TimeSpan(2,0,0)
};
app.SetDefaultSignInAsAuthenticationType(cookieOptions.AuthenticationType);
app.UseCookieAuthentication(cookieOptions);
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ApplicationCookie);
// Based on https://github.com/aspnet/AspNetKatana/tree/v3.0.1/src/Microsoft.Owin.Security.Google
// GoogleOAuth2AuthenticationMiddleware , GoogleOAuth2AuthenticationHandler,
// Essentialy, redirects user for login to external server
// Get the relavent data and creates a claims identity
// Declares user logged in.
app.UseCustomAuthentication(new CustomAuthenticationOptions()
{
ClientId = ConfigurationManager.AppSettings["clientKey"],
ClientSecret = ConfigurationManager.AppSettings["clientSecret"],
AuthenticationMode = AuthenticationMode.Passive,
SignInAsAuthenticationType = "CustomAuth",
BeforeRedirectPath = new PathString("/Account/BeforeAuthRedirect")
});
帐户控制器中的代码。
[AllowAnonymous]
public ActionResult Login(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
var result = new ChallengeResult(
"CustomAuth",
Url.Action("CallBackURL", "Account", new
{
ReturnUrl = returnUrl
})
);
return result;
}
挑战者的相关代码
public override void ExecuteResult(ControllerContext context)
{
var properties = new AuthenticationProperties { RedirectUri = RedirectUri };
context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider);
}
我的猜测是,cookie在管道下方的某个地方没有正确解密,并且它产生了无效的身份。
如果有人可以帮助我弄清楚ApplicationCookie确定要解密并重新补充身份将非常有帮助。
任何其他想法都将不胜感激。
对于Owin,您至少必须具有内存本地数据库。您不需要将细节存储在持久的商店中,例如DB。,但是您确实需要使用模仿此行为的数据结构。
索赔在哪里存储?您是否在视野中坚持下去?需要有一个指索赔和用户会话的地方。
我怀疑您的用户将用户重定向到外部身份验证的原因纯粹是因为您的应用程序没有跟踪谁已授权。
更新:阅读评论,我们俩都在说同样的话,但用不同的术语说。
Owin在IAuthenticationManager
接口中具有自己的身份验证管理器版本,该版本附加到您的HTTPContext对象。要访问它:
HttpContext.GetOwinContext().Authentication;
签名与ClaimsIdentity
一起使用,其中包含用户信息。存储的用户细节存储在索赔中。
创建一个可以充当身份验证的上下文
的辅助函数private IAuthenticationManager AuthenticationManager
{
get { return HttpContext.GetOwinContext().Authentication; }
}
您的标志在助手中
var claims = new List<Claim>();
claims.Add(new Claim(ClaimTypes.NameIdentifier, <userid>));
claims.Add(new Claim(ClaimTypes.Name, <name>));
var identity = new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties()
{
AllowRefresh = true,
IsPersistent = isPersistent,
ExpiresUtc = DateTime.UtcNow.AddDays(7)
}, identity);
这些是您的cookie的基础,也是Owin的身份许多作品缺少,但是您必须有这些来实现饼干的主张。
,如果您不以另一种方式持续索赔,则全部完成。
弄清楚了。因此,为其他可能遇到此问题的人发布。
解决此问题的关键是需要定期刷新页面。之所以发生这种情况,是因为外部cookie(由自定义身份验证设置)未明确设置到期日期,因此使cookie默认为60秒。
由于滑行到期的事实,反复刷新页面使会话活着。
在正常情况下,当您拥有本地身份时,这并不重要,因为主cookie auth的持续时间更长。但是,当您没有本地身份时,下面的主要cookie中间件代码...
var cookieOptions = new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
SlidingExpiration = true,
ExpireTimeSpan = new TimeSpan(2,0,0)
};
app.SetDefaultSignInAsAuthenticationType(cookieOptions.AuthenticationType);
app.UseCookieAuthentication(cookieOptions);
未设置cookie ,它只是在后续页面加载中读取它以重新填充身份。
这就是我们将外部cookie名称与应用程序cookie名称相同的原因。
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ApplicationCookie);
从实现的角度来看,当您在中间件中生成身份验证票时,请记住明确设置到期日期并在需要时坚持下去。即
//Inside CustomAuthenticationHandler
private async Task<bool> InvokeReplyPathAsync()
{
if (Options.CallbackPath.HasValue && Options.CallbackPath == Request.Path)
{
_//InvokeReplyPathAsync: - Match {Request.Path}");
AuthenticationTicket ticket = await AuthenticateAsync();
if (ticket == null)
{
// InvokeReplyPathAsync: Null Ticket Recieved. Unable to redirect.
Response.StatusCode = 500;
return true;
}
//***** IMPORTANT ******WILL CAUSE SEEMINGLY RANDOM LOGOUT IF NOT SET *******
// Defaults to 60 Seconds if not set
ticket.Properties.ExpiresUtc = DateTimeOffset.UtcNow.AddDays(2); // Set as per your application need
ticket.Properties.IsPersistent = true;
ticket.Properties.IssuedUtc = DateTimeOffset.UtcNow;