MongoDB Spring Data and Spring Security with custom UserDetailsService
In a earlier write up we saw how we can setup basic spring security. In this one we will see how to make a custom UserDetailsService instead of using the credentials in the configuration file. We will use MongoDB as a datasource.
We will use the project from the Spring Security project we created earlier and add the MongoDB config details and user service to provide the capability. See my previous blog for setting up MongoDB Spring Data.
Add the following to the mongo-config.xml file in the location WEB-INF/mongo/
<?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:mongo="http://www.springframework.org/schema/data/mongo" xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd"> <!-- Default bean name is 'mongo' --> <mongo:mongo host="localhost" port="27017" /> <bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate"> <constructor-arg ref="mongo" /> <constructor-arg name="databaseName" value="codesilo" /> </bean> <!-- To translate any MongoExceptions thrown in @Repository annotated classes --> <context:annotation-config /> </beans>
Add the Spring Data dependencies in the pom.xml file
<dependency> <groupId>org.mongodb</groupId> <artifactId>mongo-java-driver</artifactId> <version>2.7.2</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-mongodb</artifactId> <version>1.1.0.M1</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.2</version> </dependency>
Add the mongo-config.xml file to the contextConfigLocation on web.xml. The context param will look like this
<context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring/root-context.xml /WEB-INF/spring/security-app-context.xml /WEB-INF/mongo/mongo-config.xml </param-value> </context-param>
We will now create a CustomUserDetailsService class that implements UserDetailsService. We will add the implementation of loadUserByUsername method. The CustomUserDetailsService is as follows. The getUserDetail method queries to get the user object from MongoDB (it is assumed that the data is already there).
package com.wordpress.codesilo.model; import java.util.ArrayList; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; public class CustomUserDetailsService implements UserDetailsService { private MongoTemplate mongoTemplate; public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = getUserDetail(username); System.out.println(username); org.springframework.security.core.userdetails.User userDetail = new org.springframework.security.core.userdetails.User(user.getUsername(),user.getPassword(),true,true,true,true,getAuthorities(user.getRole())); return userDetail; } @Autowired public void setMongoTemplate(MongoTemplate mongoTemplate) { this.mongoTemplate = mongoTemplate; } public List<GrantedAuthority> getAuthorities(Integer role) { List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>(); if (role.intValue() == 1) { authList.add(new SimpleGrantedAuthority("ROLE_USER")); authList.add(new SimpleGrantedAuthority("ROLE_ADMIN")); } else if (role.intValue() == 2) { authList.add(new SimpleGrantedAuthority("ROLE_USER")); } return authList; } public User getUserDetail(String username){ MongoOperations mongoOperation = (MongoOperations)mongoTemplate; User user = mongoOperation.findOne( new Query(Criteria.where("username").is(username)), User.class); System.out.println(user.toString()); return user; } }
We will also create a user POJO which is as follows.
package com.wordpress.codesilo.model; public class User { public String username; public String password; public String firstName; public String lastName; public String email; public int role; public User() { } public int getRole() { return role; } public void setRole(int role) { this.role = role; } public User(String username, String password, String firstName, String lastName) { this.username = username; this.password = password; this.firstName = firstName; this.lastName = lastName; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } @Override public String toString(){ return "First Name:" + this.firstName + " Last Name:" + this.lastName + " Username:" + this.username ; } }
As a last step we will change the security-app-context.xml to have the customUserDetailsService bean and user-service-ref in the authentication provider.
<beans:bean id="customUserDetailsService" class="com.wordpress.codesilo.model.CustomUserDetailsService"/> <authentication-manager> <authentication-provider user-service-ref="customUserDetailsService"/> </authentication-manager>
Now when we try to login to the application it will use the MongoDB to get the credentials.
We can also use the Security Context to get the principal and display the username and roles on home.jsp.
Add this to the pom dependencies.
<dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-taglibs</artifactId> <version>${org.springframework-version}</version> </dependency>
Add the following taglib to the home.jsp page
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
and the following content on the jsp page to display the data.
<sec:authorize access="isAuthenticated()"> Username: <sec:authentication property="principal.username" /> Role: <sec:authentication property="principal.authorities"/> </sec:authorize>
Reblogged this on Agile Mobile Developer.
Concise and practical example, it helped me to understand easily the general concept and to start my own implementation. Thank you! +1
I’m trying to fold this example into my existing app, but I keep getting the error:
java.lang.IllegalAccessError: tried to access method org.springframework.core.GenericTypeResolver.getTypeVariableMap(Ljava/lang/Class;)Ljava/util/Map; from class org.springframework.data.util.ClassTypeInformation
Ryan, the problem could be because of an incorrect jar or an incorrect class import. Can you provide some more information on where you are getting this error?
I figured it out. The project has multiple modules with their own POMs, and there were conflicting versions with one of the Spring artifacts that two of the modules were including.
Hi,
Could You share any download link? I’m trying to integrate your example with my code, but no success. I can’t login. MongoTemplate reference in CustomUserDetailsService is null. Best regards
Where does this check to make sure that the entered password is equal to the password stored in mongo? As best I can tell, this configuration would let anyone log in who guesses the username of a user in the system.
Is the complete project available as a download? It would be great to be able to look the whole thing over. Thanks.
Thanks for the comment Tim. I don’t have the project ready right now. Will make it a point to upload it in the future.
2 years later and still relevant. Thanks.
Hi,
I got the following error
[org.springframework.web.servlet.PageNotFound] (http-localhost-127.0.0.1-8080-1) No mapping found for HTTP request with URI [/securityspring/j_spring_security_check] in DispatcherServlet with name ‘appServlet’
please help me.
it is not working for me
its not logging in
and it doest say any error