Tuesday, March 15, 2011

Getting started with Spring Security 3 with JBoss and MySQL

Hi Folks,

Lets get started.

You need to download,

1. Spring Security Module 3
2. MySQL 5.1
3. JBoss 4.2 GA


I'm using eclipse as the IDE.

Create a simple Web Project in eclipse IDE,

Among the jar files of the Spring Security module you will see a 2 WAR files. open the WAR file spring-security-samples-tutorial-3.0.5.RELEASE.war with WinZIP or WinRAR like tool and copy all the JAR files there to the lib folder of the project.

Add all the copied JAR files to buildpath.

First of all you need to create a login page for your web application. create a simple JSP file and name it as login.jsp and paste the following code fragment there.


<form action="/test/j_spring_security_check" method="post"> 
Username : <input type="text" name="j_username" id="j_username">
Password : <input type="password" name="j_password" id="j_password">
<input type="submit" value="Submit">
</form>

Remember that,

1. j_spring_security_check is the URL you need to call as the action (/test is the context root of your web application).

2. Name of your username and the password fields should be j_username and j_password

Once the user been authenticated, we need to forward the user to a different page. so lets create another jsp file for that and name it as user_authenticated.jsp

You are going to authorize the user according to their user roles, so here I have created separate JSPs to show some user role level authorization. my user roles are,

1. ROLE_SUPERVISOR (supervisor.jsp)
2. ROLE_USER (user.jsp)
3. ROLE_TELLER (teller.jsp)

Now let's configure Spring.

You will need 2 xml files to hold Spring Security info and the Datasource info to the MySQL DB. See below,

1. applicationContext-security.xml (Your Spring Security configuration file)
2. applicationContext-db.xml (Your datasource configuration file)

Create above 2 files in your WEB-INF directory.

applicationContext-security.xml

Simply you can copy the same file from the tutorial war file that I have mentioned above, and modify its contents as I have described below.

<http use-expressions="true">
<intercept-url pattern="/user_authenticated.jsp" access="isAuthenticated()" />
<intercept-url pattern="/supervisor.jsp" access="hasRole('ROLE_SUPERVISOR')" />
<intercept-url pattern="/teller.jsp" access="hasRole('ROLE_TELLER')" />
<intercept-url pattern="/user.jsp" access="hasRole('ROLE_USER')" />
<intercept-url pattern="/**"  access="permitAll" />
<form-login login-page='/login.jsp'/>
<logout />
<remember-me />
<!--

Uncomment to enable X509 client authentication support

<x509 />

-->

<!-- Uncomment to limit the number of sessions a user can have -->
<session-management invalid-session-url="/timeout.jsp">
<concurrency-control max-sessions="1" error-if-maximum-exceeded="true" />
</session-management>
</http> 

<authentication-manager>
<authentication-provider>
<password-encoder hash="md5"/>
<jdbc-user-service data-source-ref="mysqlds"/>
</authentication-provider>
</authentication-manager>

<intercept-url pattern="/user_authenticated.jsp" access="isAuthenticated()" />

This is the JSP (user_authenticated.jsp) that we want to forward the user after getting authenticated, that will tell by this: access="isAuthenticated().

<intercept-url pattern="/supervisor.jsp" access="hasRole('ROLE_SUPERVISOR')" />

This will tell the Spring security module to authorize this JSP page access only to the users who are having ROLE_SUPERVISOR role.


Within the <authentication-manager> tag you will enter all the details for authenticating a particular user.

Passwords will be encoded with md5 algorithm. (<password-encoder hash="md5"/>).

This will point the datasource to the DB where the user credentials are stored:

<jdbc-user-service data-source-ref="mysqlds"/>

mysqlds is the datasource name.

applicationContext-db.xml

This Spring configuration file is to configure the datasource lookup. (here im assuming that your JBoss installed on your local machine)

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd">


<bean id="mysqlds" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>java:comp/env/jdbc/mysqlds</value>
</property>
<property name="jndiEnvironment">
<props>
<prop key="java.naming.factory.initial">org.jnp.interfaces.NamingContextFactory</prop>
<prop key="java.naming.provider.url">jnp://localhost:1099</prop>
</props>
</property>
</bean>
</beans>

Since you are going to deploy the application JBoss application server, you need to create another jboss-web.xml file with datasource information.

jboss-web.xml

<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
<resource-ref>
<res-ref-name>jdbc/mysqlds</res-ref-name>
<jndi-name>java:/mysqlds</jndi-name>
</resource-ref>
</jboss-web>

Finally you need to wire all the things with web.xml

web.xml

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/applicationContext-security.xml
/WEB-INF/applicationContext-db.xml
</param-value>
</context-param>

Here we need to specify where are our Spring security configuration file and the datasource configuration file.

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class> org.springframework.web.filter.DelegatingFilterProxy
</filter-class>
</filter>

<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

Finally with the datasource information,

<resource-ref>
<res-ref-name>jdbc/mysqlds</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>


Spring assumes that your database contains particular tables in default. so you need to create those tables in order to follow the default way of Spring.

Create those tables from below script.
create table users(
      username varchar(50) not null primary key,
      password varchar(50) not null,
      enabled boolean not null);

  create table authorities (
      username varchar(50) not null,
      authority varchar(50) not null,
      constraint fk_authorities_users foreign key(username) references users(username));
      create unique index ix_auth_username on authorities (username,authority);

Now you need to create the datasource file in JBoss and configure it.

Go to JBOSS_HOME\server\default\deploy. Get a copy of the existing hsqldb-ds.xml file and name it as mysqldb-ds.xml and modify its contents as below.

mysqldb-ds.xml


   
      
      mysqlds

      jdbc:mysql://localhost:3306/test 
      com.mysql.jdbc.Driver
      root
      root
      5
      20
      0

   



save the file to the deploy folder of the JBoss and restart the server. (dont forget to put some values in the tables, since we are encoding passwords with md5 you need to put the encrypted passwords in the table.

Use this site to do that.

http://md5.gromweb.com/

You are done now.

Right click on the eclipse project and export it as a WAR file and copy it to the JBoss deploy folder, and access the URL http://localhost:8080/test/user_authenticated.jsp (i have used test as the WAR file name)

Now your eclipse project structure should look like this,

13 comments:

  1. I am kelly software developer please sent me this example to my email kelvinomwaka@yahoo.com

    ReplyDelete
  2. Well... I have done this few months back. Im not sure where I have it now. Why dont you follow the tutorial. Message me if somewhere is not clear.

    ReplyDelete
  3. When i deploy in jboss, i'm getting the below error

    19:08:18,818 INFO [TomcatDeployment] deploy, ctxPath=/XYZ
    19:08:19,056 ERROR [StandardContext] Context [/XYZ] startup failed due to previous errors
    java.lang.RuntimeException: mapped-name is required for sessionRegistry of deployment XYZ.war

    can you please help

    ReplyDelete
  4. It seems the problem is with your environment. What is the JBoss version you are using? and you sure you included the JAR files only from the sample WAR files which came from the Spring security module?

    ReplyDelete
  5. Hi Kushan, i need to store the intercept-urls in a database rather than configuring in the xmls. Is that possible, please help me.

    ReplyDelete
  6. Hi Bhaskara, sorry for the late reply. Yes certainly. You can get it configured in a DB. I'm hoping to write a post on that soon. There might be some tips on the google also.

    ReplyDelete
  7. I tried to follow the setup. But when I deploy the project on JBoss version 5.0.1, it shows the following error message.

    org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Unable to locate Spring NamespaceHandler for XML schema namespace [http://www.springframework.org/schema/security]
    Offending resource: ServletContext resource [/WEB-INF/applicationContext-security.xml]

    Can you please help. Thanks in advance

    ReplyDelete
  8. You sure you added all the library(JAR) files from the spring-security-samples-tutorial-3.0.5.RELEASE.war.

    Do not include any library file except from the WAR file that I have mentioned above.

    ReplyDelete
  9. After following the tutorial and getting the project running, I found the authentication page is using HTTP. Is there a way to change it to HTTPS?

    Thanks and regards.

    ReplyDelete
  10. Its Good to hear you got it working :) Yes you can make it HTTPS. For that you need to enable HTTPS for JBoss with a security certificate

    ReplyDelete
  11. Hi , is there any way to inject custom authorities if you use ActiveDirectoryLdapAuthenticationProvider for LDAP authentication ?

    ReplyDelete
  12. I want to run this example on Tomcat 6.0. What changes should be done?

    ReplyDelete
  13. Modify the context.xml file in conf directory in tomcat as below.

    Resource name="jdbc/mysqlds"
    auth="Container"
    type="javax.sql.DataSource"
    driverClassName="com.mysql.jdbc.Driver"
    url="jdbc:mysql://localhost:3306/test"
    username="root"
    password="root"
    maxActive="20"
    maxIdle="30"
    maxWait="-1"



    You need following Java snippet to get the connection from JNDI.

    Context initCtx = new InitialContext();
    Context envCtx = (Context) initCtx.lookup("java:comp/env");

    DataSource ds = (DataSource)
    envCtx.lookup("jdbc/mysqlds");

    Connection conn = ds.getConnection();
    conn.close();

    ReplyDelete