 |
|
|
To better understand the importance of this new feature
to the language and how CFCs fit into the overall
application
development architecture, read Ben Elmore's white paper, ColdFusion
MX Components: A New Methodology for Building Applications (PDF 770K). This white paper details all of the various
features of CFCs and presents a basic methodology for their
usage in application development.
This article takes a closer
look at an actual CFC in order
to understand the basic programmatic structures of
CFC Functions and their invocation methods. As one of the
more
powerful
features of CFCs, functions provide a way for developers
to call specific sections of ColdFusion code through
a variety of invocation methods (direct code invocation
with the cfinvoke tag,
URL parameters, form posting, Macromedia Flash Remoting,
Flash ActionScript, cfscript .
Also, Web Services optionally pass arguments to CFCs, and
receive return responses from CFCs.
To illustrate CFC Function invocation, this article steps
through a familiar example for developers. It is necessary
to understand not only the logical reasons for using a CFC
(instead of a Custom Tag, for example), but also to show
an actual working code example of a CFC to learn from.
The problem for consideration: A basic security model For
our example, consider a problem that most developers face
on an ongoing basis, the "basic security model." Nearly
all applications that secure data in one form or another
require a security model to be in place. That security model
may be as simple as a username and password check. A much
more advanced security model could have a multiple level,
cascading, roles-based model with inherited access permissions.
This article is not a discourse on security implementations,
but it's important that a developer first understands the
problem in order to figure out a suitable solution.
In most cases, security models must provide the following:
|
|
1
|
AuthenticationThis
is the most basic requirement. Ensure that an individual
user is who they say they are. This is handled by comparing
username and password provided by the user (through
a form, for instance), against a listing of usernames
and passwords in a system (generally through a database
lookup). |
|
2
|
AuthorizationOnce
a user is authenticated, and the user is identified
in the system, you'll often need to know if the user
has access to a specific function of the system that
he or she may request. For example, a general "site"
user should not be allowed to access the site's administration
functions. Authorization is handled by assigning the
user to "security groups" and then checking that user's
security group assignment against an access list of
acceptable groups for that particular function in your
application. For instance, the general user may be assigned
to the security group, "site users," but the administration
area of your application may require a user to be in
the security group, "administrators," to gain access
to the area. Therefore, since
the user is assigned to the "site users" group, he cannot
gain access to the administration area. |
|
Since the problem is clear, you can generate a solution. First,
look at your database to create a security model. The database
is comprised of three tables: Security, Groups, and Security_Groups.
|
|
1
|
SecurityThis
table holds the username and password information. Notice
that there is a userID as well. This ID is a foreign
key for the specific user information stored in a separate
system table that is dependent on the particular application. |
|
2
|
GroupsThis
table contains the name and ID of the various security
groups. |
|
3
|
Security_GroupsThis
table is a many-to-many lookup table and contains the
usernames and groupIDs for users. In this way, users
can be affiliated with more than one group. |
|
|
Note: You can download the ZIP file at the end of this
article to view the database table.
The solution
The problem has been defined, and a
database has been created. At this point, it's up to
the developer to design a code solution around the
database
presented.
Keep in mind, in ColdFusion 5, the best solution to the
problem would be to create custom tags to manage the authentication
and authorization functions. You might have created one
custom tag to accept a username and password provided by
code. It would return a result to the "caller" variable
scope with the userID if the match was successful, or a
"no results found" result if the match was unsuccessful.
You might have created a second tag could that could accept
a list of accepted Security Groups for the function requested
and the username of the user. It would return a Boolean
result to a "caller" variable scope if the user is assigned
to one of the groups in the list.
This solution works well and is an accepted way of coding
this problem prior to CFCs. But what if you needed to access
this functionality through a Flash Movie, or wanted to provide
access to the authentication and authorization functions
through a web service, or needed to add additional security
functions?
With the custom tag solution, you would create additional
ColdFusion pages to handle the different ways of access
and functionality. Also note that although these two functions
are both considered security functionality, they are actually
two independent ColdFusion pages. If the security database
ever changed, you would need to search and replace all data
source references within security templates throughout the
application.
None of these issues are new of course, but with the advent
of CFCs, these issues are easily solved!
The CFC solution
Since you can invoke CFCs in multiple
ways, contain many functions in one CFC, and generate the
required information for use as a web service, it is easy
to see that the CFC is a far better alternative to multiple
custom tags
But how does it all come together?
It's simple. You create CFCs with ColdFusion tags, just
like all other CFML applications. The new tags explained
below include everything you need to create a fully implemented
CFC. The new tags are as follows:
|
|
|
the cfcomponent
tag |
|
|
the cffunction
tag |
|
|
the cfargument
tag |
|
|
the cfreturn
tag |
|
|
the cfinvoke
tag |
|
It all starts with the cfcomponent
tag. This tag surrounds the entire page and identifies the
code as being a CFC.
<cfcomponent>
.
.
.
</cfcomponent>
Inside of the cfcomponent
tags, our core functions are identified and coded. As discussed
above, we need two functions for our CFC, Authentication
and Authorization. These functions are defined within the
CFC with the cffunction
tag. This tag also has an opening and closing tag surrounding
all code to take place when the function is called.
<cfcomponent>
<cffunction>
.
.
.
</cffunction>
</cfcomponent>
This cffunction tag
contains several important attributes that need to be defined
for the function to work properly when called. Take, for
example, the code for our first function "Authenticate":
<cffunction name="authenticate"
access="public" output="false">
This cffunction tag
contains three attributes:
|
|
|
name="authenticate"This
attribute defines the name of the function or the component
method that will be used when invoking this function. |
|
|
access="public"CFC
functions have two different security options available
to them. The first is the "access" level attribute,
and is shown here. The access level determines where
the function can be executed. For this example, the
access level is public, which means that it can be called
from all other ColdFusion pages and CFCs. The other
security attribute is "roles," a group security scheme
that is beyond the scope of this article. For more information,
see the ColdFusion MX documentation and CFML Reference
manual. |
|
|
output="false"This
argument tells the CFC that the code is for execution
only and does not produce HTML. This is the preferred
way of using CFCs. That is, to provide method-based
functionality for your application, and not end-user
display. In MVC architecture, CFCs act as the conduit
for data and business logic access. |
|
At this point, you have now built a functional CFC. Not much
interesting would happen when it is called, however, but
the
CFC is operational.
To authenticate a user, pass in data to
the CFC. This is done through arguments in a similar
manner to custom tags.
In addition, you must also identify those arguments
within the CFC functions themselves. This is done with
the cfargument tag.
<cfcomponent>
<cffunction>
<cfargument name="arg1"/>
<cfargument name="arg2"/>
<cfargument name="arg3"/>
</cffunction>
</cfcomponent>
To authenticate a user, provide the CFC with two arguments:
a user and a passwd. These arguments should both be
required and have a data type of "string." Within the cffunction
tag, the cfargument tag
would be as follows:
<cfargument name="user" type="string"
required="true"/>
<cfargument name="passwd" type="string"
required="true"/>
The attributes for the cfargument
tag define each argument's purpose. There are four attributes
you can use, as follows:
|
|
|
nameThe name
of the argument to pass in. |
|
|
typeThe data type for the argument.
If another data type is passed other than that which
is listed in this attribute, an exception is thrown.
|
|
|
requiredDetermines whether
the parameter is required to execute the component method. |
|
|
defaultA type; if no argument
is passed, specifies a default argument value similar
to the cfparam tag. If this attribute is present, the
required attribute must be set to "false" or not specified. |
|
Any values passed into the CFC are accessible through a
new variable scope called "arguments." This scope functions
similarly to the custom tag "attributes" scope. For example,
to access the value of the user argument as defined
above, the code would be as follows:
arguments.user
Once the component is built to this point, you can write
the execution logic. The following is the completed CFC
including the function for authentication (this code is
included in the accompanying CFC security.cfc included with
the ZIP file at the end of this article):
<cfcomponent>
<cffunction name="authenticate" access="public" output="false">
<cfargument name="user" type="string" required="true"/>
<cfargument name="passwd" type="string" required="true"/>
<cfquery name="checkAuthentication" datasource="SecurityDB">
SELECT username
FROM Security
WHERE username = (#arguments.user#)
AND password = (#arguments.passwd#)
</cfquery>
</cffunction>
</cfcomponent>
There is still one remaining issue: no results were delivered
to the invoker. In a ColdFusion custom tag, the result is
passed to a calling ColdFusion page with the "caller" scope.
This was problematic if you tried to call the custom tag
from outside of ColdFusion Server. Luckily, CFCs provide
a simple way to return values, regardless of the calling
process. This is accomplished with the cfreturn
tag.
The cfreturn tag has
a simple signature:
<cfreturn expression />
In the following example, the code is complete and includes
a cfreturn tag that checks
to see if a record was returned. If it is true, and the
username and password match the data in the system, the
CFC returns the username to the caller for future use. If
it is false, then a Boolean false result is returned. The
following example demonstrates this logic:
<cfcomponent>
<cffunction name="authenticate" access="public" output="false">
<cfargument name="user" type="string" required="true"/>
<cfargument name="passwd" type="string" required="true"/>
<cfquery name="checkAuthentication" datasource="SecurityDB">
SELECT username
FROM Security
WHERE username = (#arguments.user#)
AND password = (#arguments.passwd#)
</cfquery>
<cfif checkAuthentication.recordCount>
<cfreturn checkAuthentication.user/>
<cfelse>
<cfreturn false/>
</cfif>
</cffunction>
</cfcomponent>
Now that the first function is complete, it's time to add
the second function for authorization. The following is
a complete CFC that includes the function for authorization:
<cfcomponent>
<cffunction name="authenticate" access="public" output="false">
É
</cffunction>
<cffunction access="public" name="authorize" output="false">
<cfargument name="user" type="string" required="true"/>
<cfargument name="lAcceptedGroupIDs" type="string" required="true"/>
<cfquery name="getUserGroups" datasource="SecurityDB">
SELECT groupID
FROM Security_Groups
WHERE username = '#arguments.user#'
</cfquery>
<!--- use a query of query to determine if group IDs for user are in list --->
<cfquery name="checkGroups" dbtype="query">
SELECT groupID
FROM getUserGroups
WHERE groupID IN (#arguments.lAcceptedGroupIDs#)
</cfquery>
<cfif checkGroups.recordCount>
<cfreturn true/>
<cfelse>
<cfreturn false/>
</cfif>
</cffunction>
</cfcomponent>
Invoking the CFC
Invoking a CFC can happen in many
ways. This feature alone makes CFCs a far more powerful
solution to the problem at hand than what a custom tag could
provide. This one page of code can be used throughout not
only this application, but also any other ColdFusion application,
exposed as a web service, called from a Macromedia Flash
movie, and so forth, with very little or no changes in code!
The two most common ways to invoke a CFC function are through
the cfinvoke tag and
inside of a cfscript
block (something not possible in ColdFusion 5 with custom
tags), using the createObject() ColdFusion function.
Using the cfinvoke
tag
The cfinvoke tag
provides a simple CFML based solution to executing a CFC
function. For instance, if we wanted to call the authenticate
function by submitting a form with username and password
form variables, the cfinvoke
tag would look something similar to the following:
<cfinvoke
component="security"
method="authenticate"
returnVariable="authenticated"
user="#form.username#"
passwd="#form.password#"
>
Note: This code is in the accompanying ColdFusion file,
index.cfm, starting at line 34. It is included in the ZIP
file at the end of this article.
You can place this code block in any ColdFusion application
on your ColdFusion server to execute the security authenticate
function (or method). It returns a local variable called
"authenticated" that your application logic can manipulate
in any way appropriate to the particular application. That's
all there is to it.
The attributes for this cfinvoke
tag are as follows:
|
|
|
component="security"This
attribute specifies the name of the component to execute.
In this case, the component name is "security" and corresponds
to the CFC filename, security.cfc. Save all CFCs with
a CFC extension (example, security.cfc) and place them
in an accessible location for ColdFusion MX. You can
place them outside of your webroot, but you will need
to add a web server mapping. Additional rules apply
to CFCs in directory structures, called "packages,"
that will be addressed in future articles. This is required
if the "method" attribute is not specified. |
|
|
method="authenticate"This
is the name of the method (or function) within a component
(or CFC file). For a web service, it is the name of
an operation. This is required if the "component" attribute
is not specified. |
|
|
returnVariable="authenticated"Specify
the name of a variable for the invocation result. For
this call, either the username of a properly authenticated
user, or "0" is returned as the value for this variable.
See the index.cfm file that accompanies this article
(in the ZIP archive) for more information on using this
code. |
|
|
arguments as defined in the CFCThese
are the name-value pairs for the arguments defined by
the cfargument tags in the function. Note that this
is not the only way to pass arguments to a CFC with
the cfinvoke tag.
You can also use the "argumentCollection" attribute,
which holds a structure of name/value pairs. This is
useful when you need to supply a large amount of arguments. |
|
|
Using the cfscript tag
and the createObject()
function
The ability to call CFCs with the cfscript
tag is a wonderful addition to ColdFusion MX, and the best
part about it: it is easy to use. Using a ColdFusion function
called createObject(),
places an instance of the CFC into a variable. From that
variable, you can call the functions by using programmatic
dot notation. Take a look at the following code sample (line
45 in the index.cfm file, in the ZIP available at the end
of this article):
<cfscript>
objSecurity = createObject("component","security");
bAuthorized = objSecurity.authorize(form.user,form.groupID);
</cfscript>
This code calls the authorize function with two lines of
code.
First, it creates a "component object" called "objSecurity"
by setting the "objSecurity" variable equal to the createObject
ColdFusion function. This function takes two
arguments. The first argument tells the function that a
component object will be created. The second argument names
the component to use. In this case, the security.cfc file
is used.
Next, it creates and initializes a local variable called
"bAuthorized" to the return value of the authorization security
function call. By using programmatic dot notation (such
as objectName.functionName(arguments)), the code executes
the function. If you instead wanted to call the authenticate
method, the code would be as follows:
objSecurity.authenticate(arg1,arg2).
As with the cfinvoke
tag, you can call a function and pass arguments in a cfscript
tag block in multiple ways as well. See the ColdFusion MX
documentation for more information on passing arguments.
Other methods
As touched upon throughout the article,
you can call CFCs in other ways: through a URL, from a form
post, as a web service, and through Flash Remoting (with
ColdFusion MX and Macromedia Flash MX, within a Macromedia
Flash movie). Read more about these other methods in the
ColdFusion Developer Center.
How does this work?
To enable a CFC for execution
through an external source (such as a URL, form post, Flash
Remoting, and so forth), modify one slight code attribute.
Change the access attribute of the cffunction
tag to remote="access". That's it!
Creating a web service is a little more involved, but CFCs
come to the rescue here as well. All CFCs contain metadata
that describe their functions. With this metadata information,
the WSDL file (an XML document used to describe an exposed
web service) can be auto-generated! In fact, a method exists
for all CFCs (called "cfcToWSDL") that will generate the
file for you.
These methods are covered in other tutorials, articles,
sample applications, and white papers in the ColdFusion
Developer Center, and will continue to be
discussed in future articles. Also refer to the ColdFusion
MX documentation and the sample applications in your ColdFusion
MX administrator.
Download the sample files
You can experiment with
this ColdFusion MX component by downloading the sample files
below:
|
|
1
|
Download the
ZIP file. |
| |
|
|
2
|
Extract the files to your
ColdFusion MX webroot. |
|
3
|
Browse index.cfm from
your browser. For example, browse the following URL
in a browser: http://localhost/securitycfc/index.cfm.
Change this URL based on your web server configuration.
|
|
4
|
Test the authenticate
functionality. |
|
5
|
Test the authorize functionality |
|
Conclusion
CFCs have created a new paradigm for the
architecture and development of ColdFusion MX applications.
CFCs provide all of the following:
|
|
1
|
A consistent model for
the creation of MVC based component architectures. |
|
2
|
The ability to work with sets of functionality
in one template. |
|
3
|
A simple, yet powerful syntax for function
creation. |
|
4
|
A multitude of ways to invoke functions created including:
|
a
|
The cfinvoke
tag. |
|
b
|
Between the cfscript
tag blocks and the createObject()
ColdFusion function |
|
c
|
direct URL |
|
d
|
form post |
|
e
|
Macromedia Flash movies through
Macromedia Flash MX and Flash Remoting |
|
f
|
as a web service (with auto-generating
WSDL) |
|
|
|
By implementing the downloadable test case for this simple
security model as a CFC, and comparing this new way to the
challenges of doing the same functionality with custom tags,
it is clear that CFCs are indeed a more powerful and elegant
solution. The CFC is a welcome addition to any developer's
toolset when creating ColdFusion MX applications.
|
|
About the author
Anthony McClure is a Senior Technologist with RemoteSite
Technologies Inc., out of Austin, TX. He is a long time
developer with over 8 years of experience in all aspects
of development from Mainframe to Internet. As a Macromedia
Certified Instructor and Advanced ColdFusion Certified Developer,
Anthony has distinguished himself within the ColdFusion
development platform. He is an active member of the ColdFusion
community speaking at Macromedia ColdFusion User Groups,
leading technology seminars, and generally pushing Macromedia
technology amoung his peers. Other publications by Anthony
include a Macromedia Developer Center article for Macromedia
Spectra and wireless technology, and an upcoming book on
dynamic publishing using the Macromedia ColdFusion MX platform.
|
|
|
|