Java and J2EE Tutorials, Jsp and Servlet Tutorials, Spring MVC, Solr, XML, JSON Examples, Hibernate & Struts 2 Hello World projects



Wednesday, 28 May 2014

Spring Security Authentication and Authorization Example with Database Credentials

In our previous discussions we go through 'What is Spring Security' and 'Custom login form in Spring Security', previously we have used XML based username and password to authenticate a user. In this particular blog we will see how to use username, password and role details from database to authenticate a user. At the end of the blog we will see how to perform authorization with spring security based on different roles.


Project Structure

Let us start our discussion with project structure, please go through the pom.xml file to get information regarding dependencies needed in spring-security. Setup a Simple Maven project and import it in Eclipse, and follow the step by step instructions mentioned below.



Database setup

As per spring security documentation, we need to create two suggested tables 'users' and 'user_roles' in database with exact datatypes and columns. Here is the script to create tables in DB.
-- Dumping database structure for spring_security
CREATE DATABASE IF NOT EXISTS `spring_security` /*!40100 DEFAULT CHARACTER SET latin1 */;
USE `spring_security`;


-- Dumping structure for table spring_security.users
CREATE TABLE IF NOT EXISTS `users` (
  `username` varchar(45) NOT NULL,
  `password` varchar(450) NOT NULL,
  `enabled` tinyint(4) NOT NULL DEFAULT '1',
  PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

-- Dumping structure for table spring_security.user_roles
CREATE TABLE IF NOT EXISTS `user_roles` (
  `user_role_id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(45) NOT NULL,
  `ROLE` varchar(45) NOT NULL,
  PRIMARY KEY (`user_role_id`),
  UNIQUE KEY `uni_username_role` (`ROLE`,`username`),
  KEY `fk_username_idx` (`username`),
  CONSTRAINT `fk_username` FOREIGN KEY (`username`) REFERENCES `users` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=latin1;


Let us now add two users 'andrew' and 'robert' and their roles to the database, 'andrew' have two roles 'ROLE_USER' and 'ROLE_ADMIN' and 'robert' have only one role 'ROLE_USER'. See the script below:
INSERT INTO `users` (`username`, `password`, `enabled`) VALUES
 ('andrew', 'andrew@123', 1),
 ('robert', 'robert@123', 1);

INSERT INTO `user_roles` (`user_role_id`, `username`, `ROLE`) VALUES
 (2, 'andrew', 'ROLE_ADMIN'),
 (1, 'andrew', 'ROLE_USER'),
 (3, 'robert', 'ROLE_USER');


Dependencies required for spring-security - pom.xml

Here is a list of all required dependencies that we need to add in pom.xml, to get spring security basic features all we need to do is to add 'spring-security-web' and 'spring-security-config' to pom.xml file.
<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.beingjavaguys.sprscr</groupId>
 <artifactId>SpringSecurityDemo</artifactId>
 <packaging>war</packaging>
 <version>1.0-SNAPSHOT</version>
 <name>SpringSecurityDemo Maven Webapp</name>
 <url>http://maven.apache.org</url>
 <properties>
  <spring.version>4.0.5.RELEASE</spring.version>
  <jdk.version>1.7</jdk.version>
  <context.path>SpringSecurityDemo</context.path>
 </properties>
 <developers>

  <developer>
   <id>Nagesh Chauhan</id>
   <email>beingjavaguy@gmail.com</email>
   <organization>beingjavaguys.com</organization>
   <organizationUrl>http://www.beingjavaguys.com</organizationUrl>
   <roles>
    <role>Java Developer</role>
   </roles>
   <timezone>+5:30</timezone>
  </developer>
 </developers>

 <build>
  <finalName>${pom.artifactId}</finalName>

  <plugins>
   <!-- Maven compiler plugin -->
   <plugin>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
     <source>${jdk.version}</source>
     <target>${jdk.version}</target>
    </configuration>
   </plugin>
  </plugins>
 </build>
 <dependencies>



  <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>

  <dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
   <version>5.1.6</version>
  </dependency>

  <dependency>
   <groupId>jstl</groupId>
   <artifactId>jstl</artifactId>
   <version>1.2</version>
  </dependency>

  <dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-web</artifactId>
   <version>3.2.4.RELEASE</version>
  </dependency>
  <dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-config</artifactId>
   <version>3.2.4.RELEASE</version>
  </dependency>
 </dependencies>
</project>



\src\main\webapp\WEB-INF\web.xml

The very first thing we need to do is adding following filters to our web.xml. These filters tells the container that all upcoming requests will be handled by spring security for security purpose. DelegatingFilterProxy is a class present in spring-security jars which delegates control to a filter chaining defined in spring-security internals. Note that we have given the bean name as ‘springSecurityFilterChain’ one should not change the name of this bean as this is being configured by spring-security internal infrastructure. By adding these filters to web.xml now we are ready to configure security in the application.
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
       http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
 version="2.5">

 <display-name>Sample Spring Maven Project</display-name>

 <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/security-config.xml
   </param-value>
 </context-param>

 <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>
< br/>

\src\main\webapp\WEB-INF\mvc-dispatcher-servlet.xml

This is a simple spring configuration file with a view resolver bean to show jsp's and two useful annotation information regarding component-scan and annotation-driven.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
 xmlns:util="http://www.springframework.org/schema/util" xmlns:mvc="http://www.springframework.org/schema/mvc"
 xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
  http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">

 <context:component-scan base-package="com.beingjavaguys.controller" />
 <mvc:annotation-driven />

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

 <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>
 
 
</beans>

\src\main\webapp\WEB-INF\security-config.xml

By adding filter to web.xml we are now able to configure security in our application, we can start with minimal http configuration for incoming requests. This tells that all upcoming requests which are matching to the pattern given in ‘pattern’ attribute will be secured bu spring-security and these requests will need ‘ROLE_USER’ role to access them. Here the thing to be noted is that ‘ROLE_’ prefix is a marker to define roles, ‘access’ attribute can accept a number of roles separated by a comma. Regarding ‘’ we can have a number of different entries for different url’s, these entries will be evaluated in the order they are defined here. The very first match to the incoming request will be executed. We can also add a method attribute to limit the match to a particular HTTP method (GET, POST, PUT etc.).
<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.2.xsd">

 <http auto-config="true">
  <access-denied-handler error-page="/403page" />
  <intercept-url pattern="/user**" access="ROLE_USER" />
  <intercept-url pattern="/admin**" access="ROLE_ADMIN" />
  <form-login login-page='/login' username-parameter="username"
   password-parameter="password" default-target-url="/user"
   authentication-failure-url="/login?authfailed" />
  <logout logout-success-url="/login?logout" />
 </http>

 <!-- <authentication-manager> <authentication-provider> <user-service> <user 
  name="user" password="user@123" authorities="ROLE_ADMIN" /> </user-service> 
  </authentication-provider> </authentication-manager> -->

 <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 username, role from user_roles where username =?  " />
  </authentication-provider>
 </authentication-manager>

</beans:beans>


\src\main\java\com\beingjavaguys\controller\LoginController.java

This is a simple Controller class with few request mappings in it, we have '/login' to redirect the user to login form along with custom messages if required. Another mapping id for admin page '/admin' and '/user' only logged in users with required 'USER_ROLES' will be able to see the pages.
package com.beingjavaguys.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class LoginController {

 @RequestMapping("login")
 public ModelAndView getLoginForm(
   @RequestParam(required = false) String authfailed, String logout,
   String denied) {
  String message = "";
  if (authfailed != null) {
   message = "Invalid username of password, try again !";
  } else if (logout != null) {
   message = "Logged Out successfully, login again to continue !";
  } else if (denied != null) {
   message = "Access denied for this user !";
  }
  return new ModelAndView("login", "message", message);
 }

 @RequestMapping("user")
 public String geUserPage() {
  return "user";
 }

 @RequestMapping("admin")
 public String geAdminPage() {
  return "admin";
 }

 @RequestMapping("403page")
 public String ge403denied() {
  return "redirect:login?denied";
 }

}



\src\main\webapp\WEB-INF\pages\login.jsp

This is actual custom login form that will be rendered to the user to add credentials and log in to the application. In this file all we need to do is adding spring specific action 'j_spring_security_check' so that spring-security can intercept it and can authentication done.
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<title>Login Form | www.beingjavaguys.com</title>
</head>
<body>
 <center>
  <br /> <br /> <br />
  <h2>Login Here</h2>
  <br />
  <div style="text-align: center; padding: 30px;border: 1px solid green;width: 250px;">
   <form method="post" action="<c:url value='j_spring_security_check' />">

    <table>
     <tr>
      <td colspan="2" style="color: red">${message}</td>

     </tr>
     <tr>
      <td>User Name:</td>
      <td><input type="text" name="username" />
      </td>
     </tr>
     <tr>
      <td>Password:</td>
      <td><input type="password" name="password" />
      </td>
     </tr>
     <tr>
      <td> </td>
      <td><input type="submit" value="Login" />
      </td>

     </tr>
    </table>
   </form>
  </div>
 </center>
</body>
</html>

\src\main\webapp\WEB-INF\pages\admin.jsp

This is another simple jsp file, this will be rendered to the browser if the user with 'ROLE_ADMIN' is logged in successfully. We have also added a logout link in the page, logout functionality to work onw has to make the spring specific link '/j_spring_security_logout'. By clicking the link spring-security will logged out the current logged in used.
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<title>Admin Page | www.beingjavaguys.com</title>
</head>
<body>
<c:url value="/j_spring_security_logout" var="logoutUrl" />
 <center>
  <br /> <br /> <br />
  <h2>Admin | You are now logged in</h2>
  <h3><a href="${logoutUrl}">Logout</a></h3>
 </center>
</body>
</html>


\src\main\webapp\WEB-INF\pages\user.jsp

This is another simple jsp file, this will be rendered to the browser if the user with 'ROLE_USER' role is logged in successfully. We have also added a logout link in the page, logout functionality to work onw has to make the spring specific link '/j_spring_security_logout'. By clicking the link spring-security will logged out the current logged in used.
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<title>User Page | www.beingjavaguys.com</title>
</head>
<body>
<c:url value="/j_spring_security_logout" var="logoutUrl" />
 <center>
  <br /> <br /> <br />
  <h2>User | You are now logged in</h2>
  <h3><a href="${logoutUrl}">Logout</a></h3>
 </center>
</body>
</html>


Here we are done with all required configurations, if everything goes right you will see below mentioned screens in your browser.

Authorization with spring security

Authentication means who can enter in the system based on their login credentials, whereas authorization tells the system that what are the authenticated users those can access a particular system functionality.

Trying to aceess '/admin' page, hit url "http://localhost:8080/SpringSecurityDemo/admin"
Spring security will redirect it to login page, because according to the interceptor configuration only a logged in user can view this page and that too having user role as 'ROLE_ADMIN'.



Lets try to login with 'robert' we know that 'robert' does not have 'USER_ADMIN' role, so there will be a '403' access denied error, this is what we called 'Authorization in Spring security'. Although 'robert' has valid login-password but cant view the url's whose role it does not have.



Now let us try to login again with 'andrew' credential, 'andrew' has required role i.e.'ROLE_ADMIN' to view '/admin' page so this will be a successful login and will be redirected to admin page.



We know that both users have 'ROLE_USER', if you try to access 'http://localhost:8080/SpringSecurityDemo/user' then it will be success login and you will see user page.

This is all about ' Spring Security Authentication and Authorization Example with Database Credentials'. In upcoming blogs we will see more about Spring, Hibernate, Java and Other opensource technologies.








Thanks for reading !
Being Java Guys Team

Download "Spring Security Custom Login Form Example Project" from "SkyDrive"





9 comments:

  1. Wrong project link given to download it does not contain database code
    please upload the right project and inform me(rajgopal527@hotmail.com)

    ReplyDelete
  2. This comment has been removed by the author.

    ReplyDelete
  3. can we add additional fields to database table "user"

    ReplyDelete
  4. Good try..but after logout it direct by default to user login page....and admin can not login after logout.....

    ReplyDelete
  5. if some one could fix issue in this example Issue is if you give correct login ID & pwd for USER_ROLE from admin login it will give you are not authorized that is fine but at the same time if you hit user URL it will directly take you to user.jsp without any login page in between

    ReplyDelete
  6. I am getting error as

    DEBUG o.s.s.a.d.DaoAuthenticationProvider - Authentication failed: password does not match stored value

    15:44:29.782 [http-nio-8080-exec-8] DEBUG o.s.s.w.a.UsernamePasswordAuthenticationFilter - Authentication request failed: org.springframework.security.authentication.BadCredentialsException: Bad credentials

    ReplyDelete
  7. Hello! I had deployment error: "Error during artifact deployment".
    Tomcal localhost log wrote this: " org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#0': Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping found. Cannot map 'loginController' bean method
    "

    Can you help me, please?

    ReplyDelete
  8. Great tutorial! Thank you!

    ReplyDelete
  9. very much understandable blog thank you

    ReplyDelete

Like Us on Facebook


Like Us On Google+



Contact

Email: neel4soft@gmail.com
Skype: neel4soft