ASP.NET MVC中动态设置OWIN重定向URI至用户原页面
我之前也碰到过类似的需求,要让用户从哪个页面发起认证就回到哪个页面,其实核心思路就是在发起认证时保存当前页面的URL,然后在认证流程中动态调整跳转目标,具体可以按这几个步骤来:
解决思路:让O365认证后返回原发起页面
1. 发起认证时保存原页面URL
当用户触发认证(比如点击登录按钮),不要直接用默认的Challenge方法,而是先获取当前请求的来源URL,把它作为目标地址存在AuthenticationProperties里,确保后续能追踪到原页面。比如在你的Controller的Login动作里:
public ActionResult Login() { // 获取用户发起认证前的页面URL,默认跳转到首页 string returnUrl = Request.UrlReferrer?.ToString() ?? Url.Action("Index", "Home"); // 封装认证属性,传递原页面URL var authProperties = new AuthenticationProperties { RedirectUri = returnUrl }; return new ChallengeResult("OpenIdConnect", authProperties); }
如果项目里没有ChallengeResult类,需要添加这个自定义类来处理认证跳转:
public class ChallengeResult : HttpUnauthorizedResult { public ChallengeResult(string provider, AuthenticationProperties properties = null) { LoginProvider = provider; RedirectUri = properties?.RedirectUri; } public string LoginProvider { get; set; } public string RedirectUri { get; set; } public override void ExecuteResult(ControllerContext context) { var properties = new AuthenticationProperties { RedirectUri = RedirectUri }; context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider); } }
2. 动态调整OpenID Connect的跳转参数
接下来修改Startup.Auth.cs里的OpenIdConnectAuthenticationOptions配置,重点通过两个通知事件处理动态跳转:
public partial class Startup { public void ConfigureAuth(IAppBuilder app) { private static string redirectUri = ConfigurationManager.AppSettings["ida:RedirectUri"]; // ...其他配置 app.UseOpenIdConnectAuthentication( new OpenIdConnectAuthenticationOptions { ClientId = appId, Authority = "https://login.microsoftonline.com/organizations/v2.0", PostLogoutRedirectUri = redirectUri, // 保留默认RedirectUri作为 fallback RedirectUri = redirectUri, Notifications = new OpenIdConnectAuthenticationNotifications { // 跳转到O365登录页前,动态设置回调地址和state参数 RedirectToIdentityProvider = async (context) => { var challengeProps = context.OwinContext.Authentication.AuthenticationResponseChallenge.Properties; if (!string.IsNullOrEmpty(challengeProps.RedirectUri)) { // 动态替换回调地址(需符合应用注册的通配符规则) context.ProtocolMessage.RedirectUri = challengeProps.RedirectUri; // 把原页面URL存入state参数,用于后续验证和跳转 context.ProtocolMessage.State = challengeProps.RedirectUri; } }, AuthorizationCodeReceived = async (context) => { Dictionary<string, string> data = new Dictionary<string, string>(); data.Add("client_id", appId); data.Add("client_secret", appSecret); data.Add("code", context.ProtocolMessage.Code); data.Add("grant_type", "authorization_code"); // 使用动态设置的回调地址,而非固定值 data.Add("redirect_uri", context.ProtocolMessage.RedirectUri); // ...后续的token获取逻辑不变 }, // 认证成功后,跳转到原页面 SecurityTokenValidated = (context) => { // 从state参数中取出原页面URL string returnUrl = context.ProtocolMessage.State; if (!string.IsNullOrEmpty(returnUrl)) { context.AuthenticationTicket.Properties.RedirectUri = returnUrl; } return Task.CompletedTask; } } }); } }
3. 关键注意事项
- 重定向URI规则:确保动态生成的回调地址符合应用注册时设置的通配符规则,比如注册的是
https://*.yourdomain.com/*,那么所有子域名下的页面URL都能被允许。 - CSRF安全:通过
state参数传递原URL是安全的,OpenID Connect会自动验证state的一致性,避免跨站请求伪造风险。 - 兼容性:这种方法不依赖Session存储,即使应用禁用Session也能正常工作,兼容性更强。
内容的提问来源于stack exchange,提问作者Douglas Anderson
相关产品推荐
相关产品推荐

