我使用ASP.NET MVC 4 Web应用程序的前端,一些WCF服务。 所有用户登录/注销和会话控制是在后端完成。 MVC应用程序只能存储与会话ID一个Cookie。 我的客户不允许使用窗体身份验证,一切都必须进行定制。
我已经建立了在我的web.config以下:
<system.web>
...
<authentication mode="None" />
</system.web>
<system.webServer>
<modules>
...
<remove name="FormsAuthentication" />
...
</modules>
</system.webServer>
我也有一个全球性的过滤器:
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
// Force all actions to request auth. Only actions marked with [AllowAnonymous] will be allowed.
filters.Add(new MyAuthorizeAttribute());
}
}
这被称为在Global.asax中
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
我已经标有[使用AllowAnonymous]每个控制器和行动,并不需要授权。
而现在我要实现MyAuthorizeAttribute。 我曾尝试一些教程,但他们都不完全符合我的情景。
基本上,我有处理每个动作的以下情形:
- 如果有一个有效的cookie,当前请求应被视为授权(不会有任何角色检查,只有一个类型的用户)。
- 如果没有Cookie,我应该重写默认的MVC处理程序(它试图加载帐户/登录)和用户重定向到首页/索引页,用户应登录的消息。
- 如果WCF方法调用抛出的FaultException在那里我们的定制SecurityFault说,会议已过期(SecurityFault具有包含异常的原因的自定义枚举场),我要毁了我的自定义会话cookie和用户再次首页/索引页面重定向用消息,因为他的最后一次会议已过期,用户应登录。 对于所有其他SecurityFaults我可以让他们经历 - 我有一个全球性的错误处理程序。
据我了解,我需要重写AuthorizeCore(检查我的Cookie,看看是否存在会话,并仍然有效),并HandleUnauthorizedRequest(将用户重定向到首页/指数,而不是默认的登录页面)。
对于重定向我尝试:
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
base.HandleUnauthorizedRequest(filterContext);
filterContext.Result = new RedirectResult("/Home/Index/NeedsLogin");
}
这似乎处理的情况下第二次罚款(我不知道有关该基地的呼叫,但 - ?需要它)。
对于第一个方案,我需要实现AuthorizeCore。 我不知道,怎么做是正确的。 我已经看到,AuthorizeAttribute具有处理缓存的情况下,也许还有更多隐藏的功能,一些代码,我不想打破它。
对于第三个方案,我不知道如果MyAuthorizeAttribute将能够处理它。 可以AuthorizeAttribute行动的内部发生,不然我就在我的全局错误处理程序来处理SecurityFault.SessionExpired情况下捕获异常?
不能完全确定我得到它,但如果你创建一个自定义授权筛选,从System.Web.MVC.Authorize继承属性这样。
public class CustomAuthorize : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (CookieIsValid(filterContext.Request.Cookies["cookieyouwant"])
{
filterContext.Result = new RedirectResult("DestUrl");
}
else
{
filterContext.Result = new RedirectResult("/Home/Index/NeedsLogin");
}
}
}
然后装点你的方法需要使用这个授权会是这样的伎俩?
这是我如何做它现在:
public class MyAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
bool authorized = false;
/// MVC 4 boilerplate code follows
if (filterContext == null)
throw new ArgumentNullException("filterContext");
bool skipAuthorization = filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), inherit: true)
|| filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), inherit: true);
if (skipAuthorization)
{
return;
}
if (OutputCacheAttribute.IsChildActionCacheActive(filterContext))
{
throw new InvalidOperationException(
"MyAuthorizeAttribute cannot be used within a child action caching block."
);
}
// end of MVC code
// custom code
if (!AuthorizeCore(filterContext.HttpContext))
{
// if not authorized from some other Action call, let's try extracting user data from custom encrypted cookie
var identity = MyEncryptedCookieHelper.GetFrontendIdentity(filterContext.HttpContext.Request);
// identity might be null if cookie not received
if (identity == null)
{
filterContext.HttpContext.User = new GenericPrincipal(new GenericIdentity(""), null);
}
else
{
authorized = true;
filterContext.HttpContext.User = new MyFrontendPrincipal(identity);
}
// make sure the Principal's are in sync - there might be situations when they are not!
Thread.CurrentPrincipal = filterContext.HttpContext.User;
}
// MVC 4 boilerplate code follows
if (authorized)
{
// ** IMPORTANT **
// Since we're performing authorization at the action level, the authorization code runs
// after the output caching module. In the worst case this could allow an authorized user
// to cause the page to be cached, then an unauthorized user would later be served the
// cached page. We work around this by telling proxies not to cache the sensitive page,
// then we hook our custom authorization code into the caching mechanism so that we have
// the final say on whether a page should be served from the cache.
HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache;
cachePolicy.SetProxyMaxAge(new TimeSpan(0));
cachePolicy.AddValidationCallback(CacheValidateHandler, null /* data */);
}
else
{
HandleUnauthorizedRequest(filterContext);
}
//end of MVC code
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext == null)
throw new ArgumentNullException("httpContext");
// check to make sure the user is authenticated as my custom identity
var principal = httpContext.User as MyFrontendPrincipal;
if (principal == null)
return false;
var identity = principal.Identity as MyFrontendIdentity;
if (identity == null)
return false;
return true;
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
// default MVC result was:
// filterContext.Result = new HttpUnauthorizedResult();
// but I redirect to index login page instead of kicking 401
filterContext.Result = new RedirectResult("/Home/Index/NeedsLogin");
}
// MVC 4 boilerplate code follows
private void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus)
{
validationStatus = OnCacheAuthorization(new HttpContextWrapper(context));
}
// This method must be thread-safe since it is called by the caching module.
protected virtual HttpValidationStatus OnCacheAuthorization(HttpContextBase httpContext)
{
if (httpContext == null)
throw new ArgumentNullException("httpContext");
bool isAuthorized = AuthorizeCore(httpContext);
return (isAuthorized) ? HttpValidationStatus.Valid : HttpValidationStatus.IgnoreThisRequest;
}
}
它不处理我的第三方案,虽然如此,我将在全局错误处理程序执行。
关于你的第一个要求:
正如你已经发现, OnAuthorization
需要多个方面的护理,包括如缓存。
如果你只在定制中,用户证书验证的方式感兴趣,我建议你去覆盖AuthorizeCore
代替。 例如:
public class ClientCookieAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
HttpCookie cookie = httpContext.Request.Cookies[_tokenCookieName];
bool isAuthenticated = ValidateUserByCookie(cookie);
return isAuthenticated;
}
private bool ValidateUserByCookie(HttpCookie cookie)
{
var result = false;
// Perform validation
// You could include httpContext as well, to check further information
return result;
}
private static const string _tokenCookieName = "myCookieName";
}
你可能也想给看看这个其他线程:
- SO -自定义授权属性
- ASP.NET -自定义AuthorizationFilter重定向问题
- 忍者的日记