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”.
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.
To see the HTML React inserted into the root element you can use the browser’s inspector (Ctrl-Shift-J in Chrome).
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!
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.
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.
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.
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”.
To give a user the required rights, you need to add them to a Stormpath group via the Admin Console.
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!
Your article is very interesting, but difficult to follow for someone not familiar with the subject.
Nicely done.
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…
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 :-)
AFAIU you forgot to add Employees REST endpoint on server side.
Great Article , lombok is part of spring boot POM so you don’t need to include the version
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.
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.