In this post, we will create a simple application, that displays Spring Security form-based authentication. The user is prompted to give his credentials. If they are correct, he enters to the dashboard. If they are wrong, he gets an error message. Dashboard is accessible only by authenticated users.
The full github repository can be found here: SpringSecuritySimpleAuthentication
Technologies Used
- Spring MVC
- Spring Security
- Hibernate
- MySQL
- Maven
- Jsp , Jstl
Database
We need one table in our database which will contain the users. We create only a username and a password field and perform our first insert. The password value equals with ‘123456’ but is encrypted using the bcrypt function, in order to comply with our security configuration that we will setup later.
queries.sql
CREATE TABLE user ( user_id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT , username VARCHAR(400) NOT NULL, password VARCHAR(400) NOT NULL ); INSERT INTO user (username, password) VALUES ("testUser", "$2a$10$04TVADrR6/SPLBjsK0N30.Jf5fNjBugSACeGv1S69dZALR7lSov0y");
Implementation
The first step is to add Spring Security to the Maven configuration.
pom.xml :
<dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>3.2.5.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>3.2.5.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-taglibs</artifactId> <version>3.2.5.RELEASE</version> </dependency>
Next the must initialize the Spring Security Filter Chain, which will enable the security implementation. In web.xml file we configure the filter, and the location of the spring-security.xml file.
web.xml :
<!--Spring Security Filter--> <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>
Spring Security Configuration
Now Spring Security is activated, and we need to configure it. We will do that in another xml file, which we will name spring-security.xml. We have already setup the file’s location in the web.xml.
In this file we configure the following :
- The package where our functionality that needs to be secured is.
- The urls that will be secured.
- The roles/authorities/permissions.
- The authorization failure url.
- The login page url.
- The default target url, in which the authorized users will be redirected after the successfull login.
- The logout success url.
- The names of the username and password parameters that Spring Security expects.
- The authentication manager.
- The User Details Service which we inject into the authentication provider.
- The encryption type that will be used for the password.
spring-security.xml :
<?xml version="1.0" encoding="UTF-8"?> <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" xmlns:context="http://www.springframework.org/schema/context" 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://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <context:component-scan base-package="com.antogeo.*"/> <http auto-config="true" use-expressions="true"> <intercept-url pattern="/dashboard**" access="hasAuthority('USER')" /> <form-login login-page="/login" default-target-url="/dashboard" authentication-failure-url="/login?error" username-parameter="username" password-parameter="password" /> <logout logout-success-url="/login?logout" /> </http> <authentication-manager> <authentication-provider user-service-ref="userDetailsService" > <password-encoder hash="bcrypt" /> </authentication-provider> </authentication-manager> </beans:beans>
User Details Service
The last but also very important step is to implement the User Details Service interface which we have already injected into the Authentication Provider. We override the loadUserByUsername method and inside it we get the user from the database using the username, collect the authorities, assign them to the user object and build the spring security User object.
CustomUserDetailsService.java :
@Service("userDetailsService") public class CustomUserDetailsService implements UserDetailsService { @Autowired private UserDao userDao; /**Method that returns a UserDetails object * * @param username * @return * @throws org.springframework.security.core.userdetails.UsernameNotFoundException */ @Transactional(readOnly = true) @Override public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException { //get user from the database, via Hibernate com.antogeo.entity.User user = userDao.getUserByUsername(username); //Collect User authorities List<GrantedAuthority> authorities = buildUserAuthority(user); //Build Spring Security User User springSecurityUser = buildUserForAuthentication(user, authorities); return springSecurityUser; } /**Build the Spring Security User. * * @param user * @param authorities * @return */ private User buildUserForAuthentication(com.antogeo.entity.User user, List<GrantedAuthority> authorities) { return new User(user.getUsername(), user.getPassword(), true, true, true, true, authorities); } private List<GrantedAuthority> buildUserAuthority(com.antogeo.entity.User user) { Set<GrantedAuthority> authoritiesSet = new HashSet<GrantedAuthority>(); //Use one role for all authoritiesSet.add(new SimpleGrantedAuthority("USER")); //Transform the Set into List List<GrantedAuthority> result = new ArrayList<GrantedAuthority>(authoritiesSet); return result; } public UserDao getUserDao() {return userDao;} public void setUserDao(UserDao userDao) {this.userDao = userDao;} }
The Login Page
The only action we have to make in the login.jsp page, is to set the post url to “/j_spring_security_check”. By doing this, the form post request will be sent through the security filter chain and end up to the default-target-url or the authentication-failure-url based on the result.
login.jsp :
<c:url var="post_url" value="/j_spring_security_check" /> <form:form action ="${post_url}" method='POST' commandName="loginForm" cssClass="form-signin"> <h2 class="form-signin-heading">Welcome!</h2> <label for="inputUsername" class="sr-only">Username</label> <form:input path="username" id="inputUsername" placeholder="Username" size="30" cssClass="form-control" /> <form:errors path="username" cssClass="error"/> <label for="inputPassword" class="sr-only">Password</label> <form:password path="password" id="inputPassword" placeholder="Password" size="30" cssClass="form-control"/> <form:errors path="password" cssClass="error"/> <button class="btn btn-lg btn-primary btn-block" type="submit">Login</button> </form:form>
Use Case
Let’s run and try the example now. We use the tomcat’s url following by our apps name and the login page opens. In the login form, we try to enter the app with wrong credentials and we see the error message.
After that we use the correct username and password and click login.
We get redirected to the Dashboard page. There we can see our username which is displayed using the Principal object and the logout button.
By clicking the logout button our session gets invalidated and we are get redirected back to the login page with a message.
Installation and Run
- Run the database/queries.sql queries to your database.
- Add your database credentials to resources/db.properties file.
- Build the project using mvn clean install
- Run the application using tomcat
Test Credentials
Username | Password |
testUser | 123456 |