As you know, J2EE allows you to protect web pages and other web resources such as files, directories, and servlets through declarative security. This approach won't provide protection to local beans. In this recipe, you will see how to extend JSF security configuration beyond web pages using managed bean methods. For this we will use the classes provided by Vinicius Senger on http://blogs.sun.com/enterprisetechtips/entry/improving_jsf_security_configuration_with.
Vinicius Senger has provided a sample application at http://java.sun.com/mailers/techtips/enterprise/2007/download/ttsept2007FacesSec.zip. This application contains all the classes necessary to secure local beans. Download this ZIP file and extract it to your favored location. You can try it with JSF 1.2 and 2.0.
Next, we will analyze Vinicius's solution and see how to use it. The two most important classes are the following (the sources of these classes are in the /src
folder):
br.com.globalcode.jsf.security.SecureActionListener
: This intercepts calls to managed bean methods and checks for annotated method permissions.
br.com.globalcode.jsf.security.SecureNavigationHandler
: This forwards the user to a requested view if the user has the required credentials and roles.
These classes should be activated in your JSF descriptor, faces-config.xml
, as shown:
<application> <action-listener> br.com.globalcode.jsf.security.SecureActionListener </action-listener> <navigation-handler> br.com.globalcode.jsf.security.SecureNavigationHandler </navigation-handler> </application>
In addition, we can set up user object providers. You can choose between ContainerUserProvider
and SessionUserProvider
.
ContainerUserProvider
The following is the context parameter to set up the default container user provider (since containers already provide declarative security, this configuration is all that you need):
<context-param> <param-name>jsf-security-user-provider</param-name> <param-value> br.com.globalcode.jsf.security.usersession.ContainerUserProvider </param-value> </context-param>
ContainerUserProvider
references the ContainerUser
class. This class is available in the srcjavarcomglobalcodejsfsecuritycontainer
folder.
SessionUserProvider
In the case of a custom security authentication and authorization process, you can provide a user class adapter that implements the given user interface and bind a user object instance into the HTTP session with the key name user
.
To begin with you have to create a User
interface implementation. This interface provides two methods, named getLoginName
and isUserInRole
(in the package model
there is a class MyUser
representing a User
implementation). Next you have to provide page login with a navigation case called login
(this can be seen in the login.jsp
page in the /web
folder). And you must write a login managed bean that checks the user credentials and puts (or not) the user object into the HTTP session (in the srcjavacontroller
folder you can find the LoginMB
example). Finally, you have to add a context parameter to the web.xml
file to set up the user provider to look up the HTTP session for the user
object:
<context-param> <param-name>jsf-security-user-provider</param-name> <param-value> br.com.globalcode.jsf.security.usersession.SessionUserProvider </param-value> </context-param>
Vinicius has built an example of a JSF page that contains a View
button and a Delete
button (see the index.jsp
page in the /web
folder) and when the user press the Delete
button then the CustomerCRUD.delete
method called. This method includes an annotation that declares a required role for the method:
@SecurityRoles("customer-admin-adv, root") public String delete() { System.out.println("I'm a protected method!"); return "delete-customer"; }
The complete source code of CustomerCRUD
is available in the srcjavacontroller
folder.
The official page of this tip is at http://blogs.sun.com/enterprisetechtips/entry/improving_jsf_security_configuration_with. Thanks to Vinicius Senger for sharing this tip with us.