Spring Boot REST API integration with Salesforce

December 26, 2017 1 comment

This blog covers ways to integrate with Salesforce using REST API.
We will use Spring Boot to authenticate (get access_token and instance_url from Salesforce). Although this can be done using a plain Java Application, we will assume that a real world use case requires a web application to call the services.

The Spring Boot application we create will have have a service exposed through a plain HTTP GET call. The application will call the authentication on Salesforce and use the response from the authentication to make a call to get some data from an Object in Salesforce. In an actual implementation the authentication can be cached and need not be called each time before making an API call to Salesforce. The cache can be refreshed more frequently than the timeout on the session, or, it can be refreshed after the authentication failure exception because of the timeout.

1. Creating the Spring Boot Application

The easiest way to create a starter project is to use Spring STS (https://spring.io/tools). You could also follow the steps here if you don’t want to use STS and want to use some other IDE. (https://spring.io/guides/gs/spring-boot/)

To create the Project in STS, run the install of STS and create a new workspace for yourself. Click on File | New | Spring Starter Project
In the new Window, provide the name of the Project (for this example we give the name SalesforceRESTAPI), group name (com.wordpress.codesilo.salesforce), Artifcat (keep it as is – should have autofilled the name of the Project above), package (com.wordpress.codesilo.salesforce). These can be changed according to your namespace.

Screen Shot 2017-12-22 at 3.53.54 PM

In the next step select “Web” from the dependencies and proceed to create the project. To start the instance, right click on the project and to to Run As | Spring Boot App. This should start the server and you should be able to see the progress in the console. On the browser, go to http://localhost:8080 and you should be presented with a page with a “Whitelabel Error Page” header

Screen Shot 2017-12-22 at 6.08.34 PM

So far, we have created a default Spring Boot Application with “Web” added as a dependency (spring-boot-starter-web dependency). The next step is to add a simple Controller and a class to call the Salesforce API. But before we do that, we will create an object and create an “App” on a Salesforce instance that we want to connect to.

2. Setting up Salesforce Connected App

We will use a new Salesforce Developer Edition Org. Sign up for on here (https://developer.salesforce.com/signup)
Once logged in, go to the Classic mode in Salesforce and then to Setup | Create | Apps

Click “New” on Connected Apps and fill the form in the next page. Add the Connected App Name, API Name and email as required. We don’t need these fields for the integration however. Click on “Enable OAuth Settings” and add the following as the Callback Url “http://localhost”. For selected OAuth Scopes, choose Full Access for now. Change these settings as required later. Save the changes – it should take a few minutes for this to take effect.
In order to create Connected App in Salesforce Lightning see here https://help.salesforce.com/articleView?id=connected_app_create.htm&type=5

You should now be able to get the following information from the Connected App created – Consumer Key, Consumer Secret. If you don’t have a Security Token for your account yet (had you created the Developer Edition instance earlier), go to, “My Profile” and in the Quick Find look for “Reset Security Token”. This should send you a security token to your email. Note: If the “Login IP” ranges for your Profile has been set appropriately, you might not require the security token.

3. Create the Controller on Spring Boot App

We will create a new Class for the controller in the following package. com.wordpress.codesilo.salesforce.controller. The class will take a simple GET request and return the text “home” in the body.

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class SalesforceAPIController {

@RequestMapping("/home")
public String getSalesforceObject(){
return "home";
}

}

4. Create the class to make the API call to Salesforce

Create the model class to store the response from the authentication on Salesforce

package com.wordpress.codesilo.salesforce.model;

public class AuthenticationResponse {

private String access_token;
private String instance_url;
private String token_type;
private String issued_at;

public String getAccess_token() {
return access_token;
}
public void setAccess_token(String access_token) {
this.access_token = access_token;
}
public String getInstance_url() {
return instance_url;
}
public void setInstance_url(String instance_url) {
this.instance_url = instance_url;
}
public String getToken_type() {
return token_type;
}
public void setToken_type(String token_type) {
this.token_type = token_type;
}
public String getIssued_at() {
return issued_at;
}
public void setIssued_at(String issued_at) {
this.issued_at = issued_at;
}

@Override
public String toString() {
return "AuthenticationResponse [access_token=" + access_token + ", instance_url=" + instance_url
+ ", token_type=" + token_type + ", issued_at=" + issued_at + "]";
}
}

We now create a class (SalesforceAPIService) to make the REST API calls to Salesforce. We first have to make a call to authenticate with Salesforce. (Replace the parameters marked between ** below with appropriate values of your instance/Org)

public AuthenticationResponse login(){
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, String> params= new LinkedMultiValueMap<String, String>();

params.add("username", ** USERNAME HERE **);
params.add("password", ** CONCATENATION OF PASSWORD AND SECURITY TOKEN HERE ** );
params.add("client_secret", ** CONSUMER SECRET FROM CONNECTED APP HERE ** );
params.add("client_id", ** CONSUMER KEY FROM CONNECTED APP HERE **);
params.add("grant_type","password");

HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(params, headers);

RestTemplate restTemplate = new RestTemplate();
ResponseEntity response = restTemplate.postForEntity("https://login.salesforce.com/services/oauth2/token", request, AuthenticationResponse.class);
return response.getBody();;
}

The response from the call above has the token and the instance url that we use in the next call to get the data from an Object.

In the next API call, we will query the Account object to get the Id and Name from the object. We will assume that we will get a specific object by querying based on an Id. Use an Id from your Developer Edition to query and get a result. The response is going to be in json in the following format. Here is the sample:

{
"totalSize": 1,
"done": true,
"records": [
{
"attributes": {
"type": "Account",
"url": "/services/data/v22.0/sobjects/Account/001f4000005WEvpAAG"
},
"Id": "001f4000005WEvpAAG",
"Name": "Burlington Textiles Corp of America"
}
]
}

We need to create the Java Objects to store the response. In order to create the POJO, we can use http://www.jsonschema2pojo.org/

The following classes are to be created for the POJOs

public class AccountResponse {

private int totalSize;
private boolean done;
private List records;

public int getTotalSize() {
return totalSize;
}
public void setTotalSize(int totalSize) {
this.totalSize = totalSize;
}
public boolean isDone() {
return done;
}
public void setDone(boolean done) {
this.done = done;
}
public List getRecords() {
return records;
}
public void setRecords(List records) {
this.records = records;
}

}
public class Attribute{
private String type;
private String url;

public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}

public class Account {
@JsonProperty("attributes")
private Attribute attributes;
@JsonProperty("Id")
private String id;
@JsonProperty("Name")
private String name;
@JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();

@JsonProperty("attributes")
public Attribute getAttributes() {
return attributes;
}

@JsonProperty("attributes")
public void setAttributes(Attribute attributes) {
this.attributes = attributes;
}

@JsonProperty("Id")
public String getId() {
return id;
}

@JsonProperty("Id")
public void setId(String id) {
this.id = id;
}

@JsonProperty("Name")
public String getName() {
return name;
}

@JsonProperty("Name")
public void setName(String name) {
this.name = name;
}

@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}

@JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}

In the class we use to call the API to Salesforce add the following method to get the data from the Account Object.

public AccountResponse getAccountData(String accessToken, String instanceUrl){
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", "Bearer " + accessToken);
MultiValueMap<String, String> params= new LinkedMultiValueMap<String, String>();

HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(params, headers);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity salesforceTestData = restTemplate.exchange(instanceUrl+ "/services/data/v22.0/query?q=select Id, Name from Account where Id = '001f4000005WEvp'", HttpMethod.GET, request, AccountResponse.class);
log.info(salesforceTestData.getBody().getRecords().get(0).getName());
return salesforceTestData.getBody();
}

The accessToken and instanceUrl parameters are the values from the AuthenticationResponse above.

5. Remap the Controller class to call the service

We will mark the SalesforceAPIService class with the @Component annotation so that it is available for autowiring in the controller.
We now call the SalesforceAPIService from the controller and pass the response to the service on Spring.

@RequestMapping("/account")
public AccountResponse getSalesforceAccount(){
AuthenticationResponse authenticationResponse = salesforceAPIService.login();
AccountResponse accountResponse = salesforceAPIService.getAccountData(authenticationResponse.getAccess_token(), authenticationResponse.getInstance_url());
return accountResponse;
}

Once we start the server and call go the following url (GET request) http://localhost:8080/account , we get the response from the service call to Salesforce.

Screen Shot 2017-12-25 at 10.45.50 PM.png

Drools 6 Decision Table with Spring MVC

June 8, 2015 Leave a comment

Drools is the Java Rules engine from Jboss. This article covers on how to use the Drools Decision Table using excel sheets with a Spring MVC project.

To start with, create a basic Spring MVC project using Spring STS. Add the following dependencies for drools on the pom file generated by the template in STS.


<dependency>
 <groupId>org.drools</groupId>
 <artifactId>drools-core</artifactId>
 <version>6.2.0.Final</version> 
 </dependency>
 <dependency>
 <groupId>org.drools</groupId>
 <artifactId>drools-compiler</artifactId>
 <version>6.2.0.Final</version> 
 </dependency>
 <dependency>
 <groupId>org.kie</groupId>
 <artifactId>kie-spring</artifactId>
 <version>6.2.0.Final</version> 
 </dependency>
 <dependency>
 <groupId>org.kie</groupId>
 <artifactId>kie-api</artifactId>
 <version>6.2.0.Final</version> 
 </dependency>
 <dependency>
 <groupId>org.kie</groupId>
 <artifactId>kie-internal</artifactId>
 <version>6.2.0.Final</version> 
 </dependency>

Create a spring-context.xml file in the following location /WEB-INF/spring with the following content


<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:kie="http://drools.org/schema/kie-spring"
 xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://drools.org/schema/kie-spring http://drools.org/schema/kie-spring.xsd">

<kie:kmodule id="kmodule1">
 <kie:kbase name="kbase1" packages="com.codesilo.wordpress.rules">
 <kie:ksession name="ksession1" type="stateless" />
 </kie:kbase>
 </kie:kmodule>

<beans:bean id="kiePostProcessor"
 class="org.kie.spring.KModuleBeanFactoryPostProcessor" />

</beans:beans>

The package here “com.codesilo.wordpress.rules” denotes the location where the decision tables are located. We will create that one we have created the POJO.

Add the /WEB-INF/spring/spring-context.xml on the contextConfigLocation in web.xml. The contextConfigLocation should now look like


<context-param>
 <param-name>contextConfigLocation</param-name>
 <param-value>/WEB-INF/spring/root-context.xml
 /WEB-INF/spring/root-context.xml</param-value>
 </context-param>

We create the POJO named as RouteAttribute in the following package com.wordpress.codesilo.model. This object will be used as the fact for the rules defined,


package com.wordpress.codesilo.model;

public class RouteAttribute {

private String country;
 private String state;
 private boolean newEntry;
 
 //Result
 private String ansOnRule;

public String getCountry() {
 return country;
 }

public void setCountry(String country) {
 this.country = country;
 }

public String getState() {
 return state;
 }

public void setState(String state) {
 this.state = state;
 }

public boolean isNewEntry() {
 return newEntry;
 }

public void setNewEntry(boolean newEntry) {
 this.newEntry = newEntry;
 }

public String getAnsOnRule() {
 return ansOnRule;
 }

public void setAnsOnRule(String ansOnRule) {
 this.ansOnRule = ansOnRule;
 }
 
}


Now we create the decision table (Rules.xls) on the following location com.codesilo.wordpress.rules

Rules

The file can also be downloaded from here.

As a last step, we change HomeController.java to fire the rules. This can be later moved to any controller/MVC flow. We inject the KieBase object by the following..

@Autowired
private KieBase kieBase;

Add the following in the default method on the HomeController class.


RouteAttribute routingComponent = new RouteAttribute();

routingComponent.setCountry("US");

StatelessKieSession statelessKieSession =  kbase.newStatelessKieSession();

statelessKieSession.execute(routingComponent);

Now, if we bring up the server, the default method is going to run and call the rules from above.

Monoprice 10x 6.25 Graphic Tablet on Ubuntu

June 1, 2015 Leave a comment

I recently purchased a 10×6.25 Monoprice Graphic tablet from Monoprice. (wasn’t at a spot to shell out the amount that Wacom was asking for it’s tablet). The tablet has lived upto my expectations so far but did not work as expected when I tried using it on Ubuntu 14.04.

Checking the output from lsusb shows that the graphic tablet is a rebranded UC Logic tablet. The following output shows on running lsusb on the terminal

Bus 005 Device 002: ID 5543:0781 UC-Logic Technology Corp.

Searching on Google showed me that UC Logic graphic tablets can be configured for Ubuntu by using the Wizardpen driver. The DIGImend drivers do not support it but they have a good article on how to configure these rebranded tablets. The steps below are just a summary of what I did based on the article found here (https://digimend.github.io/support/howto/drivers/wizardpen/)

  • Download the Wizardpen driver using the following command
wget https://launchpad.net/~doctormo/+archive/xorg-wizardpen/+files/xserver-xorg-input-wizardpen_0.8.1-0ubuntu3.tar.gz
  • Untar the driver that we downloaded above
tar xvzf xserver-xorg-input-wizardpen_0.8.1-0ubuntu3.tar.gz
  • Install the required dependencies from Wizardpen
sudo apt-get install build-essential xutils-dev xutils libx11-dev libxext-dev xautomation xinput xserver-xorg-dev autoconf libtool pkg-config
  • Go to the extracted Wizardpen dir above and run the following commands
$ ./autogen.sh --prefix=/usr
$ make
$ sudo make install
  • Go to the x.org.conf dir in usr/share/X11 and create the file 52-tablet.conf and add the following
Section "InputClass"
 Identifier "Tablet on WizardPen"
 #MatchIsTablet "on"
 MatchProduct "keyword"
 MatchDevicePath "/dev/input/event*"
 Driver "wizardpen"
 # Apply custom Options below.
EndSection
  • The keyword above needs to be replaced by the product id for your graphic tablet. In my case I ran the xinput list with the tablet plugged in and got the following in the output
↳ UC-LOGIC TWHA60 id=11 [slave pointer (2)]
⎜ ↳ UC-LOGIC TWHA60 id=12 [slave pointer (2)]
⎜ ↳ UC-LOGIC TWHA60 id=13 [slave pointer (2)]
  • Based on the output above, I changed the keyword in the conf and the conf now looked like this..
Section "InputClass"
 Identifier "Tablet on WizardPen"
 #MatchIsTablet "on"
 MatchProduct "UC-LOGIC TWHA60"
 MatchDevicePath "/dev/input/event*"
 Driver "wizardpen"
 # Apply custom Options below.
EndSection
  • After going through the above steps, my graphics tablet was detected but the stylus did not position itself correctly. I decided to try calibrating the tablet as suggested in the DIGImend pages. This requires to run a calibrate command for wizardpen from the “calibrate” dir in the downloaded wizardpen files. Before doing that we need to identify the tablet-event to be supplied to the command. In order to get it, run the following command in the terminal
ls /dev/input/by-id/
  • The output produced by the command above in my case was the following..
usb-UC-LOGIC_TWHA60-event-mouse
usb-UC-LOGIC_TWHA60-if01-event-mouse
usb-UC-LOGIC_TWHA60-if01-mouse
usb-UC-LOGIC_TWHA60-if02-event-kbd
usb-UC-LOGIC_TWHA60-mouse
  • Now run the following command to calibrate (needs to be run in the calibrate folder in wizardpen)
sudo ./wizardpen-calibrate /dev/input/by-id/usb-UC-LOGIC_TWHA60-if01-event-mouse

The command then prompts you to click on one of the corners on the graphic tablet using the stylus. Once clicked, it asks to click on the opposite end of the tablet. The full output after the clicks looks like follows.

Please, press the stilus at ANY
corner of your desired working area: ok, got 9,6

Please, press the stilus at OPPOSITE
corner of your desired working area: ok, got 2047,2047

According to your input you may put the following
lines into your XF86Config/X.Org configuration file:

 Driver "wizardpen"
 Option "Device" "/dev/input/by-id/usb-UC-LOGIC_TWHA60-event-mouse"
 Option "TopX" "9"
 Option "TopY" "6"
 Option "BottomX" "2047"
 Option "BottomY" "2047"
  • Apply this to the conf file created earlier. My conf file looked like this after the changes.
Section "InputClass"
 Identifier "Tablet on WizardPen"
 MatchIsTablet "on"
 MatchProduct "UC-LOGIC TWHA60"
 MatchDevicePath "/dev/input/event*"
 Driver "wizardpen"
 # Apply custom Options below.
 Option "Device" "/dev/input/by-id/usb-UC-LOGIC_TWHA60-event-mouse"
 Option "TopX" "7"
 Option "TopY" "18"
 Option "BottomX" "2047"
 Option "BottomY" "2047"
EndSection
  • Restarting the system should get the configuration in effect and the tablet should start working fine.

Spring MVC with Sitemesh 3

July 11, 2013 3 comments

In one of my earlier posts I showed some steps to create a project with Struts Tiles. This post mentions the steps to get a basic Spring MVC project running with Sitemesh 3. Sitemesh is a decorator framework. More information on Sitemesh 3 can be found here.

CREATING A BASIC SPRING MVC PROJECT

We will use the Spring Developer Toolsuite and Maven 3 for our project. Go to the toolsuite workspace and select File>New>Spring Template Project and choose Spring MVC Project from the list. Click yes on the download prompt. We will name the project as Sitemesh-SpringMVC and the package as com.wordpress.codesilo.

SETTING UP SITEMESH 3

Open the pom.xml of the Project and add the following dependency.

<dependency>
 <groupId>org.sitemesh</groupId>
 <artifactId>sitemesh</artifactId>
 <version>${sitemesh.version}</version>
</dependency>

Also add the following under properties

<sitemesh.version>3.0-alpha-2</sitemesh.version>

To the web.xml add the following

<filter>
 <filter-name>sitemesh</filter-name>
 <filter-class>org.sitemesh.config.ConfigurableSiteMeshFilter</filter-class>
</filter>
<filter-mapping>
 <filter-name>sitemesh</filter-name>
 <url-pattern>/*</url-pattern>
</filter-mapping>

Now create a sitemesh3.xml file in the web-inf folder of the project and add the decorator files mapping in the file like follows. We will create the decorator files next.

<sitemesh>
 <mapping path="/*" decorator="/WEB-INF/decorators/defaultDecorator.jsp"/>
 <mapping path="/user" decorator="/WEB-INF/decorators/userDecorator.jsp"/>
</sitemesh>

Create the decorators folder in your project and add the following  jsp files.

defaultDecorator.jsp

<html>
 <head>
 <title><sitemesh:write property='title'/></title>
 <sitemesh:write property='head'/>
 </head>

 <body>
 This is the default body in decorator:
 <sitemesh:write property='body'/>
 </body>
</html>

userDecorator.jsp

<html>
 <head>
 <title><sitemesh:write property='title'/></title>
 <sitemesh:write property='head'/>
 </head>

 <body>
 This is the decorator body in user:
 <sitemesh:write property='body'/>
 </body>
</html>

For the default decorator example we will use the home.jsp that was created as a part of the project template. For the user decorator, we will create a new view in the views folder. We will keep it very simple as follows.

user.jsp

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
 <title>User</title>
</head>
<body>
<h1>
 Hello User!
</h1>

<P> Welcome user ! </P>
</body>
</html>

We will also change the HomeController (created again as a part of the template) to add another method for the user request mapping. We will copy the method for the default request mapping and change it as follows. (Add the following code to the HomeConrtoller)

@RequestMapping(value = "/user", method = RequestMethod.GET)
public String user(Locale locale, Model model) {
 logger.info("Welcome home! The client locale is {}.", locale);
 return "user";
}

Now, if we run the application and try the following urls we will see the different results. Notice the content added by the decorators.

http://localhost:8080/codesilo/

SitemeshImage1

http://localhost:8080/codesilo/user

SitemeshImage2

ADDING POST TO SITEMESH3

I read somewhere that Sitemesh 3 had a bug that did not allow it to use POST on the requests. I tried to give it a shot. We will add a field on the default view to accept a name. We will send that as a POST to the user method and display that on the user view.

Here is User.java class.

package com.wordpress.codesilo;

public class User {

private String name;

public String getName() {
 return name;
 }

public void setName(String name) {
 this.name = name;
 }

}

The HomeController now looks like this:

 @RequestMapping(value = "/", method = RequestMethod.GET)
 public ModelAndView home(Locale locale, Model model) {
 logger.info("Welcome home! The client locale is {}.", locale);

 Date date = new Date();
 DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);

 String formattedDate = dateFormat.format(date);

 model.addAttribute("serverTime", formattedDate );

 return new ModelAndView("home", "command", new User());
 }

 @RequestMapping(value = "/user", method = RequestMethod.POST)
 public String user(Locale locale, @ModelAttribute("SpringWeb")User user, Model model) {
 logger.info("Welcome " + user.getName());
 model.addAttribute(user);
 return "user";
 }

We will change the home.jsp to

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<%@ page session="false" %>
<html>
<head>
 <title>Home</title>
</head>
<body>
<h1>
 Hello world!
</h1>

<P> The time on the server is ${serverTime}. </P>

<form:form method="POST" action="/codesilo/user">
 <form:input path="name" />
 <input type="submit" value="submit"/>
</form:form>

</body>
</html>

And, change user.jsp to

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
 <title>User</title>
</head>
<body>
<h1>
 Hello User!
</h1>

<P> Welcome ${user.name} ! </P>
</body>
</html>

Now if we call the urls we will see this..

http://localhost:8080/codesilo/

SitemeshImage3

Once we hit submit we will get the following

SitemeshImage4

It seems that the POST works too. Maybe it’s some specific case that I have not run into yet.

 

Hashing passwords in Spring Security

July 8, 2012 1 comment

Passwords should not be stored in clear text. It is a best practice to have a one way hash created before storing it. Spring Security provides a very easy way to accomplish this. Just add a passwordEncoder to the authentication-provider in the spring security configuration.
An easy way to this in Spring 3 and later is to have the following config. This uses a SHA-256 algorithm and the username is used to salt the hash.

<authentication-manager>
 <authentication-provider user-service-ref="customUserDetailsService">
 <password-encoder hash="sha-256">
 <salt-source user-property="username"/>
 </password-encoder>
 </authentication-provider>
 </authentication-manager>

In order to use hashed passwords, it becomes necessary that we store them hashed too. In order to this in the application, we can have the SignUp class hash the passwords before storing it in the datasource. (in our case MongoDB).
Here is an excerpt from my class …

@RequestMapping(value = "/signUp", method = RequestMethod.POST)
 public String home(Locale locale, Model model, @RequestParam("username") String username, @RequestParam("password") String password, @RequestParam("firstName") String firstName, @RequestParam("lastName") String lastName) {
 MongoOperations mongoOperation = (MongoOperations)mongoTemplate;
 User user = new User(username, passwordEncoder.encodePassword(password, username), firstName, lastName);
 user.setRole(2);

 logger.debug(user.toString());
 mongoOperation.save(user, "user");
 logger.debug("Adding the user to the database");
 model.addAttribute("user", user );
 return "signUpSuccessful";
 }

The following bean is used in the mongo-config.xml class to support the signUp process.

<bean id="passwordEncoder"
 class="org.springframework.security.authentication.encoding.ShaPasswordEncoder">
 <constructor-arg value="256"/>
</bean>

Spring security provides one iteration of the hash by default. In order to provide Key Stretching we can provide more iterations by using the following config instead.

<bean id="passwordEncoder"
 class="org.springframework.security.authentication.encoding.ShaPasswordEncoder">
 <constructor-arg value="256"/>
 <property name="iterations" value="1000"/>
 </bean>

The authentication-manager in the security config file can be used to reference the same bean so that the same number of iterations are performed at authentication.

<authentication-manager>
 <authentication-provider user-service-ref="customUserDetailsService">
 <password-encoder ref="passwordEncoder">
 <salt-source user-property="username"/>
 </password-encoder>
 </authentication-provider>
 </authentication-manager>

References:
Spring Source Doc

MongoDB Spring Data and Spring Security with custom UserDetailsService

July 8, 2012 12 comments

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>