Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Pro ASP.NET 2.0 In CSharp 2005 (2005) [eng]

.pdf
Скачиваний:
107
Добавлен:
16.08.2013
Размер:
29.8 Mб
Скачать

798 C H A P T E R 2 3 A U T H O R I Z AT I O N A N D R O L E S

The application defines application-specific roles that are typical for a collaborative portal solution. You can assign both Windows users and Windows groups to these roles. SharePoint by default includes the roles Administrator, Web Author, Designer, and Reader. All of these roles are optimized for performing authorization within the portal. For example, while a Web Author automatically gets permission to create new workspaces for meetings and structure contents displayed on the portal, a Reader is just able to view information on the portal. Every Windows user assigned to one of these roles, or every Windows user who is a member of a Windows group assigned to one of these roles, automatically gets the appropriate permissions. Therefore, SharePoint is independent of the network structure deployed in the Windows network where it is used. You will learn more details about implementing such concepts in your own application in Chapter 26 where you will learn details about custom Membership and Roles providers.

Protecting Non-Web-Page Resources

All the authorization and authentication systems you’ve about learned so far have one limitation— they work only on file types that ASP.NET handles. In other words, if a user requests a GIF file or HTML page from the same virtual directory, that user will completely bypass your authentication and authorization mechanisms. Depending on your security needs, this may be completely acceptable, simply irrelevant, or potentially dangerous.

This behavior is a result of the way IIS uses file mappings, which were first covered in Chapter 18. By default, ASP.NET is registered to deal with a small set of relevant files. These include files it needs to execute, such as web pages and web services, and files it wants to protect, such as source code files, configuration files, and project files. If you want requests for other file types to filter through the ASP.NET request processing architecture and security models, you have to map those file types to the ASP.NET ISAPI filter.

Mapping additional file types to ASP.NET gives you some extra features. For example, it gives you the ability to deny anonymous user requests for image files. However, it can also add overhead, because files that would normally be served directly now require ASP.NET to perform some work. This overhead is fairly minimal if you aren’t expecting users to request non-ASP.NET file types and if you’re simply using this technique to provide a higher level of security.

Furthermore, if you map a file type with the ASP.NET runtime, you have to tell the runtime how it should process this file type. You can do this by creating your own HTTP handler, as you will learn later in this chapter.

Note If you’re using Windows authentication, it is technically possible to force IIS to authenticate all requests, even those that are for non-ASP.NET files. To do so, you simply need to remove the Anonymous access option for the virtual directory. However, this option isn’t as useful as it seems, because it doesn’t allow you to enforce authentication for specific files or file types. As a result, you may find it more useful to allow anonymous access but use the techniques described in the following sections to protect specific resources.

Adding a File Type Mapping

To add a file type mapping to IIS 5, follow these steps:

1.Launch IIS Manager, and browse to the virtual directory in the tree.

2.Right-click the virtual directory, and select Properties.

3.Choose the Virtual Directory tab. Then click the Configuration button in the Application section of the Virtual Directory tab. The Application Configuration dialog box will appear, as shown in Figure 23-7.

C H A P T E R 2 3 A U T H O R I Z AT I O N A N D R O L E S

799

Figure 23-7. Application mappings

4.You need to add a new mapping for each file type you want to protect with forms authentication. This mapping will route requests for that file type to the ASP.NET ISAPI DLL. Click the Add button to create a new mapping. You’ll see the dialog box shown in Figure 23-8.

Figure 23-8. Adding an application mapping

5.The executable you want to use is aspnet_isapi.dll. The exact directory depends on the version of ASP.NET you have installed. In .NET 2.0, it’s c:\[WinDir]\Microsoft.Net\Framework\ v2.0.50215\aspnet_isapi.dll. You also need to enter the file extension you want to map.

Finally, you should also specify that you want to perform this mapping for all verbs (a verb is a method for requesting the file from the server over HTTP, such as GET or POST).

6.Once you have taken these steps, click OK to add the extension.

800 C H A P T E R 2 3 A U T H O R I Z AT I O N A N D R O L E S

Writing a Custom HTTP Handler

Every resource processed by ASP.NET is processed by an actor called an HTTP handler. For example, web pages with the extension .aspx are processed by a page handler, while web services with the extension .asmx are processed by a SOAP handler. All these classes are implementations of the IHttpHandler interface.

When you associate your custom file type with the ASP.NET runtime, as shown in the previous section, you have to tell ASP.NET how to process this resource. The way to do this is to write a custom HTTP handler class that implements this interface. A custom handler processing any type of binary file looks like this:

namespace RolesDemo.Handlers

{

public class GenericHandler : IHttpHandler

{

#region IHttpHandler Members

public bool IsReusable

{

get { return true; }

}

public void ProcessRequest(HttpContext context)

{

byte[] ret = null;

// Open the file specified in the context

string PhysicalPath = context.Server.MapPath(context.Request.Path); using (FileStream fs = new FileStream(PhysicalPath, FileMode.Open))

{

ret = new byte[fs.Length]; fs.Read(ret, 0, (int)fs.Length);

}

// If it is not null, return the byte array if (ret != null)

{

context.Response.BinaryWrite(ret);

}

}

#endregion

}

}

This handler simply determines the local physical path of the resource requested by calling Server.MapPath. Afterward it uses a FileStream for opening the resource and returning the bytes included for this resource. You have to configure this HTTP handler as well. For this purpose, you just add a <httpHandlers> section within the <system.web> section of your web.config application configuration, as follows:

<httpHandlers>

<add verb="GET,POST" path="*.txt"

type="RolesDemo.Handlers.GenericHandler"/>

</httpHandlers>

C H A P T E R 2 3 A U T H O R I Z AT I O N A N D R O L E S

801

PROBLEMS WITH SOME FILE TYPES

Developers have reported some problems when using forms authentication to protect Adobe Acrobat (PDF) files. It’s possible that similar problems could affect other file types, especially if they require a web browser plug-in to be displayed.

With PDF files, the problems are caused by a combination of the ActiveX component that allows Internet Explorer to view Acrobat files and IIS. The problem is that PDF files are sent from the server to the client in chunks so that the user does not have to wait until the whole file has downloaded to start viewing it. For some reason, this system is adversely affected by redirections (for example, using the Response.Redirect() method). Redirecting to a PDF file causes the file to be reported as corrupted. This creates problems if you try to use forms authentication to protect PDF files. After the user logs in, the redirection back to the original PDF causes the file to be reported as corrupted or simply not displayed.

To solve this problem, you need to avoid using FormsAuthentication.RedirectFromLoginPage() or Response.Redirect() to send the user back to the PDF file. Fortunately, you simply need to write an HTML page that instructs the browser to redirect itself using the Response.AppendHeader() method. This header has the name refresh and takes the form 0;url=[originalUrl]. This causes the browser to immediately load the target URL (the 0 indicates a delay of 0 seconds).

Here’s the code statement you need to use instead of the RedirectFromLoginPage() method:

Response.AppendHeader("refresh","0;url=" + url);

Remember that because you aren’t using the RedirectFromLoginPage() method, you’ll also need to create and attach the cookie before you perform the redirect.

The type attribute includes the full namespace and class name of the IHttpHandler implementation, and optionally if it is placed in a different assembly, you have to specify the name of the assembly in the format “namespace.typename, assembly” within it. The additional attributes specify the HTTP verb (GET, PUT, POST, or * for all) as well as the path and file types for which the handler will be used.

Summary

Authorization provides an effective way to control access to resources. In this chapter, you learned how to safeguard different pages, directories, and code routines in your web application using authorization. You also saw how to use the Roles Service for managing and associating users with roles for simpler authorization.

In the next chapter, you’ll take a look at a few advanced security techniques that you can use to extend ASP.NET authentication and authorization.

C H A P T E R 2 4

■ ■ ■

Profiles

In previous chapters, you learned how to use a range of ASP.NET security features. Many of these features are geared to identifying individual users (authentication) and then determining what actions they should be able to perform (authorization). But you need to uniquely identify and authenticate users for another important reason—to keep track of user-specific information.

In ASP.NET 1.x, the only practical option to store user-specific information was to create your own data access component (a topic covered in Chapter 8). Your web page could call the methods of your data access component to retrieve the current user’s data and then save any changes. As you’ll see in this chapter, this approach still makes a lot of sense in many scenarios. However, ASP.NET 2.0 adds another option with a new profiles feature. When you use profiles, ASP.NET handles retrieving and updating user-specific data automatically by using a back-end data source (typically a database).

Conceptually, the profiles feature is a lot like creating your own database component. However, it adds some neat conveniences. Most impressively, it integrates with the ASP.NET authentication model in such a way that user information is automatically retrieved for the current user when needed and (if this information is changed) written back to the database at the end of the current request. Best of all, your web-page code can access the current user’s profile data using strongly typed properties.

In this chapter, you’ll learn how to use profiles, how the profiles system works, and when profiles make the most sense. You’ll also learn how to extend the Profiles API with a custom Profiles provider.

Understanding Profiles

One of the most significant differences between profiles and other types of state management (as discussed in Chapter 6) is that profiles are designed to store information permanently by using a back-end data source such as a database. Most other types of state management are designed to maintain information for a series of requests that occur in a relatively short space of time (such as session state and caching) or in the current browser session (such as cookies and view state) or to transfer information from one page to another (such as cross-page posting and the query string).

If you need to store information for the longer term in a database, profiles simply provide a convenient model that manages the retrieval and persistence of this information for you.

Before you begin using profiles, you need to assess them carefully. In the following sections, you’ll learn how they stack up.

Profile Performance

The goal of ASP.NET’s profiles feature is to provide a transparent way to manage user-specific information, without forcing you to write custom data access code using the ADO.NET data classes.

803

804C H A P T E R 2 4 P R O F I L E S

Unfortunately, many features that seem convenient suffer from poor performance or scalability. This is particularly a concern with profiles, because they involve database access, and database access can easily become a scalability bottleneck for any distributed application.

So, do profiles suffer from scalability problems? This question has no simple answer. It all depends on how much data you need to store and how often you plan to access it. To make an informed decision, you need to know a little more about how profiles work.

Profiles plug into the page life cycle in two ways:

The first time you access the Profile object in your code, ASP.NET retrieves the complete profile data for the current user from the database. From this point onward, you can read the profile information in your code without any database work.

If you change any profile data, the update is deferred until the page processing is complete. At that point (after the PreRender, PreRenderComplete, and Unload events have fired for the page), the profile is written back to the database. This way, multiple changes are batched into one operation. If you don’t change the profile data, no extra database work is incurred.

Note Profile reading and saving is implemented by a dedicated ProfileModule, which runs during each request. Chapter 5 discusses HTTP modules in more detail.

Overall, the profiles feature could result in two extra database trips for each request (in a readwrite scenario) or one extra database trip (if you are simply reading profile data). The profiles feature doesn’t integrate with caching, so every request that uses profile data requires a database connection.

From a performance standpoint, profiles work best when the following is true:

You have a relatively small number of pages that access the profile data.

You are storing small amounts of data.

They tend to work less well when the following is true:

You have a large number of pages that need to use profile information.

You are storing large amounts of data. This is particularly inefficient if you need to use only some of that data in a given request (because the profile model always retrieves the full block of profile data).

Of course, you can combine profiles with another type of state management. For example, imagine your website includes an order wizard that walks the user through several steps. At the beginning of this process, you could retrieve the profile information and store it in session state.

You could then use the Session collection for the remainder of the process. Assuming you’re using the in-process or out-of-process state server to maintain session data, this approach is more efficient because it saves you from needing to connect to the database repeatedly.

How Profiles Store Data

The most significant limitation with profiles doesn’t have anything to do with performance— instead, it’s a limitation of how the profiles are serialized. The default Profiles provider included with ASP.NET serializes profile information into a block of data that’s inserted into a single field in a database record. For example, if you serialize address information, you’ll end up with something like this:

Marty Soren315 Southpart DriveLompocCalifornia93436U.S.A.

C H A P T E R 2 4 P R O F I L E S

805

Another field indicates where each value starts and stops, using a format like this:

Name:S:0:11:Street:S:11:19:City:S:30:6:State:S:36:10:ZipCode:S:46:5:Country:S:51:6

Although this approach gives you the flexibility to store just about any type of data, it makes it more difficult to use this data in other applications. You can write custom code to parse the profile data in order to find the information you want, but depending on the amount of data and the data types you’re using, this can be an extremely tedious process. And even if you do this, you’re still limited in the ways you can reuse this information. For example, imagine you use profiles to store customer address information. Because of the proprietary format, it’s no longer possible to generate customer lists in an application such as Microsoft Word or perform queries that filter or sort records using this profile data. (For example, you can’t easily perform a query to get all the customers living in a specific city.)

This problem has two solutions:

Use custom data access components instead of profiles to store and retrieve data in a database.

Create a custom Profiles provider that’s designed to store information using your database schema.

Out of the two options, creating a custom data access component is easier, and it gives you more flexibility. You can design your data component to have any interface you want, and you can then reuse that component with other .NET applications. Currently, ASP.NET developers are more likely to use this approach because it has been around since .NET 1.0 and is well understood.

The second option is interesting because it allows your page to keep using the profile model. In fact, you could create an application that uses the standard profile serialization with the SqlProfileProvider and then switch it later to use a custom provider. To make this switch, you don’t need to change any code. Instead, you simply modify the profile settings in the web.config file. As it becomes more common for websites to use profiles, custom Profiles providers will become more attractive.

Note It’s also important to consider the type of data that works best in a profile. As with many other types of state management, you can store any serializable types into a profile, including simple types and custom classes.

Profiles and Authentication

One significant difference between profiles and other types of state management is that profiles are stored as individual records, each of which is uniquely identified by user name. This means that profiles require you to use some sort of authentication system. It makes no difference what type of authentication system you use (Windows, forms, or a custom authentication system)—the only requirement is that authenticated users are assigned a unique user name. That user name is used to find the matching profile record in the database.

Note Later in this chapter (in the section “Anonymous Profiles”), you’ll also learn how the anonymous identification feature lets you temporarily store profile information for users who haven’t logged in.

806 C H A P T E R 2 4 P R O F I L E S

Profiles vs. Custom Data Components

Profiles are a natural competitor with custom data components of the kind you saw in Chapter 8. Clearly, data components are far more flexible. They allow you not only to maintain user-specific information but also to store other types of information and perform more complex business tasks.

For example, an e-commerce website could realistically use profiles to maintain customer address information (with the limitations discussed in the previous section). However, you wouldn’t use a profile to store information about previous orders. Not only is it far too much information to store efficiently, it’s also awkward to manipulate.

Tip As a rule of thumb, use a profile to store only the same sort of information you’d place in the user table. Don’t use it to store related data that you’d place in separate tables.

The standard Profiles provider that’s included with ASP.NET (named SqlProfileProvider) doesn’t provide many additional features. The following list includes some features that you can easily add through a custom database component but aren’t available if you’re using the SqlProfileProvider.

If you need any of these features, you’ll need to abandon profiles and create your own data access component, or you’ll need to design a custom Profiles provider.

Encryption: Profile data can be serialized into a string, XML, or a binary representation. But no matter what you choose, you’ll always end up storing the raw text. If you have sensitive information, your only option is to encrypt it manually before you store it, which has the undesirable result of putting encryption logic in your user interface code.

Validation: You can’t restrict the type of information that can be placed in a profile. You need to use other tools (such as validator controls and custom data classes) to prevent invalid data.

Caching: If profile information is used in a page, it’s always retrieved from the database. You can’t keep profile information around in memory. Although you can copy profile information into the cache, it becomes more difficult to track this information.

Auditing: When you design a custom database component, you have the ability to add any logging or tracing code you want. You can use this to diagnose unexpected errors or monitor the performance of your web application. However, if you want these features with profiles, you’ll need to build a custom Profiles provider that has the logging code.

Now that you know the ins and outs of profiles, you’re ready to try them.

Using the SqlProfileProvider

The SqlProfileProvider allows you to store profile information in a SQL Server 7.0 or later database. You can choose to create the profile tables in any database. However, you can’t change any of the other database schema details, which means you’re locked into specific table names, column names, and serialization formats.

From start to finish, you need to perform the following steps to use profiles:

1.Create the profile tables.

2.Configure the provider.

3.Define some profile properties.

4.Enable authentication for a portion of your website.

5.Use the profile properties in your web-page code.

C H A P T E R 2 4 P R O F I L E S

807

You’ll tackle these steps in the following sections.

Creating the Profile Tables

To create the profile tables, you use the aspnet_regsql.exe command-line utility, the same tool that allows you to generate databases for other ASP.NET features, such as SQL Server–based session state, membership, roles, database cache dependencies, and web parts personalization. You can find the aspnet_regsql.exe tool in the c:\[WinDir]\Microsoft.NET\Framework\[Version] folder.

To create the tables, views, and stored procedures required for profiles, you use the -A p command-line option. The only other detail you need to supply is the server location (-S), database name (-d), and authentication information for connecting to the database (use -U and -P to supply a password and user name, or use -E to use the current Windows account). If you leave the other server location and database name, aspnet_regsql.exe uses the default instance on the current computer and creates a database named aspnetdb.

Here’s an example that creates the aspnetdb database with the default name on the current computer by logging into the database using the current Windows account:

aspnet_regsql.exe –A p –E

Table 24-1 shows the tables that aspnet_regsql.exe creates. (The rather unexciting views aren’t included.)

Table 24-1. Database Tables Used for Profiles

Table Name

Description

aspnet_Applications

Lists all the web applications that have records in this database. It’s

 

possible for several ASP.NET applications to use the same aspnetdb

 

database. In this case, you have the option of separating the profile

 

information so that it’s distinct for each application (by giving each

 

application a different application name when you register the

 

Profiles provider) or of sharing it (by giving each application the

 

same application name).

aspnet_Profile

Stores the user-specific profile information. Each record contains the

 

complete profile information for a single user. The PropertyNames

 

field lists the property names, and the PropertyValuesString and

 

PropertyValuesBinary fields list all the property data, although you’ll

 

need to go to some work if you want to parse this information for use

 

in other non-ASP.NET programs. Each record also includes the last

 

update date and time (LastUpdatedDate).

aspnet_SchemaVersions

Lists the supported schemas for storing profile information. In the

 

future, this could allow new versions of ASP.NET to provide new ways

 

of storing profile information without breaking support for old profile

 

databases that are still in use.

aspnet_Users

Lists user names and maps them to one of the applications in

 

aspnet_Applications. Also records the last request date and time

 

(LastActivityDate) and whether the record was generated

 

automatically for an anonymous user (IsAnonymous). Anonymous

 

user support is discussed later in this chapter (in the section

 

“Anonymous Profiles”).