vs2013没有再分webform、mvc、api项目,使用vs2013创建一个web项目模板选MVC,身份验证选个人用户账户。项目会生成ASP.NET Identity的一些代码。这些代码主要在AccountController中。

ASP.NET Identity微软宣称的好处就不说了,这是原文

ASP.NET Identity

As the membership story in ASP.NET has evolved over the years, the ASP.NET team has learned a lot from feedback from customers.

The assumption that users will log in by entering a user name and password that they have registered in your own application is no longer valid. The web has become more social. Users are interacting with each other in real time through social channels such as Facebook, Twitter, and other social web sites. Developers want users to be able to log in with their social identities so that they can have a rich experience on their web sites. A modern membership system must enable redirection-based log-ins to authentication providers such as Facebook, Twitter, and others.

As web development evolved, so did the patterns of web development. Unit testing of application code became a core concern for application developers. In 2008 ASP.NET added a new framework based on the Model-View-Controller (MVC) pattern, in part to help developers build unit testable ASP.NET applications. Developers who wanted to unit test their application logic also wanted to be able to do that with the membership system.

Considering these changes in web application development, ASP.NET Identity was developed with the following goals:
• One ASP.NET Identity system •ASP.NET Identity can be used with all of the ASP.NET frameworks, such as ASP.NET MVC, Web Forms, Web Pages, Web API, and SignalR.
• ASP.NET Identity can be used when you are building web, phone, store, or hybrid applications. 

• Ease of plugging in profile data about the user •You have control over the schema of user and profile information. For example, you can easily enable the system to store birth dates entered by users when they register an account in your application.
 Persistence control •By default, the ASP.NET Identity system stores all the user information in a database. ASP.NET Identity uses Entity Framework Code First to implement all of its persistence mechanism.
•Since you control the database schema, common tasks such as changing table names or changing the data type of primary keys is simple to do.
•It's easy to plug in different storage mechanisms such as SharePoint, Windows Azure Storage Table Service, NoSQL databases, etc., without having to throw System.NotImplementedExceptions exceptions.

• Unit testability • ASP.NET Identity makes the web application more unit testable. You can write unit tests for the parts of your application that use ASP.NET Identity.

• Role provider • There is a role provider which lets you restrict access to parts of your application by roles. You can easily create roles such as “Admin” and add users to roles.

• Claims Based • ASP.NET Identity supports claims-based authentication, where the user’s identity is represented as a set of claims. Claims allow developers to be a lot more expressive in describing a user’s identity than roles allow. Whereas role membership is just a boolean (member or non-member), a claim can include rich information about the user’s identity and membership.

•Social Login Providers • You can easily add social log-ins such as Microsoft Account, Facebook, Twitter, Google, and others to your application, and store the user-specific data in your application.

• Windows Azure Active Directory •You can also add log-in functionality using Windows Azure Active Directory, and store the user-specific data in your application. For more information, see  Organizational Accounts in Creating ASP.NET Web Projects in Visual Studio 2013

• OWIN Integration • ASP.NET authentication is now based on OWIN middleware that can be used on any OWIN-based host. ASP.NET Identity does not have any dependency on System.Web. It is a fully compliant OWIN framework and can be used in any OWIN hosted application.
•ASP.NET Identity uses OWIN Authentication for log-in/log-out of users in the web site. This means that instead of using FormsAuthentication to generate the cookie, the application uses OWIN CookieAuthentication to do that.

• NuGet package • ASP.NET Identity is redistributed as a NuGet package which is installed in the ASP.NET MVC, Web Forms and Web API templates that ship with Visual Studio 2013. You can download this NuGet package from the NuGet gallery.
•Releasing ASP.NET Identity as a NuGet package makes it easier for the ASP.NET team to iterate on new features and bug fixes, and deliver these to developers in an agile manner.

 

我的感兴趣的主要是几个方面。1、可以扩展用户类的字段;2、使用EF codefirst存储;3、OWIN集成 。4、基于Claims(Claims是什么,到底有什么用我都不知道,只知道是.NET框架里的一个命名空间)。

我看了整个代码觉得它有点类似于三层架构。这里要特别重要的几个类:

  • ApplicationDbContext类。就是继承字自ef的DbContext,EF codefirst的必备。
  • UserStore类。封装了数据库访问一系列方法,通过DbContext操作数据库。类似三层架构中的数据访问层。
  • UserManager类。用户管理的类,封装了用户、角色、Claim等一系列的方法,通过UserStore类与数据库交互。类似三层的业务逻辑层。
  • AuthenticationManager类。Owin的一些东西,包含了用户登录、注销、验证等一些方法。
  • ApplicationUser。用户模型。继承自IdentityUser,可以自行扩展属性。

这个东西怎么用呢:

一、设置ApplicationDbContext类

先看下代码:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
    {
        public ApplicationDbContext()
            : base("DefaultConnection")
        {
        }
    }

这个类继承自IdentityDbContext类,ApplicationUser是用户模型。

这里要设置的就是base("DefaultConnection")。数据库的链接字符串。在web.config的connectionStrings节设置。

VS2013中web项目中自动生成的ASP.NET Identity代码思考_sed

我们再分析IdentityDbContext类的元数据。

VS2013中web项目中自动生成的ASP.NET Identity代码思考_ASP_02

可以看出它继承自DbContext。共再数据库创建两个表,Users根据传输的用户模型来创建。角色表 Roles表根据IdentityRole来创建(这个类只有两个属性ID和name)看以看出这里角色的字段是不能扩展的。

二、扩展用户模型(ApplicationUser)

当然不扩展可以直接用,当预设不满足要求时就可以自行扩展字段。首先看下代码

// You can add profile data for the user by adding more properties to your ApplicationUser class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
    public class ApplicationUser : IdentityUser
    {
    }

是个继承自IdentityUser的空类,需要扩展的字段直接写这就行,如果扩展个年龄(Age)

// You can add profile data for the user by adding more properties to your ApplicationUser class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
    public class ApplicationUser : IdentityUser
    {
        public int Age { get; set; }
    }

我们看下其继承的IdentityUser类,这里面都是一些预知的属性。IUser是一个接口只有ID和username两个属性。

public class IdentityUser : IUser
    {
        public IdentityUser();
        public IdentityUser(string userName);

        public virtual ICollection<IdentityUserClaim> Claims { get; }
        public virtual string Id { get; set; }
        public virtual ICollection<IdentityUserLogin> Logins { get; }
        public virtual string PasswordHash { get; set; }
        public virtual ICollection<IdentityUserRole> Roles { get; }
        public virtual string SecurityStamp { get; set; }
        public virtual string UserName { get; set; }
    }

三、利用UserManager,AuthenticationManager进行用户相关操作

这一步就可直接在控制器中写代码了。我们先看下AccountController的构造函数

[Authorize]
    public class AccountController : Controller
    {
        public AccountController()
            : this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())))
        {
        }

注意,UserManager,ApplicationUser,UserStore,ApplicationDbContext

1、看下用户注册代码

[HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> Register(RegisterViewModel model)
        {
            if (ModelState.IsValid)
            {
                var user = new ApplicationUser() { UserName = model.UserName };
                var result = await UserManager.CreateAsync(user, model.Password);
                if (result.Succeeded)
                {
                    await SignInAsync(user, isPersistent: false);
                    return RedirectToAction("Index", "Home");
                }
                else
                {
                    AddErrors(result);//一个循环向ModelState添加错误消息的方法
                }
            }

            // 如果我们进行到这一步时某个地方出错,则重新显示表单
            return View(model);
        }

我们来看下这段代码。

public async Task<ActionResult> Register(RegisterViewModel model)这句将Register声明为异步action。

var user = new ApplicationUser() { UserName = model.UserName };这句是ApplicationUser这个创建模型类。

var result = await UserManager.CreateAsync(user, model.Password);这句是一个异步添加用户。利用UserManager这个类的CreateAsync方法创建用户。

类元数据如下图,里面封装了各种用户操作的方法。

VS2013中web项目中自动生成的ASP.NET Identity代码思考_ASP_03

await SignInAsync(user, isPersistent: false);这句依然。这里是AccountController的一个函数,代码如下:

private async Task SignInAsync(ApplicationUser user, bool isPersistent)
        {
            AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
            var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
            AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
        }

可以看出利用AuthenticationManager的SignOut和SignIn进行清除cookie和登录。

2、再看下注销

[HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult LogOff()
        {
            AuthenticationManager.SignOut();
            return RedirectToAction("Index", "Home");
        }

使用AuthenticationManager.SignOut方法。这是类似于 WebFor中的FormsAuthentication所使用的FormsAuthentication.SignOut方法。

3、再看登录代码

[HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
        {
            if (ModelState.IsValid)
            {
                var user = await UserManager.FindAsync(model.UserName, model.Password);
                if (user != null)
                {
                    await SignInAsync(user, model.RememberMe);
                    return RedirectToLocal(returnUrl);
                }
                else
                {
                    ModelState.AddModelError("", "Invalid username or password.");
                }
            }

            // 如果我们进行到这一步时某个地方出错,则重新显示表单
            return View(model);
        }

代码比较类似,用UserManager的FindAsync查找用户,成功又有是用调用SignInAsync方法进行登录(方法内部是用AuthenticationManager的SignIn进行登录)

四、总结

这里用到的是ApplicationDbContext、UserStore、UserManager、AuthenticationManager、ApplicationUser这5个类。其中ApplicationDbContext需要设置连接字符串,ApplicationUser可以扩展用户字段,UserStore、UserManager、AuthenticationManager这三个类都封装好的类直接拿来用就行,关键是要理解方法和属性的含义、清楚其用途。

五、问题及思考

  • 依靠[Authorize]应该是用来验证用户是否登录的,直接用来进行权限管理(控制器上写[Authorize(Roles="管理员")])是不是太僵化了?
  • Claims这是东西什么东西,从来没用过,是否与权限有关?

作者:洞庭夕照