[no_toc]Tutorial: Reactjs Jwt SpringBoot Token Authentication Example – React.js Spring Security Login
JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. And “How to build Reactjs Jwt SpringBoot Token Based Authentication Example?” is one of the most common questions for SpringBoot Java development world. So in the tutorial, I introduce how to implement an application “Reactjs JWT SpringBoot token Authentication Example” with details step by step and 100% running sourcecode.
– I give you an Epic of the application, a fullstack excutive flow from frontend to backend with overall architecture diagram.
– I give you a layer diagram of Reactjs JWT Application.
– I guide you detail-steps how to implement a security Jwt Token SpringBoot backend.
– I guide you step by step how to develop a Reactjs JWT Authentication application.
– Finally, I do an integrative testing from Reactjs JWT Authentication application to jwt SpringBoot Security RestAPIs.
- Overview Reactjs Jwt SpringBoot Token Authentication Example
- Jwt SpringBoot Token Authentication RestAPIs Implementation
- Review SpringBoot Jwt Architecture Diagram
- Technology Stack
- Project Structure
- Jwt SpringBoot Token Authentication Goal
- Create Jwt Springboot Project
- Create User Defined Models for Jwt authentication
- Implement UserRepository
- Implement Jwt Token Authentication Spring Security
- Implement UserDetails Service
- Jwt SpringBoot Authentication classes
- Implement Security SpringBoot Rest-Controllers
- Configure Jwt SpringBoot Application Properties
- Testing Jwt SpringBoot RestAPIs
- Reactjs JWT Token Authentication Implementation
- Review Reactjs Architecture Diagram
- Step by step to build Reactjs JWT Authentication Application
- Setup Reactjs Project
- Implement Reactjs JWT Authentication Service
- Implement Reactjs JWT Backend Service
- Implement Reactjs JWT AppNavbar component
- Implement Reactjs JWT Home component
- Implement Reactjs JWT SignUp component
- Implement Reactjs JWT Login component
- Implement Reactjs JWT Profile component
- Implement Reactjs jwt token User Page
- Implement Reactjs jwt token PM Page
- Implement Reactjs jwt token Admin Page
- Define Reactjs Router
- Configure URL proxy request
- Integrative Testing – Reactjs JWT Authentication with Backend Security
- Further Reading
- Sourcecode
Overview Reactjs Jwt SpringBoot Token Authentication Example
JWT Token Introduction
JSON Web Token (JWT) defines a compact and self-contained way for securely transmitting information between parties as a JSON object.
Scenarios where JSON Web Tokens are useful:
Authorization
: the most common scenario for using JWT. Single Sign On is a feature that widely uses JWTInformation Exchange
: Because JWTs can be signed, JSON Web Tokens are a good way of securely transmitting information between parties.
JSON Web Tokens consist of 3 parts:
- Header
- Payload
- Signature
-> JWT
looks like Header-Base64-String.Payload-Base64-String.Signature-Base64-String
Header consists of two parts:
- token type.
- hashing algorithm.
-> Example:
{ "alg": "HS256", "typ": "JWT" }
Payload contains the claims. Claims are statements about an entity and additional information.
There are 3 types of claims ->
Registered claims
-> These are a set of predefined claims: iss (issuer), exp (expiration time), sub (subject)Public claims
Private claims
Example:
{ "sub": "thomasgkz", "iat": 1537603195, "exp": 1537689595 }
Signature -> To create the signature part you have to take the encoded header, the encoded payload, a secret, the algorithm specified in the header, and sign that.
Example:
HMACSHA512( base64UrlEncode(header) + "." + base64UrlEncode(payload), your-256-bit-secret )
Combine all together, we get 3 Base64-URL strings separated by dots,
-> Example:
eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0aG9tYXNna3oiLCJpYXQiOjE1Mzc2MDMxOTUsImV4cCI6MTUzNzY4OTU5NX0.m2YMjTYmOnfR7nnVNxqCzWbQ2FhKRe1eiizxnC2TF4eAoEzKlwo7PheVkKcxj08ST3vB-ZOIhiORvYVfSgzcog
When accessing a protected route or resource, the user agent should send the JWT, typically in the Authorization header using the Bearer schema.
Example:
Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0aG9tYXNna3oiLCJpYXQiOjE1Mzc2MDMxOTUsImV4cCI6MTUzNzY4OTU5NX0.m2YMjTYmOnfR7nnVNxqCzWbQ2FhKRe1eiizxnC2TF4eAoEzKlwo7PheVkKcxj08ST3vB-ZOIhiORvYVfSgzcog
Overall System Architecture Diagram

For the Reactjs JWT Authentication tutorial, we have 2 projects:
– Backend project (using SpringBoot or Nodejs Express) provides secured RestAPIs with JWT token.
– Reactjs project will request RestAPIs from Backend system with the JWT Token Authentication implementation.
JWT Authentication Sequence Diagram
The diagram below show how our system handles User Registration and User Login processes:

1. User Registration Phase:
– User uses a React.js register form to post user’s info (name, username, email, role, password) to Backend API /api/auth/signup
.
– Backend will check the existing users in database and save user’s signup info to database. Finally, It will return a message (successfully or fail) to
2. User Login Phase:
– User posts user/password to signin to Backend RestAPI /api/auth/signin
.
– Backend will check the username/password, if it is right, Backend will create and JWT string with secret then return it to Reactjs client.
After signin, user can request secured resources from backend server by adding the JWT token in Authorization Header. For each request, backend will check the JWT signature and then returns back the resources based on user’s registered authorities.
Reactjs JWT Authentication Diagram Overview

Reactjs JWT Authentication would be built with 5 main kind blocks:
- Reactjs Router is a standard library for routing in React. It enables the navigation among views of various components in a React Application, allows changing the browser URL, and keeps the UI in sync with the URL.
- Reactjs Components let you split the UI into independent, reusable pieces, and think about each piece in isolation.
- Reactjs Service is a bridge between Reactjs Component and Backend Server, it is used to do technical logic with Backend Server (using Ajax Engine to fetch data from Backend, or using Local Storage to save user login data) and returned a response data to React.js Components
- Local Storage allow to save key/value pairs in a web browser. It is a place to save the login user’s info.
- Axios – (an Ajax Engine) is a promise-based HTTP client for the browser and Node. js. Axios makes it easy to send asynchronous HTTP requests to REST endpoints and perform CRUD operations.
Jwt SpringBoot Token Security RestAPIs Diagram Overview
This is diagram for Spring Security/JWT (Springboot Token Based Authentication Example) classes that are separated into 3 layers:
– HTTP
– Spring Security
– REST API

Look at the diagram above, we can easily associate these components with Spring Security Authentication process: receive HTTP request, filter, authenticate, store Authentication data, generate token, get User details, authorize, handle exception…
At a glance:
– SecurityContextHolder
provides access to the SecurityContext
.
– SecurityContext
holds the Authentication
and possibly request-specific security information.
– Authentication
represents the principal which includes GrantedAuthority
that reflects the application-wide permissions granted to a principal.
– UserDetails
contains necessary information to build an Authentication
object from DAOs or other source of security data.
– UserDetailsService
helps to create a UserDetails from a String-based username and is usually used by AuthenticationProvider
.
– JwtAuthTokenFilter
(extends OncePerRequestFilter
) pre-processes HTTP request, from Token
, create Authentication
and populate it to SecurityContext
.
– JwtProvider
validates, parses token String or generates token String from UserDetails
.
– UsernamePasswordAuthenticationToken
gets username/password from login Request
and combines into an instance of Authentication
interface.
– AuthenticationManager
uses DaoAuthenticationProvider
(with help of UserDetailsService
& PasswordEncoder
) to validate instance of UsernamePasswordAuthenticationToken
, then returns a fully populated Authentication
instance on successful authentication.
– SecurityContext
is established by calling SecurityContextHolder.getContext().setAuthentication(…)
with returned authentication object above.
– AuthenticationEntryPoint
handles AuthenticationException
.
– Access to Restful API is protected by HTTPSecurity
and authorized with Method Security Expressions.
When a HTTP request comes (from a browser, a web service client, an HttpInvoker or an AJAX application – Spring doesn’t care), it will go through a chain of filters for authentication and authorization purposes.
So, it is also true for a User Authentication request, that filter chain will be applied until relevant Authentication Filter is found.
In this architecture, we add our JwtAuthTokenFilter
(that extends Spring OncePerRequestFilter
abstract class) to the chain of filters.
class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { ... http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class); } }
JwtAuthTokenFilter
validates the Token using JwtProvider
:
class JwtAuthTokenFilter extends OncePerRequestFilter { @Autowired private JwtProvider tokenProvider; @Override protected void doFilterInternal(...) { String jwt = getJwt(request); if (jwt!=null && tokenProvider.validateJwtToken(jwt)) { ... } filterChain.doFilter(request, response); } }
Now we have 2 cases for Springboot Token Based Authentication Example:
– Login/SignUp: RestAPI with non-protected APIs -> authenticate Login Request with AuthenticationManager
, if error occurs, handle AuthenticationException
with AuthenticationEntryPoint
.
– With protected Resources:
+ jwt token is null/invalid -> if Authenticated Error occurs, handle AuthenticationException
with AuthenticationEntryPoint
.
+ jwt token is valid -> from token, get User
information, then create AuthenticationToken
.
More details, you can following a session of tutorial: Spring Security JWT Architecture – Springboot Token Based Authentication
Project Goal
We create a Reactjs JWT Authentication project as below:

It includes 8 components and 2 services and a router in app.js
file.
– Home page:

– User Register page:

– Login Page:

– Profile Page:

– Use Page:

– Project Manager Page:

– Reactjs Admin page:

Jwt SpringBoot Token Authentication RestAPIs Implementation
Review SpringBoot Jwt Architecture Diagram

Details check the above session: Jwt SpringBoot Token Security RestAPIs Diagram Overview
Technology Stack
For Springboot Token Based Authentication Example, we use below stack of tech:
– Spring Boot
– jjwt – 0.9.0
– Spring Security
– Spring JPA
– MySQL
Project Structure
We create a SpringBoot project as below:

– model
package defines 2 entities User
& Role
that have many-to-many relationship:

– repository
package contains interfaces that use Hibernate JPA to store/retrieve data from MySQL database.
– controller
package defines RestAPIs for user signup/signin and testing protected resources that is secured with JWT.
– message
package defines payload data transferred from user agents (Browser/RestClient…) to RestAPIs and message back.
– security
package is the main part of the project that implements JWT security.
Jwt SpringBoot Token Authentication Goal
In the tutorial “Reactjs Jwt Springboot Token Based Authentication Example”, we expose 2 RestAPIs to signup
and signin
:
Sign up: /api/auth/signup

– Sign In /api/auth/signin
:

– In the tutorial, We expose 3 RestAPIs to test protected resources:
@GetMapping("/api/test/user") @PreAuthorize("hasRole('USER') or hasRole('ADMIN')") public String userAccess() { return ">>> User Contents!"; } @GetMapping("/api/test/pm") @PreAuthorize("hasRole('PM') or hasRole('ADMIN')") public String projectManagementAccess() { return ">>> Board Management Project"; } @GetMapping("/api/test/admin") @PreAuthorize("hasRole('ADMIN')") public String adminAccess() { return ">>> Admin Contents"; }
– Example Access Successfully:

– Example Unauthorized:

Create Jwt Springboot Project
In the tutorial Springboot Token Based Authentication Example, We create a SpringBoot project with below dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
If you use MySQL database, add more the below dependency:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
If you use PostgreSQL database, add more the below dependency:
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
Create User Defined Models for Jwt authentication
– User.java
model contains 5 attributes:
- id
- name
- username
- password
package com.ozenero.jwtauthentication.model; import java.util.HashSet; import java.util.Set; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.Table; import javax.persistence.UniqueConstraint; import javax.validation.constraints.Email; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Size; import org.hibernate.annotations.NaturalId; @Entity @Table(name = "users", uniqueConstraints = { @UniqueConstraint(columnNames = { "username" }), @UniqueConstraint(columnNames = { "email" }) }) public class User{ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @NotBlank @Size(min=3, max = 50) private String name; @NotBlank @Size(min=3, max = 50) private String username; @NaturalId @NotBlank @Size(max = 50) @Email private String email; @NotBlank @Size(min=6, max = 100) private String password; @ManyToMany(fetch = FetchType.LAZY) @JoinTable(name = "user_roles", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id")) private Setroles = 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 getRoles() { return roles; } public void setRoles(Set roles) { this.roles = roles; } }
– Role.java
model contains 2 attributes:
- id
- rolename
package com.ozenero.jwtauthentication.model; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; import org.hibernate.annotations.NaturalId; @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.java
:
package com.ozenero.jwtauthentication.model; public enum RoleName { ROLE_USER, ROLE_PM, ROLE_ADMIN }
Implement UserRepository
– UserRepository
:
package com.ozenero.jwtauthentication.repository; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import com.ozenero.jwtauthentication.model.User; @Repository public interface UserRepository extends JpaRepository{ Optional findByUsername(String username); Boolean existsByUsername(String username); Boolean existsByEmail(String email); }
– RoleRepository.java
:
package com.ozenero.jwtauthentication.repository; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import com.ozenero.jwtauthentication.model.Role; import com.ozenero.jwtauthentication.model.RoleName; @Repository public interface RoleRepository extends JpaRepository{ Optional findByName(RoleName roleName); }
Implement Jwt Token Authentication Spring Security
– Configure WebSecurityConfig.java
:
package com.ozenero.jwtauthentication.security; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import com.ozenero.jwtauthentication.security.jwt.JwtAuthEntryPoint; import com.ozenero.jwtauthentication.security.jwt.JwtAuthTokenFilter; import com.ozenero.jwtauthentication.security.services.UserDetailsServiceImpl; @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity( prePostEnabled = true ) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired UserDetailsServiceImpl userDetailsService; @Autowired private JwtAuthEntryPoint unauthorizedHandler; @Bean public JwtAuthTokenFilter authenticationJwtTokenFilter() { return new JwtAuthTokenFilter(); } @Override public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception { authenticationManagerBuilder .userDetailsService(userDetailsService) .passwordEncoder(passwordEncoder()); } @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(HttpSecurity http) throws Exception { http.cors().and().csrf().disable(). authorizeRequests() .antMatchers("/api/auth/**").permitAll() .anyRequest().authenticated() .and() .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class); } }
– @EnableWebSecurity
is used to enable web security in a project.
– @EnableGlobalMethodSecurity(prePostEnabled = true)
is used to enable Spring Security global method security.
-> Example:
@PreAuthorize("hasRole('USER') or hasRole('ADMIN')") public String userAccess() { @GetMapping("/api/test/pm") @PreAuthorize("hasRole('PM') or hasRole('ADMIN')") @GetMapping("/api/test/admin") @PreAuthorize("hasRole('ADMIN')")
– PasswordEncoder
uses the BCrypt strong hashing function.
Implement UserDetails Service
– UserDetailsServiceImpl
implements UserDetailsService
that will override loadUserByUsername
method. loadUserByUsername
method will find a record from users database tables to build a UserDetails object for authentication.
package com.ozenero.jwtauthentication.security.services; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.ozenero.jwtauthentication.model.User; import com.ozenero.jwtauthentication.repository.UserRepository; @Service public class UserDetailsServiceImpl implements UserDetailsService { @Autowired UserRepository userRepository; @Override @Transactional public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userRepository.findByUsername(username) .orElseThrow(() -> new UsernameNotFoundException("User Not Found with -> username or email : " + username) ); return UserPrinciple.build(user); } }
-> UserPrinciple
will implement UserDetails
.
UserPrinciple
is not used directly by Spring Security for security purposes.
It simply stores user information which is later encapsulated into Authentication
objects. This allows non-security related user information (such as email addresses, telephone numbers etc) to be stored.
package com.ozenero.jwtauthentication.security.services; import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import com.fasterxml.jackson.annotation.JsonIgnore; import com.ozenero.jwtauthentication.model.User; public class UserPrinciple implements UserDetails { private static final long serialVersionUID = 1L; private Long id; private String name; private String username; private String email; @JsonIgnore private String password; private Collection extends GrantedAuthority> authorities; public UserPrinciple(Long id, String name, String username, String email, String password, Collection extends GrantedAuthority> authorities) { this.id = id; this.name = name; this.username = username; this.email = email; this.password = password; this.authorities = authorities; } public static UserPrinciple build(User user) { Listauthorities = user.getRoles().stream().map(role -> new SimpleGrantedAuthority(role.getName().name()) ).collect(Collectors.toList()); return new UserPrinciple( user.getId(), user.getName(), user.getUsername(), user.getEmail(), user.getPassword(), authorities ); } public Long getId() { return id; } public String getName() { return name; } public String getEmail() { return email; } @Override public String getUsername() { return username; } @Override public String getPassword() { return password; } @Override public Collection extends GrantedAuthority> getAuthorities() { return authorities; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; UserPrinciple user = (UserPrinciple) o; return Objects.equals(id, user.id); } }
Jwt SpringBoot Authentication classes
– JwtAuthTokenFilter
extends OncePerRequestFilter
.
org.springframework.web.filter.OncePerRequestFilter
-> Executes once per request. This is a filter base class that is used to guarantee a single execution per request dispatch. It provides a doFilterInternal
method with HttpServletRequest
and HttpServletResponse
arguments.
In JwtAuthTokenFilter
class, the doFilterInternal
method will do:
- get JWT token from header
- validate JWT
- parse username from validated JWT
- load data from users table, then build an authentication object
- set the authentication object to Security Context
package com.ozenero.jwtauthentication.security.jwt; import java.io.IOException; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.web.filter.OncePerRequestFilter; import com.ozenero.jwtauthentication.security.services.UserDetailsServiceImpl; public class JwtAuthTokenFilter extends OncePerRequestFilter { @Autowired private JwtProvider tokenProvider; @Autowired private UserDetailsServiceImpl userDetailsService; private static final Logger logger = LoggerFactory.getLogger(JwtAuthTokenFilter.class); @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { try { String jwt = getJwt(request); if (jwt!=null && tokenProvider.validateJwtToken(jwt)) { String username = tokenProvider.getUserNameFromJwtToken(jwt); UserDetails userDetails = userDetailsService.loadUserByUsername(username); UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authentication); } } catch (Exception e) { logger.error("Can NOT set user authentication -> Message: {}", e); } filterChain.doFilter(request, response); } private String getJwt(HttpServletRequest request) { String authHeader = request.getHeader("Authorization"); if (authHeader != null && authHeader.startsWith("Bearer ")) { return authHeader.replace("Bearer ",""); } return null; } }
– JwtAuthEntryPoint
is used to handle Error exception when having unauthorized requests.
package com.ozenero.jwtauthentication.security.jwt; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.stereotype.Component; @Component public class JwtAuthEntryPoint implements AuthenticationEntryPoint { private static final Logger logger = LoggerFactory.getLogger(JwtAuthEntryPoint.class); @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException { logger.error("Unauthorized error. Message - {}", e.getMessage()); response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Error -> Unauthorized"); } }
– JwtProvider
is an util class, it implements useful functions:
- generate a JWT token
- valiate a JWT token
- parse username from JWT token
package com.ozenero.jwtauthentication.security.jwt; import java.util.Date; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.Authentication; import org.springframework.stereotype.Component; import com.ozenero.jwtauthentication.security.services.UserPrinciple; import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.MalformedJwtException; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.SignatureException; import io.jsonwebtoken.UnsupportedJwtException; /** * @Copyright https://ozenero.com * @author ozenero.com * */ @Component public class JwtProvider { private static final Logger logger = LoggerFactory.getLogger(JwtProvider.class); @Value("${ozenero.app.jwtSecret}") private String jwtSecret; @Value("${ozenero.app.jwtExpiration}") private int jwtExpiration; public String generateJwtToken(Authentication authentication) { UserPrinciple userPrincipal = (UserPrinciple) authentication.getPrincipal(); return Jwts.builder() .setSubject((userPrincipal.getUsername())) .setIssuedAt(new Date()) .setExpiration(new Date((new Date()).getTime() + jwtExpiration*1000)) .signWith(SignatureAlgorithm.HS512, jwtSecret) .compact(); } public boolean validateJwtToken(String authToken) { try { Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken); return true; } catch (SignatureException e) { logger.error("Invalid JWT signature -> Message: {} ", e); } catch (MalformedJwtException e) { logger.error("Invalid JWT token -> Message: {}", e); } catch (ExpiredJwtException e) { logger.error("Expired JWT token -> Message: {}", e); } catch (UnsupportedJwtException e) { logger.error("Unsupported JWT token -> Message: {}", e); } catch (IllegalArgumentException e) { logger.error("JWT claims string is empty -> Message: {}", e); } return false; } public String getUserNameFromJwtToken(String token) { return Jwts.parser() .setSigningKey(jwtSecret) .parseClaimsJws(token) .getBody().getSubject(); } }
Implement Security SpringBoot Rest-Controllers
- Create Payload Message
LoginForm.java
contains username
& password
->
package com.ozenero.jwtauthentication.message.request; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Size; public class LoginForm { @NotBlank @Size(min=3, max = 60) private String username; @NotBlank @Size(min = 6, max = 40) private String password; 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; } }
– SignUpForm.java
contains:
- name
- username
- role
- password
package com.ozenero.jwtauthentication.message.request; import java.util.Set; import javax.validation.constraints.*; public class SignUpForm { @NotBlank @Size(min = 3, max = 50) private String name; @NotBlank @Size(min = 3, max = 50) private String username; @NotBlank @Size(max = 60) @Email private String email; private Setrole; @NotBlank @Size(min = 6, max = 40) private String password; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } 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 getRole() { return this.role; } public void setRole(Set role) { this.role = role; } }
– JwtResponse.java
is returned by SpringBoot server after successful authentication, it contains 2 parts:
- JWT Token
- Schema Type of Token
package com.ozenero.jwtauthentication.message.response; public class JwtResponse { private String token; private String type = "Bearer"; public JwtResponse(String accessToken) { this.token = accessToken; } public String getAccessToken() { return token; } public void setAccessToken(String accessToken) { this.token = accessToken; } public String getTokenType() { return type; } public void setTokenType(String tokenType) { this.type = tokenType; } }
– AuthRestAPIs.java
defines 2 APIs:
/api/auth/signup
: sign up
-> check username/email is already in use.
-> create User object
-> store to database/api/auth/signin
: sign in
-> attempt to authenticate withAuthenticationManager
bean.
-> add authentication object toSecurityContextHolder
-> Generate JWT token, then return JWT to client
package com.ozenero.jwtauthentication.controller; import java.util.HashSet; import java.util.Set; import javax.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.ozenero.jwtauthentication.message.request.LoginForm; import com.ozenero.jwtauthentication.message.request.SignUpForm; import com.ozenero.jwtauthentication.message.response.JwtResponse; import com.ozenero.jwtauthentication.model.Role; import com.ozenero.jwtauthentication.model.RoleName; import com.ozenero.jwtauthentication.model.User; import com.ozenero.jwtauthentication.repository.RoleRepository; import com.ozenero.jwtauthentication.repository.UserRepository; import com.ozenero.jwtauthentication.security.jwt.JwtProvider; @CrossOrigin(origins = "*", maxAge = 3600) @RestController @RequestMapping("/api/auth") public class AuthRestAPIs { @Autowired AuthenticationManager authenticationManager; @Autowired UserRepository userRepository; @Autowired RoleRepository roleRepository; @Autowired PasswordEncoder encoder; @Autowired JwtProvider jwtProvider; @PostMapping("/signin") public ResponseEntity> authenticateUser(@Valid @RequestBody LoginForm loginRequest) { Authentication authentication = authenticationManager.authenticate( new UsernamePasswordAuthenticationToken( loginRequest.getUsername(), loginRequest.getPassword() ) ); SecurityContextHolder.getContext().setAuthentication(authentication); String jwt = jwtProvider.generateJwtToken(authentication); return ResponseEntity.ok(new JwtResponse(jwt)); } @PostMapping("/signup") public ResponseEntityregisterUser(@Valid @RequestBody SignUpForm signUpRequest) { if(userRepository.existsByUsername(signUpRequest.getUsername())) { return new ResponseEntity ("Fail -> Username is already taken!", HttpStatus.BAD_REQUEST); } if(userRepository.existsByEmail(signUpRequest.getEmail())) { return new ResponseEntity ("Fail -> Email is already in use!", HttpStatus.BAD_REQUEST); } // Creating user's account User user = new User(signUpRequest.getName(), signUpRequest.getUsername(), signUpRequest.getEmail(), encoder.encode(signUpRequest.getPassword())); Set strRoles = signUpRequest.getRole(); Set roles = new HashSet<>(); strRoles.forEach(role -> { switch(role) { case "admin": Role adminRole = roleRepository.findByName(RoleName.ROLE_ADMIN) .orElseThrow(() -> new RuntimeException("Fail! -> Cause: User Role not find.")); roles.add(adminRole); break; case "pm": Role pmRole = roleRepository.findByName(RoleName.ROLE_PM) .orElseThrow(() -> new RuntimeException("Fail! -> Cause: User Role not find.")); roles.add(pmRole); break; default: Role userRole = roleRepository.findByName(RoleName.ROLE_USER) .orElseThrow(() -> new RuntimeException("Fail! -> Cause: User Role not find.")); roles.add(userRole); } }); user.setRoles(roles); userRepository.save(user); return ResponseEntity.ok().body("User registered successfully!"); } }package com.ozenero.jwtauthentication.controller; import java.util.HashSet; import java.util.Set; import javax.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.ozenero.jwtauthentication.message.request.LoginForm; import com.ozenero.jwtauthentication.message.request.SignUpForm; import com.ozenero.jwtauthentication.message.response.JwtResponse; import com.ozenero.jwtauthentication.model.Role; import com.ozenero.jwtauthentication.model.RoleName; import com.ozenero.jwtauthentication.model.User; import com.ozenero.jwtauthentication.repository.RoleRepository; import com.ozenero.jwtauthentication.repository.UserRepository; import com.ozenero.jwtauthentication.security.jwt.JwtProvider; @CrossOrigin(origins = "*", maxAge = 3600) @RestController @RequestMapping("/api/auth") public class AuthRestAPIs { @Autowired AuthenticationManager authenticationManager; @Autowired UserRepository userRepository; @Autowired RoleRepository roleRepository; @Autowired PasswordEncoder encoder; @Autowired JwtProvider jwtProvider; @PostMapping("/signin") public ResponseEntity> authenticateUser(@Valid @RequestBody LoginForm loginRequest) { Authentication authentication = authenticationManager.authenticate( new UsernamePasswordAuthenticationToken( loginRequest.getUsername(), loginRequest.getPassword() ) ); SecurityContextHolder.getContext().setAuthentication(authentication); String jwt = jwtProvider.generateJwtToken(authentication); return ResponseEntity.ok(new JwtResponse(jwt)); } @PostMapping("/signup") public ResponseEntity registerUser(@Valid @RequestBody SignUpForm signUpRequest) { if(userRepository.existsByUsername(signUpRequest.getUsername())) { return new ResponseEntity ("Fail -> Username is already taken!", HttpStatus.BAD_REQUEST); } if(userRepository.existsByEmail(signUpRequest.getEmail())) { return new ResponseEntity ("Fail -> Email is already in use!", HttpStatus.BAD_REQUEST); } // Creating user's account User user = new User(signUpRequest.getName(), signUpRequest.getUsername(), signUpRequest.getEmail(), encoder.encode(signUpRequest.getPassword())); Set strRoles = signUpRequest.getRole(); Set roles = new HashSet<>(); strRoles.forEach(role -> { switch(role) { case "admin": Role adminRole = roleRepository.findByName(RoleName.ROLE_ADMIN) .orElseThrow(() -> new RuntimeException("Fail! -> Cause: User Role not find.")); roles.add(adminRole); break; case "pm": Role pmRole = roleRepository.findByName(RoleName.ROLE_PM) .orElseThrow(() -> new RuntimeException("Fail! -> Cause: User Role not find.")); roles.add(pmRole); break; default: Role userRole = roleRepository.findByName(RoleName.ROLE_USER) .orElseThrow(() -> new RuntimeException("Fail! -> Cause: User Role not find.")); roles.add(userRole); } }); user.setRoles(roles); userRepository.save(user); return ResponseEntity.ok().body("User registered successfully!"); } }
– TestRestAPIs
define 3 RestAPIs:
/api/test/user
-> access by users hasUSER_ROLE
orADMIN_ROLE
/api/test/pm
-> access by users hasUSER_PM
orADMIN_ROLE
/api/test/admin
-> access by users hasADMIN_ROLE
package com.ozenero.jwtauthentication.controller; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class TestRestAPIs { @GetMapping("/api/test/user") @PreAuthorize("hasRole('USER') or hasRole('ADMIN')") public String userAccess() { return ">>> User Contents!"; } @GetMapping("/api/test/pm") @PreAuthorize("hasRole('PM') or hasRole('ADMIN')") public String projectManagementAccess() { return ">>> Board Management Project"; } @GetMapping("/api/test/admin") @PreAuthorize("hasRole('ADMIN')") public String adminAccess() { return ">>> Admin Contents"; } }
Configure Jwt SpringBoot Application Properties
- Configure for MySQL database:
spring.datasource.url=jdbc:mysql://localhost:3306/ozenerodb spring.datasource.username=root spring.datasource.password=12345 spring.jpa.generate-ddl=true # App Properties ozenero.app.jwtSecret=jwtLoizenai.comSecretKey ozenero.app.jwtExpiration=86400
- Configure for PostgreSQL database:
## PostgreSQL spring.datasource.url=jdbc:postgresql://localhost:5432/ozenerodb spring.datasource.username=postgres spring.datasource.password=123 #drop & create table again, good for testing, comment this in production spring.jpa.hibernate.ddl-auto=create
Testing Jwt SpringBoot RestAPIs
– Start Springboot server by commandline mvn spring-boot:run
.
– Insert data to roles table :
INSERT INTO roles(name) VALUES('ROLE_USER'); INSERT INTO roles(name) VALUES('ROLE_PM'); INSERT INTO roles(name) VALUES('ROLE_ADMIN');
Sign-Up 3 users:
- Jack has ROLE_USER
role
- Adam has ROLE_PM
& ROLE_USER
roles
- Thomas has ROLE_ADMIN
role

– Check database’s tables:

SignIn with normal user
– Jack can access api/test/user url, can NOT access others.
-> Sign In:

Access Protected Resources:


SignIn with PM user
– Adam can access api/test/user and api/test/pm url.
Can NOT access /api/test/admin url.
-> Sign In:

-> Access Protected Resources:



SignIn with Admin user
– Thomas can access all URLs.
-> Sign In:

-> Access Protected Resource:

Reactjs Login JWT Token Authentication Implementation
Review Reactjs Login jwt token architecture Diagram

More details you can see more at above session: Reactjs JWT Authentication Diagram Overview
Step by step to build Reactjs JWT Authentication Application

For building the Reactjs JWT Authentication, we do below steps:
- We create Reactjs Jwt Authentication application
- We implement React.js Authentication Service. It provides RestAPIs to signin/signup and retrieve user's login info from Local Storage
- We implement React.js Backend Service. It provides RestAPIs to retrieve data from Security Backend
- We implement Reactjs components to signIn/signUp and show data from backend server
- We define a Reactjs router to navigate between UI components
Setup Reactjs Project
Create React App is a command line utility that generates React projects for you. It’s a convenient tool because it also offers commands that will build and optimize your project for production.
The create-react-app
will set up everything you need to run a React application.
– Create a new project in the app directory with Yarn.
yarn create react-app Reactjs-Jwt-Authentication
Project Structure:

More details you can see at: Create Reactjs Project
After the app creation process completes, navigate into the app directory and install Bootstrap, React Router, and Reactstrap.
– Reactstrap: This library contains React Bootstrap 4 components that favor composition and control. The library does not depend on jQuery or Bootstrap javascript.
– React Router: Components are the heart of React’s powerful, declarative programming model. React Router is a collection of navigational components that compose declaratively with your application.
cd Reactjs-Jwt-Authentication yarn add bootstrap react-cookie react-router-dom reactstrap
Implement Reactjs JWT Authentication Service
The service AuthenticationService
implements 4 main functions to signup
/signin
/signout
and get current login user's info:
To do http requests with Backend server, the AuthenticationService
use Axios (an HttpClient) to interact with Server. AuthenticationService
uses localStorage
to store user login's info.
import axios from "axios"; /** * @Copyright by https://ozenero.com * youtube ozenero */ class AuthenticationService { signin = (username, password) => { return axios.post("/api/auth/signin", {username, password}) .then(response => { if (response.data.accessToken) { localStorage.setItem("user", JSON.stringify(response.data)); } return response.data; }) .catch(err => { console.log(err); throw err; }); } signOut() { localStorage.removeItem("user"); } register = async(firstname, lastname, username, email, password) => { return axios.post("/api/auth/signup", { firstname, lastname, username, email, password }); } getCurrentUser() { return JSON.parse(localStorage.getItem('user'));; } } export default new AuthenticationService();
Implement Reactjs JWT Backend Service
BackendService
is used to get resources from server:
- getUserBoard()
- getPmBoard()
- getAdminBoard()
With each request from BackendService
to server, we use axios interceptors to attach the Authorization
header with JWT token.
import axios from 'axios'; // Add a request interceptor axios.interceptors.request.use( config => { const user = JSON.parse(localStorage.getItem('user')); if(user && user.accessToken){ const token = 'Bearer ' + user.accessToken; config.headers.Authorization = token; } return config; }); class BackendService { async getUserBoard() { return await axios.get("/api/test/user"); } async getPmBoard() { return await axios.get("/api/test/pm"); } async getAdminBoard() { return await axios.get("/api/test/admin"); } } export default new BackendService();
Implement Reactjs JWT AppNavbar component

import React, { Component } from 'react'; import { Collapse, Nav, Navbar, NavbarBrand, NavbarToggler, NavbarText, NavItem, NavLink } from 'reactstrap'; import { Link } from 'react-router-dom'; import { withRouter } from 'react-router-dom'; import AuthenticationService from '../services/AuthenticationService'; class AppNavbar extends Component { constructor(props) { super(props); this.state = {isOpen: false}; this.toggle = this.toggle.bind(this); this.state = { showUser: false, showPM: false, showAdmin: false, username: undefined, login: false }; } componentDidMount() { const user = AuthenticationService.getCurrentUser(); if (user) { const roles = []; user.authorities.forEach(authority => { roles.push(authority.authority) }); this.setState({ showUser: true, showPM: roles.includes("ROLE_PM") || roles.includes("ROLE_ADMIN"), showAdmin: roles.includes("ROLE_ADMIN"), login: true, username: user.username }); } } signOut = () => { AuthenticationService.signOut(); this.props.history.push('/home'); window.location.reload(); } toggle() { this.setState({ isOpen: !this.state.isOpen }); } render() { return <Navbar color="dark" dark expand="md"> <NavbarBrand tag={Link} to="/home">Loizenai.com</NavbarBrand> <Nav className="mr-auto"> <NavLink href="/home">Home</NavLink> {this.state.showUser && <NavLink href="/user">User</NavLink>} {this.state.showPM && <NavLink href="/pm">PM</NavLink>} {this.state.showAdmin && <NavLink href="/admin">Admin</NavLink>} </Nav> <NavbarToggler onClick={this.toggle}/> <Collapse isOpen={this.state.isOpen} navbar> { this.state.login ? ( <Nav className="ml-auto" navbar> <NavItem> <NavbarText> Signed in as: <a href="/profile">{this.state.username}</a> </NavbarText> </NavItem> <NavItem> <NavLink href="#" onClick={this.signOut}>SignOut</NavLink> </NavItem> </Nav> ) : ( <Nav className="ml-auto" navbar> <NavItem> <NavLink href="/signin">Login</NavLink> </NavItem> <NavItem> <NavLink href="/signup">SignUp</NavLink> </NavItem> </Nav> ) } </Collapse> </Navbar>; } } export default withRouter(AppNavbar);
- In AppNavbar
component, We use componentDidMount()
to load user info, and then extracts data from user login info to determine what links would be showed on React.js's UI.
Implement Reactjs JWT Home component

import React, { Component } from 'react'; import AppNavbar from './AppNavbar'; import { Link } from 'react-router-dom'; import { Button, Container } from 'reactstrap'; import { Alert } from "react-bootstrap" class Home extends Component { constructor(props) { super(props); } componentDidMount() { } render() { return ( <div> <AppNavbar/> <Container fluid> <div style={{marginTop:"20px"}}> <Alert variant="primary"> <h2>Reactjs JWT Authentication Application</h2> <Button color="success"><Link to="/signin"><span style={{color:"white"}}>Login</span></Link></Button> </Alert> </div> </Container> </div> ); } } export default Home;
Implement Reactjs JWT SignUp component

- For posting data from register-form to backend server, SignUp component uses the register(...)
function of AuthenticationService
service.
- SignUp
component implements a changeHandler()
function to save the values from input fields of Register form to the state object of SignUp
component.
- The function changeHandler()
also includes segment code to validate input data from register-form:

- Detail Coding:
import React, { Component } from 'react'; import AppNavbar from './AppNavbar'; import { Container } from 'reactstrap'; import { Button, Form, FormGroup, Input, Label, Row, Col } from "reactstrap"; import { Alert } from "react-bootstrap" import Authentication from '../services/AuthenticationService' const validEmailRegex = RegExp(/^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i); const validateForm = (errors) => { let valid = true; Object.values(errors).forEach( (val) => val.length > 0 && (valid = false) ); return valid; } class SignUp extends Component { constructor(props) { super(props); this.state = { firstname: "", lastname: "", username: "", email: "", password: "", message: "", successful: false, validForm: true, errors: { firstname: '', lastname: '', username: '', email: '', password: '' } }; } changeHandler = (event) => { const { name, value } = event.target; let errors = this.state.errors; switch (name) { case 'firstname': errors.firstname = value.length < 3 ? 'FirstName must be 3 characters long!' : ''; break; case 'lastname': errors.lastname = value.length < 3 ? 'LastName must be 3 characters long!' : ''; break; case 'username': errors.username = value.length < 5 ? 'Username must be 5 characters long!' : ''; break; case 'email': errors.email = validEmailRegex.test(value) ? '' : 'Email is not valid!'; break; case 'password': errors.password = value.length < 8 ? 'Password must be 8 characters long!' : ''; break; default: break; } this.setState({errors, [name]: value}, ()=> { console.log(errors) }) } signUp = (e) => { e.preventDefault(); const valid = validateForm(this.state.errors); this.setState({validForm: valid}); if(valid){ Authentication.register( this.state.firstname, this.state.lastname, this.state.username, this.state.email, this.state.password ).then( response => { this.setState({ message: response.data.message, successful: true }); }, error => { console.log("Fail! Error = " + error.toString()); this.setState({ successful: false, message: error.toString() }); } ); } } render() { const title = <h2>Register User</h2>; const errors = this.state.errors; let alert = ""; if(this.state.message){ if(this.state.successful){ alert = ( <Alert variant="success"> {this.state.message} </Alert> ); }else{ alert = ( <Alert variant="danger"> {this.state.message} </Alert> ); } } return ( <div> <AppNavbar/> <Container fluid> <Row> <Col sm="12" md={{ size: 4, offset: 4 }}> {title} <Form onSubmit={this.signUp}> <FormGroup controlId="forFirstname"> <Label for="firstname">First Name</Label> <Input type="text" placeholder="Enter First Name" name="firstname" id="firstname" value={this.state.firstname} autoComplete="firstname" onChange={this.changeHandler} /> { errors.firstname && ( <Alert variant="danger"> {errors.firstname} </Alert> ) } </FormGroup> <FormGroup controlId="forLastname"> <Label for="lastname">Last Name</Label> <Input type="text" placeholder="Enter Last Name" name="lastname" id="lastname" value={this.state.lastname} autoComplete="lastname" onChange={this.changeHandler} /> { errors.lastname && ( <Alert variant="danger"> {errors.firstname} </Alert> ) } </FormGroup> <FormGroup controlId="forUsername"> <Label for="username">Username</Label> <Input type="text" placeholder="Enter UserName" name="username" id="username" value={this.state.username} autoComplete="username" onChange={this.changeHandler} /> { errors.username && ( <Alert variant="danger"> {errors.username} </Alert> ) } </FormGroup> <FormGroup controlId="formEmail"> <Label for="email">Email</Label> <Input required type="text" placeholder="Enter Email" name="email" id="email" value={this.state.email} autoComplete="email" onChange={this.changeHandler} /> { errors.email && ( <Alert variant="danger"> {errors.email} </Alert> ) } </FormGroup> <FormGroup controlId="formPassword"> <Label for="password">Password</Label> <Input required type="password" placeholder="Enter Password" name="password" id="password" value={this.state.password} autoComplete="password" onChange={this.changeHandler} /> { errors.password && ( <Alert key="errorspassword" variant="danger"> {errors.password} </Alert> ) } </FormGroup> <Button variant="primary" type="submit"> Create </Button> { !this.state.validForm && ( <Alert key="validForm" variant="danger"> Please check the inputs again! </Alert> ) } {alert} </Form> </Col> </Row> </Container> </div>); } } export default SignUp;
Implement Reactjs JWT Login component

- Login
component implement a function doLogin
that uses a function signin()
of AuthenticationService
service to signin to secured Backend server with Jwt token.
import React, { Component } from 'react'; import AppNavbar from './AppNavbar'; import { Container } from 'reactstrap'; import { Form, Alert, FormGroup, Input, Label, Row, Col } from "reactstrap"; import {Button} from 'react-bootstrap'; import AuthenticationService from "../services/AuthenticationService"; import avatar from '../../avatar.png'; import '../../App.css'; class Login extends Component { constructor(props) { super(props); this.state = { username: "", password: "", error: "" }; } changeHandler = (event) => { let nam = event.target.name; let val = event.target.value; this.setState({[nam]: val}); } doLogin = async (event) => { event.preventDefault(); AuthenticationService .signin(this.state.username, this.state.password) .then( () => { this.props.history.push('/profile'); }, error => { console.log("Login fail: error = { " + error.toString() + " }"); this.setState({error: "Can not signin successfully ! Please check username/password again"}); } ); } render() { return ( <div> <AppNavbar/> <Container fluid> <Row style={{marginTop:"20px"}}> <Col sm="12" md={{ size: 3, offset: 4 }}> <div style={{marginBottom: "10px"}}> <img src={avatar} alt="Avatar" className="avatar center" style={{width: "50%", height: "auto"}}/> </div> <Form onSubmit={this.doLogin}> <FormGroup> <Label for="username"><strong>Username</strong></Label> <Input autoFocus type="text" name="username" id="username" value={this.state.username} placeholder="Enter Username" autoComplete="username" onChange={this.changeHandler} /> </FormGroup> <FormGroup> <Label for="password"><strong>Password</strong></Label> <Input type="password" name="password" id="password" value={this.state.password} placeholder="Enter Password" autoComplete="password" onChange={this.changeHandler} /> </FormGroup> <Button type="submit" variant="primary" size="lg" block> Sign In </Button> { this.state.error && ( <Alert color="danger"> {this.state.error} </Alert> ) } </Form> </Col> </Row> </Container> </div>); } } export default Login;
Implement Reactjs JWT Profile component
Profile
component just uses a function componentDidMount()
that calls a function getCurrentUser()
of AuthenticationService
service to get the current user login's info and shows them on UI.

import React, { Component } from 'react'; import AppNavbar from './AppNavbar'; import { Link } from 'react-router-dom'; import { Button, Container } from 'reactstrap'; import { Alert } from "react-bootstrap" import AuthenticationService from '../services/AuthenticationService'; class Profile extends Component { constructor(props) { super(props); this.state = {user: undefined}; } componentDidMount() { const user = AuthenticationService.getCurrentUser(); this.setState({user: user}); } render() { let userInfo = ""; const user = this.state.user; // login if (user && user.accessToken) { let roles = ""; user.authorities.forEach(authority => { roles = roles + " " + authority.authority }); userInfo = ( <div style={{marginTop:"20px"}}> <Alert variant="info"> <h2>User Info</h2> <ul> <li>Username: {user.username}</li> <li>Access Token: {user.accessToken}</li> <li>Authorities: {roles}</li> </ul> </Alert> </div> ); } else { // not login userInfo = <div style={{marginTop:"20px"}}> <Alert variant="primary"> <h2>Profile Component</h2> <Button color="success"><Link to="/signin"><span style={{color:"white"}}>Login</span></Link></Button> </Alert> </div> } return ( <div> <AppNavbar/> <Container fluid> {userInfo} </Container> </div> ); } } export default Profile;
Implement Reactjs jwt token User Page

In the componentDidMount()
function, the component UserPage
uses the service BackendService
to load user-content from secured backend-server and show it on UI.
import AppNavbar from './AppNavbar'; import React, { Component } from 'react'; import { Container } from 'reactstrap'; import BackendService from '../services/BackendService'; import { Alert } from "react-bootstrap" class UserPage extends Component { constructor(props) { super(props); this.state={ content: "", error: "" } } componentDidMount() { BackendService.getUserBoard() .then( response => { this.setState({content: response.data}) } , error => { console.log(error); this.setState({ error: error.toString() }); }); } render() { return ( <div> <AppNavbar/> <Container fluid> { this.state.content ? ( <div style={{marginTop: "20px"}}> <Alert variant="info"> <h2>{this.state.content}</h2> </Alert> </div> ) : ( <div style={{marginTop: "20px"}}> <Alert variant="danger"> {this.state.error} </Alert> </div> ) } </Container> </div> ); } } export default UserPage;
Implement Reactjs jwt token PM Page

In the componentDidMount()
function, the component ProjectManagerPage
uses the service BackendService
to load pm-content from secured backend-server and show it on UI.
import AppNavbar from './AppNavbar'; import React, { Component } from 'react'; import { Container } from 'reactstrap'; import { Alert } from "reactstrap"; import BackendService from '../services/BackendService'; class ProjectManagerPage extends Component { constructor(props) { super(props); this.state={ content: "", error: "" } } componentDidMount() { BackendService.getPmBoard() .then( response => { this.setState({ content: response.data }) } , error => { console.log(error); this.setState({ error: error.toString() }); }); } render() { return ( <div> <AppNavbar/> <Container fluid> { this.state.content ? ( <div style={{marginTop: "20px"}}> <Alert color="info"> <h2>{this.state.content}</h2> </Alert> </div> ) : ( <div style={{marginTop: "20px"}}> <Alert color="danger"> {this.state.error} </Alert> </div> ) } </Container> </div> ); } } export default ProjectManagerPage;
Implement Reactjs jwt token Admin Page

In the componentDidMount()
function, the component AdminPage
uses the service BackendService
to load admin-content from secured backend-server and show it on UI.
import AppNavbar from './AppNavbar'; import React, { Component } from 'react'; import { Container } from 'reactstrap'; import { Alert } from "reactstrap"; import BackendService from '../services/BackendService'; class AdminPage extends Component { constructor(props) { super(props); this.state={ content: "", error: "" } } componentDidMount() { BackendService.getAdminBoard() .then( response => { this.setState({ content: response.data }) } , error => { console.log(error); this.setState({ error: error.toString() }); }); } render() { return ( <div> <AppNavbar/> <Container fluid> { this.state.content ? ( <div style={{marginTop: "20px"}}> <Alert variant="info"> <h2>{this.state.content}</h2> </Alert> </div> ) : ( <div style={{marginTop: "20px"}}> <Alert variant="danger"> {this.state.error} </Alert> </div> ) } </Container> </div> ); } } export default AdminPage;
Define Reactjs Router
App.js
uses React Router to navigate between components:
- path “/” and "/home" is mapped with
Home
component - path “/profile” is mapped with
Profile
component - path “/user” is mapped with
UserPage
component - path “/pm” is mapped with
ProjectManagerPage
component - path “/admin” is mapped with
Admin
component - path “/signin” is mapped with
Login
component - path “/signup” is mapped with
SignUp
component
import React, { Component } from 'react'; import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'; import './App.css'; import Home from './app/components/Home'; import Profile from './app/components/Profile'; import UserPage from './app/components/UserPage'; import ProjectManagerPage from './app/components/ProjectManagerPage'; import SignUp from './app/components/SignUp'; import AdminPage from './app/components/AdminPage'; import Login from './app/components/Login'; class App extends Component { render() { return ( <Router> <Switch> <Route path='/' exact={true} component={Home}/> <Route path='/home' exact={true} component={Home}/> <Route path='/profile' exact={true} component={Profile}/> <Route path='/user' exact={true} component={UserPage}/> <Route path='/pm' exact={true} component={ProjectManagerPage}/> <Route path='/admin' exact={true} component={AdminPage}/> <Route path='/signin' exact={true} component={Login}/> <Route path='/signup' exact={true} component={SignUp}/> </Switch> </Router> ) } } export default App;
Configure URL proxy request
To proxy from /api to http://localhost:8080/api, add a proxy setting to app/package.json.
"scripts": {...}, "proxy": "http://localhost:8080",
Integrative Testing – Reactjs JWT Authentication with Backend Security
- Start Backend Server at port 8080.
- Start Reactjs application with cmd: yarn start
Testcase 1 – Reactjs JWT SignUp

- Check MySQL database:

Testcase 2 – Reactjs JWT Login with user role

Then re-login with right username/password: jack-ozenero.com/123456789.
-> after signin successfully, Reactjs application will be redirected to Profile component:

- Get User content:

Testcase 3 – Reactjs JWT SignOut
To signout, just click to the signout link at the most right side of Navigation-Bar, after signout successfully, Reactjs application will be redirected to Home page.

Testcase 4 – Reactjs JWT SignIn with other roles
1. Register a Adam user, with PM_ROLE and USER_ROLE:

- Check MySQL:

- Adam logins successfully with below user login's info:

2. Register Thomas user, with ADMIN_ROLE:

- Check MySQL:

- Access the admin-content:


Further Reading
Sourcecode
Full sourcecode: "Reactjs Jwt SpringBoot Token Authentication Example - React.js Spring Security Login"
1. Reactjs Frontend:
reactjs-jwt-token-authentication
Github sourceode:
reactjs-jwt-authentication-github
2. Jwt SpringBoot Token Authentication Backend:
jwt-springboot-reactjs-token-authentication-example
Github sourcode:
jwt-springboot-reactjs-jwt-authentication