Spring Security Authentication and Authorization Using Database
In our previous post, we have discussed how to use custom login page instead of default one provided by Spring security. In that example we declared username and password in spring-security.xml which is suitable for testing or POC purpose but in real time we need to use database or ldap authentication.In most of the cases, we will read credentials from database. In this post, we will discuss how to do authentication using database in spring security. We used MySQL and jdbc for database authentication.
We are going to use same example which we discussed in our previos post Spring Security Custom Login Form Example. But instead of declaring the credentials in spring-security.xml we are fetching it from Database.
In our previous post, we have discussed how to use custom login page instead of default one provided by Spring security. In that example we declared username and password in spring-security.xml which is suitable for testing or POC purpose but in real time we need to use database or ldap authentication.In most of the cases, we will read credentials from database. In this post, we will discuss how to do authentication using database in spring security. We used MySQL and jdbc for database authentication.
We are going to use same example which we discussed in our previos post Spring Security Custom Login Form Example. But instead of declaring the credentials in spring-security.xml we are fetching it from Database.
Tools used for this Application:
- Spring 4.2.1.RELEASE
- Spring Security 4.0.3.RELEASE
- Spring JDBC 3.2.8.RELEASE
- Eclipse Luna
- JDK 1.8
- Maven 3
- Tomcat 8
- MySQL Server 5.6
Step 1: Creating Database Tables
USERS
It contains user credentials (login and password).
CREATE TABLE USERS ( user_id INT(10) UNSIGNED NOT NULL PRIMARY KEY, user_name VARCHAR(50) NOT NULL, password VARCHAR(50) NOT NULL, enabled boolean )ENGINE=InnoDB DEFAULT CHARSET=utf8;
USERS_ROLES
It contains user roles (authorities).
CREATE TABLE USERS_ROLES ( user_role_id INT(10) UNSIGNED NOT NULL PRIMARY KEY, user_id INT(10) UNSIGNED NOT NULL, authority VARCHAR(45) NOT NULL, FOREIGN KEY (user_id) REFERENCES users (user_id) )ENGINE=InnoDB DEFAULT CHARSET=utf8;
Create some sample users and their Roles in database.
INSERT INTO users (user_id, user_name, password, enabled) VALUES (101, 'mukesh', 'mukesh123', true), (102, 'ravi', 'ravi123', true), (103, 'amit', 'amit123', true); INSERT INTO user_roles (user_role_id, user_id, authority) VALUES (101, 101, 'ROLE_ADMIN'), (102, 102, 'ROLE_ADMIN'), (103, 103, 'ROLE_ADMIN');
Step 2: Create Dynamic Web Project in Maven
Create a Dynamic Web Project named "SpringSecurityCustomLoginForm" by using maven. Please refer Spring MVC Form Handling Example if you are not familier with the steps.
Step 3: Update project dependencies in pom.xml
Include spring security dependency in the pom.xml.
<?xml version="1.0" encoding="UTF-8"?> <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.jwt</groupId> <artifactId>SpringSecurityDBAuthentication</artifactId> <name>SpringSecurityDBAuthentication</name> <packaging>war</packaging> <version>1.0.0-BUILD-SNAPSHOT</version> <properties> <spring.version>4.2.1.RELEASE</spring.version> <security.version>4.0.3.RELEASE</security.version> <jdk.version>1.8</jdk.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <!-- Spring Security --> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-core</artifactId> <version>${security.version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>${security.version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>${security.version}</version> </dependency> <!-- Servlet --> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <!-- Spring JDBC dependency --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>3.2.8.RELEASE</version> </dependency> <!-- Mysql connector dependency --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.34</version> </dependency> </dependencies> <build> <finalName>SpringSecurityDBAuthentication</finalName> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>${jdk.version}</source> <target>${jdk.version}</target> </configuration> </plugin> </plugins> </build> </project>
Step 4: Add security specific configuration in spring-security.xml
Let's create spring-security.xml under WEB-INF folder to add spring security configuration.
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-4.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.0.xsd"> <http auto-config="true" use-expressions="true"> <intercept-url pattern="/welcome/**" access="permitAll" /> <intercept-url pattern="/admin*" access="hasRole('ROLE_ADMIN')" /> <form-login login-page="/login" default-target-url="/admin" authentication-failure-url="/loginError" /> <logout logout-success-url="/logout" logout-url="/j_spring_security_logout" /> <csrf disabled="true" /> </http> <authentication-manager> <authentication-provider> <jdbc-user-service data-source-ref="dataSource" users-by-username-query="select user_name, password, enabled from USERS where user_name = ?" authorities-by-username-query="select u.user_name, ur.authority from USERS u, USER_ROLES ur where u.user_id = ur.user_id and u.user_name = ? " /> </authentication-provider> </authentication-manager> <beans:bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <beans:property name="driverClassName" value="com.mysql.jdbc.Driver" /> <beans:property name="url" value="jdbc:mysql://localhost:3306/spring" /> <beans:property name="username" value="root" /> <beans:property name="password" value="mukesh" /> </beans:bean> </beans:beans>
In the above security configuration xml file we have used <form-login> element. If an user tries to access any secured URL, then user will be authenticated with custom login page based on the configuration of <form-login> element.
Lets understand meaning of each attribute of form-login tag
- login-page : we need to specify the path of controller which returns the custom login page.In our case URL is /login.
- default-target-url : default-target-url specifies the path of a method in controller where it has to go after successful authentication, in our case it is /admin.
- authentication-failure-url : authentication-failure-url specifies the path of a method in controller where it has to go if authentication is failed.In our case it is /loginError .
intercept-url configures for particular URL pattern what kind of security is configured. For example: If http request url has pattern /hello*(hello.jsp,helloworld.jsp,helloworld.html etc), it will be accessed by "ROLE_ADMIN" role only.
The tag <authentication-manager> processes the authentication information; <authentication-provider> defines the credential information and the roles given to each user (authentication information). In our previous post we declared users and their roles in security configuration xml file but in this example instead of declaring the users in this file we are fetching users and their roles from database.
Here we created a datasource to connect to MySQL database.
Next in the authentication-provider tag we specified:
- datasource to use by Spring Security for authentication and authorization mechanisms.
- Specified the query to execute by Spring Security for user authentication.
- Specified the query to execute by Spring Security for user authorization.
Step 5: Create Spring configuration file
Let's create spring-mvc.xml under WEB-INF folder.
spring-mvc.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" 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.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="com.jwt.spring" /> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix"> <value>/WEB-INF/views/</value> </property> <property name="suffix"> <value>.jsp</value> </property> </bean> </beans>
Step 6: Creating Spring MVC Controller
Create the LoginController class to map the requests.
package com.jwt.spring; import java.security.Principal; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; 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 = "/") public String welcome(Model model) { model.addAttribute("name", "Home Page"); model.addAttribute("description", "unsecured page !"); return "home"; } @RequestMapping("/admin") public String admin(Model model, Principal principal) { String loggedInUserName = principal.getName(); model.addAttribute("user", loggedInUserName); model.addAttribute("name", "Spring Security Custom Login Demo"); model.addAttribute("description", "Protected page !"); return "admin"; } @RequestMapping(value = "/login", method = RequestMethod.GET) public String login(ModelMap model) { return "login"; } @RequestMapping(value = "/logout", method = RequestMethod.GET) public String logout(ModelMap model) { model.addAttribute("message", "You have successfully logged off from application !"); return "logout"; } @RequestMapping(value = "/loginError", method = RequestMethod.GET) public String loginError(ModelMap model) { model.addAttribute("error", "true"); return "login"; } }
As you can see, this controller is designed to handle five requests
- "/": the request to the application’s context root, or app landing page.
- "/admin": the request to the administrator page, which will be secured by Spring security.
- "/login": the request to the login page.
- "/loginError": authentication-failure-url.
- "/logout": logout-url.
admin() method of LoginController.java will handle all the request with /admin url. In this method we have used Model and Principal Object as a parameter.The Model is container for our application data. Principal object is used to get current logged in username. It is set by Spring security framework.
Step 7: Creating View Page of the application
a)Creating Index Page(home.jsp)
Create a directory called views under the /WEB-INF directory, then create an home.jsp file with the following HTML code.
<html> <body> <h1>Name : ${name}</h1> <h1>Description : ${description}</h1> <br> <h2> <a href="admin">Click here for Admin Area</a> </h2> </body> </html>
b)Creating Admin Page(admin.jsp)
Next, create an admin.jsp file under the /WEB-INF/views directory with the following code.
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <%@page session="true"%> <html> <body> <h1>Name : ${name}</h1> <h1>Description : ${description}</h1> <c:if test="${pageContext.request.userPrincipal.name != null}"> <h2> Welcome : ${user} | <a href="<c:url value="/j_spring_security_logout" />"> Logout</a> </h2> </c:if> </body> </html>
c)Creating login Page(login.jsp)
Next, create an login.jsp file under the /WEB-INF/views directory with the following code.
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <html> <head> <title>Custom Login Page</title> <style> .errorblock { color: #ff0000; background-color: #ffEEEE; } </style> </head> <body onload='document.loginForm.j_username.focus();'> <h3>Custom Login Page</h3> <% String errorString = (String) request.getAttribute("error"); if (errorString != null && errorString.trim().equals("true")) { out.println("<span class=\"errorblock\">Incorrect login name or password. Please try again"); } %> <form name='loginForm' action="<c:url value='login' />" " method='POST'> <table> <tr> <td>User:</td> <td><input type='text' name='username' value=''></td> </tr> <tr> <td>Password:</td> <td><input type='password' name='password' /></td> </tr> <tr> <td><input name="submit" type="submit" value="submit" /></td> <td><input name="reset" type="reset" /></td> </tr> </table> </form> </body> </html>
d)Creating logout Page(logout.jsp)
Next, create an logout.jsp file under the /WEB-INF/views directory with the following code.
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <%@page session="true"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>logout page</title> </head> <body> <h1>You have successfully logged off from application !</h1> </body> </html>
Final Project Structure
Test the Sample Application
Right click on the Project and then Run As -> Run on Server -> Select Tomcat 8.0 and then Finish.
type the following URL in your browser:
http://localhost:8080/SpringSecurityDBAuthentication/
You should see the index page looks like as following
On this page, click on the link "Click here for Admin Area" which will open the URL http://localhost:8080/SpringSecurityDBAuthentication/admin. As this URL is secured with Spring Security ,Spring security redirects you to the custom login page like this:
If username/password is wrong, authentication failed, display custom error messages as shown below.
Now, enter the correct username and password Spring security takes you to the admin page whose URL is http://localhost:8080/SpringSecurityDBAuthentication/admin.
On clicking 'Logout' link, user be logged out and forwarded to logout page.
You can download the source code of the example by clicking on the Download link below.
Related Articles