惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Know Your Adversary
Know Your Adversary
小众软件
小众软件
L
LangChain Blog
月光博客
月光博客
博客园 - Franky
Microsoft Azure Blog
Microsoft Azure Blog
Y
Y Combinator Blog
有赞技术团队
有赞技术团队
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
MongoDB | Blog
MongoDB | Blog
Recorded Future
Recorded Future
V
Visual Studio Blog
TaoSecurity Blog
TaoSecurity Blog
S
Schneier on Security
C
Cybersecurity and Infrastructure Security Agency CISA
P
Privacy & Cybersecurity Law Blog
T
Threat Research - Cisco Blogs
D
DataBreaches.Net
L
LINUX DO - 热门话题
C
Check Point Blog
F
Fortinet All Blogs
Hugging Face - Blog
Hugging Face - Blog
The Hacker News
The Hacker News
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Microsoft Security Blog
Microsoft Security Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
V
V2EX
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
The GitHub Blog
The GitHub Blog
P
Proofpoint News Feed
L
Lohrmann on Cybersecurity
博客园 - 司徒正美
T
Threatpost
P
Palo Alto Networks Blog
A
About on SuperTechFans
Spread Privacy
Spread Privacy
Engineering at Meta
Engineering at Meta
N
News | PayPal Newsroom
T
Tailwind CSS Blog
The Last Watchdog
The Last Watchdog
Blog — PlanetScale
Blog — PlanetScale
A
Arctic Wolf
量子位
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
博客园 - 聂微东
Google Online Security Blog
Google Online Security Blog
Google DeepMind News
Google DeepMind News
www.infosecurity-magazine.com
www.infosecurity-magazine.com
V
Vulnerabilities – Threatpost
H
Hacker News: Front Page

博客园 - 俩醒叁醉

ASP.NET MVC 4 WebAPI. Support Areas in HttpControllerSelector SQL2000安装问题(转) sql server 2008安装需要一直重启。但重启后又没有达到效果。 为数据库中所有的用户数据表生成分页存储过程 SQL 2005 字段备注获取 __doPostBack方法解析 如何在三个月掌握三年的经验(转载&&笔记) jQuery Ajax的使用 JQuery资源网站 Cookie跨域、虚拟目录 深入分析跨域cookie的问题 CnBlogsDotText使用实例 轻松搭建博客平台-开源ASP.NET 博客Subtext 的安装 表达式目录树(源MSDN) Web 2.0 编程思想:16条法则 Control Adapter 以下代码提供查询数据库中是否存在某个值 URL Routing CSharp——Lambda 表达式
MVC Controllers和Forms验证
俩醒叁醉 · 2009-03-15 · via 博客园 - 俩醒叁醉

想想下,一组从Forms验证抽象出来的登入/登出Controllers行为特有的ActionResult实现。。

我已经使用ASP.NETMVC重新实现了我的个人网站(projects.nikhilk.net)的一部分。其中之一就是登入/登出功能的实现。当你创建一个新的ASP.NETMVC应用程序,默认的就会包含一个简单的AccountController。这个Controller中包含了登入/登出,并实现了IFormsAuthentication接口。其默认实现基于System.Web.Security.FormsAuthentication APIs(SetAuthCookie and SignOut)。可以仿照给这个接口的实现以便用于United测试。

尽管如此,当我在自己的Controller中实现登入/登出时,我发现一个奇怪现象,行为(actions)直接的访问FormsAuthentication(甚至是通过接口)。我以为行为应该简单地完成用户验证,而后返回一个处理生成响应的匹配的ActionResult,这该实例中就是设置或清除cookie,并重定向Url。为此,我创建了FormsLoginResult和FormsLogoutResult。很明显的他们是成对的出现的,but I thought I'd go ahead and share anyway。

首先,我将显示AccountController中修改过的Login和Logout方法。

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Login(string username, string password, bool rememberMe) {
    ...
    if (ViewData.ModelState.IsValid) {
        // Attempt to login
        bool loginSuccessful = Provider.ValidateUser(username, password);
        if (loginSuccessful) {
            return new FormsLoginResult(username, rememberMe);
        }
        else {
            ...
        }
    }

    // If we got this far, something failed, redisplay form
    ...
    return View();
}

public ActionResult Logout() {
    return new FormsLogoutResult();
}

现在Login和Logout行为不再和authentication是否通过cookie跟踪以及需要重定位到哪里(默认主页、参数关联的url等)相关联。

其次、建立这两个函数的测试单元。我使用Moq实现测试需要的MembershipProvider的模拟,当是不再需要IFormsAuthentication的实现。

[TestClass]
public class AccountControllerTest {

    [TestMethod]
    public void LoginSuccessful() {
        string testUserName = "TestUser";
        string testPassword = "TestPassword";

        Mock<MembershipProvider> mockMembership = new Mock<MembershipProvider>();
        mockMembership.Expect<bool>(m => m.ValidateUser(testUserName, testPassword))
           .Returns(true).AtMostOnce().Verifiable();

        AccountController controller = new AccountController(mockMembership.Object);
        ActionResult result = controller.Login(testUserName, testPassword, false);

        Assert.IsInstanceOfType(result, typeof(FormsLoginResult));

        FormsLoginResult loginResult = (FormsLoginResult)result;
        Assert.AreEqual<string>(loginResult.UserName, testUserName);
        Assert.AreEqual<bool>(loginResult.PersistentCookie, false);
    }

    [TestMethod]
    public void LoginFailure() {
        string testUserName = "TestUser";
        string testPassword = "TestPassword";

        Mock<MembershipProvider> mockMembership = new Mock<MembershipProvider>();
        mockMembership.Expect<bool>(m => m.ValidateUser(testUserName, testPassword))
           .Returns(true).AtMostOnce().Verifiable();

        AccountController controller = new AccountController(mockMembership.Object);
        ActionResult result = controller.Login(testUserName, "badPassword", false);

        Assert.IsInstanceOfType(result, typeof(ViewResult));
    }
}

下面是FormsLoginResult 和FormsLogoutResult的实现。这里使用了MVC提供的构建自定义action results可扩展性较好的实现。

public class FormsLoginResult : ActionResult {
    private string _userName;
    private string _userData;
    private bool _persistentCookie;

    public FormsLoginResult(string userName)
        : this(userName, /* persistentCookie */ false) {
    }

    public FormsLoginResult(string userName, bool persistentCookie) {
        if (String.IsNullOrEmpty(userName)) {
            throw new ArgumentNullException("userName");
        }
        _userName = userName;
        _persistentCookie = persistentCookie;
    }

    public bool PersistentCookie {
        get { return _persistentCookie; }
    }

    public string UserData {
        get { return _userData; }
        set { _userData = value; }
    }

    public string UserName {
        get { return _userName; }
    }

    public override void ExecuteResult(ControllerContext context) {
        HttpResponseBase response = context.HttpContext.Response;

        if (String.IsNullOrEmpty(_userData)) {
            FormsAuthentication.SetAuthCookie(_userName, _persistentCookie);
        }
        else {
            FormsAuthenticationTicket ticket =
                new FormsAuthenticationTicket(1, _userName,
                                              DateTime.Now, DateTime.Now.AddMinutes(30),
                                              _persistentCookie,
                                              _userData, 
                                              FormsAuthentication.FormsCookiePath);
            string encryptedTicket = FormsAuthentication.Encrypt(ticket);

            HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName,
                                               encryptedTicket);
            cookie.HttpOnly = true;
            cookie.Secure = FormsAuthentication.RequireSSL;
            cookie.Path = FormsAuthentication.FormsCookiePath;
            if (FormsAuthentication.CookieDomain != null) {
                cookie.Domain = FormsAuthentication.CookieDomain;
            }

            response.Cookies.Add(cookie);
        }

        response.Redirect(FormsAuthentication.GetRedirectUrl(_userName, _persistentCookie));
    }
}

令人庆幸的是,FormsLoginResult同样的在FormsAuth cookie内封装了一些自定义用户数据,这些数据可以通过HttpApplication的Authenticate事件获取,并可以使用FormsAuthentication.Decrypt将AuthCookie内数据转换为一个FormsAuthenticationTicket实例,以及从Ticket中获取的用户数据。我在我的网站中使用这个跟踪一些元数据以便于在Requests后重新创建代理实例。

第二个action result FormsLogoutResult非常简单。

public class FormsLogoutResult : ActionResult {
    private string _url;

    public FormsLogoutResult()
        : this(FormsAuthentication.DefaultUrl) {
    }

    public FormsLogoutResult(string url) {
        if (String.IsNullOrEmpty(url)) {
            throw new ArgumentNullException("url");
        }
        _url = url;
    }

    public string Url {
        get { return _url; }
    }

    public override void ExecuteResult(ControllerContext context) {
        FormsAuthentication.SignOut();
        context.HttpContext.Response.Redirect(_url);
    }
}

源:http://www.nikhilk.net/Default.aspx