Integrating Spring Boot and React With Spring Security: Basic and JWT Authentication
Look at this in-depth tutorial on how to integrate Spring Boot and React with Spring security.
Join the DZone community and get the full member experience.
Join For FreeThis guide helps you setup Spring Security with Basic and JWT authentication with a full stack application using React as a frontend framework and Spring Boot as the backend REST API. We will be using JavaScript as the frontend language and Java as the backend language.
You Will Learn
- How to use React as a Frontend Framework
- How to use Spring to create Backend REST Service API
- How to setup Basic Authentication with Spring Boot
- How to use Spring Security with Spring Boot
- How to use JWT Security with Spring Boot
- How to verify Basic Authentication at user login calling a REST API with React
- How to make secured REST API calls from React frontend
- How to use sessionStorage to track tokens in the React frontend application
10-Step Reference Courses
- Spring Framework for Beginners in 10 Steps
- Spring Boot for Beginners in 10 Steps
- Angular in 10 Steps
- React in 10 Steps
- JPA and Hibernate in 10 Steps
- Complete in28Minutes Course Guide
Step 0: Get an Overview of the Full Stack Application
Understanding Basic Features of the Application
The following screenshot shows the application we would like to build:
We would request user for authentication credentials on the login page.
Once the user is authenticated using Basic Authentication or JWT Authentication, we show the course details page.
The user would be able to log out by clicking the logout button.
Understanding Full Stack Architecture
Following Screenshot shows the architecture of the application we would create:
Important points to note:
- REST API is exposed using Spring Boot
- REST API is secured using Spring Security. We discuss two approaches - Basic Auth and JWT.
- REST API is consumed from React Frontend to present the UI
- The Database, in this example, is a hardcoded in-memory static list.
You can find more details about Full Stack Architecture here — Full Stack Application Architecture - Spring Boot and React
Getting an Overview of Spring Boot and Spring Security REST API Resources
In this guide, we will create these services:
@GetMapping("/instructors/{username}/courses")
: Get Request Method exposing the list of courses taught by a specific instructor
Services for Basic Authentication
@GetMapping(path = "/basicauth")
: Get Request Method for Basic Authentication. You can send a request to this method to see if the user credentials entered on the login page are valid.
Services for JWT Authentication
- @RequestMapping(value = “/authentication”, method = RequestMethod.POST) - Resource to get a JWT token providing user credentials.
- @RequestMapping(value = “/refresh”, method = RequestMethod.GET) - Resource to refresh a JWT Token before it expires.
Downloading the Complete Maven Project With Code Examples
In this example, the backend Spring Boot projects are different for JWT Authentication and Basic Authentication
Front-end React projects are almost the same except for a minor change. The front-end project can be used from the project. For JWT, a minor change needs to be done which is explained at the project.
Complete Code Example shows all the code.
Our Github repository has all the code examples — Github Repo
Understanding Spring Boot REST API Project Structure
Following screenshot shows the structure of the Spring Boot project we create for Basic Authentication.
Following screenshot shows the structure of the Spring Boot project we create for JWT Authentication.
A few details:
Common Files
CourseResource.java
- Rest Resource exposing all the service methods discussed above.Course.java, CoursesHardcodedService.java
- Business Logic for the application. CoursesHardcodedService exposes a few methods we would invoke from our Rest Resource.SpringBootFullStackBasicAuthLoginLogoutApplication.java
- Launcher for the Spring Boot Application. To run the application, launch this file as Java Application.pom.xml
- Contains all the dependencies needed to build this project. We use Spring Boot Starter Web and Spring Boot DevTools.
Basic Authentication Files
AuthenticationBean.java
- Simple bean which will be used to send a response for the basic authentication request.BasicAuthenticationController.java
- Provides the implementation for@GetMapping(path = "/basicauth")
to check if the basic authentication credential are validSpringSecurityConfigurationBasicAuth.java
- Contains Spring Configuration to enable Basic Authentication.
JWT Authentication Files
JwtAuthenticationRestController.java
- Exposes all the URLs related to JWT Authentication.JwtUserDetails.java
- Implementation ofUserDetails
interface providing user details.JwtInMemoryUserDetailsService.java
- Provides an in-memory implementation ofUserDetailsService
storing the user credentials.JwtTokenAuthorizationOncePerRequestFilter.java
JwtTokenRequest.java
- Represents the structure of a request to get a JWT Token.JwtTokenResponse.java
- Represents to structure of response containing the JWT Token.JwtTokenUtil.java
- Provide JWT Utilities to encrypt and decrypt JWT tokens.JwtUnAuthorizedResponseAuthenticationEntryPoint.java
- Used when a valid token is not provided with a REST API callJWTWebSecurityConfig.java
- Customizes Spring Security for JWT Authentication Needs by extendingWebSecurityConfigurerAdapter
AuthenticationException.java
- Use to throw invalid credentials or token exception
Understanding React Frontend Project Structure
Following screenshot shows the structure of the React project we create.
Quick Tip: You can get a high-level overview of all files in the React Project Structure watching this video React Project Structure
A few details:
InstructorApp.jsx
: React Component representing the high-level structure of the application. Routing is defined in this file.ListCoursesComponent.jsx
- React Component for listing all the courses for an instructor.CourseDataService.js
- Service using axios framework to make the Backend REST API Calls.LoginComponent.jsx
- Login Component representing the login screen.LogoutComponent.jsx
- Logout Component handles the click of Logout button at the top right corner of the screenMenuComponent.jsx
- Handles display of the top menu.AuthenticationService.jsx
- Service handling all details related to JWT and Basic Authentication.AuthenticatedRoute.jsx
- We would want certain routes like /courses to be accessed only by authenticated users.AuthenticationRoute
helps us implement this.
Understanding the Tools You Need to Build This Project
- Maven 3.0+ for building Spring Boot API Project
- npm, webpack for building frontend
- Your favorite IDE. We use Eclipse for Java and Visual Studio Code for Frontend - JavaScript, TypeScript, Angular and React.
- JDK 1.8+
- Node v8+
- Embedded Tomcat, built into Spring Boot Starter Web
Installing Node Js (npm) and Visual Studio Code
- Click to see video Playlist
- Step 01 - Installing NodeJs and NPM - Node Package Manager
- Step 02 - Quick Introduction to NPM
- Step 03 - Installing Visual Studio Code - Front End JavaScript Editor
Installing Java, Eclipse and Embedded Maven
- Click to see video Playlist
- 0 - Overview - Installation Java, Eclipse and Maven
- 1 - Installing Java JDK
- 2 - Installing Eclipse IDE
- 3 - Using Embedded Maven in Eclipse
- 4 - Troubleshooting Java, Eclipse and Maven
Steps 1, 2, and 3: Creating a Simple Full Stack Application With React and Spring Boot — Step-by-Step Approach
We will use a step by step approach to creating the full stack application
- Create a Spring Boot Application with Spring Boot Initializr
- Create a React application using Create React App
- Create the Retrieve Courses REST API and Enhance the React Front end to retrieve the courses using the axios framework
You can get an introduction to REST here - Introduction to REST API
The above steps are similar to the full stack CRUD application guide here — Creating Spring Boot and React CRUD Full Stack Application with Maven
You can follow Step 1, 2, and 3 from the above guide.
When you launch the React app in the browser, it will appear as shown below:
Step 4: Adding Basic Authentication to Backend
All that you need to do is to add Spring Boot Starter Security to your pom.xml
org.springframework.boot spring-boot-starter-security
You would see that the Basic Authentication is now enabled.
If you restart the backend application, you would see the basic authentication password is printed into the console
Using generated security password: 5434c56e-05bc-4b33-b54d-cf3a7c358f5b
If you try to launch the url http://localhost:8080/instructors/in28minutes/courses
, you will be asked for credentials. The user id as user
and password is shown above.
Instead of using Random Password, we can configure our own user credentials in application.properties
.
spring.security.user.name=in28minutes
spring.security.user.password=dummy
When you launch the url http://localhost:8080/instructors/in28minutes/courses
in a new browser, you can use the username and password specified above.
If you go the frontend and refresh the page, you would see that the page does not load as expected.
Step 5: Updating Frontend React Application to Use Hardcoded Basic Authentication
Let’s now update the react front end to use basic authentication credentials to call the course listing API.
You can see the complete listing of CourseDataService below:
import axios from 'axios'
const INSTRUCTOR = 'in28minutes'
const PASSWORD = 'dummy'
const COURSE_API_URL = 'http://localhost:8080'
const INSTRUCTOR_API_URL = `${COURSE_API_URL}/instructors/${INSTRUCTOR}`
class CourseDataService {
retrieveAllCourses(name) {
console.log('executed service')
return axios.get(`${INSTRUCTOR_API_URL}/courses`,
{ headers: { authorization: 'Basic ' + window.btoa(INSTRUCTOR + ":" + PASSWORD) } }
);
}
}
export default new CourseDataService()
If you refresh the course listing page, you should see another error.
Access to XMLHttpRequest at 'http://localhost:8080/basicauth' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
Step 5: Updating Spring Security to Allow Preflight OPTION Requests
Preflight request failed — what does that mean?
When you are using authentication, before executing the actual request, the browser sends something called an OPTION request.
If you go to the network tab, you would see the request and responses with the following headers
Request URL: http://localhost:8080/instructors/in28minutes/courses
Request Method: OPTIONS
Status Code: 401
Remote Address: [::1]:8080
Referrer Policy: no-referrer-when-downgrade
We can configure Spring Security to enable all OPTIONS requests. Following snippets show the spring security configuration needed.
@Configuration
@EnableWebSecurity
public class SpringSecurityConfigurationBasicAuth extends WebSecurityConfigurerAdapter{
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS,"/**").permitAll()
.anyRequest().authenticated()
.and()
//.formLogin().and()
.httpBasic();
}
}
When you launch the React app in the browser, it will appear as shown below:
If you look at the request to http://localhost:8080/instructors/in28minutes/courses in the browser, you would see the following headers:
Accept: application/json, text/plain, */*
authorization: Basic aW4yOG1pbnV0ZXM6ZHVtbXk=
You would also see a successful response.
Congratulations on your first authenticated API call!
Is this the end of the story? Nope. Ideally, we should have a Login Page to get the credentials from the user, authenticate the credentials and use those details for subsequent REST API calls.
Let’s focus on this in the next steps.
Step 6: Create an API for Basic Authentication
When we create a login page, we need to call an authentication URL to validate the user credentials. You don’t want to use some random URL. Let’s create a REST API.
We create a simple URL “/basicauth” returning an AuthenticationBean
of successful.
@CrossOrigin(origins={ "http://localhost:3000", "http://localhost:4200" })
@RestController
public class BasicAuthenticationController {
@GetMapping(path = "/basicauth")
public AuthenticationBean authenticate() {
//throw new RuntimeException("Some Error has Happened! Contact Support at ***-***");
return new AuthenticationBean("You are authenticated");
}
}
public class AuthenticationBean {
private String message;
//constructors, getters
}
There is no authentication logic in
authenticate()
method. Why? Because, we already configured spring security, it auto protects all URLs with basic authentication. An user would be able to get to/basicauth
only if they provide the right credentials.
Step 07: Create a Login Component With Hardcoded Authentication
We will create a Login Component with hardcoded authentication.
import React, { Component } from 'react'
import AuthenticationService from '../service/AuthenticationService';
class LoginComponent extends Component {
constructor(props) {
super(props)
this.state = {
username: 'in28minutes',
password: '',
hasLoginFailed: false,
showSuccessMessage: false
}
this.handleChange = this.handleChange.bind(this)
this.loginClicked = this.loginClicked.bind(this)
}
handleChange(event) {
this.setState(
{
[event.target.name]
: event.target.value
}
)
}
loginClicked() {
if(this.state.username==='in28minutes' && this.state.password==='dummy'){
AuthenticationService.registerSuccessfulLogin(this.state.username,this.state.password)
this.setState({showSuccessMessage:true})
this.setState({hasLoginFailed:false})
}
else {
this.setState({showSuccessMessage:false})
this.setState({hasLoginFailed:true})
}
}
render() {
return (
<div>
<h1>Login</h1>
<div className="container">
{this.state.hasLoginFailed && <div className="alert alert-warning">Invalid Credentials</div>}
{this.state.showSuccessMessage && <div>Login Sucessful</div>}
User Name: <input type="text" name="username" value={this.state.username} onChange={this.handleChange} />
Password: <input type="password" name="password" value={this.state.password} onChange={this.handleChange} />
<button className="btn btn-success" onClick={this.loginClicked}>Login</button>
</div>
</div>
)
}
}
export default LoginComponent
Let’s look at above component in depth:
We are using state
to represent user credentials entered by user and also to contain messages for showing success and failed authentication messages.
this.state = {
username: 'in28minutes',
password: '',
hasLoginFailed: false,
showSuccessMessage: false
}
We are creating a simple form with username and password which is tied to the state
:
User Name: <input type="text" name="username" value={this.state.username} onChange={this.handleChange} />
Password: <input type="password" name="password" value={this.state.password} onChange={this.handleChange} />
<button className="btn btn-success" onClick={this.loginClicked}>Login</button>
We are using events to update state as soon as values in the text fields change.
handleChange(event) {
this.setState(
{
[event.target.name]
: event.target.value
}
)
}
loginClicked
is called when user clicks Login button. We add a bit of hardcoded validation for now.
loginClicked() {
if(this.state.username==='in28minutes' && this.state.password==='dummy'){
this.setState({showSuccessMessage:true})
this.setState({hasLoginFailed:false})
}
else {
this.setState({showSuccessMessage:false})
this.setState({hasLoginFailed:true})
}
}
If you update the InstructorApp
to show the LoginComponent
, you can play with the login component.
class InstructorApp extends Component {
render() {
return (<>
<h1>Instructor Application</h1>
<LoginComponent/>
</>
)
}
}
export default InstructorApp
Step 8: Adding Routing to React Application
When user click login and user is successfully authenticated, we would want to see the course listing page. We would want to route from LoginComponent
to ListCoursesComponent
. We would need to implement routing.
In Step 03, we added a framework to support routing — react-router-dom
When the user clicks the update course button on the course listing page, we would want to route to the course page. How do we do it? That’s where Routing comes into the picture.
Let’s update InstructorApp
to define the routes.
/src/component/InstructorApp.jsx
class InstructorApp extends Component {
render() {
return (
<>
<Router>
<>
<MenuComponent />
<Switch>
<Route path="/" exact component={LoginComponent} />
<Route path="/login" exact component={LoginComponent} />
<AuthenticatedRoute path="/courses" exact component={ListCoursesComponent} />
</Switch>
</>
</Router>
</>
)
}
}
We are defining a Router around all the components and configuring paths to each of them.
- http://localhost:3000/ and http://localhost:3000/login takes you to login page
- http://localhost:3000/courses takes you to course listing page
Now that we have the routes defined, we can route from LoginComponent
to ListCoursesComponent
on successful login.
loginClicked() {
if(this.state.username==='in28minutes' && this.state.password==='dummy'){
this.props.history.push(`/courses`)
//this.setState({showSuccessMessage:true})
//this.setState({hasLoginFailed:false})
}
else {
this.setState({showSuccessMessage:false})
this.setState({hasLoginFailed:true})
}
}
You would see that we are now able to route from login to course listing page if we enter the right credentials.
Step 9: Remove Authentication Hardcoding and Call the REST API for Basic Authentication
Do you like hardcoded authentication? Let’s fix it.
if(this.state.username==='in28minutes' && this.state.password==='dummy'){
Let’s create AuthenticationService
to call the basic authentication url.
class AuthenticationService {
executeBasicAuthenticationService(username, password) {
return axios.get(`${API_URL}/basicauth`,
{ headers: { authorization: this.createBasicAuthToken(username, password) } })
}
createBasicAuthToken(username, password) {
return 'Basic ' + window.btoa(username + ":" + password)
}
}
Let’s update the LoginComponent
to use the service.
loginClicked() {
//in28minutes,dummy
// if(this.state.username==='in28minutes' && this.state.password==='dummy'){
// AuthenticationService.registerSuccessfulLogin(this.state.username,this.state.password)
// this.props.history.push(`/courses`)
// //this.setState({showSuccessMessage:true})
// //this.setState({hasLoginFailed:false})
// }
// else {
// this.setState({showSuccessMessage:false})
// this.setState({hasLoginFailed:true})
// }
AuthenticationService
.executeBasicAuthenticationService(this.state.username, this.state.password)
.then(() => {
this.props.history.push(`/courses`)
}).catch(() => {
this.setState({ showSuccessMessage: false })
this.setState({ hasLoginFailed: true })
})
}
When we call REST API we need to use promises to define success (then
) and failure scenarios (catch
).
Step 10: Remove Hardcoded Header From Course Listing REST API Call
Earlier we hardcoded the basic authentication header in the REST API call. This is not ideal.
return axios.get(`${INSTRUCTOR_API_URL}/courses`,
{ headers: { authorization: 'Basic ' + window.btoa(INSTRUCTOR + ":" + PASSWORD) } }
When we make any REST API call, we would want to automatically add the authorization header that we added for the authentication api call.
How do we do that?
When user login is successful, we can set up an axios header to added an authorization header on every subsequent API call. Does that sound like a plan?
AuthenticationService.registerSuccessfulLogin(this.state.username, this.state.password)
Let’s add these methods to AuthenticationService
:
registerSuccessfulLogin(username, password) {
//let basicAuthHeader = 'Basic ' + window.btoa(username + ":" + password)
//console.log('registerSuccessfulLogin')
sessionStorage.setItem(USER_NAME_SESSION_ATTRIBUTE_NAME, username)
this.setupAxiosInterceptors(this.createBasicAuthToken(username, password))
}
setupAxiosInterceptors(token) {
axios.interceptors.request.use(
(config) => {
if (this.isUserLoggedIn()) {
config.headers.authorization = token
}
return config
}
)
}
setupAxiosInterceptors
sets up the axios interceptor to add the authorization token on every subsequent REST API call. config.headers.authorization = token
Let’s call it on successful login in loginClicked
in LoginComponent
:
AuthenticationService
.executeBasicAuthenticationService(this.state.username, this.state.password)
.then(() => {
AuthenticationService.registerSuccessfulLogin(this.state.username, this.state.password)
this.props.history.push(`/courses`)
}).catch(() => {
this.setState({ showSuccessMessage: false })
this.setState({ hasLoginFailed: true })
})
Let’s also remove the hardcode authorization from CourseDataService
class CourseDataService {
retrieveAllCourses(name) {
//console.log('executed service')
return axios.get(`${INSTRUCTOR_API_URL}/courses`,
//{ headers: { authorization: 'Basic ' + window.btoa(INSTRUCTOR + ":" + PASSWORD) } }
);
}
}
Cool! If you run the app now, you should be fine.
Congratulations on implementing Basic Authentication
You can look up the MenuComponent
, LogoutComponent
to add features like Menu and Logout.
Step 11: Implementing JWT Authentication for Backend
JWT stands for JavaScript Web Token.
You can refer https://jwt.io/ for a quick introduction to JWT.
Basic Authentication Header does not have an expiry time and therefore a hacker get his hand on a basic authentication header, he can use it until the password is changed.
Instead of passing userid and password, in JWT, we pass a token with every request.
When a user logs in, we use his credential to get a JWT token. In all subsequent rest api requests, we use the token. Since the token has an expiration time, it offers better security than Basic Authentication.
Implementing JWT Authentication for Spring Boot is complex. Following are some of the important components involved.
JWT Authentication Files
JwtAuthenticationRestController.java
- Exposes all the URLs related to JWT Authentication.JwtUserDetails.java
- Implementation ofUserDetails
interface providing user details.JwtInMemoryUserDetailsService.java
- Provides an in memory implementation ofUserDetailsService
storing the user credentials.JwtTokenAuthorizationOncePerRequestFilter.java
-JwtTokenRequest.java
- Represents the structure of request to get a JWT Token.JwtTokenResponse.java
- Represnets to structure of response containing the JWT Token.JwtTokenUtil.java
- Provide JWT Utilities to encrypt and decrypt JWT tokens.JwtUnAuthorizedResponseAuthenticationEntryPoint.java
- Used when a valid token is not provided with a REST API callJWTWebSecurityConfig.java
- Customizes Spring Security for JWT Authentication Needs by extendingWebSecurityConfigurerAdapter
AuthenticationException.java
- Use to throw invalid credentials or token exception
I would recommend to check out the code for these components in the Complete Code Example
at the end of this article.
For setting up the JWT Backend Spring Boot API , I would recommend to download the project at this URL - Github Repo. Once you import the project, you can launch
SpringBootFullStackJwtAuthLoginLogoutApplication
as a Java Application.
JWT Authentication URLs
You can send a POST request to http://localhost:8080/authenticate
with the request body containing the credentials.
{
"username":"in28minutes",
"password":"dummy"
}
The Response contains the JWT token
{
"token": "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJyYW5nYSIsImV4cCI6MTU0MjQ3MjA3NCwiaWF0IjoxNTQxODY3Mjc0fQ.kD6UJQyxjSPMzAhoTJRr-Z5UL-FfgsyxbdseWQvk0fLi7eVXAKhBkWfj06SwH43sY_ZWBEeLuxaE09szTboefw"
}
We can use this token for subsequent API calls.
To refresh the token, we can send a GET request to http://localhost:8080/authenticate
with the token in the header. You would get a new token in the response
Step 12: Updating Front End Code to Use JWT
Updating front end code to use JWT is very simple.
We will create a method to call the JWT authenticate url with POST method in AuthenticationService
.
executeJwtAuthenticationService(username, password) {
console.log(username);
return axios.post(`${API_URL}/authenticate`, {
username,
password
})
}
We will create another method in AuthenticationService
to register successful login for JWT. This will setup the token in sessionStorage and also setup the axios interceptors for subsequent api calls.
registerSuccessfulLoginForJwt(username, token) {
sessionStorage.setItem(USER_NAME_SESSION_ATTRIBUTE_NAME, username)
this.setupAxiosInterceptors(this.createJWTToken(token))
}
createJWTToken(token) {
return 'Bearer ' + token
}
We can now comment out the Basic Authentication call and call JWT authentication service in LoginComponent
.
AuthenticationService
.executeJwtAuthenticationService(this.state.username, this.state.password)
.then((response) => {
AuthenticationService.registerSuccessfulLoginForJwt(this.state.username, response.data.token)
this.props.history.push(`/courses`)
}).catch(() => {
this.setState({ showSuccessMessage: false })
this.setState({ hasLoginFailed: true })
})
When you login, you should the REST API request to /authenticate
returning with a response containing JWT token. This JWT token is used in all subsequent REST API calls.
Complete Code Example
/backend-spring-boot-react-jwt-auth-login-logout/pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.in28minutes.fullstack.springboot.jwt.basic.authentication</groupId>
<artifactId>spring-boot-fullstack-jwt-auth-login-logout</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-fullstack-jwt-auth-login-logout</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<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.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
/backend-spring-boot-react-jwt-auth-login-logout/src/test/java/com/in28minutes/fullstack/springboot/jwt/basic/authentication/springbootreactjwtauthloginlogout/SpringBootReactJwtAuthLoginLogoutApplicationTests.java
package com.in28minutes.fullstack.springboot.jwt.basic.authentication.springbootreactjwtauthloginlogout;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBootReactJwtAuthLoginLogoutApplicationTests {
@Test
public void contextLoads() {
}
}
/backend-spring-boot-react-jwt-auth-login-logout/src/main/resources/application.properties
jwt.signing.key.secret=mySecret
jwt.get.token.uri=/authenticate
jwt.refresh.token.uri=/refresh
jwt.http.request.header=Authorization
jwt.token.expiration.in.seconds=604800
/backend-spring-boot-react-jwt-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/jwt/basic/authentication/springbootjwtauthloginlogout/jwt/JwtTokenUtil.java
package com.in28minutes.fullstack.springboot.jwt.basic.authentication.springbootjwtauthloginlogout.jwt;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Clock;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.impl.DefaultClock;
@Component
public class JwtTokenUtil implements Serializable {
static final String CLAIM_KEY_USERNAME = "sub";
static final String CLAIM_KEY_CREATED = "iat";
private static final long serialVersionUID = -3301605591108950415L;
private Clock clock = DefaultClock.INSTANCE;
@Value("${jwt.signing.key.secret}")
private String secret;
@Value("${jwt.token.expiration.in.seconds}")
private Long expiration;
public String getUsernameFromToken(String token) {
return getClaimFromToken(token, Claims::getSubject);
}
public Date getIssuedAtDateFromToken(String token) {
return getClaimFromToken(token, Claims::getIssuedAt);
}
public Date getExpirationDateFromToken(String token) {
return getClaimFromToken(token, Claims::getExpiration);
}
public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
final Claims claims = getAllClaimsFromToken(token);
return claimsResolver.apply(claims);
}
private Claims getAllClaimsFromToken(String token) {
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
}
private Boolean isTokenExpired(String token) {
final Date expiration = getExpirationDateFromToken(token);
return expiration.before(clock.now());
}
private Boolean ignoreTokenExpiration(String token) {
// here you specify tokens, for that the expiration is ignored
return false;
}
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
return doGenerateToken(claims, userDetails.getUsername());
}
private String doGenerateToken(Map<String, Object> claims, String subject) {
final Date createdDate = clock.now();
final Date expirationDate = calculateExpirationDate(createdDate);
return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(createdDate)
.setExpiration(expirationDate).signWith(SignatureAlgorithm.HS512, secret).compact();
}
public Boolean canTokenBeRefreshed(String token) {
return (!isTokenExpired(token) || ignoreTokenExpiration(token));
}
public String refreshToken(String token) {
final Date createdDate = clock.now();
final Date expirationDate = calculateExpirationDate(createdDate);
final Claims claims = getAllClaimsFromToken(token);
claims.setIssuedAt(createdDate);
claims.setExpiration(expirationDate);
return Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, secret).compact();
}
public Boolean validateToken(String token, UserDetails userDetails) {
JwtUserDetails user = (JwtUserDetails) userDetails;
final String username = getUsernameFromToken(token);
return (username.equals(user.getUsername()) && !isTokenExpired(token));
}
private Date calculateExpirationDate(Date createdDate) {
return new Date(createdDate.getTime() + expiration * 1000);
}
}
/backend-spring-boot-react-jwt-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/jwt/basic/authentication/springbootjwtauthloginlogout/jwt/JwtInMemoryUserDetailsService.java
package com.in28minutes.fullstack.springboot.jwt.basic.authentication.springbootjwtauthloginlogout.jwt;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
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;
@Service
public class JwtInMemoryUserDetailsService implements UserDetailsService {
static List<JwtUserDetails> inMemoryUserList = new ArrayList<>();
static {
inMemoryUserList.add(new JwtUserDetails(1L, "in28minutes",
"$2a$10$3zHzb.Npv1hfZbLEU5qsdOju/tk2je6W6PnNnY.c1ujWPcZh4PL6e", "ROLE_USER_2"));
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Optional<JwtUserDetails> findFirst = inMemoryUserList.stream()
.filter(user -> user.getUsername().equals(username)).findFirst();
if (!findFirst.isPresent()) {
throw new UsernameNotFoundException(String.format("USER_NOT_FOUND '%s'.", username));
}
return findFirst.get();
}
}
/backend-spring-boot-react-jwt-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/jwt/basic/authentication/springbootjwtauthloginlogout/jwt/JwtTokenRequest.java
package com.in28minutes.fullstack.springboot.jwt.basic.authentication.springbootjwtauthloginlogout.jwt;
import java.io.Serializable;
public class JwtTokenRequest implements Serializable {
private static final long serialVersionUID = -5616176897013108345L;
private String username;
private String password;
public JwtTokenRequest() {
super();
}
public JwtTokenRequest(String username, String password) {
this.setUsername(username);
this.setPassword(password);
}
public String getUsername() {
return this.username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
}
/backend-spring-boot-react-jwt-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/jwt/basic/authentication/springbootjwtauthloginlogout/jwt/JwtAuthenticationRestController.java
package com.in28minutes.fullstack.springboot.jwt.basic.authentication.springbootjwtauthloginlogout.jwt;
import java.util.Objects;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@CrossOrigin(origins={ "http://localhost:3000", "http://localhost:4200" })
public class JwtAuthenticationRestController {
@Value("${jwt.http.request.header}")
private String tokenHeader;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private UserDetailsService jwtInMemoryUserDetailsService;
@RequestMapping(value = "${jwt.get.token.uri}", method = RequestMethod.POST)
public ResponseEntity<?> createAuthenticationToken(@RequestBody JwtTokenRequest authenticationRequest)
throws AuthenticationException {
authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword());
final UserDetails userDetails = jwtInMemoryUserDetailsService.loadUserByUsername(authenticationRequest.getUsername());
final String token = jwtTokenUtil.generateToken(userDetails);
return ResponseEntity.ok(new JwtTokenResponse(token));
}
@RequestMapping(value = "${jwt.refresh.token.uri}", method = RequestMethod.GET)
public ResponseEntity<?> refreshAndGetAuthenticationToken(HttpServletRequest request) {
String authToken = request.getHeader(tokenHeader);
final String token = authToken.substring(7);
String username = jwtTokenUtil.getUsernameFromToken(token);
JwtUserDetails user = (JwtUserDetails) jwtInMemoryUserDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.canTokenBeRefreshed(token)) {
String refreshedToken = jwtTokenUtil.refreshToken(token);
return ResponseEntity.ok(new JwtTokenResponse(refreshedToken));
} else {
return ResponseEntity.badRequest().body(null);
}
}
@ExceptionHandler({ AuthenticationException.class })
public ResponseEntity<String> handleAuthenticationException(AuthenticationException e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(e.getMessage());
}
private void authenticate(String username, String password) {
Objects.requireNonNull(username);
Objects.requireNonNull(password);
try {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
} catch (DisabledException e) {
throw new AuthenticationException("USER_DISABLED", e);
} catch (BadCredentialsException e) {
throw new AuthenticationException("INVALID_CREDENTIALS", e);
}
}
}
/backend-spring-boot-react-jwt-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/jwt/basic/authentication/springbootjwtauthloginlogout/jwt/JwtUserDetails.java
package com.in28minutes.fullstack.springboot.jwt.basic.authentication.springbootjwtauthloginlogout.jwt;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
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;
public class JwtUserDetails implements UserDetails {
private static final long serialVersionUID = 5155720064139820502L;
private final Long id;
private final String username;
private final String password;
private final Collection<? extends GrantedAuthority> authorities;
public JwtUserDetails(Long id, String username, String password, String role) {
this.id = id;
this.username = username;
this.password = password;
List<SimpleGrantedAuthority> authorities = new ArrayList<SimpleGrantedAuthority>();
authorities.add(new SimpleGrantedAuthority(role));
this.authorities = authorities;
}
@JsonIgnore
public Long getId() {
return id;
}
@Override
public String getUsername() {
return username;
}
@JsonIgnore
@Override
public boolean isAccountNonExpired() {
return true;
}
@JsonIgnore
@Override
public boolean isAccountNonLocked() {
return true;
}
@JsonIgnore
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@JsonIgnore
@Override
public String getPassword() {
return password;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public boolean isEnabled() {
return true;
}
}
package com.in28minutes.fullstack.springboot.jwt.basic.authentication.springbootjwtauthloginlogout.jwt;
import java.io.IOException;
import java.io.Serializable;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
@Component
public class JwtUnAuthorizedResponseAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable {
private static final long serialVersionUID = -8970718410437077606L;
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
"You would need to provide the Jwt Token to Access This resource");
}
}
package com.in28minutes.fullstack.springboot.jwt.basic.authentication.springbootjwtauthloginlogout.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.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import io.jsonwebtoken.ExpiredJwtException;
@Component
public class JwtTokenAuthorizationOncePerRequestFilter extends OncePerRequestFilter {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private UserDetailsService jwtInMemoryUserDetailsService;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Value("${jwt.http.request.header}")
private String tokenHeader;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
logger.debug("Authentication Request For '{}'", request.getRequestURL());
final String requestTokenHeader = request.getHeader(this.tokenHeader);
String username = null;
String jwtToken = null;
if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
jwtToken = requestTokenHeader.substring(7);
try {
username = jwtTokenUtil.getUsernameFromToken(jwtToken);
} catch (IllegalArgumentException e) {
logger.error("JWT_TOKEN_UNABLE_TO_GET_USERNAME", e);
} catch (ExpiredJwtException e) {
logger.warn("JWT_TOKEN_EXPIRED", e);
}
} else {
logger.warn("JWT_TOKEN_DOES_NOT_START_WITH_BEARER_STRING");
}
logger.debug("JWT_TOKEN_USERNAME_VALUE '{}'", username);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.jwtInMemoryUserDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
chain.doFilter(request, response);
}
}
/backend-spring-boot-react-jwt-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/jwt/basic/authentication/springbootjwtauthloginlogout/jwt/JwtTokenResponse.java
package com.in28minutes.fullstack.springboot.jwt.basic.authentication.springbootjwtauthloginlogout.jwt;
import java.io.Serializable;
public class JwtTokenResponse implements Serializable {
private static final long serialVersionUID = 8317676219297719109L;
private final String token;
public JwtTokenResponse(String token) {
this.token = token;
}
public String getToken() {
return this.token;
}
}
/backend-spring-boot-react-jwt-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/jwt/basic/authentication/springbootjwtauthloginlogout/jwt/JWTWebSecurityConfig.java
package com.in28minutes.fullstack.springboot.jwt.basic.authentication.springbootjwtauthloginlogout.jwt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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.builders.WebSecurity;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class JWTWebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtUnAuthorizedResponseAuthenticationEntryPoint jwtUnAuthorizedResponseAuthenticationEntryPoint;
@Autowired
private UserDetailsService jwtInMemoryUserDetailsService;
@Autowired
private JwtTokenAuthorizationOncePerRequestFilter jwtAuthenticationTokenFilter;
@Value("${jwt.get.token.uri}")
private String authenticationPath;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(jwtInMemoryUserDetailsService)
.passwordEncoder(passwordEncoderBean());
}
@Bean
public PasswordEncoder passwordEncoderBean() {
return new BCryptPasswordEncoder();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf().disable()
.exceptionHandling().authenticationEntryPoint(jwtUnAuthorizedResponseAuthenticationEntryPoint).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.anyRequest().authenticated();
httpSecurity
.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
httpSecurity
.headers()
.frameOptions().sameOrigin() //H2 Console Needs this setting
.cacheControl(); //disable caching
}
@Override
public void configure(WebSecurity webSecurity) throws Exception {
webSecurity
.ignoring()
.antMatchers(
HttpMethod.POST,
authenticationPath
)
.antMatchers(HttpMethod.OPTIONS, "/**")
.and()
.ignoring()
.antMatchers(
HttpMethod.GET,
"/" //Other Stuff You want to Ignore
)
.and()
.ignoring()
.antMatchers("/h2-console/**/**");//Should not be in Production!
}
}
/backend-spring-boot-react-jwt-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/jwt/basic/authentication/springbootjwtauthloginlogout/jwt/AuthenticationException.java
package com.in28minutes.fullstack.springboot.jwt.basic.authentication.springbootjwtauthloginlogout.jwt;
public class AuthenticationException extends RuntimeException {
public AuthenticationException(String message, Throwable cause) {
super(message, cause);
}
}
/backend-spring-boot-react-jwt-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/jwt/basic/authentication/springbootjwtauthloginlogout/course/CoursesHardcodedService.java
package com.in28minutes.fullstack.springboot.jwt.basic.authentication.springbootjwtauthloginlogout.course;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Service;
@Service
public class CoursesHardcodedService {
private static List<Course> courses = new ArrayList<>();
private static long idCounter = 0;
static {
courses.add(new Course(++idCounter, "in28minutes", "Learn Full stack with Spring Boot and Angular"));
courses.add(new Course(++idCounter, "in28minutes", "Learn Full stack with Spring Boot and React"));
courses.add(new Course(++idCounter, "in28minutes", "Master Microservices with Spring Boot and Spring Cloud"));
courses.add(new Course(++idCounter, "in28minutes",
"Deploy Spring Boot Microservices to Cloud with Docker and Kubernetes"));
}
public List<Course> findAll() {
return courses;
}
}
/backend-spring-boot-react-jwt-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/jwt/basic/authentication/springbootjwtauthloginlogout/course/Course.java
package com.in28minutes.fullstack.springboot.jwt.basic.authentication.springbootjwtauthloginlogout.course;
public class Course {
private Long id;
private String username;
private String description;
public Course() {
}
public Course(long id, String username, String description) {
super();
this.id = id;
this.username = username;
this.description = description;
}
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 getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((description == null) ? 0 : description.hashCode());
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((username == null) ? 0 : username.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Course other = (Course) obj;
if (description == null) {
if (other.description != null)
return false;
} else if (!description.equals(other.description))
return false;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (username == null) {
if (other.username != null)
return false;
} else if (!username.equals(other.username))
return false;
return true;
}
}
/backend-spring-boot-react-jwt-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/jwt/basic/authentication/springbootjwtauthloginlogout/course/CourseResource.java
package com.in28minutes.fullstack.springboot.jwt.basic.authentication.springbootjwtauthloginlogout.course;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@CrossOrigin(origins = { "http://localhost:3000", "http://localhost:4200" })
@RestController
public class CourseResource {
@Autowired
private CoursesHardcodedService courseManagementService;
@GetMapping("/instructors/{username}/courses")
public List<Course> getAllCourses(@PathVariable String username) {
return courseManagementService.findAll();
}
}
/backend-spring-boot-react-jwt-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/jwt/basic/authentication/springbootjwtauthloginlogout/SpringBootFullStackJwtAuthLoginLogoutApplication.java
package com.in28minutes.fullstack.springboot.jwt.basic.authentication.springbootjwtauthloginlogout;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootFullStackJwtAuthLoginLogoutApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootFullStackJwtAuthLoginLogoutApplication.class, args);
}
}
/backend-spring-boot-react-basic-auth-login-logout/pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.in28minutes.fullstack.springboot.fullstack.basic.authentication</groupId>
<artifactId>spring-boot-fullstack-basic-auth-login-logout</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-fullstack-basic-auth-login-logout</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<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>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
/backend-spring-boot-react-basic-auth-login-logout/src/test/java/com/in28minutes/fullstack/springboot/react/basic/authentication/springbootreactbasicauthloginlogout/SpringBootReactBasicAuthLoginLogoutApplicationTests.java
package com.in28minutes.fullstack.springboot.react.basic.authentication.springbootreactbasicauthloginlogout;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBootReactBasicAuthLoginLogoutApplicationTests {
@Test
public void contextLoads() {
}
}
/backend-spring-boot-react-basic-auth-login-logout/src/main/resources/application.properties
spring.security.user.name=in28minutes
spring.security.user.password=dummy
/backend-spring-boot-react-basic-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/fullstack/basic/authentication/springbootfullstackbasicauthloginlogout/course/CoursesHardcodedService.java
package com.in28minutes.fullstack.springboot.fullstack.basic.authentication.springbootfullstackbasicauthloginlogout.course;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Service;
@Service
public class CoursesHardcodedService {
private static List<Course> courses = new ArrayList<>();
private static long idCounter = 0;
static {
courses.add(new Course(++idCounter, "in28minutes", "Learn Full stack with Spring Boot and Angular"));
courses.add(new Course(++idCounter, "in28minutes", "Learn Full stack with Spring Boot and React"));
courses.add(new Course(++idCounter, "in28minutes", "Master Microservices with Spring Boot and Spring Cloud"));
courses.add(new Course(++idCounter, "in28minutes",
"Deploy Spring Boot Microservices to Cloud with Docker and Kubernetes"));
}
public List<Course> findAll() {
return courses;
}
}
/backend-spring-boot-react-basic-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/fullstack/basic/authentication/springbootfullstackbasicauthloginlogout/course/Course.java
package com.in28minutes.fullstack.springboot.fullstack.basic.authentication.springbootfullstackbasicauthloginlogout.course;
public class Course {
private Long id;
private String username;
private String description;
public Course() {
}
public Course(long id, String username, String description) {
super();
this.id = id;
this.username = username;
this.description = description;
}
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 getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((description == null) ? 0 : description.hashCode());
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((username == null) ? 0 : username.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Course other = (Course) obj;
if (description == null) {
if (other.description != null)
return false;
} else if (!description.equals(other.description))
return false;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (username == null) {
if (other.username != null)
return false;
} else if (!username.equals(other.username))
return false;
return true;
}
}
/backend-spring-boot-react-basic-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/fullstack/basic/authentication/springbootfullstackbasicauthloginlogout/course/CourseResource.java
package com.in28minutes.fullstack.springboot.fullstack.basic.authentication.springbootfullstackbasicauthloginlogout.course;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@CrossOrigin(origins = { "http://localhost:3000", "http://localhost:4200" })
@RestController
public class CourseResource {
@Autowired
private CoursesHardcodedService courseManagementService;
@GetMapping("/instructors/{username}/courses")
public List<Course> getAllCourses(@PathVariable String username) {
return courseManagementService.findAll();
}
}
/backend-spring-boot-react-basic-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/fullstack/basic/authentication/springbootfullstackbasicauthloginlogout/basic/auth/AuthenticationBean.java
package com.in28minutes.fullstack.springboot.fullstack.basic.authentication.springbootfullstackbasicauthloginlogout.basic.auth;
public class AuthenticationBean {
private String message;
public AuthenticationBean(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public String toString() {
return String.format("HelloWorldBean [message=%s]", message);
}
}
/backend-spring-boot-react-basic-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/fullstack/basic/authentication/springbootfullstackbasicauthloginlogout/basic/auth/BasicAuthenticationController.java
package com.in28minutes.fullstack.springboot.fullstack.basic.authentication.springbootfullstackbasicauthloginlogout.basic.auth;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
//Controller
@CrossOrigin(origins={ "http://localhost:3000", "http://localhost:4200" })
@RestController
public class BasicAuthenticationController {
@GetMapping(path = "/basicauth")
public AuthenticationBean helloWorldBean() {
//throw new RuntimeException("Some Error has Happened! Contact Support at ***-***");
return new AuthenticationBean("You are authenticated");
}
}
/backend-spring-boot-react-basic-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/fullstack/basic/authentication/springbootfullstackbasicauthloginlogout/basic/auth/SpringSecurityConfigurationBasicAuth.java
package com.in28minutes.fullstack.springboot.fullstack.basic.authentication.springbootfullstackbasicauthloginlogout.basic.auth;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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;
@Configuration
@EnableWebSecurity
public class SpringSecurityConfigurationBasicAuth extends WebSecurityConfigurerAdapter{
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS,"/**").permitAll()
.anyRequest().authenticated()
.and()
//.formLogin().and()
.httpBasic();
}
}
/backend-spring-boot-react-basic-auth-login-logout/src/main/java/com/in28minutes/fullstack/springboot/fullstack/basic/authentication/springbootfullstackbasicauthloginlogout/SpringBootFullStackBasicAuthLoginLogoutApplication.java
package com.in28minutes.fullstack.springboot.fullstack.basic.authentication.springbootfullstackbasicauthloginlogout;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootFullStackBasicAuthLoginLogoutApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootFullStackBasicAuthLoginLogoutApplication.class, args);
}
}
/frontend-spring-boot-react-basic-auth-login-logout/public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta name="theme-color" content="#000000" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>My Full Stack Application with Spring Boot and React</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
/frontend-spring-boot-react-basic-auth-login-logout/public/manifest.json
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
/frontend-spring-boot-react-basic-auth-login-logout/src/App.css
@import url(https://unpkg.com/bootstrap@4.1.0/dist/css/bootstrap.min.css)
/frontend-spring-boot-react-basic-auth-login-logout/src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
/frontend-spring-boot-react-basic-auth-login-logout/src/component/ListCoursesComponent.jsx
import React, { Component } from 'react'
import CourseDataService from '../service/CourseDataService.js';
const INSTRUCTOR = 'in28minutes'
class ListCoursesComponent extends Component {
constructor(props) {
super(props)
this.state = {
courses: [],
message: null
}
this.refreshCourses = this.refreshCourses.bind(this)
}
componentDidMount() {
this.refreshCourses();
}
refreshCourses() {
CourseDataService.retrieveAllCourses(INSTRUCTOR)//HARDCODED
.then(
response => {
this.setState({ courses: response.data })
}
)
}
render() {
console.log('render')
return (
<div className="container">
<h3>All Courses</h3>
<div className="container">
<table className="table">
<thead>
<tr>
<th>Id</th>
<th>Description</th>
</tr>
</thead>
<tbody>
{
this.state.courses.map(
course =>
<tr key={course.id}>
<td>{course.id}</td>
<td>{course.description}</td>
</tr>
)
}
</tbody>
</table>
</div>
</div>
)
}
}
export default ListCoursesComponent
/frontend-spring-boot-react-basic-auth-login-logout/src/component/AuthenticatedRoute.jsx
import React, { Component } from 'react'
import { Route, Redirect } from 'react-router-dom'
import AuthenticationService from '../service/AuthenticationService';
class AuthenticatedRoute extends Component {
render() {
if (AuthenticationService.isUserLoggedIn()) {
return <Route {...this.props} />
} else {
return <Redirect to="/login" />
}
}
}
export default AuthenticatedRoute
/frontend-spring-boot-react-basic-auth-login-logout/src/component/LoginComponent.jsx
import React, { Component } from 'react'
import AuthenticationService from '../service/AuthenticationService';
class LoginComponent extends Component {
constructor(props) {
super(props)
this.state = {
username: 'in28minutes',
password: '',
hasLoginFailed: false,
showSuccessMessage: false
}
this.handleChange = this.handleChange.bind(this)
this.loginClicked = this.loginClicked.bind(this)
}
handleChange(event) {
this.setState(
{
[event.target.name]
: event.target.value
}
)
}
loginClicked() {
//in28minutes,dummy
// if(this.state.username==='in28minutes' && this.state.password==='dummy'){
// AuthenticationService.registerSuccessfulLogin(this.state.username,this.state.password)
// this.props.history.push(`/courses`)
// //this.setState({showSuccessMessage:true})
// //this.setState({hasLoginFailed:false})
// }
// else {
// this.setState({showSuccessMessage:false})
// this.setState({hasLoginFailed:true})
// }
AuthenticationService
.executeBasicAuthenticationService(this.state.username, this.state.password)
.then(() => {
AuthenticationService.registerSuccessfulLogin(this.state.username, this.state.password)
this.props.history.push(`/courses`)
}).catch(() => {
this.setState({ showSuccessMessage: false })
this.setState({ hasLoginFailed: true })
})
}
render() {
return (
<div>
<h1>Login</h1>
<div className="container">
{/*<ShowInvalidCredentials hasLoginFailed={this.state.hasLoginFailed}/>*/}
{this.state.hasLoginFailed && <div className="alert alert-warning">Invalid Credentials</div>}
{this.state.showSuccessMessage && <div>Login Sucessful</div>}
{/*<ShowLoginSuccessMessage showSuccessMessage={this.state.showSuccessMessage}/>*/}
User Name: <input type="text" name="username" value={this.state.username} onChange={this.handleChange} />
Password: <input type="password" name="password" value={this.state.password} onChange={this.handleChange} />
<button className="btn btn-success" onClick={this.loginClicked}>Login</button>
</div>
</div>
)
}
}
export default LoginComponent
import React, { Component } from 'react'
import { Link, withRouter } from 'react-router-dom'
import AuthenticationService from '../service/AuthenticationService';
class MenuComponent extends Component {
render() {
const isUserLoggedIn = AuthenticationService.isUserLoggedIn();
return (
<header>
<nav className="navbar navbar-expand-md navbar-dark bg-dark">
<div><a href="http://www.in28minutes.com" className="navbar-brand">in28Minutes</a></div>
<ul className="navbar-nav">
<li><Link className="nav-link" to="/courses">Courses</Link></li>
</ul>
<ul className="navbar-nav navbar-collapse justify-content-end">
{!isUserLoggedIn && <li><Link className="nav-link" to="/login">Login</Link></li>}
{isUserLoggedIn && <li><Link className="nav-link" to="/logout" onClick={AuthenticationService.logout}>Logout</Link></li>}
</ul>
</nav>
</header>
)
}
}
export default withRouter(MenuComponent)
/frontend-spring-boot-react-basic-auth-login-logout/src/component/InstructorApp.jsx
import React, { Component } from 'react';
import ListCoursesComponent from './ListCoursesComponent';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
import LoginComponent from './LoginComponent';
import LogoutComponent from './LogoutComponent';
import MenuComponent from './MenuComponent';
import AuthenticationService from '../service/AuthenticationService';
import AuthenticatedRoute from './AuthenticatedRoute';
class InstructorApp extends Component {
render() {
return (
<>
<Router>
<>
<MenuComponent />
<Switch>
<Route path="/" exact component={LoginComponent} />
<Route path="/login" exact component={LoginComponent} />
<AuthenticatedRoute path="/logout" exact component={LogoutComponent} />
<AuthenticatedRoute path="/courses" exact component={ListCoursesComponent} />
</Switch>
</>
</Router>
</>
)
}
}
export default InstructorApp
/frontend-spring-boot-react-basic-auth-login-logout/src/component/LogoutComponent.jsx
import React, { Component } from 'react'
class LogoutComponent extends Component {
render() {
return (
<>
<h1>You are logged out</h1>
<div className="container">
Thank You for Using Our Application.
</div>
</>
)
}
}
export default LogoutComponent
/frontend-spring-boot-react-basic-auth-login-logout/src/index.css
body {
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
monospace;
}
/frontend-spring-boot-react-basic-auth-login-logout/src/App.test.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<App />, div);
ReactDOM.unmountComponentAtNode(div);
});
/frontend-spring-boot-react-basic-auth-login-logout/src/serviceWorker.js
// This optional code is used to register a service worker.
// register() is not called by default.
// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
// will only see deployed updates on subsequent visits to a page, after all the
// existing tabs open on the page have been closed, since previously cached
// resources are updated in the background.
// To learn more about the benefits of this model and instructions on how to
// opt-in, read https://bit.ly/CRA-PWA
const isLocalhost = Boolean(
window.location.hostname === 'localhost' ||
// [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' ||
// 127.0.0.1/8 is considered localhost for IPv4.
window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
)
);
export function register(config) {
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
if (publicUrl.origin !== window.location.origin) {
// Our service worker won't work if PUBLIC_URL is on a different origin
// from what our page is served on. This might happen if a CDN is used to
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
return;
}
window.addEventListener('load', () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
if (isLocalhost) {
// This is running on localhost. Let's check if a service worker still exists or not.
checkValidServiceWorker(swUrl, config);
// Add some additional logging to localhost, pointing developers to the
// service worker/PWA documentation.
navigator.serviceWorker.ready.then(() => {
console.log(
'This web app is being served cache-first by a service ' +
'worker. To learn more, visit https://bit.ly/CRA-PWA'
);
});
} else {
// Is not localhost. Just register service worker
registerValidSW(swUrl, config);
}
});
}
}
function registerValidSW(swUrl, config) {
navigator.serviceWorker
.register(swUrl)
.then(registration => {
registration.onupdatefound = () => {
const installingWorker = registration.installing;
if (installingWorker == null) {
return;
}
installingWorker.onstatechange = () => {
if (installingWorker.state === 'installed') {
if (navigator.serviceWorker.controller) {
// At this point, the updated precached content has been fetched,
// but the previous service worker will still serve the older
// content until all client tabs are closed.
console.log(
'New content is available and will be used when all ' +
'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
);
// Execute callback
if (config && config.onUpdate) {
config.onUpdate(registration);
}
} else {
// At this point, everything has been precached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
console.log('Content is cached for offline use.');
// Execute callback
if (config && config.onSuccess) {
config.onSuccess(registration);
}
}
}
};
};
})
.catch(error => {
console.error('Error during service worker registration:', error);
});
}
function checkValidServiceWorker(swUrl, config) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl)
.then(response => {
// Ensure service worker exists, and that we really are getting a JS file.
const contentType = response.headers.get('content-type');
if (
response.status === 404 ||
(contentType != null && contentType.indexOf('javascript') === -1)
) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => {
registration.unregister().then(() => {
window.location.reload();
});
});
} else {
// Service worker found. Proceed as normal.
registerValidSW(swUrl, config);
}
})
.catch(() => {
console.log(
'No internet connection found. App is running in offline mode.'
);
});
}
export function unregister() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(registration => {
registration.unregister();
});
}
}
/frontend-spring-boot-react-basic-auth-login-logout/src/service/CourseDataService.js
import axios from 'axios'
const INSTRUCTOR = 'in28minutes'
const PASSWORD = 'dummy'
const COURSE_API_URL = 'http://localhost:8080'
const INSTRUCTOR_API_URL = `${COURSE_API_URL}/instructors/${INSTRUCTOR}`
class CourseDataService {
retrieveAllCourses(name) {
//console.log('executed service')
return axios.get(`${INSTRUCTOR_API_URL}/courses`,
//{ headers: { authorization: 'Basic ' + window.btoa(INSTRUCTOR + ":" + PASSWORD) } }
);
}
}
export default new CourseDataService()
/frontend-spring-boot-react-basic-auth-login-logout/src/service/AuthenticationService.js
import axios from 'axios'
const API_URL = 'http://localhost:8080'
export const USER_NAME_SESSION_ATTRIBUTE_NAME = 'authenticatedUser'
class AuthenticationService {
executeBasicAuthenticationService(username, password) {
return axios.get(`${API_URL}/basicauth`,
{ headers: { authorization: this.createBasicAuthToken(username, password) } })
}
executeJwtAuthenticationService(username, password) {
console.log(username);
return axios.post(`${API_URL}/authenticate`, {
username,
password
})
}
createBasicAuthToken(username, password) {
return 'Basic ' + window.btoa(username + ":" + password)
}
registerSuccessfulLogin(username, password) {
//let basicAuthHeader = 'Basic ' + window.btoa(username + ":" + password)
//console.log('registerSuccessfulLogin')
sessionStorage.setItem(USER_NAME_SESSION_ATTRIBUTE_NAME, username)
this.setupAxiosInterceptors(this.createBasicAuthToken(username, password))
}
registerSuccessfulLoginForJwt(username, token) {
sessionStorage.setItem(USER_NAME_SESSION_ATTRIBUTE_NAME, username)
this.setupAxiosInterceptors(this.createJWTToken(token))
}
createJWTToken(token) {
return 'Bearer ' + token
}
logout() {
sessionStorage.removeItem(USER_NAME_SESSION_ATTRIBUTE_NAME);
}
isUserLoggedIn() {
let user = sessionStorage.getItem(USER_NAME_SESSION_ATTRIBUTE_NAME)
if (user === null) return false
return true
}
getLoggedInUserName() {
let user = sessionStorage.getItem(USER_NAME_SESSION_ATTRIBUTE_NAME)
if (user === null) return ''
return user
}
setupAxiosInterceptors(token) {
axios.interceptors.request.use(
(config) => {
if (this.isUserLoggedIn()) {
config.headers.authorization = token
}
return config
}
)
}
}
export default new AuthenticationService()
/frontend-spring-boot-react-basic-auth-login-logout/src/logo.svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3">
<g fill="#61DAFB">
<path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/>
<circle cx="420.9" cy="296.5" r="45.7"/>
<path d="M520.5 78.1z"/>
</g>
</svg>
/frontend-spring-boot-react-basic-auth-login-logout/src/App.js
import React, { Component } from 'react';
import './App.css';
import InstructorApp from './component/InstructorApp.jsx';
class App extends Component {
render() {
return (
<div className="container">
<InstructorApp />
</div>
);
}
}
export default App;
/frontend-spring-boot-react-basic-auth-login-logout/package.json
{
"name": "spring-boot-react-basic-auth-login-logout",
"version": "0.1.0",
"private": true,
"dependencies": {
"axios": "^0.18.0",
"react": "^16.8.5",
"react-dom": "^16.8.5",
"react-router-dom": "^5.0.0",
"react-scripts": "2.1.8"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
]
}
If you enjoyed this article and want to learn more about React, check out this collection of tutorials and articles on all things React.
Published at DZone with permission of Ranga Rao Karanam. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments