Wednesday, 9 February 2011 02:03 AM
If you look at the ASP.NET Web Pages (aka Razor) tutorials on the ASP.NET web site, there‘s a chapter devoted to adding security to a site in the form of membership (that is, a login capability). This chapter basically recommends that you use the Starter Site template, which already includes membership pages. It then shows you a few ways to extend the existing membership facilities, which it does by, effectively, re-implementing manually some of the pages created by the template.
But what if you want to add membership/login to an existing site? Razor lets you pretty easily build login capabilities into any site. However, it‘s not necessarily obvious how to do it. What I want to show you here is an absolutely bare-bones way to add login to your site, and how do it all manually and from scratch. The emphasis really is going to be on using the APIs -- which are simple -- to implement security.
I‘ll really try to keep it as straightforward as possible. However, because I think there‘s such a thing as too simple, I‘ll give you a three-pronged set of techniques:
- The absolute minimum thing you must do.
- The things that are "nice to do" or maybe even that you almost probably have to do (like ask for a password twice). I‘ll go ahead and do these, but point them out.
- Things you would (must) do in a real production application (like use SSL to protect password pages). I won‘t show you those here, or at least, not today.
A little background
ASP.NET Razor has an infrastructure for security/login that automates a lot of the process. For starters, membership information (user names, passwords, like that) are stored in a database in your site. Razor includes a membership provider, which is a component that handles the details of managing this database. In effect, if you don‘t want to, you never need to think about how or where the login information is being handled. For purposes of this little tutorial, the assumption will be that you‘ll just be happy with whatever it‘s doing under the hood. (More on that in a moment.)
Razor also includes the WebSecurity
helper and the Membership
and Roles
objects, which between them include the methods that you need in order manage membership. There‘s a create-user method, a login method, a logout method, stuff like that. This tutorial basically consists of exercising some of the basic capabilities. (You will not be surprised to hear that there are a lot more than you‘ll see here.)
What you need to do
Ok, here‘s what I‘ll show how to do:
- Initialize the membership system. [Link]
- Create a home page, which will be the site‘s public content. [Link]
- Create a registration page where people can sign up. [Link]
- Create a login page. [Link]
- Create a logout page. [Link] (You don‘t necessarily have to send them to a separate page, but that‘s the easiest.)
- Create some content that should be viewable only to people who are logged in. [Link]
- Provide a way to manage roles (create/delete them, add/delete users in roles). [Link]
- Protect content by role, i.e., make content available only to users who are in a specific role (e.g., "administrator"). [Link]
The site layout will look like this:
You can see this in action, sort of, on a site that implements this stuff. Start on the home page and then follow along with the pages listed below.
If you‘ve seen the existing security tutorial, these tasks will be familiar. In fact, if you‘ve seenany ASP.NET membership tutorial, they‘ll be familiar, because those are essentially the things you do with membership. The difference here is that I will, as noted, attempt to get this up and running in the sparsest way possible.
Initialize the membership system
Before you use the membership system, it has to be initialized. Technically you can do this any time, as long as you do it before you start interacting with membership system. In practice, you want to do this as soon as the application starts up, which means you do it in the site‘s _AppStart.cshtml file.
The code for the _AppStart.cshtml page here.
Must do |
Details later about these values below. Hint: You don‘t need to worry about them. |
Do in real app |
|
Initialization values
"TestMembership" |
The name of the database to store membership info. This can be any name. ASP.NET will create this database if it doesn‘t exist.UpdateThis is the name of an existing database where ASP.NET should store membership information. The database must exist; the initialization method will create the appropriate tables if they don‘t already exist. Anyway, with this code in place, when the site starts up, the database and membership tables are just there, ready to go. |
"UserProfile" |
The name of the table where user info is stored. See below. |
"UserId" |
The name of the primary-key column for user info in the user-profile table. (Also see below.) |
"Email" |
The name of the column that holds the user name (presumed to be an email address) for the user in the user-profile table. |
Given the circumstances here (simplest possible membership), it doesn‘t matter what you pass for these values. Use the site name for the database name and just copy the rest of the values as they are here.
What these values are for
As an aside, some details about the membership database. If you don‘t care, skip this section, it isn‘t essential. So if the membership system is so danged automated, why do you need to pass all these values to the initialization method? We don‘t need to for now. However, the membership system is designed so it can integrate with any existing database you might have that already has user information in it. If you already have a contacts list or an employee table, etc., this initialization code lets you point to that database and to the table and columns that contain user ID and name (email) information, and the membership system will use those.
That‘s half the story. The membership system actually makes a distinction between "profile" data and "membership" data. The profile data is the user name and ID, plus whatever else you have for your users (address, etc.). In contrast, the membership data is the detailed security stuff that the membership system needs, like password hash, last password change date, etc. This is information that‘s not only unlikely to be in the profile database table, but that you probably don‘t even want to keep there. Anyway, this split between profile data and membership data makes it easier for ASP.NET to use your existing user database.
Here‘s a screenshot of the membership database structure. The UserProfile table is where the profile data lives; webpages_Membership is the membership data. (The other two tables pertain to roles, which you‘ll implement shortly.) If you were using a user table that you already had, you wouldn‘t need to have the UserProfile table you see here.
As noted, during initialization the membership system creates and/or opens the database. The last thing it does is establish a database-type relationship between the profile table and the membership table. Then it‘s ready to go.
Create a home page
The code for the Home.cshtml page is here.
Must do |
|
Nice to do |
|
Do in real app |
|
Create a registration page
A simple registration page has a place for user name (can be email) and password. Typically you make users enter the password twice, since they can‘t see what they‘re typing.
The code for the Register.cshtml page is here.
Must do |
|
Nice to do |
|
Do in real app |
|
Create a login page
The code for the Login.cshtml page is here.
Must do |
|
Nice to do |
|
Do in real app |
|
Create a logout page
A logout page just logs the user out. (Under the covers, it removes the cookie that‘s on the user‘s browser that lets ASP.NET know that the user is authenticated.)
The code for the Logout.cshtml page is here.
Must do |
|
Nice to do |
|
Do in real app |
|
Protect content
Protected content can only be viewed by people are logged in. Basically what you do is put pages into a folder that‘s guarded by a piece of code that only lets them through if they‘re logged in (authenticated). It doesn‘t matter what user name they‘re logged in under, just that they‘re logged in.
The code for the _PageStart.cshtml page is here.
Must do |
|
Do in real app |
|
Create a page to manage roles
Roles are a convenient way to group users together. This is handy if you want different logged-in users to have access to different pages. The typical example is that all users can access pages in the root. Logged-in users can access pages in a members folder, plus all public pages. And then users in a specific role (e.g., "Admin") are allowed access to pages in yet another subfolder, plus member pages, plus public pages.
There are no built-in roles; a role is just a name that you create. You can think of it as a tag you assign to a user name. You can then check for that tag as a way to determine whether you‘ll allow someone access to pages.
You typically don‘t let users manage roles themselves. Unlike the other pages for this little sample, the page you‘ll create here is one that should be available only to an administrator or super-user (you). In a slightly weird meta way, the page should be protected so that only users in some sort of admin role can get to it. In this example, the page is assumed to be in anAdmin folder that you‘ll protect. (However, you‘ll protect the folder only after you‘ve put your own user name into the admin role.) The page shown here is just one of many ways you could manage roles. However, it does illustrate the fundamental tasks: creating (and deleting) roles, and adding (or removing) users in roles.
The code for the ManageRoles.cshtml page is here.
Note that in this case there‘s no "must do," because there‘s no one way to manage roles. For example, you could do everything by directly editing the database in WebMatrix. So this just shows some ways you could use APIs to manage roles.
Nice to do |
Everything listed in this section happens in that page. I‘ll break it down into pieces because it‘s a little more complex than the other pages. Display existing roles (and users in roles):
Create and delete roles:
Add and delete users in roles:
|
Do in real app |
|
Protect content by role
The point of roles is to protect content so only users in certain roles can see the content. This is almost exactly like just protecting content by limiting it to authenticated users. Note that you should add this protection after you‘ve added yourself to the Admin role, else you‘ll never be able to get to this page.
The code for _PageStart.cshtml page for roles is here.
Must do |
|
Nice to do |
|
Ok, that‘s it. I hope this is useful and hasn‘t been presented in an unusually confusing way. If you have questions, leave a comment.