创建Cookie会话的最佳方法是什么? (代码克隆问题)

时间:2016-05-04 21:34:03

标签: c# asp.net asp.net-mvc session cookies

我没有使用任何默认的ASP.NET MVC身份验证,所以我使用cookie&创建自己的身份验证。为了我的安全,我的项目会话。下面的代码是我的函数Login,第一次生成cookie和会话。

HistoricScheduler

然后重定向到[HttpPost] [ValidateAntiForgeryToken] public ActionResult Login(User user) { bool ValidEmail = db.Users.Any(u => u.Username == user.Username); if (!ValidEmail) { return RedirectToAction("Login", "Home"); } string Password = db.Users.Where(u => u.Username == user.Username).Select(u => u.Password).Single(); user.Password = GenerateHashPassword(user.Password); if (Password != user.Password) { return RedirectToAction("Login", "Home"); } string AuthID = Guid.NewGuid().ToString(); Session["AuthID"] = AuthID; var Cookie = new HttpCookie("AuthID"); Cookie.Values["AuthID"] = AuthID; Cookie.Values["Username"] = user.Username; Cookie.Values["LastVisit"] = DateTime.Now.ToString(); Cookie.Expires = DateTime.Now.AddDays(365); Response.Cookies.Add(Cookie); return RedirectToAction("Index", "Project"); } 。对于这个控制器中的每个函数,我做了一些if语句,如果Cookies不等于Session由相同的AuthID值,那么它会重定向到主页(登录前)。例如:

ProjectController

这可以防止未处于登录状态的用户无法访问该链接(例如[HttpGet] public ActionResult Index() { try { if (Request.Cookies["AuthID"].Values["AuthID"] == Session["AuthID"].ToString()) { // main code here ... } else { return RedirectToAction("Index", "Home"); } } catch { return RedirectToAction("Index", "Home"); } } [HttpGet] public ActionResult Create() { try { if (Request.Cookies["AuthID"].Values["AuthID"] == Session["AuthID"].ToString()) { // main code here ... } else { return RedirectToAction("Index", "Home"); } } catch { return RedirectToAction("Index", "Home"); } } [HttpPost] [ValidateAntiForgeryToken] public ActionResult Create(Project project) { try { if (Request.Cookies["AuthID"].Values["AuthID"] == Session["AuthID"].ToString()) { // main code here ... } else { return RedirectToAction("Index", "Home"); } } catch { return RedirectToAction("Index", "Home"); } } [HttpGet] public ActionResult Edit() { try { if (Request.Cookies["AuthID"].Values["AuthID"] == Session["AuthID"].ToString()) { // main code here ... } else { return RedirectToAction("Index", "Home"); } } catch { return RedirectToAction("Index", "Home"); } } ... localhost:60612/Projectlocalhost:60612/Project/Create等)并将重定向到主页。

这里的问题是如何最小化每个函数中重复的localhost:60612/Project/Edit/3代码,记住一些软件质量保证方面:

  

如果有一些相同的确切代码,请在新函数中写入一次,然后通过其他需要的函数调用它(提取方法)。这样可以防止您的软件项目出现代码克隆代码重复问题(Bad Smell),并提高您的软件性能质量

我该如何最小化此代码?或者还有其他解决方案吗?

3 个答案:

答案 0 :(得分:1)

理想情况下,您希望使用 FormAuthentication ,它可以使用默认的 AuthorizeAttribute

由于您实现了自己的逻辑,因此您希望覆盖 AuthorizeAttribute

例如 -

public class MyAuthorizeAttribute : AuthorizeAttribute
{
    private bool AuthorizeUser(AuthorizationContext filterContext)
    {
        bool isAuthorized = false;

        if (filterContext.RequestContext.HttpContext != null)
        {
            var context = filterContext.RequestContext.HttpContext;

            if (context.Session["AuthID"] != null &&
                context.Request.Cookies["AuthID"].Values["AuthID"] ==
                context.Session["AuthID"].ToString())
            {
                isAuthorized = true;
            }
        }
        return isAuthorized;
    }

    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        if (filterContext == null)
            throw new ArgumentNullException("filterContext");

        if (AuthorizeUser(filterContext))
            return;

        base.OnAuthorization(filterContext);
    }
}

用法

[MyAuthorizeAttribute]
public class MyController : Controller
{
   ...
}

答案 1 :(得分:0)

我认为这通常是使用身份验证过滤器完成的。也许只是谷歌它,但这是一篇相关的文章http://www.dotnetcurry.com/aspnet-mvc/957/aspnet-mvc-authentication-filters

答案 2 :(得分:0)

应用于许多操作的通用逻辑通常应由操作过滤器处理 但是对于授权/认证案例,你真的应该使用一些经过验证和强化的广泛使用的解决方案,而不是实现自己的解决方案。

关于动作过滤器,基本的一般原则是从ActionFilterAttribute派生,在其OnActionExecuting方法中实现您的自定义通用逻辑,并将其应用于您的操作。有关一般情况的详细信息,请参阅here

对于授权案例,使用您的自定义逻辑,您应该实现IAuthorizationFilter

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method,
    Inherited = true, AllowMultiple = false)]
public class YourAuthorizationFilterAttribute :
    FilterAttribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationContext filterContext)
    {
        var httpContext = filterContext.HttpContext;
        if (httpContext.Request.Cookies["AuthID"].Values["AuthID"] ==
            httpContext.Session["AuthID"].ToString())
            // Let the action get executed
            return;

        // Otherwise redirect
        filterContext.Result = new RedirectResult(
            filterContext.Controller.Url("Index", "Home"));
    }
}
[YourAuthorizationFilter]
public ActionResult Index()
{
    // main code here
}

为了避免使用此属性装饰大多数操作,您可以将其设置在控制器上或将其设置为全局过滤器(在需要身份验证的操作上不要忘记它的最佳解决方案)。

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new YourAuthorizationFilter());
        ...

但是,您可能需要一种方法来停用某些操作,例如登录操作。应该使用其他操作过滤器属性(例如YourAllowAnonymousAttribute)来完成,该属性将由YourActionFilterAttribute进行测试,如果存在,则YourAuthorizationFilter将被停用。

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method,
    Inherited = true, AllowMultiple = false)]
public class YourAllowAnonymousAttribute : ActionFilterAttribute
{
    // Nothing to implement.
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method,
    Inherited = true, AllowMultiple = false)]
public class YourAuthorizationFilterAttribute :
    FilterAttribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationContext filterContext)
    {
        if (filterContext.ActionDescriptor.IsDefined(
                typeof(YourAllowAnonymousAttribute), inherit) ||
            filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(
                typeof(YourAllowAnonymousAttribute), true))
            return;
        ...
public class HomeController : Controller
{
     ...
     [YourAllowAnonymous]
     public ActionResult Index()
     {
         ...

从此answer中的AuthorizeFilter派生不会将您重定向到您想要的“开箱即用”,您必须在web.config中配置Asp.Net表单身份验证(这就是为什么它提到“FormAuthentication”)。

另一种解决方案是使用自定义基本控制器并覆盖其OnActionExecuting方法,但应首选操作过滤器。

但我坚持认为,实施自己的授权方案通常是一种反模式。安全是一件难事,这种逻辑有许多陷阱。也许这对你的应用程序来说根本不是问题,但以防万一:

  • 由于会话基于cookie,因此会话可能容易受到会话盗窃的影响。
  • 应签署身份验证cookie,并在每次请求时检查此签名,以避免浏览器用户修改身份验证cookie数据,例如用户名。
  • Cookie应标记为HttpOnly,以避免JavaScript能够访问它。
  • 如果通过HTTPS提供服务,则应将其标记为Secure,以避免以后通过不安全的HTTP进行传输。
  • 应将过期日期嵌入其数据中(并包含在签名中),以避免无休止重播(过期时cookie可能会受到攻击者修改)。
  • ...