ASP.NET 使用 OpenId 身份验证:如果未授权,则重定向到 url
ASP.NET with OpenIdAuthentication: redirect to url if not authorized
我正在尝试编写一个使用混合身份验证方案的 ASP.NET 应用程序。
用户可以将其用户名和密码哈希存储在 UserStore 中,也可以通过 Azure Active Directory 进行身份验证。
我已经创建了如图所示的登录表单。它有标准的 UserName
和 Password
输入,还有一个 "Login via Active Directory" 按钮。
这个效果很好。
现在问题来了:应用程序的主页有 [Authorize]
属性。
public class DefaultController : Controller
{
[Authorize]
public ViewResult Index()
{
// Implementation
}
}
如果用户没有登录,我希望它重定向到页面Account/Login
,允许用户选择身份验证方法。
将 IAppBuilder.UseOpenIdConnectAuthentication
添加到管道设置后,它不再重定向到该页面。相反,它会直接转到 Microsoft 登录页面。
如何配置它,使 OpenID 身份验证成为系统的一部分,但允许我指定在用户未通过身份验证时如何执行重定向?
这是我设置管道的代码:
appBuilder.SetDefaultSignInAsAuthticationType(CookieAuthenticationDefaults.AuthenticationType_;
var cookieAuthenticationOptions = new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationType.ApplicationCookie,
LoginPath = new Microsoft.Owin.PathString("/Account/Login"),
Provider = new Security.CookieAuthenticationProvider()
};
appBuilder.UseCookieAuthentication(cookieAuthenticationOptions);
// Now the OpenId authentication
var notificationHandlers = new OpenIdConnectAuthenticationNotificationHandlers
{
AuthorizationCodeReceived = async(context) => {
var jwtSecurityToken = context.JwtSecurityToken;
// I've written a static method to convert the claims
// to a user
var user = await GetOrCreateUser(context.OwinContext, jwtSecurityToken.Claims);
var signInManager = context.OwinContext.Get<SignInManager>();
await signInManager.SignInAsync(user, true, false);
}
}
var openIdOptions = new OpenIdConnectAuthenticationOptions
{
ClientId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx",
Authority = "https://login.microsoftonline.com/xxxxx.onmicrosoft.com",
PostLogoutRedirectUri = "https://localhost:52538/Account/Login",
Notifications = notifcationHandlers
}
appBuilder.UseOpenIdConnectAuthentication(openIdOptions);
当您点击 "Active Directory Signin" 时,它会发布到 "Account/SignInWithOpenId"
public ActionResult SignInWithOpenId()
{
// Send an OpenID Connect sign-in request.
if (!Request.IsAuthenticated)
{
var authenticationProperties = new AuthenticationProperties
{
RedirectUri = "/"
};
HttpContext.GetOwinContext().Authentication.Challenge
(
authenticationProperties,
OpenIdConnectAuthenticationDefaults.AuthenticationType
);
return new EmptyResult();
}
else
{
return RedirectToAction("Index", "Default");
}
}
请尝试将 UseOpenIdConnectAuthentication
移动到 UseCookieAuthentication
之前,然后它将用户重定向到 "login" 页面。
// Now the OpenId authentication
var notificationHandlers = new OpenIdConnectAuthenticationNotificationHandlers
{
// Handler methods here
}
var openIdOptions = new OpenIdConnectAuthenticationOptions
{
ClientId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx",
Authority = "https://login.microsoftonline.com/xxxxx.onmicrosoft.com",
PostLogoutRedirectUri = "https://localhost:52538/Account/Login",
Notifications = notifcationHandlers
}
appBuilder.UseOpenIdConnectAuthentication(openIdOptions);
appBuilder.SetDefaultSignInAsAuthticationType(CookieAuthenticationDefaults.AuthenticationType_;
var cookieAuthenticationOptions = new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationType.ApplicationCookie,
LoginPath = new Microsoft.Owin.PathString("/Account/Login"),
Provider = new Security.CookieAuthenticationProvider()
};
appBuilder.UseCookieAuthentication(cookieAuthenticationOptions);
请告诉我是否有帮助。
更新:
或者您可以使用自定义授权属性来覆盖默认行为,例如:
public class CustomAuthorizeAttribute: AuthorizeAttribute
{
public CustomAuthorizeAttribute(): base()
{
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAuthenticated)
{
}
else
{
filterContext.HttpContext.Response.Redirect("Account/login");
}
}
}
然后只需将您的用法更改为:
[CustomAuthorize]
public ActionResult Index()
{
return View();
}
对 IAppBuilder.UseOpenIdConnectAuthentication(...)
的调用将 Owin 中间件组件放入管道中。当 ASP.NET MVC returns 401(未经授权)的 HttpResponse 时,Owin 中间件组件会检测到这一点并将其更改为 Http 重定向(代码 302),重定向路径指向 Open Id 提供程序。
但是有一种方法可以解决这个问题:在中间件组件执行重定向之前,它会调用回调 RedirectToIdentityProvider
。从这里,您可以覆盖此重定向。
这是我的代码,它覆盖了重定向,除非它来自请求路径 Account/SignInWithOpenId
。
var notificationHandlers = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = async(context) => {
// Sign in the user here
},
RedirectToIdentityProvider = (context) => {
if(context.OwinContext.Request.Path.Value != "/Account/SignInWithOpenId")
{
context.OwinContext.Response.Redirect("/Account/Login");
context.HandleResponse();
}
return Task.FromResult(0);
}
}
var openIdOptions = new OpenIdConnectAuthenticationOptions
{
ClientId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx",
Authority = "https://login.microsoftonline.com/xxxxx.onmicrosoft.com",
PostLogoutRedirectUri = "https://localhost:52538/Account/Login",
Notifications = notifcationHandlers
}
appBuilder.UseOpenIdConnectAuthentication(openIdOptions);
如果您将 OIDC 的身份验证模式设置为被动,它应该只响应明确的 Open ID Connect 请求(例如发布到 Account/SignInWithOpenId):
var openIdOptions = new OpenIdConnectAuthenticationOptions
{
// ...
AuthenticationMode = AuthenticationMode.Passive
}
的“明确使用质询和注销”部分
我正在尝试编写一个使用混合身份验证方案的 ASP.NET 应用程序。 用户可以将其用户名和密码哈希存储在 UserStore 中,也可以通过 Azure Active Directory 进行身份验证。
我已经创建了如图所示的登录表单。它有标准的 UserName
和 Password
输入,还有一个 "Login via Active Directory" 按钮。
这个效果很好。
现在问题来了:应用程序的主页有 [Authorize]
属性。
public class DefaultController : Controller
{
[Authorize]
public ViewResult Index()
{
// Implementation
}
}
如果用户没有登录,我希望它重定向到页面Account/Login
,允许用户选择身份验证方法。
将 IAppBuilder.UseOpenIdConnectAuthentication
添加到管道设置后,它不再重定向到该页面。相反,它会直接转到 Microsoft 登录页面。
如何配置它,使 OpenID 身份验证成为系统的一部分,但允许我指定在用户未通过身份验证时如何执行重定向?
这是我设置管道的代码:
appBuilder.SetDefaultSignInAsAuthticationType(CookieAuthenticationDefaults.AuthenticationType_;
var cookieAuthenticationOptions = new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationType.ApplicationCookie,
LoginPath = new Microsoft.Owin.PathString("/Account/Login"),
Provider = new Security.CookieAuthenticationProvider()
};
appBuilder.UseCookieAuthentication(cookieAuthenticationOptions);
// Now the OpenId authentication
var notificationHandlers = new OpenIdConnectAuthenticationNotificationHandlers
{
AuthorizationCodeReceived = async(context) => {
var jwtSecurityToken = context.JwtSecurityToken;
// I've written a static method to convert the claims
// to a user
var user = await GetOrCreateUser(context.OwinContext, jwtSecurityToken.Claims);
var signInManager = context.OwinContext.Get<SignInManager>();
await signInManager.SignInAsync(user, true, false);
}
}
var openIdOptions = new OpenIdConnectAuthenticationOptions
{
ClientId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx",
Authority = "https://login.microsoftonline.com/xxxxx.onmicrosoft.com",
PostLogoutRedirectUri = "https://localhost:52538/Account/Login",
Notifications = notifcationHandlers
}
appBuilder.UseOpenIdConnectAuthentication(openIdOptions);
当您点击 "Active Directory Signin" 时,它会发布到 "Account/SignInWithOpenId"
public ActionResult SignInWithOpenId()
{
// Send an OpenID Connect sign-in request.
if (!Request.IsAuthenticated)
{
var authenticationProperties = new AuthenticationProperties
{
RedirectUri = "/"
};
HttpContext.GetOwinContext().Authentication.Challenge
(
authenticationProperties,
OpenIdConnectAuthenticationDefaults.AuthenticationType
);
return new EmptyResult();
}
else
{
return RedirectToAction("Index", "Default");
}
}
请尝试将 UseOpenIdConnectAuthentication
移动到 UseCookieAuthentication
之前,然后它将用户重定向到 "login" 页面。
// Now the OpenId authentication
var notificationHandlers = new OpenIdConnectAuthenticationNotificationHandlers
{
// Handler methods here
}
var openIdOptions = new OpenIdConnectAuthenticationOptions
{
ClientId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx",
Authority = "https://login.microsoftonline.com/xxxxx.onmicrosoft.com",
PostLogoutRedirectUri = "https://localhost:52538/Account/Login",
Notifications = notifcationHandlers
}
appBuilder.UseOpenIdConnectAuthentication(openIdOptions);
appBuilder.SetDefaultSignInAsAuthticationType(CookieAuthenticationDefaults.AuthenticationType_;
var cookieAuthenticationOptions = new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationType.ApplicationCookie,
LoginPath = new Microsoft.Owin.PathString("/Account/Login"),
Provider = new Security.CookieAuthenticationProvider()
};
appBuilder.UseCookieAuthentication(cookieAuthenticationOptions);
请告诉我是否有帮助。
更新:
或者您可以使用自定义授权属性来覆盖默认行为,例如:
public class CustomAuthorizeAttribute: AuthorizeAttribute
{
public CustomAuthorizeAttribute(): base()
{
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAuthenticated)
{
}
else
{
filterContext.HttpContext.Response.Redirect("Account/login");
}
}
}
然后只需将您的用法更改为:
[CustomAuthorize]
public ActionResult Index()
{
return View();
}
对 IAppBuilder.UseOpenIdConnectAuthentication(...)
的调用将 Owin 中间件组件放入管道中。当 ASP.NET MVC returns 401(未经授权)的 HttpResponse 时,Owin 中间件组件会检测到这一点并将其更改为 Http 重定向(代码 302),重定向路径指向 Open Id 提供程序。
但是有一种方法可以解决这个问题:在中间件组件执行重定向之前,它会调用回调 RedirectToIdentityProvider
。从这里,您可以覆盖此重定向。
这是我的代码,它覆盖了重定向,除非它来自请求路径 Account/SignInWithOpenId
。
var notificationHandlers = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = async(context) => {
// Sign in the user here
},
RedirectToIdentityProvider = (context) => {
if(context.OwinContext.Request.Path.Value != "/Account/SignInWithOpenId")
{
context.OwinContext.Response.Redirect("/Account/Login");
context.HandleResponse();
}
return Task.FromResult(0);
}
}
var openIdOptions = new OpenIdConnectAuthenticationOptions
{
ClientId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx",
Authority = "https://login.microsoftonline.com/xxxxx.onmicrosoft.com",
PostLogoutRedirectUri = "https://localhost:52538/Account/Login",
Notifications = notifcationHandlers
}
appBuilder.UseOpenIdConnectAuthentication(openIdOptions);
如果您将 OIDC 的身份验证模式设置为被动,它应该只响应明确的 Open ID Connect 请求(例如发布到 Account/SignInWithOpenId):
var openIdOptions = new OpenIdConnectAuthenticationOptions
{
// ...
AuthenticationMode = AuthenticationMode.Passive
}
的“明确使用质询和注销”部分