Enterprise Java

Build a CRUD Application with React, Spring Boot, and User Authentication

Building Identity Management, including authentication and authorization? Try Stormpath! Our REST API and robust Java SDK support can eliminate your security risk and can be implemented in minutes. Sign up, and never build auth again!

React is one of the most popular libraries for creating web application frontends. With Spring Boot it’s easier than ever to create a CRUD backend for your React-fronted application. In this tutorial, we’ll tie those together and then use Stormpath to add authentication and authorization protocols.

We’ll start by creating a static data view using React. Then we will create a REST backend with Spring Boot, tie it in, and add user security with Stormpath. Everything should be straight-forward, even if you’ve never used React before.

The source code that backs this post can be found in this GitHub repo.

Serving the Front

Normally React applications are served up using Node.js, but if you’re a Java dev you’ll likely be super comfortable with this Spring Boot approach.

Initially, you’ll put the whole application in one file, index.html. To tell Spring Boot to serve it as the homepage you can use the @Controller annotation.

@Controller
public class HomeController {
 
    @RequestMapping(value = "/")
    public String index() {
        return "index.html";
    }
}

Create an empty directory and put the above code into src/main/java/tutorial/HomeController.java. Spring Boot will then look for src/main/resources/static/index.html when you load the site.

<!DOCTYPE html>
<html>
<head>
    <title>React + Spring</title>
</head>
<body>
</body>
</html>

Create a pom.xml and a Spring Boot application class. Use the following for your POM.

<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
 
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.4.1.RELEASE</version>
    </parent>
 
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
 
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

Put the following into src/main/java/tutorial/Application.java.

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

When you start the server with mvn spring-boot:run and visit localhost:8080 you should see a blank page with the title “React + Spring”.

5563044

Remove Restarts

Normally you’d have to restart your server each time you make a change to your front-end which is a pain. Using Spring Boot’s developer tools allows us to get around this. Add the following dependency to your POM.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <optional>true</optional>
</dependency>

Also add this configuration to your Spring Boot Maven Plugin:

<configuration>
    <addResources>true</addResources>
</configuration>

Now, when you make a change to your application, or recompile any classes, it should update when you refresh your browser.

React Barebones HTML

On to React! The most basic React page has three things: a root element, JavaScript imports, and a script tag.

<!DOCTYPE html>
<html>
<head>
    <title>React + Spring</title>
</head>
<body>
    <div id='root'></div>
 
    <script src="https://fb.me/react-15.0.1.js"></script>
    <script src="https://fb.me/react-dom-15.0.1.js"></script>
    <script src="https://www.javacodegeeks.com/wp-content/litespeed/localres/aHR0cHM6Ly9jZG5qcy5jbG91ZGZsYXJlLmNvbS8=ajax/libs/babel-core/5.8.23/browser.min.js"></script>
 
    <script type="text/babel"></script>
</body>
</html>

The root element is where React will insert our view HTML. The imports pull in three libraries—two for React itself and another to translate our view template using babel.

Note: for ease of testing we are pulling in libraries using a CDN but normally you would use something like webpack to combine all your Javascript into one file.

Now put your React code inside the script tag.

React Basics

As described in the thinking in react tutorial, you should start coding your application by breaking the interface down into components.

<script type="text/babel">
var Employee = React.createClass({});
var EmployeeTable = React.createClass({});
</script>

Here you’ve created two—one for a table of employees and another for an employee entry. Each component then needs a render function which describes the HTML to generate.

<script type="text/babel">
var Employee = React.createClass({
  render: function() {
    return (<div>employee</div>);
  }
});
var EmployeeTable = React.createClass({
  render: function() {
    return (<div>employee table</div>);
  }
});
</script>

Here’s where the Babel compiler comes in to convert HTML code into the correct React statements. Note how the div tags are returned from the render statement.

You need to tell React to insert the parent component’s HTML into the root element. This is done using the ReactDOM.render method.

<script type="text/babel">
var Employee = React.createClass({
  render: function() {
    return (<div>employee</div>);
  }
});
var EmployeeTable = React.createClass({
  render: function() {
    return (<div>employee table</div>);
  }
});
 
ReactDOM.render(
  <EmployeeTable />, document.getElementById('root')
);
</script>

By refreshing the browser you should see the simple text element you created.

26089170

To see the HTML React inserted into the root element you can use the browser’s inspector (Ctrl-Shift-J in Chrome).

22301758

Tying Components Together

Now that you have components, let’s tie them together. You can start by trying to render hard-coded data; you’ll use the REST server later.

Above the ReactDOM command enter the following:

var EMPLOYEES = [
  {name: 'Joe Biden', age: 45, years: 5},
  {name: 'President Obama', age: 54, years: 8},
  {name: 'Crystal Mac', age: 34, years: 12},
  {name: 'James Henry', age: 33, years: 2}
];

Then add employees={EMPLOYEES} when you instantiate your table.

ReactDOM.render(
  <EmployeeTable employees={EMPLOYEES} />, document.getElementById('root')
);

As you might expect, this passes the data into a variable named employees. Inside EmployeeTable you can access this using this.props. Let’s use that to generate a table with a row for each employee.

var EmployeeTable = React.createClass({
  render: function() {
    var rows = [];
    this.props.employees.forEach(function(employee) {
      rows.push(<Employee employee={employee} />);
    });
    return (
      <table>
        <thead>
          <tr>
            <th>Name</th><th>Age</th><th>Years</th>
          </tr>
        </thead>
        <tbody>{rows}</tbody>
      </table>);
  }
});

This instantiates a new Employee class for each element in the data (setting the employee attribute) and pushes it to an array. Then {rows} drops in the required HTML from the child class.

Now all you need do is update the render method on Employee.

var Employee = React.createClass({
  render: function() {
    return (
      <tr>
        <td>{this.props.employee.name}</td>
        <td>{this.props.employee.age}</td>
        <td>{this.props.employee.years}</td>
      </tr>);
  }
});

You can add Bootstrap to make the table look nice. Add the following just below your script import tags:

<link rel="stylesheet" href="https://www.javacodegeeks.com/wp-content/litespeed/localres/aHR0cHM6Ly9tYXhjZG4uYm9vdHN0cmFwY2RuLmNvbS8=bootstrap/3.3.7/css/bootstrap.min.css">

Then surround your main table with a container div and give the table element some Bootstrap class names.

<div className="container">
  <table className="table table-striped">
    <thead>
      <tr>
        <th>Name</th>
        <th>Age</th>
        <th>Years</th>
      </tr>
    </thead>
    <tbody>{rows}</tbody>
  </table>
</div>

Refreshing your browser should give a nice view of the data you hard-coded!

86263098

Adding Real Data

To use data objects coming from the server, you need to add a server! Doing this with Spring Boot is super simple. Inside src/main/java/tutorial/Employee.java add the following code:

@Data
@Entity
public class Employee {
 
    private @Id @GeneratedValue Long id;
    private String name;
    private int age;
    private int years;
 
    private Employee() {}
 
    public Employee(String name, int age, int years) {
        this.name = name;
        this.age = age;
        this.years = years;
    }
}

This is our bean. Note: the @Data annotation is from Project Lombok.

Now, create a repository using Spring Data JPA.

public interface EmployeeRepository extends CrudRepository<Employee, Long> {}

To load data, create a CommandLineRunner implementation that uses the repository to create new records in the database.

@Component
public class DatabaseLoader implements CommandLineRunner {
 
    private final EmployeeRepository repository;
 
    @Autowired
    public DatabaseLoader(EmployeeRepository repository) {
        this.repository = repository;
    }
 
    @Override
    public void run(String... strings) throws Exception {
        this.repository.save(new Employee("Joe Biden", 45, 5));
        this.repository.save(new Employee("President Obama", 54, 8));
        this.repository.save(new Employee("Crystal Mac", 34, 12));
        this.repository.save(new Employee("James Henry", 33, 2));
    }
}

The only thing left is pulling in dependencies. Adding the following to your pom.xml will allow your repository to become a REST endpoint.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

You’ll also need to include Project Lombok (which lets you ignore creating getters and setters for your beans).

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.16.10</version>
    <scope>provided</scope>
</dependency>

And you need a database (which Spring Boot autoconfigures). You can use H2, which is embedded (i.e. in memory / won’t last a reboot).

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
</dependency>

And that’s it! If you reboot now you’ll have a functioning REST server with data.

Mapping the URL

If you add the following to src/main/resources/application.properties then all your REST endpoint calls will be at localhost:8080/api

spring.data.rest.basePath=/api

Calling localhost:8080/api/employees from the command line should give a list of the data you loaded.

React and REST

Now you need to pull the data into your React view from the REST endpoint. You can do this with jQuery. Add the following import to your HTML:

<script src="https://www.javacodegeeks.com/wp-content/litespeed/localres/aHR0cHM6Ly9jZG5qcy5jbG91ZGZsYXJlLmNvbS8=ajax/libs/jquery/3.1.1/jquery.min.js"></script>

Now create a wrapper class that returns an EmployeeTable in its render method.

var App = React.createClass({
 
  loadEmployeesFromServer: function () {
    var self = this;
    $.ajax({
      url: "http://localhost:8080/api/employees"
    }).then(function (data) {
      self.setState({employees: data._embedded.employees});
    });
  },
 
  getInitialState: function () {
    return {employees: []};
  },
 
  componentDidMount: function () {
    this.loadEmployeesFromServer();
  },
 
  render() {
    return ( <EmployeeTable employees={this.state.employees}/> );
  }
});

You have to set state first by using getInitialState to initialise, and then componentDidMount to do what’s needed when everything is loaded.

Now replace the main ReactDOM.render with your new class.

ReactDOM.render(<App />, document.getElementById('root') );

On refresh you should see the same view as before, except now the data is being loaded from the server.

Interactivity

The last thing you’ll want for your frontend is interactivity. Let’s add a delete button to see how that might work.

Add the following column to your employee render.

<td>
    <button className="btn btn-info" onClick={this.handleDelete}>Delete</button>
</td>

You’ll write the handleDelete method in a sec. After adding another heading to the employee table class, you should see buttons appear alongside each entry.

8223957

Deleting from the Server

Before you send delete requests to the backend, it’s a good idea to add notification messages. For that you can use Toastr which will allow you to show popups. Include the following at the top of your HTML:

<script src="https://www.javacodegeeks.com/wp-content/litespeed/localres/aHR0cHM6Ly9jZG5qcy5jbG91ZGZsYXJlLmNvbS8=ajax/libs/toastr.js/2.1.3/toastr.min.js"></script>
<link rel="stylesheet" href="https://www.javacodegeeks.com/wp-content/litespeed/localres/aHR0cHM6Ly9jZG5qcy5jbG91ZGZsYXJlLmNvbS8=ajax/libs/toastr.js/2.1.3/toastr.min.css">

Now in your script you can send messages with commands like toastr.error('something went wrong').

Let’s test that! Change your employee class to the following:

var Employee = React.createClass({
  getInitialState: function() {
    return {display: true };
  },
  handleDelete() {
    var self = this;
    $.ajax({
      url: self.props.employee._links.self.href,
      type: 'DELETE',
      success: function(result) {
        self.setState({display: false});
      },
      error: function(xhr, ajaxOptions, thrownError) {
        toastr.error(xhr.responseJSON.message);
      }
    });
  },
  render: function() {
    if (this.state.display==false) return null;
    else return (
      <tr>
        <td>{this.props.employee.name}</td>
        <td>{this.props.employee.age}</td>
        <td>{this.props.employee.years}</td>
        <td>
          <button className="btn btn-info" onClick={this.handleDelete}>Delete</button>
        </td>
      </tr>
    );
  }
});

This sets a display state which determines whether to render or not. If the employee is deleted successfully, this variable is set to true. The handleDelete method sends a delete request to the server (using the href you got back from the get request). If successful, display is set to false and the render is updated. Otherwise, Toastr notifies the user that an error occurred.

Try deleting an entry and refreshing the page. It should stay deleted.

Note: Restarting the server will bring back the same data since you’re using an in-memory database.

Add User Authentication

Let’s add one final feature to our React application, Stormpath for user authentication. You’ll need a forever-free developer account with Stormpath.

The first thing you need do is put your Stormpath application details inside of your application.properties.

stormpath.application.href = <your app href>
stormpath.client.apiKey.id = <your api key id>
stormpath.client.apiKey.secret = <your api key secret>

Note: For security reasons you should not store your Stormpath keys inside of project files. Rather use environment variables. See here.

Next, add the Stormpath starter to your Maven dependencies.

<dependency>
    <groupId>com.stormpath.spring</groupId>
    <artifactId>stormpath-default-spring-boot-starter</artifactId>
    <version>1.1.2</version>
</dependency>

You’ll also need to move your index.html file to src/main/resources/templates This is because Stormpath’s Spring Boot starter uses the Thymeleaf templating library by default. Change the HomeController to return index as well.

@Controller
public class HomeController {
 
    @RequestMapping(value = "/")
    public String index() {
        return "index";
    }
}

You’ll also need to move your React code into a separate file. This is because Thymeleaf won’t like some of the characters. Move the code from the inside of the script tag into src/main/webapp/public/app.js. This folder is open to the public by default. Then import this script at the bottom of your HTML.

<script type="text/babel" src="/public/app.js"></script>

Then create a security adapter which calls apply on stormpath().

@Configuration
public class Security extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.apply(stormpath());
    }
}

Now when you reboot your server and try reach the homepage you’ll be prompted with a login page.

3503857

Typing in the details attached to the Stormpath application you put in application.properties should take you to the data view page as before.

Logging Out

You need to be able to log out as well. This is as easy as adding a form that sends a post to /logout(which Stormpath sets up by default).

<div class='container'>
    <div id='root'></div>
    <form action="/logout" method="post">
        <input class="btn btn-danger center-block" type="submit" value="Logout" />
    </form>
</div>

You can surround both the React root element and the form with a Bootstrap container for better alignment.

 

3245088

Clicking the logout button should take you back to the login screen as before.

Set up Authorization

Lastly, you’ll want to only let users with the correct access delete employees. To lock things down, you can use Spring Security’s PreAuthorize annotation. Change the repository code to the following:

public interface EmployeeRepository extends CrudRepository<Employee, Long> {
 
    @PreAuthorize("hasAuthority('ROLE_ADMIN')")
    @Override
    void delete(Long aLong);
}

Now only users with the authority ROLE_ADMIN will be able to delete. If you restart your server and try to click delete you should get a message saying “Access is denied”.

36408723

To give a user the required rights, you need to add them to a Stormpath group via the Admin Console.

73134913-1024x423

In this example, there is a group called Supervisor that’s been attached to the relevant application. To integrate with this group, you simply need to replace the ROLE_ADMIN string with the HREF of the group and restart. If the user that’s logged in is a member of the Supervisor group (see Accounts), you should be allowed to delete.

Done and Dusted

Just like that you have created a compliant CRUD web application with authorization and React as the front-end. I hope you found this tutorial useful! If you have any questions about integrating React, Spring Boot and Stormpath, please leave a comment.

To see a more complete React application using a Spring Boot backend see React.js and Spring Data REST.

Building Identity Management, including authentication and authorization? Try Stormpath! Our REST API and robust Java SDK support can eliminate your security risk and can be implemented in minutes. Sign up, and never build auth again!

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

8 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Bill Fly
Bill Fly
7 years ago

Your article is very interesting, but difficult to follow for someone not familiar with the subject.

Carl J. Mosca
7 years ago

Nicely done.

Srinivas Pakala
Srinivas Pakala
7 years ago

Your article is very much important for those who want to learn React with Spring. Really good…many many thanks…It’s easy to follow…

Bernd
7 years ago

Nice tutorial, thanks a lot! I tried to upgrade to Spring Boot 1.4.3 but that did not work. I guess it’s connected to Spring Data REST but can’t really tell. One other thing I had a bit difficulties was how to create the Stormpath properties for the application, but I finally managed to do it, as a complete Stormpath newbie :-)

Alexander
Alexander
7 years ago

AFAIU you forgot to add Employees REST endpoint on server side.

Ali Shahbour
Ali Shahbour
7 years ago

Great Article , lombok is part of spring boot POM so you don’t need to include the version

Cassius Vinicius
Cassius Vinicius
7 years ago

Hello,

I updated the deṕendencies versions to the latest stable ones.

I am able to logon.

The datatable doesn’t appears.

What I am doing wrong, please?

Thanks in advance.

Paul Balogh
Paul Balogh
7 years ago

I was running into this as well. Ends up that “/public/app.js” was returning a 404 when I was running the application as a Spring Boot jar. Ends up that “src/main/webapp” will only be available with WAR deployments; for an executable jar, you should leave the app.js in the “resources/static” directory. Once I relocated that file, all worked well.

On a separate note, I had to generate getters/setters for “Employee” as without them, the webservice json only had the HATEOS links and not the actual attributes. I just ended up ignoring Project Lombok.

Back to top button