RESTful Web Service to Save a New User in Database

In this blog post I am going to share with you a way to create a RESTful Web Service to:

  • Create a new user profile,
  • Generate and save in database a user secure user password rather than an actual password provided by user,
  • Return back as a response a custom user profile object(JSON) with information that we want. User clear password,  and other secure details will not be returned.

This example is simplified. There are things that I am skipping like for example checking for all the required fields or email address validation or even the error codes and most of the error handling. But even though it is a simplified example, the source code is fully functional.

And by the way, for a free video tutorial on how to build a user interface with Xcode and Swift to implement User Sign up, Sign in, Sign out and learn how to send HTTP Request to a Restful Web Service mentioned in this tutorial check out this Youtube playlist. Here is an introduction video:

In my following blog post I will share with you how to send a request to a protected web service(user will need to be logged in first) and how to authenticate user with a provided web token. So stay tuned! 🙂

To create a RESTful web service in this blog post I will use JAX-RS, Jersey, Spring Framework and to store data into MySQL database I will use Hibernate framework. I will show you how to add all of these frameworks into your Java project using Maven.

Create New Java Web Application Using Maven

We will start my creating a new Java Web Application using Maven. Earlier I have published a few blog posts on how to create a java project with Maven and to make this blog post shorter, just follow this link to create a new Java Web Application and add Jersey framework support to it.

Add Support for MySQL, Hibernate and Spring Frameworks

To add to your newly created Java Web Project support for MySQL, Hibernate and Spring Frameworks you can follow this blog post I have published earlier “Persist Java Object in MySQL Database With Hibernate” which will also show you how to create a hibernate.cfg.xml file which contains database access details and Hibernate SessionFactory class.

Below is my current pom.xml file which includes support for MySQL, Hibernate and Spring frameworks. You can copy and paste into your project the dependencies that I have in the example below:

<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.appsdeveloperblog.ws</groupId>
    <artifactId>HibernateExamples</artifactId>
    <packaging>war</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>HibernateExamples Maven Webapp</name>
    <url>http://maven.apache.org</url>
    <dependencies>
        
        <!-- https://mvnrepository.com/artifact/junit/junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>2.8.47</version>
            <scope>test</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.glassfish.jersey.bundles/jaxrs-ri -->
        <dependency>
            <groupId>org.glassfish.jersey.bundles</groupId>
            <artifactId>jaxrs-ri</artifactId>
            <version>2.25</version>
        </dependency>
        
        <!-- https://mvnrepository.com/artifact/org.glassfish.jersey.media/jersey-media-moxy -->
        <dependency>
            <groupId>org.glassfish.jersey.media</groupId>
            <artifactId>jersey-media-moxy</artifactId>
            <version>2.25</version>
        </dependency>
    
        <!-- Hibernate Dependencies -->
        <!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-core -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>5.2.8.Final</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-validator -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>5.4.0.Final</version>
        </dependency>
        
        <!-- MySQL Dependencies -->
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.41</version>
        </dependency>
 

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>4.3.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>4.3.7.RELEASE</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.3.7.RELEASE</version>
        </dependency>
        
        <dependency>
            <groupId>org.glassfish.jersey.ext</groupId>
            <artifactId>jersey-spring3</artifactId>
            <version>2.25.1</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>      
        
 
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>4.3.1.Final</version>
        </dependency>

    </dependencies>
    <build>
        <resources>
            <resource>
                <filtering>true</filtering>
                <directory>src/test/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                 <include>**/*.xml</include>
                </includes>
                <excludes>
                    <exclude>**/*local.properties</exclude>
                </excludes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
            </resource>
        </resources>
        <finalName>HibernateExamples</finalName>
    </build>
    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
</project>

Create JAX-RS Application Starting Point

With this Java class we are going to create we will define the base URI for all resource URIs. This base URI will be a prefix to all of our web services end points. Because I am creating an API for my mobile application I will create the base URI for my web services as “api”. Now, when I create a new Web Service Endpoint and give it a name “users” I will need to access it using the /api/users. If this class defines application path as “my-api” then, I will be able to access my newly created Web Service Endpoint with a name users like this: /my-api/users. You get the idea.

package com.appsdeveloperblog.ws;
 
import javax.ws.rs.ApplicationPath;
import org.glassfish.jersey.server.ResourceConfig;
 
@ApplicationPath("api")
public class MyApi extends ResourceConfig {
}

 User Profile Details Submitted with HTTP POST Request

When Mobile Application Submits HTTP POST Request containing JSON Payload in HTTP Request Body, our web service end point will accept this HTTP Request and the JSON Payload will be converted into a Java object. The fields in this Java class that we need to create must should match the keys in JSON Payload. If JSON Payload has more or has different keys than our Java class defines, then JAX-RS will not be able to convert this JSON Payload into a Java object and an error message will be generated. Below is the JSON Payload that our Web Service will be expecting and the Java class into which this JSON Payload will be converted.

The JSON Payload Sent To a Web Service: 

{
  "firstName":"Sergey",
  "lastName":"Kargopolov”,
  "userName":"[email protected]",
  "userPassword":"123"
}

 User Profile Java Class

The above JSON payload will be converted into a Java class below. It is important to create a Java class with fields that match the field names in the JSON document.

package com.appsdeveloperblog.ws.ui.rest;

import javax.xml.bind.annotation.XmlRootElement;
 
@XmlRootElement
public class UserProfile {
    private String firstName;
    private String lastName;
    private String userName;
    private String userPassword;

    /**
     * @return the firstName
     */
    public String getFirstName() {
        return firstName;
    }

    /**
     * @param firstName the firstName to set
     */
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    /**
     * @return the lastName
     */
    public String getLastName() {
        return lastName;
    }

    /**
     * @param lastName the lastName to set
     */
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    /**
     * @return the userName
     */
    public String getUserName() {
        return userName;
    }

    /**
     * @param userName the userName to set
     */
    public void setUserName(String userName) {
        this.userName = userName;
    }

    /**
     * @return the userPassword
     */
    public String getUserPassword() {
        return userPassword;
    }

    /**
     * @param userPassword the userPassword to set
     */
    public void setUserPassword(String userPassword) {
        this.userPassword = userPassword;
    }
}

 RESTful Web Service Root Resource Class

The below code example is a our RESTful Web Service Root Resource Class which will contain Web Service Entry points to accept HTTP Requests and Response back with HTTP Response. The web service entry point we are interested in this blog post is @Path(“/users”) which accept HTTP POST Request.

Please note that:

  • UsersService class is being @Autowired with Spring Framework. If you do not use Spring Framework you can simply instantiate the implementation of this service implementation manually like so:
UsersService usersService = new UserServiceImpl(database, authenticationUtil)
  • The JSON Payload will be converted and made available to our method as an argument userProfile of time UserProfile.
  • We will respond back with a restful class of different type: UserProfileResponse.   UserProfileResponse will contain only information we want to return about user and will not contain user password, database user id, salt or any other details which could compromise the security of our application.
  • The business logic of saving the user profile to database is delegated to the implementation of UsersService which is a next layer of our application.
package com.appsdeveloperblog.ws.entrypoints;

import com.appsdeveloperblog.ws.service.UsersService;
import com.appsdeveloperblog.ws.shared.dto.UserProfileDto;
import com.appsdeveloperblog.ws.ui.rest.UserProfile;
import com.appsdeveloperblog.ws.ui.rest.UserProfileResponse;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.springframework.beans.BeanUtils; 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 *
 * @author skargopolov
 */
@Component
@Path("/users")
public class Users {
 
    @Autowired
    UsersService usersService;
 
    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
    public UserProfileResponse createUserProfile(UserProfile userProfile) {

        UserProfileResponse returnValue = null;
        
        UserProfileDto userProfileDto = new UserProfileDto();
        BeanUtils.copyProperties(userProfile, userProfileDto);
       
        UserProfileDto storedUserDetails = usersService.saveUser(userProfileDto);
         
        if(storedUserDetails != null && !storedUserDetails.getFirstName().isEmpty())
        {
            returnValue = new UserProfileResponse();
            BeanUtils.copyProperties(storedUserDetails, returnValue);
        }

       // And when we are done, we can return user profile back
        return returnValue;
    }
 
}

UserProfileResponse Class

import javax.xml.bind.annotation.XmlRootElement;

/**
 *
 * @author skargopolov
 */
@XmlRootElement
public class UserProfileResponse {
    private String firstName;
    private String lastName;
    private String userName;
    private String userId;

    /**
     * @return the firstName
     */
    public String getFirstName() {
        return firstName;
    }

    /**
     * @param firstName the firstName to set
     */
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    /**
     * @return the lastName
     */
    public String getLastName() {
        return lastName;
    }

    /**
     * @param lastName the lastName to set
     */
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    /**
     * @return the userName
     */
    public String getUserName() {
        return userName;
    }

    /**
     * @param userName the userName to set
     */
    public void setUserName(String userName) {
        this.userName = userName;
    }

    /**
     * @return the userId
     */
    public String getUserId() {
        return userId;
    }

    /**
     * @param userId the userId to set
     */
    public void setUserId(String userId) {
        this.userId = userId;
    }
}

  UserService Interface Source Code

package com.appsdeveloperblog.ws.service;

import com.appsdeveloperblog.ws.shared.dto.UserProfileDto;

/**
 *
 * @author skargopolov
 */
public interface UsersService {
    public UserProfileDto saveUser(UserProfileDto user);
}

UserServiceImpl Source Code

package com.appsdeveloperblog.ws.service.impl;

import com.appsdeveloperblog.ws.io.dao.Database;
import com.appsdeveloperblog.ws.io.entity.UserProfileEntity;
import com.appsdeveloperblog.ws.service.UsersService;
import com.appsdeveloperblog.ws.shared.dto.UserProfileDto;
import com.appsdeveloperblog.ws.shared.exceptions.UserServiceException;
import com.appsdeveloperblog.ws.utils.AuthenticationUtil;
import java.security.spec.InvalidKeySpecException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

 
@Service("usersService")
public class UsersServiceImpl implements UsersService {

    Database database;
    AuthenticationUtil authenticationUtil;
    
    @Autowired
    public UsersServiceImpl(Database database, AuthenticationUtil authenticationUtil)
    {
        this.database = database;
        this.authenticationUtil = authenticationUtil;
    }

    @Override
    public UserProfileDto saveUser(UserProfileDto userDto) throws UserServiceException {
        
        UserProfileDto returnValue = null;
        
        // Generate salt
        String salt = authenticationUtil.generateSalt(30);
        
        // Generate secure user password 
        String secureUserPassword = null;
        
        try {
            secureUserPassword = authenticationUtil.
                    generateSecurePassword(userDto.getUserPassword(), salt);
        } catch (InvalidKeySpecException ex) {
            Logger.getLogger(UsersServiceImpl.class.getName()).log(Level.SEVERE, null, ex);
            throw new UserServiceException(ex.getLocalizedMessage());
        } 
        
        //Generate secure public user id
        String securePublicUserId = authenticationUtil.generateUserId(30);
        
        userDto.setSalt(salt);
        userDto.setUserPassword(secureUserPassword); 
        userDto.setUserId(securePublicUserId);
                
        UserProfileEntity userEntity = new UserProfileEntity();
        BeanUtils.copyProperties(userDto, userEntity);
     
        // Connect to database 
        try {
            this.database.openConnection();
            UserProfileEntity storedUserEntity = this.database.saveUserProfile(userEntity);
            if(storedUserEntity != null && storedUserEntity.getId()>0)
            {
                returnValue = new UserProfileDto();
                BeanUtils.copyProperties(storedUserEntity, returnValue);
            }
        }  finally {
            this.database.closeConnection();
        }
        
        return returnValue;
    }

}

Please have a look at the implementation of saveUser method of UserService Interface. Its main goal is to:

  • Generate Salt which is being used to produce a secure password,
  • Generate secure password. It is never a good idea to store the password user provided as is in clear text. This is why I am using salt and Password Based Encryption to encrypt the password user provided and store the encrypted password in the database. Each record will have a unique value of salt, which means that even if the malicious user compromises our database and somehow learns from the user the value of clear password, they will be able to compromise only one single user. Other users are protected. To make it even more secure we could rotate the value of salt and I might show you how to do it in my following blog post,
  • Generate secure public user id which will be eventually returned back to user and stored in secure storage on device like KeyChain on iOS for example. The reason I prefer to generate user id this way, is because it not possible to predict the id of the next record or the id of a previous record in our database in the case if the content of http request gets compromised,
  • Prepare UserProfileEntity object which is then being persisted in MySQL database using by Hibernate framework.

Also, please notice that the business logic of password generation and salt generation is delegated to AuthenticationUtil class the source code of which is below:

AuthenticationUtil Source Code

package com.appsdeveloperblog.ws.utils;

import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
import java.util.Base64;
import java.util.Random;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import org.springframework.stereotype.Component;

/**
 *
 * @author skargopolov
 */
@Component
public class AuthenticationUtil {

    private static final Random RANDOM = new SecureRandom();
    private static final String ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    private static final int ITERATIONS = 10000;
    private static final int KEY_LENGTH = 256;

    public String generateSalt(int length) {
        StringBuilder returnValue = new StringBuilder(length);

        for (int i = 0; i < length; i++) {
            returnValue.append(ALPHABET.charAt(RANDOM.nextInt(ALPHABET.length())));
        }

        return new String(returnValue);
    }
    
    public String generateUserId(int length)
    {
        return generateSalt(length);
    }

    public String generateSecurePassword(String password, String salt) throws InvalidKeySpecException {
 
        byte[] securePassword = hash(password.toCharArray(), salt.getBytes());

        return Base64.getEncoder().encodeToString(securePassword);
 
    }

    private byte[] hash(char[] password, byte[] salt) throws InvalidKeySpecException {
        PBEKeySpec spec = new PBEKeySpec(password, salt, ITERATIONS, KEY_LENGTH);
        Arrays.fill(password, Character.MIN_VALUE);
        try {
            SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
            return skf.generateSecret(spec).getEncoded();
        } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
            throw new AssertionError("Error while hashing a password: " + e.getMessage(), e);
        } finally {
            spec.clearPassword();
        }
    }
}

Once User Profile Details are stored the Users Web Service Endpoint will return back as a response a JSON object containing user profile details we have specified in UserProfileResponse class. No password or salt and no real database sequential user id are being returned back to a calling client.

The above example demonstrates only how to store user profile details and generate secure password. In my following blog post I am going to share with you how to perform user login and generate a secure token which will then be used to access protected web service endpoints. Protected Web Service Endpoints are those that require user to be logged in to be able to communicate with them.

Stay tuned.

Also, if you are looking to learn more about RESTful Web Services check out the below video lessons and books available on this topic:

Java Web Services Part 1. Video Course.

Java icon

Java Web Services Part 2. Video Course.

Master advanced web services concepts and implement them in easy steps Java Web Services Part 2 icon

REST Java Web Services. Video Course.

A guide to understanding, accessing, and writing a REST Java web service using Apache and Java EE. Java Web Services Part 2 icon


2 Comments on "RESTful Web Service to Save a New User in Database"


  1. Hi
    Can you please provide me the continuation for this
    “how to perform user login and generate a secure token which will then be used to access protected web service endpoints”

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *