Basic configuration of Spring Security 3 and MySQL

In this post I explain how to implement Spring Security in a web application, as I did in a previous post but the authenticated user’s username and password are not saved to a file .xml but in a MySQL database.
As in the previous post, I configure one user only trying to access index.html and he is redirected to the standard login page for authentication.
In this project I configure log4j and maven too.

  1. create the project SpringSecuritySql
  2. create the file pom.xml
    <project
    	xmlns="http://maven.apache.org/POM/4.0.0"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    	<modelVersion>4.0.0</modelVersion>
    	<groupId>SpringSecuritySql</groupId>
    	<artifactId>SpringSecuritySql</artifactId>
    	<version>0.0.1-SNAPSHOT</version>
    	<packaging>war</packaging>
    
    	<!-- Shared version number properties -->
    	<properties>
    		<org.springframework.version>3.1.1.RELEASE</org.springframework.version>
    		<log4j.version>1.2.17</log4j.version>
    		<jstl.version>1.2</jstl.version>
    		<jdbc.version>5.1.21</jdbc.version>
    	</properties>
    
    	<dependencies>
    
    		<!-- Core utilities used by other modules. Define this if you use Spring 
    			Utility APIs (org.springframework.core.*/org.springframework.util.*) -->
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-core</artifactId>
    			<version>${org.springframework.version}</version>
    		</dependency>
    
    		<!-- Expression Language (depends on spring-core) Define this if you use 
    			Spring Expression APIs (org.springframework.expression.*) -->
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-expression</artifactId>
    			<version>${org.springframework.version}</version>
    		</dependency>
    
    		<!-- Bean Factory and JavaBeans utilities (depends on spring-core) Define 
    			this if you use Spring Bean APIs (org.springframework.beans.*) -->
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-beans</artifactId>
    			<version>${org.springframework.version}</version>
    		</dependency>
    
    		<!-- Aspect Oriented Programming (AOP) Framework (depends on spring-core, 
    			spring-beans) Define this if you use Spring AOP APIs (org.springframework.aop.*) -->
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-aop</artifactId>
    			<version>${org.springframework.version}</version>
    		</dependency>
    
    		<!-- Application Context (depends on spring-core, spring-expression, spring-aop, 
    			spring-beans) This is the central artifact for Spring's Dependency Injection 
    			Container and is generally always defined -->
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-context</artifactId>
    			<version>${org.springframework.version}</version>
    		</dependency>
    
    		<!-- Various Application Context utilities, including EhCache, JavaMail, 
    			Quartz, and Freemarker integration Define this if you need any of these integrations -->
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-context-support</artifactId>
    			<version>${org.springframework.version}</version>
    		</dependency>
    
    		<!-- Transaction Management Abstraction (depends on spring-core, spring-beans, 
    			spring-aop, spring-context) Define this if you use Spring Transactions or 
    			DAO Exception Hierarchy (org.springframework.transaction.*/org.springframework.dao.*) -->
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-tx</artifactId>
    			<version>${org.springframework.version}</version>
    		</dependency>
    
    		<!-- JDBC Data Access Library (depends on spring-core, spring-beans, spring-context, 
    			spring-tx) Define this if you use Spring's JdbcTemplate API (org.springframework.jdbc.*) -->
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-jdbc</artifactId>
    			<version>${org.springframework.version}</version>
    		</dependency>
    
    		<!-- Object-to-Relation-Mapping (ORM) integration with Hibernate, JPA, 
    			and iBatis. (depends on spring-core, spring-beans, spring-context, spring-tx) 
    			Define this if you need ORM (org.springframework.orm.*) -->
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-orm</artifactId>
    			<version>${org.springframework.version}</version>
    		</dependency>
    
    		<!-- Object-to-XML Mapping (OXM) abstraction and integration with JAXB, 
    			JiBX, Castor, XStream, and XML Beans. (depends on spring-core, spring-beans, 
    			spring-context) Define this if you need OXM (org.springframework.oxm.*) -->
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-oxm</artifactId>
    			<version>${org.springframework.version}</version>
    		</dependency>
    
    		<!-- Web application development utilities applicable to both Servlet and 
    			Portlet Environments (depends on spring-core, spring-beans, spring-context) 
    			Define this if you use Spring MVC, or wish to use Struts, JSF, or another 
    			web framework with Spring (org.springframework.web.*) -->
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-web</artifactId>
    			<version>${org.springframework.version}</version>
    		</dependency>
    
    		<!-- Spring MVC for Servlet Environments (depends on spring-core, spring-beans, 
    			spring-context, spring-web) Define this if you use Spring MVC with a Servlet 
    			Container such as Apache Tomcat (org.springframework.web.servlet.*) -->
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-webmvc</artifactId>
    			<version>${org.springframework.version}</version>
    		</dependency>
    
    		<!-- Spring MVC for Portlet Environments (depends on spring-core, spring-beans, 
    			spring-context, spring-web) Define this if you use Spring MVC with a Portlet 
    			Container (org.springframework.web.portlet.*) -->
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-webmvc-portlet</artifactId>
    			<version>${org.springframework.version}</version>
    		</dependency>
    
    		<!-- Support for testing Spring applications with tools such as JUnit and 
    			TestNG This artifact is generally always defined with a 'test' scope for 
    			the integration testing framework and unit testing stubs -->
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-test</artifactId>
    			<version>${org.springframework.version}</version>
    			<scope>test</scope>
    		</dependency>
    
    		<!-- log4j -->
    		<dependency>
    			<groupId>log4j</groupId>
    			<artifactId>log4j</artifactId>
    			<version>${log4j.version}</version>
    		</dependency>
    
    		<!-- Spring Security -->
    		<dependency>
    			<groupId>org.springframework.security</groupId>
    			<artifactId>spring-security-core</artifactId>
    			<version>${org.springframework.version}</version>
    		</dependency>
    
    		<dependency>
    			<groupId>org.springframework.security</groupId>
    			<artifactId>spring-security-web</artifactId>
    			<version>${org.springframework.version}</version>
    		</dependency>
    
    		<dependency>
    			<groupId>org.springframework.security</groupId>
    			<artifactId>spring-security-config</artifactId>
    			<version>${org.springframework.version}</version>
    		</dependency>
    
    		<!-- JSTL -->
    		<dependency>
    			<groupId>jstl</groupId>
    			<artifactId>jstl</artifactId>
    			<version>${jstl.version}</version>
    		</dependency>
    
    		<!-- spring security tag -->
    		<dependency>
    			<groupId>org.springframework.security</groupId>
    			<artifactId>spring-security-taglibs</artifactId>
    			<version>${org.springframework.version}</version>
    		</dependency>
    
    		<!-- MySQL java connector -->
    		<dependency>
    			<groupId>mysql</groupId>
    			<artifactId>mysql-connector-java</artifactId>
    			<version>${jdbc.version}</version>
    		</dependency>
    		
    	</dependencies>
    
    </project>
    

    compared to the previous post, I add the section about MySQL java connector at the bottom of pom.xml and so I import the file mysql-connector-java-5.1.21.jar

  3. create the WEB-INF/web.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        version="2.5"
        xmlns="http://java.sun.com/xml/ns/javaee"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" >
    
        <display-name>
    		SpringSecuritySql
        </display-name>
    
        <!-- Spring MVC -->
    
        <servlet>
            <servlet-name>
    			SpringSecuritySql
            </servlet-name>
            <servlet-class>
    			org.springframework.web.servlet.DispatcherServlet
            </servlet-class>
            <load-on-startup>
    			1
            </load-on-startup>
        </servlet>
    
        <servlet-mapping>
            <servlet-name>
    			SpringSecuritySql
            </servlet-name>
            <url-pattern>
    			*.html
            </url-pattern>
        </servlet-mapping>
    
        <listener>
            <listener-class>
    			org.springframework.web.context.ContextLoaderListener
            </listener-class>
        </listener>
    
        <context-param>
            <param-name>
    			contextConfigLocation
            </param-name>
            <param-value>
    			/WEB-INF/SpringSecuritySql-servlet.xml
    			/WEB-INF/spring-mysql.xml
    			/WEB-INF/spring-security.xml
            </param-value>
        </context-param>
    
        <!-- Spring Security -->
        
        <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>
    
        <!-- Log4j -->
        
        <context-param>
            <param-name>
    			log4jConfigLocation
            </param-name>
            <param-value>
    			/WEB-INF/log4j.xml
            </param-value>
        </context-param>
    
        <listener>
            <listener-class>
                org.springframework.web.util.Log4jConfigListener
            </listener-class>
        </listener>
    
        <session-config>
            <session-timeout>
                30
            </session-timeout>
        </session-config>
    
        <welcome-file-list>
            <welcome-file>
    			index.html
            </welcome-file>
        </welcome-file-list>
    
    </web-app>
    

    note the reference to /WEB-INF/spring-mysql.xml

  4. create the file WEB-INF/log4j.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE log4j:configuration PUBLIC "-//LOGGER" "log4j.dtd">
    
    <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"> 
        <appender name="stdout" class="org.apache.log4j.ConsoleAppender">
            <layout class="org.apache.log4j.PatternLayout">
                <param name="ConversionPattern" value="%d %-5p %c{1}:%L %m %n" />
    <!--
    ConversionPattern format specification
    %d      inserts the date; you can specify the format (%d{yyyy-MM-dd HH:mm:ss,SSS})
    %-5p    inserts the priority log level, 5 characters, left justified
    %c{1}   inserts the name of the class
    %L      inserts the line number
    %m      inserts the user message
    %n      inserts the separator (for example, a new line)
    -->
            </layout>
        </appender>
    
        <appender name="fileAppender" class="org.apache.log4j.RollingFileAppender">
            <param name="Threshold" value="INFO" />
            <param name="MaxFileSize" value="512KB" />
            <param name="MaxBackupIndex" value="10" />
            <param name="File" value="${webapp.root}/WEB-INF/logs/springsecurity.log"/>
            <layout class="org.apache.log4j.PatternLayout">
                <param name="ConversionPattern" value="%d %-5p %c{1}:%L %m %n" />
            </layout>
        </appender>
    
    <!--sets the priority log level for org.springframework-->
        <logger name="org.springframework">
            <level value="info"/>
        </logger>
    
    <!--sets the priority log level for eu.lucazanini.springsecurity-->
        <logger name= "eu.lucazanini.springsecurity">
            <level value="debug"/>
        </logger>
    
    <!--sets the default priority log level-->
        <root>
            <priority value="info"></priority>
            <appender-ref ref="stdout"/>
            <appender-ref ref="fileAppender"/>
        </root>
    </log4j:configuration>
    
  5. create the file WEB-INF/spring-security.xml
    <beans:beans
    	xmlns="http://www.springframework.org/schema/security"
    	xmlns:beans="http://www.springframework.org/schema/beans"
    	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.1.xsd">
    
    	<http auto-config="true">
    
    		<intercept-url
    			access="ROLE_USER"
    			pattern="/**" />
    	</http>
    
    	<beans:bean
    		id="encoder"
    		class="org.springframework.security.crypto.password.StandardPasswordEncoder" />
    
    	<authentication-manager>
    		<authentication-provider>
    
    			<password-encoder ref="encoder" />
    
    			<jdbc-user-service data-source-ref="dataSource" />
    		</authentication-provider>
    	</authentication-manager>
    
    </beans:beans>
    

    Here I specify an user named “user” and password “spring”.
    The password is encrypted using Scala and the procedure to get it is explained in the previous post.
    This file is an important part of the user authentication process, the bean with id “encoder” is used to compare the password entered by the user with the encrypted stored in the database and the tag “jdbc-user-service” sets the mysql database (dataSource is defined in the spring-mysql.xml), the basic configuration assumes the tables as explained in Security Database Schema and JdbcDaoImpl; you can use your own customized tables by using the child elements of the tag “jdbc-user-service-users”: users-by-username-query e authorities-by-username-query as you see in jdbc-user-service

  6. create the file /WEB-INF/spring-mysql.xml
    <beans xmlns="http://www.springframework.org/schema/beans"
    	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">
    
    	<bean id="dataSource"
    		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    
    		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
    		<property name="url" value="jdbc:mysql://localhost:3306/spring_users" />
    		<property name="username" value="[your_username]" />
    		<property name="password" value="[your_password]" />
    	</bean>
    
    </beans>
    

    Here I specify the connection to mysql database where [your_username] and [your_password] are those of a mysql user authorized to access to the database spring_users

  7. create the file WEB-INF/views/index.jsp
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
    <%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
    <html>
    <body>
    	<h1>Spring Security basic configuration</h1>
    	<h3><br />Hello ${username}, ${message}</h3>
    	<sec:authorize ifAnyGranted="ROLE_USER">
            <h6><br />Your role is ROLE_USER</h6>
        </sec:authorize>
    	
    	<input type="button" value="Log out" onClick="location.href='<c:url value="/j_spring_security_logout" />'"/>
    
    </body>
    </html>
    
  8. create the WEB-INF/classes/eu/lucazanini/springsecurity/LoginController.java
    package eu.lucazanini.springsecurity;
    
    import java.security.Principal;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.ModelMap;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.apache.log4j.Logger;
    
    @Controller
    public class LoginController {
    
        private static org.apache.log4j.Logger log = Logger
    	    .getLogger(LoginController.class);
    
        @RequestMapping(value = "/index", method = RequestMethod.GET)
        public String printWelcome(ModelMap model, Principal principal) {
    
    	log.debug("LoginController");
    	String name = principal.getName();
    	model.addAttribute("username", name);
    	model.addAttribute("message", "you are logged in");
    
    	return "index";
    
        }
    
    }
    
  9. create a mysql database named “spring_users” and the tables “users” e “authorities” using the following sql query:
    • create table users (
          username varchar(50) not null primary key,
          password varchar(80) not null,
          enabled boolean not null
      ) engine = InnoDb;
      
    • create table authorities (
          username varchar(50) not null,
          authority varchar(50) not null,
          foreign key (username) references users (username),
          unique index authorities_idx_1 (username, authority)
      ) engine = InnoDb;
      
    • insert into users(`username`,`password`,`enabled`) values('user','20331ba9c4935517ab16f0052097b0d79f40f0a54a1a025ec742a308e8564757e021797bf7185332',1);
      insert into authorities(`username`,`authority`) values('user','ROLE_USER');
      

The password has a maximum length of 80 characters and not 50 as specified in Security Database Schema in order to use encrypted passwords with SHA-256 and a salt of 8 bytes, for more information, see Password Encoding e StandardPasswordEncoder

  • launch the app (insert “user” and “spring”)


Comments

One response to “Basic configuration of Spring Security 3 and MySQL”

  1. Amani Avatar

    hello,
    i’ve followed all of these steps but at the end i get this error :
    ….Error
    |
    2014-07-03 10:37:01,604 [http-bio-8080-exec-10] ERROR [/SpringSecuritySql].[gsp] – Servlet.service() for servlet [gsp] in context with path [/SpringSecuritySql] threw exception
    Message: Error mapping onto view [/index]: Error processing GroovyPageView: Cannot invoke method getURLs() on null object
    Line | Method
    ->> 1145 | runWorker in java.util.concurrent.ThreadPoolExecutor
    – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – –
    | 615 | run in java.util.concurrent.ThreadPoolExecutor$Worker
    ^ 745 | run . . . in java.lang.Thread
    Caused by GroovyPagesException: Error processing GroovyPageView: Cannot invoke method getURLs() on null object
    ->> 77 | runWorker in C:\grails\SpringSecuritySql\grails-app\views\index.gsp
    – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – –
    Caused by NullPointerException: Cannot invoke method getURLs() on null object
    ->> 10 | doCall in C__grails_SpringSecuritySql_grails_app_views_index_gsp$_run_closure1
    – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – – –
    | 14 | run in C__grails_SpringSecuritySql_grails_app_views_index_gsp
    | 1145 | runWorker in java.util.concurrent.ThreadPoolExecutor
    | 615 | run in java.util.concurrent.ThreadPoolExecutor$Worker
    ^ 745 | run . . . in java.lang.Thread

    Can you please explain to me what have i done wrong?
    thanks in advance

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.