Tuesday 7 January 2014

Spring Security Login Form Using Database

In this tutorial we will see how to use database to perform the form-based login authentication in Spring Security.

Example 

Create Database Tables

We are using MySQL as database

create database for users to authenticate.
 

CREATE DATABASE maheshdb;
 
In database maheshdb create 2 tables users , user_roles 
to store user details and user role details, one to many relationship, 
one user can contains many roles.
 
you are allow to add extra columns for extra functionality. In 
additional, the table name and column name are not fixed, you can rename
to whatever name.
 

CREATE TABLE maheshdb.`users` (
  `USER_ID` INT(10) UNSIGNED NOT NULL,
  `USERNAME` VARCHAR(45) NOT NULL,
  `PASSWORD` VARCHAR(45) NOT NULL,
  `ENABLED` tinyint(1) NOT NULL,
  PRIMARY KEY (`USER_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
 
CREATE TABLE maheshdb.`user_roles` (
  `USER_ROLE_ID` INT(10) UNSIGNED NOT NULL,
  `USER_ID` INT(10) UNSIGNED NOT NULL,
  `AUTHORITY` VARCHAR(45) NOT NULL,
  PRIMARY KEY (`USER_ROLE_ID`),
  KEY `FK_user_roles` (`USER_ID`),
  CONSTRAINT `FK_user_roles` FOREIGN KEY (`USER_ID`) REFERENCES `users` (`USER_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 
 
Insert data in tables for to test application.
 


INSERT INTO maheshdb.users (USER_ID, USERNAME,PASSWORD, ENABLED)
VALUES (100, 'mahesh', '24251', TRUE);

INSERT INTO maheshdb.user_roles (USER_ROLE_ID, USER_ID,AUTHORITY)
VALUES (1, 100, 'ROLE_USER');


Here is the content for 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/maven-v4_0_0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>com.mahesh.spring</groupId>
 <artifactId>SpringSecurity</artifactId>
 <packaging>war</packaging>
 <version>1.0-SNAPSHOT</version>
 <name>SpringSecurity Maven Webapp</name>
 <url>http://maven.apache.org</url>

 <properties>
  <spring.version>3.0.5.RELEASE</spring.version>
 </properties>

 <dependencies>

  <!-- Spring 3 dependencies -->
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-core</artifactId>
   <version>${spring.version}</version>
  </dependency>

  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-web</artifactId>
   <version>${spring.version}</version>
  </dependency>

  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-webmvc</artifactId>
   <version>${spring.version}</version>
  </dependency>

  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-jdbc</artifactId>
   <version>${spring.version}</version>
  </dependency>

  <!-- Spring Security -->
  <dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-core</artifactId>
   <version>${spring.version}</version>
  </dependency>

  <dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-web</artifactId>
   <version>${spring.version}</version>
  </dependency>

  <dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-config</artifactId>
   <version>${spring.version}</version>
  </dependency>

  <!-- jstl -->
  <dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>jstl</artifactId>
   <version>1.2</version>
  </dependency>

  <!-- MySQL database driver -->
  <dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
   <version>5.1.9</version>
  </dependency>

 </dependencies>

 <build>
  <finalName>SpringSecurity</finalName>
  <plugins>
   <plugin>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
     <source>1.6</source>
     <target>1.6</target>
    </configuration>
   </plugin>
  </plugins>
 </build>

</project>

Here is the content for web.xml

web.xml should contain DelegatingFilterProxy definition with specified filtername.
 
 

<web-app id="WebApp_ID" version="2.4"
 xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
 http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

 <display-name>Spring MVC Application</display-name>

 <!-- Spring MVC -->
 <servlet>
  <servlet-name>mvc-dispatcher</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
 </servlet>
 <servlet-mapping>
  <servlet-name>mvc-dispatcher</servlet-name>
  <url-pattern>/</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/mvc-dispatcher-servlet.xml,
   /WEB-INF/spring-database.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>

</web-app>

 
 

Here is the content for Spring Database spring-database.xml

Create a data source bean, and connect to database via Spring JDBC.




<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/maheshdb" />
  <property name="username" value="root" />
  <property name="password" value="root" />
 </bean>

</beans>




Here is the content for Spring Security spring-security.xml


use “jdbc-user-service” tag, and define your query to get the data from database. 

<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.0.3.xsd">

 <http auto-config="true">
  <intercept-url pattern="/welcome*" access="ROLE_USER" />
  <form-login login-page="/login" default-target-url="/welcome"
   authentication-failure-url="/loginfailed" />
  <logout logout-success-url="/logout" />
 </http>

 <authentication-manager>
  <authentication-provider>
   <jdbc-user-service data-source-ref="dataSource"
    
    users-by-username-query="
     select username,password, enabled 
     from users where USERNAME=?" 
    
    authorities-by-username-query="
     select u.username, ur.authority from users u, user_roles ur 
     where u.user_id = ur.user_id and u.username =?  " 
     
   />
  </authentication-provider>
 </authentication-manager>

</beans:beans>





Here is the content for Spring configuration file for mvc mvc-dispatcher-servlet.xml

 
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:context="http://www.springframework.org/schema/context"
 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/context 
        http://www.springframework.org/schema/context/spring-context-3.0.xsd">

 <context:component-scan base-package="com.mahesh.common.controller" />

 <bean
  class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  <property name="prefix">
   <value>/WEB-INF/pages/</value>
  </property>
  <property name="suffix">
   <value>.jsp</value>
  </property>
 </bean>

 <bean id="messageSource"
  class="org.springframework.context.support.ResourceBundleMessageSource">
  <property name="basenames">
   <list>
    <value>mymessages</value>
   </list>
  </property>
 </bean>
 
</beans>

Here is the content for mymessages.properties


AbstractUserDetailsAuthenticationProvider.badCredentials=Invalid username or password



Here is the content for LoginController.java


package com.mahesh.common.controller;

import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
 
@Controller
public class LoginController {
 
 @RequestMapping(value="/welcome", method = RequestMethod.GET)
 public String printWelcome(ModelMap model) {
 
  User user = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
  String name = user.getUsername();
 
  model.addAttribute("username", name);
  model.addAttribute("message", "Spring Security login + database example");
  return "hello";
 
 }
 
 @RequestMapping(value="/login", method = RequestMethod.GET)
 public String login(ModelMap model) {
 
  return "login";
 
 }
 
 @RequestMapping(value="/loginfailed", method = RequestMethod.GET)
 public String loginerror(ModelMap model) {
 
  model.addAttribute("error", "true");
  return "login";
 
 }
 
 @RequestMapping(value="/logout", method = RequestMethod.GET)
 public String logout(ModelMap model) {
 
  return "login";
 
 }
 
}



Here is the content for login.jsp



<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<title>Login Page</title>
<style>
.errorblock {
 color: #ff0000;
 background-color: #ffEEEE;
 border: 3px solid #ff0000;
 padding: 8px;
 margin: 16px;
}
</style>
</head>
<body onload='document.f.j_username.focus();'>
 <h3>Login with Username and Password (Authentication with Database)</h3>

 <c:if test="${not empty error}">
  <div class="errorblock">
   Your login attempt was not successful, try again.<br /> Caused :
   ${sessionScope["SPRING_SECURITY_LAST_EXCEPTION"].message}
  </div>
 </c:if>

 <form name='f' action="<c:url value='j_spring_security_check' />"
  method='POST'>

  <table>
   <tr>
    <td>User:</td>
    <td><input type='text' name='j_username' value=''>
    </td>
   </tr>
   <tr>
    <td>Password:</td>
    <td><input type='password' name='j_password' />
    </td>
   </tr>
   <tr>
    <td colspan='2'><input name="submit" type="submit"
     value="submit" />
    </td>
   </tr>
   <tr>
    <td colspan='2'><input name="reset" type="reset" />
    </td>
   </tr>
  </table>

 </form>
</body>
</html>





Here is the content for hello.jsp



<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<body>
 <h3>Message : ${message}</h3> 
 <h3>Username : ${username}</h3> 
 
 <a href="<c:url value="/j_spring_security_logout" />" > Logout</a>
 
</body>
</html>
 
 
Steps to create war file and deploy it in web/Application server.

1. execute mvn clean install command on root directory.
2. SpringSecurity.war file will be created in target folder.
3. copy it to deployment folder of server and start server.

Below are the Screen shots

 
 
 








1 comment:

  1. Great work. I did not use the all tutorial but found within it exactly what I was looking for. Thank you.

    ReplyDelete