Hello and Welcome to the first part of an exciting series of blog posts where you will learn how to build an end-to-end full stack polling app similar to twitter polls.

We’ll build the backend server using Spring Boot where we’ll use Spring Security along with JWT authentication. We’ll use MySQL database for storage.

The front-end application will be built using React. We’ll also use Ant Design for designing our user interface.

In the end of this tutorial series, you’ll have built a fully-fledged polling application from scratch like a boss.

The complete source code for the project is hosted on Github. You can refer that anytime if you get stuck at something.

I’ve also hosted the application on AWS free tier for you to explore. You can check out the live demo at polls.callicoder.com.

Following is the screenshot of the final version of our application -

Building an End-to-End Full Stack Polling App with Spring Boot, Spring Security, JWT, MySQL and React with User Registration and Authentication Part 1

Looks great, isn’t it? Well, then let’s start building it from scratch…

In this article, We’ll set up the backend project using Spring Boot and define the basic domain models and repositories.

Creating the Backend Application using Spring Boot

Let’s bootstrap the project using Spring Initialzr web tool -

  1. Open http://start.spring.io
  2. Enter polls in Artifact field.
  3. Add Web, JPA, MySQL and Security dependencies from the Dependencies section.
  4. Click Generate Project to generate and download the project.
Spring Boot, Spring Security JWT registration and authentication

Once the project is downloaded, unzip it and import it into your favorite IDE. The directory structure of the project will look like this-

Spring Boot, Spring Security, JWT, MySQL Polling App directory structure

Adding additional dependencies

We’ll need to add few additional dependencies to our project. Open pom.xml file from the root directory of your generated project and add the following to the <dependencies> section -

<!-- For Working with Json Web Tokens (JWT) -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.0</version>
</dependency>

<!-- For Java 8 Date/Time Support -->
<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
</dependency>

Configuring the Server, Database, Hibernate and Jackson

Let’s now configure the server, database, hibernate, and jackson by adding the following properties to the src/main/resources/application.properties file -

## Server Properties
server.port= 5000

## Spring DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
spring.datasource.url= jdbc:mysql://localhost:3306/polling_app?useSSL=false&serverTimezone=UTC&useLegacyDatetimeCode=false
spring.datasource.username= root
spring.datasource.password= callicoder

## Hibernate Properties

# The SQL dialect makes Hibernate generate better SQL for the chosen database
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.ddl-auto = update

## Hibernate Logging
logging.level.org.hibernate.SQL= DEBUG

## Jackson Properties
spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS= false
spring.jackson.time-zone= UTC

All the above properties are self-explanatory. I’ve set hibernate’s ddl-auto property to update. This will automatically create/update the tables in the database according to the entities in our application.

The Jackson’s WRITE_DATES_AS_TIMESTAMPS property is used to disable serializing Java 8 Data/Time values as timestamps. All the Date/Time values will be serialized to ISO date/time string.

Before proceeding further, please create a database named polling_app in MySQL and change the spring.datasource.username and spring.datasource.password properties as per your MySQL installation.

Configuring Spring Boot to use Java 8 Date/Time converters and UTC Timezone

We’ll be using Java 8 Data/Time classes in our domain models. We’ll need to register JPA 2.1 converters so that all the Java 8 Date/Time fields in the domain models automatically get converted to SQL types when we persist them in the database.

Moreover, We’ll set the default timezone for our application to UTC.

Open the main class PollsApplication.java and make the following modifications to it-

package com.example.polls;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.data.jpa.convert.threeten.Jsr310JpaConverters;

import javax.annotation.PostConstruct;
import java.util.TimeZone;

@SpringBootApplication
@EntityScan(basePackageClasses = { 
		PollsApplication.class,
		Jsr310JpaConverters.class 
})
public class PollsApplication {

	@PostConstruct
	void init() {
		TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
	}

	public static void main(String[] args) {
		SpringApplication.run(PollsApplication.class, args);
	}
}

Creating the domain models

Our application will allow new users to register and login to our application. Every User will have one or more roles. The roles associated with a user will be used in future to decide whether the user is authorized to access a particular resource on our server or not.

In this section, We’ll create the User and Role domain models. All the domain models will be stored in a package named model inside com.example.polls.

1. User model

The User model contains the following fields -

  1. id: Primary Key
  2. username: A unique username
  3. email: A unique email
  4. password: A password which will be stored in encrypted format.
  5. roles: A set of roles. (Many-To-Many relationship with Role entity)

Here is the complete User class -

package com.example.polls.model;

import com.example.polls.model.audit.DateAudit;
import org.hibernate.annotations.NaturalId;
import javax.persistence.*;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import java.util.HashSet;
import java.util.Set;

@Entity
@Table(name = "users", uniqueConstraints = {
        @UniqueConstraint(columnNames = {
            "username"
        }),
        @UniqueConstraint(columnNames = {
            "email"
        })
})
public class User extends DateAudit {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotBlank
    @Size(max = 40)
    private String name;

    @NotBlank
    @Size(max = 15)
    private String username;

    @NaturalId
    @NotBlank
    @Size(max = 40)
    @Email
    private String email;

    @NotBlank
    @Size(max = 100)
    private String password;

    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(name = "user_roles",
            joinColumns = @JoinColumn(name = "user_id"),
            inverseJoinColumns = @JoinColumn(name = "role_id"))
    private Set<Role> roles = new HashSet<>();

    public User() {

    }

    public User(String name, String username, String email, String password) {
        this.name = name;
        this.username = username;
        this.email = email;
        this.password = password;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getName() {
        return name;
    }

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

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Set<Role> getRoles() {
        return roles;
    }

    public void setRoles(Set<Role> roles) {
        this.roles = roles;
    }
}

The User class extends the DateAudit class that we’ll define shortly. The DateAudit class will have createdAt and updatedAt fields that will be used for auditing purposes.

2. Role model

The Role class contains an id and a name field. The name field is an enum. We’ll have a fixed set of pre-defined roles. So it makes sense to make the role name as enum.

Here is the complete code for Role class -

package com.example.polls.model;

import org.hibernate.annotations.NaturalId;
import javax.persistence.*;

@Entity
@Table(name = "roles")
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Enumerated(EnumType.STRING)
    @NaturalId
    @Column(length = 60)
    private RoleName name;

    public Role() {

    }

    public Role(RoleName name) {
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public RoleName getName() {
        return name;
    }

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

RoleName enum

Following is the RoleName enum -

package com.example.polls.model;

public enum  RoleName {
    ROLE_USER,
    ROLE_ADMIN
}

I have defined two roles namely ROLE_USER and ROLE_ADMIN. You’re free to add more roles as per your project requirements.

3. DateAudit model

All right! Let’s now define the DateAudit model. It will have a createdAt and an updatedAt field. Other domain models that need these auditing fields will simply extend this class.

We’ll use JPA’s AuditingEntityListener to automatically populate createdAt and updatedAt values when we persist an entity.

Here is the Complete DateAudit class (I’ve created a package named audit inside com.example.polls.model package to store all the auditing related models) -

package com.example.polls.model.audit;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.Column;
import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
import java.io.Serializable;
import java.time.Instant;

@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
@JsonIgnoreProperties(
        value = {"createdAt", "updatedAt"},
        allowGetters = true
)
public abstract class DateAudit implements Serializable {

    @CreatedDate
    @Column(nullable = false, updatable = false)
    private Instant createdAt;

    @LastModifiedDate
    @Column(nullable = false)
    private Instant updatedAt;

    public Instant getCreatedAt() {
        return createdAt;
    }

    public void setCreatedAt(Instant createdAt) {
        this.createdAt = createdAt;
    }

    public Instant getUpdatedAt() {
        return updatedAt;
    }

    public void setUpdatedAt(Instant updatedAt) {
        this.updatedAt = updatedAt;
    }
}

To enable JPA Auditing, we’ll need to add @EnableJpaAuditing annotation to our main class or any other configuration classes.

Let’s create an AuditingConfig configuration class and add the @EnableJpaAuditing annotation to it.

We’re creating a separate class because we’ll be adding more auditing related configurations later. So it’s better to have a separate class.

We’ll keep all the configuration classes inside a package named config. Go ahead and create the config package inside com.example.polls, and then create the AuditingConfig class inside config package -

package com.example.polls.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

@Configuration
@EnableJpaAuditing
public class AuditingConfig {
    // That's all here for now. We'll add more auditing configurations later.
}

Creating the Repositories for accessing User and Role data

Now that we have defined the domain models, Let’s create the repositories for persisting these domain models to the database and retrieving them.

All the repositories will go inside a package named repository. So let’s first create the repository package inside com.example.polls.

1. UserRepository

Following is the complete code for UserRepository interface. It extends Spring Data JPA’s JpaRepository interface.

package com.example.polls.repository;

import com.example.polls.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByEmail(String email);

    Optional<User> findByUsernameOrEmail(String username, String email);

    List<User> findByIdIn(List<Long> userIds);

    Optional<User> findByUsername(String username);

    Boolean existsByUsername(String username);

    Boolean existsByEmail(String email);
}

2. RoleRepository

Following is the RoleRepository interface. It contains a single method to retrieve a Role from the RoleName-

package com.example.polls.repository;

import com.example.polls.model.Role;
import com.example.polls.model.RoleName;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;

@Repository
public interface RoleRepository extends JpaRepository<Role, Long> {
    Optional<Role> findByName(RoleName roleName);
}

Exploring the current setup and Running the Application

After creating all the above models, repositories and configurations, our current project should look like this -

Spring Security JWT Authentication and Registration with User Roles

You can run the application by typing the following command from the root directory of your project -

mvn spring-boot:run

Check out the logs and make sure that the server starts successfully.

2018-02-24 22:40:44.998  INFO 33708 --- Tomcat started on port(s): 5000 (http)
2018-02-24 22:40:45.008  INFO 33708 --- Started PollsApplication in 7.804 seconds (JVM running for 27.193)

Write to me in the comment section, if the server doesn’t start successfully for you. I’ll help you out.

Creating Default Roles

We’ll have a fixed set of predefined roles in our application. Whenever a user logs in, we’ll assign ROLE_USER to it by default.

For assigning the roles, they have to be present in the database. So let’s create the two default roles in the database by executing the following insert statements -

INSERT INTO roles(name) VALUES('ROLE_USER');
INSERT INTO roles(name) VALUES('ROLE_ADMIN');

What’s Next?

In the next chapter of this series, we’ll learn how to configure Spring Security in our project and add functionalities to register a new user and log them in.

Read Next: Full Stack Polling App with Spring Boot, Spring Security, JWT, MySQL and React - Part 2