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.
- create the project SpringSecuritySql
- 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
- 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
- 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>
- 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 - 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
- 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>
- 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"; } }
- 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
Leave a Reply