Spring boot with Spring Security and NoSQL
In the previous post we set up a spring security configuration by providing custom queries for user and authority retrieval from an sql database.
Nowadays many modern applications utilize NoSQL databases. Spring security does not come with an out of the box solution for NoSQL databases.
In those case we need to provide a solution by Implementing a Custom UserDetailsService.
We will use a MongoDB Database for this example. I will use a docker image, however it is as easy to set up a mongodb database by downloading it from the official website.
Those are some commands to get started with docker and mongodb (feel free to ignore them if you don’t use docker)
#pull the mongo image
docker pull mongo
#create a mongo container
docker run --name some-mongo -d mongo
#get the docker container id
docker ps
#get the containers ip
docker inspect --format '{{ .NetworkSettings.IPAddress }}' $CID
#connection using the ip retrieved
mongo $mongodb_container_ipThen we will write a simple initialization script called createuser.js. The script creates an document containing user information such as username password and authorities.
use springsecurity
db.users.insert({"name":"John","surname":"doe","email":"[email protected]","password":"cleartextpass","authorities":["user","admin"]})We will use mongo cli to execute it.
mongo 172.17.0.2:27017 < createuser.js
In order to use spring security with mongodb we need to retrieve the user information from the users collection.
First step is to add the mongodb dependencies to our gradle file, including the mongodb driver. Note that we will use a profile called ‘customuserdetails’.
group 'com.gkatzioura'
version '1.0-SNAPSHOT'
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.4.0.RELEASE")
}
}
apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'spring-boot'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile("org.springframework.boot:spring-boot-starter-web")
compile("org.thymeleaf:thymeleaf-spring4")
compile("org.springframework.boot:spring-boot-starter-security")
compile("org.mongodb:mongo-java-driver:1.3")
compile("org.slf4j:slf4j-api:1.6.6")
compile("ch.qos.logback:logback-core:1.1.7")
compile("ch.qos.logback:logback-classic:1.1.7")
testCompile "junit:junit:4.11"
}
bootRun {
systemProperty "spring.profiles.active", "customuserdetails"
}Then we shall create a mongodb connection bean.
package com.gkatzioura.spring.security.config;
import com.mongodb.Mongo;
import com.mongodb.MongoClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
/**
* Created by gkatzioura on 9/27/16.
*/
@Configuration
@Profile("customuserdetails")
public class MongoConfiguration {
@Bean
public MongoClient createConnection() {
//You should put your mongo ip here
return new MongoClient("172.17.0.2:27017");
}
}Then we will create a custom user details object.
package com.gkatzioura.spring.security.model;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.List;
/**
* Created by gkatzioura on 9/27/16.
*/
public class MongoUserDetails implements UserDetails{
private String username;
private String password;
private List<GrantedAuthority> grantedAuthorities;
public MongoUserDetails(String username,String password,String[] authorities) {
this.username = username;
this.password = password;
this.grantedAuthorities = AuthorityUtils.createAuthorityList(authorities);
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return grantedAuthorities;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}Next step we will add a custom UserDetailsService retrieving user details through the mongodb database.
package com.gkatzioura.spring.security.service;
import com.gkatzioura.spring.security.model.MongoUserDetails;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Filters;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* Created by gkatzioura on 9/27/16.
*/
public class CustomerUserDetailsService implements UserDetailsService {
@Autowired
private MongoClient mongoClient;
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
MongoDatabase database = mongoClient.getDatabase("springsecurity");
MongoCollection<Document> collection = database.getCollection("users");
Document document = collection.find(Filters.eq("email",email)).first();
if(document!=null) {
String name = document.getString("name");
String surname = document.getString("surname");
String password = document.getString("password");
List<String> authorities = (List<String>) document.get("authorities");
MongoUserDetails mongoUserDetails = new MongoUserDetails(email,password,authorities.toArray(new String[authorities.size()]));
return mongoUserDetails;
}
return null;
}
}Final step is to provide a spring security configuration using the custom UserDetailsService we implemented previously.
package com.gkatzioura.spring.security.config;
import com.gkatzioura.spring.security.service.CustomerUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Profile;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
/**
* Created by gkatzioura on 9/27/16.
*/
@EnableWebSecurity
@Profile("customuserdetails")
public class CustomUserDetailsSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public UserDetailsService mongoUserDetails() {
return new CustomerUserDetailsService();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
UserDetailsService userDetailsService = mongoUserDetails();
auth.userDetailsService(userDetailsService);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/public").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.permitAll()
.and()
.logout()
.permitAll();
}
}To run the application issue
gradle bootRun
You can find the source code on github
| Reference: | Spring boot with Spring Security and NoSQL from our JCG partner Emmanouil Gkatziouras at the gkatzioura blog. |





Tried to login running this example and got the following exception:
java.lang.IllegalArgumentException: Invalid salt