Now that the authentication provider is set, we have to modify the deployment descriptors of the application to set the security properties and create a login page to let users authenticate themselves.
We first need to set the security constraints by editing web.xml
:
WEB-INF/web.xml
file of the Store web application.servlet-mapping
tag:<security-constraint> <web-resource-collection> <web-resource-name>protected</web-resource-name> <url-pattern>/reservation.jsf</url-pattern> </web-resource-collection> <auth-constraint> <role-name>User</role-name> </auth-constraint> </security-constraint> <login-config> <auth-method>FORM</auth-method> <form-login-config> <form-login-page>/login.jsf</form-login-page> <form-error-page>/login.jsf</form-error-page> </form-login-config> </login-config> <security-role> <role-name>User</role-name> </security-role>
Here's the explanation of each block of the preceding code snippet:
security-constraint
tag defines where security will be applied. You need to declare a combination of HTTP methods, URL patterns, and role constraints. In our use case, we're protecting a specific URL pattern, /reservation.jsf
, and telling that only users with an abstract role named User
can proceed as per the auth-constraint
tag. We say abstract because these roles don't actually have to match the roles at WebLogic Server. This match will be done later with another deployment descriptor, weblogic.xml
.By default, all HTTP methods will be blocked since we're not explicitly specifying which ones must be secured. Here is how the same security constraint would look like protecting only POST
and GET
requests:
<url-pattern>/reservation.jsf</url-pattern> <http-method>POST</http-method> <http-method>GET</http-method>
login-config
tag plays a very important role in the security process: it defines the authentication method, which in our case is FORM
, and then points to where the the login and error pages will be available. These are the pages that users will be redirected to, to perform a login or in case any error occurs. It can also enforce SSL access by specifying the transport-guarantee
tag, which can be NONE
, CONFIDENTIAL
, or INTEGRAL
.A brief description of the available authentication methods is as follows:
BASIC: HTTP 1.0/1.1 basic authentication; a browser-controlled pop up will be displayed to input username/password.
FORM: A custom web page will be used to perform the authentication through an HTML <form>
. JSF or JSP pages are valid examples.
CLIENT-CERT: Uses digital certificates to authenticate.
DIGEST: An advanced version of the HTTP basic authentication that leverages MD5 hashing.
security-role
tag is used to declare all abstract roles available for the web application. If we have some specific resources (constraints) for administrators and customers (groups) of the application, these rules must be declared here. Remember that you can name it anything you want, but later the value will be used in the weblogic.xml
descriptor to match the abstract role and a real group in the LDAP server.After setting web.xml
, we need to tie the roles declared there and the principals in the WebLogic server, or, to be more precise, on the OpenLDAP server.
WEB-INF/weblogic.xml
file of the Store web application.</wls:library-ref>
tag:<wls:security-role-assignment> <wls:role-name>User</wls:role-name> <wls:externally-defined /> </wls:security-role-assignment>
Simple, isn't it? Now let's see the explanation in detail.
The security-role-assignment
tag can be used to define the match of abstract roles of a web application and the actual roles of WebLogic authentication providers. So a valid example could be as follows:
<wls:security-role-assignment>
<wls:role-name>User</wls:role-name>
<wls:principal-name>weblogic</wls:principal-name>
</wls:security-role-assignment
This would actually let only the WebLogic user (yes, the WebLogic administrator user; not recommended but vastly used) be a valid one for the application. Alternatively, you could point to any group available to the WebLogic security realm. Instead, with the externally defined element, we are pointing to a role that we need to set up from the WebLogic console. This extra step will provide a lot of flexibility since any group, users, or conditions that this role may map are defined from the WebLogic console, allowing us to modify the mapping without having to redeploy the application.
In order to accomplish this step, follow these instructions:
User
XACMLRoleMapper
When done, click on OK.
users
. This is the name of the group from the OpenLDAP server that holds all common users of our application. Click on Add and then on Finish.The only missing part now is the login form. In the web.xml
file, we've specified a login and an error page that will be used by the web container to perform the authentication process or redirect in case of errors. A managed bean will be created to perform the actual authentication using the HttpServlet
API and the login()
method, available in Java EE 6. Let's get started.
login.xhtml
. The page will contain a web form with username and password fields with the proper binding to the managed bean.<ui:define name="content"> <center> <p:panel header="Login" style="width: 450px;" > <h:form> <p:growl id="messages" autoUpdate="true" closable="true" /> <p:panelGrid columns="2" rendered="#{request.userPrincipal == null}" style="border:0"> <p:outputLabel value="E-mail" for="email" /> <p:inputText id="email" required="true" value="#{login.customer.email}" label="E-mail" /> <p:outputLabel value="Password" for="password" /> <p:password id="password" required="true" value="#{login.customer.password}" label="Password" /> <p:commandButton value="Login" action="#{login.login}" /> </p:panelGrid> <h:panelGrid rendered="#{request.userPrincipal != null}"> <h:outputLabel value="#{request.userPrincipal}" /> <p:commandButton value="Logout" action="#{login.logout}" /> </h:panelGrid> </h:form> </p:panel> </center> </ui:define>
com.packt.store.security
.LoginBean
.login()
and logout()
methods referenced by the login.xhtml
page that will be used to process the authentication.@Named @RequestScoped public class LoginBean implements Serializable { private static final long serialVersionUID = 1L; private Customer customer = new Customer(); private FacesContext context = FacesContext.getCurrentInstance(); private HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest(); public String login() { try { request.login(customer.getEmail(), customer.getPassword()); addSuccessMessage(String.format("Welcome back, %s!", customer.getEmail())); return "index?faces-redirect=true"; } catch (ServletException ex) { ex.printStackTrace(); addErrorMessage("Wrong username or password,please try again."); return "login?faces-redirect=true"; } } public String logout() throws ServletException { request.logout(); addSuccessMessage("You have logged out."); return "login?faces-redirect=true"; } ... // getters and setters
As we have explicitly protected only the reservation.jsf
page in web.xml
, to test you just need to hit the URL http://localhost:7001/store/reservation.jsf
. You will be redirected to the Login page and requested to input credentials of a valid user from LDAP. If any error occurs, you will be kept at the Login page and an error message will be displayed. On a successful login, the application will load the main page and you should see the principal name on the page header.
In order to check the security details of your web application using the WebLogic console, follow these steps:
web.xml
and weblogic.xml
deployment descriptors are valid, but this page is a good place to check if what has been set in the descriptors looks good.