ReactJS, Spring Boot JWT Authentication Example
Technologies used
Backend Technologies:
- Java 17
- Spring Boot 2.7.0
- Spring Security
- Spring Data JPA
- JWT
- H2 Database
Frontend Technologies:
- React 17.0.1
- Axios 0.27.2
- Redux 4.0.5
- Bootstrap 4.5.2
ReactJS - SpringBoot - JWT - Flow
Frontend Project Directory:
Following is the screenshot of our application -
User Registration:
Project 1: spring-boot-security-jwt
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 https://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.7.0</version> <relativePath /> <!-- lookup parent from repository --> </parent> <groupId>com.knf.dev</groupId> <artifactId>spring-boot-security-jwt</artifactId> <version>0.0.1-SNAPSHOT</version> <name>spring-boot-security-jwt</name> <description>Demo project for Spring Boot </description>
<properties> <java.version>17</java.version> </properties>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </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.1</version> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </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>
User.java
@Entity@Table(name = "auser")public class User {
@Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Column(unique = true) private String username; @Column(unique = true) private String email; private String password;
public User(String username, String email, String password) { 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 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 User() { super(); }}
The @Entity annotation specifies that the class is an entity and is mapped to a database table. The @Table annotation specifies the name of the database table to be used for mapping. The @Id annotation specifies the primary key of an entity and the @GeneratedValue provides for the specification of generation strategies for the values of primary keys.
UserRepository.java
import java.util.Optional;import org.springframework.data.jpa.repository.JpaRepository;import com.knf.dev.models.User;
public interface UserRepository extends JpaRepository<User, String> { Optional<User> findByUsername(String username); Boolean existsByUsername(String username); Boolean existsByEmail(String email);}
LoginRequest.java
public class LoginRequest {
private String username; 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; }}
SignupRequest.java
public class SignupRequest {
private String username; private String email; private String password;
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; }}
JwtResponse.java
public class JwtResponse { private String token; private String type = "Bearer"; private Long id; private String username; private String email;
public JwtResponse(String accessToken, Long id, String username, String email) {
this.token = accessToken; this.id = id; this.username = username; this.email = email;
}
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; }
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }}
MessageResponse.java
public class MessageResponse {
private String message;
public MessageResponse(String message) { this.message = message; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }}
UserDetailsImpl.java
public class UserDetailsImpl implements UserDetails {
private static final long serialVersionUID = 1L;
private Long id;
private String username;
private String email;
@JsonIgnore private String password;
public UserDetailsImpl(Long id, String username, String email, String password) {
this.id = id; this.username = username; this.email = email; this.password = password;
}
public static UserDetailsImpl build(User user) {
return new UserDetailsImpl(user.getId(), user.getUsername(), user.getEmail(), user.getPassword()); }
public Long getId() { return id; }
public String getEmail() { return email; }
@Override public String getPassword() { return password; }
@Override public String getUsername() { return username; }
@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; UserDetailsImpl user = (UserDetailsImpl) o; return Objects.equals(id, user.id); }
@Override public Collection<? extends GrantedAuthority> getAuthorities() { return null; }}
UserDetailsServiceImpl.java
@Servicepublic 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: " + username));
return UserDetailsImpl.build(user); }}
WebSecurityConfig.java
@Configuration@EnableWebSecurity@EnableGlobalMethodSecurity(prePostEnabled = true)public class WebSecurityConfig {
@Autowired private AuthEntryPointJwt unauthorizedHandler;
@Autowired UserDetailsServiceImpl userDetailsService;
@Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().exceptionHandling() .authenticationEntryPoint(unauthorizedHandler).and() .sessionManagement().sessionCreationPolicy (SessionCreationPolicy.STATELESS).and() .authorizeRequests() .antMatchers("/api/auth/**") .permitAll() .antMatchers("/api/test/**") .permitAll().anyRequest().authenticated();
http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build(); }
@Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }
@Bean public AuthTokenFilter authenticationJwtTokenFilter() { return new AuthTokenFilter(); }
@Bean public AuthenticationManager authenticationManager (AuthenticationConfiguration authenticationConfiguration) throws Exception { return authenticationConfiguration .getAuthenticationManager(); }}
AuthController.java
@CrossOrigin(origins = "*", maxAge = 3600)@RestController@RequestMapping("/api/auth")public class AuthController {
@Autowired AuthenticationManager authenticationManager;
@Autowired UserRepository userRepository;
@Autowired PasswordEncoder encoder;
@Autowired JwtUtils jwtUtils;
@PostMapping("/signin") public ResponseEntity<?> authenticateuser (@RequestBody LoginRequest loginRequest) {
Authentication authentication = authenticationManager .authenticate (new UsernamePasswordAuthenticationToken (loginRequest.getUsername(), loginRequest.getPassword()));
SecurityContextHolder.getContext() .setAuthentication(authentication); String jwt = jwtUtils.generateJwtToken(authentication);
UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();
return ResponseEntity .ok(new JwtResponse(jwt, userDetails.getId(), userDetails.getUsername(), userDetails.getEmail())); }
@PostMapping("/signup") public ResponseEntity<?> registerUser (@RequestBody SignupRequest signUpRequest) {
if (userRepository.existsByUsername(signUpRequest .getUsername())) {
return ResponseEntity.badRequest() .body(new MessageResponse ("Error: username is already taken!")); }
if (userRepository.existsByEmail (signUpRequest.getEmail())) {
return ResponseEntity.badRequest() .body(new MessageResponse ("Error: Email is already in use!")); }
// Create new user account User user = new User(signUpRequest.getUsername(), signUpRequest.getEmail(), encoder.encode(signUpRequest.getPassword()));
userRepository.save(user);
return ResponseEntity .ok(new MessageResponse("user registered successfully!")); }}
UserController.java
@CrossOrigin(origins = "*", maxAge = 4800)@RestController@RequestMapping("/api/test")public class UserController {
@GetMapping("/all") public MessageResponse allAccess() { return new MessageResponse("Server is up....."); }
@GetMapping("/greeting") @PreAuthorize("isAuthenticated()") public MessageResponse userAccess() {
return new MessageResponse ("Congratulations! You are an authenticated user."); }}
application.yaml
knf: app: jwtExpirationMs: 76300000 jwtSecret: knowledgeFactory
Application.java
@SpringBootApplicationpublic class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); }}
Project 2: Frontend: react-redux-jwt
package.json
{ "name": "react-redux-jwt", "version": "0.1.0", "private": true, "dependencies": { "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.3.2", "@testing-library/user-event": "^7.1.2", "bootstrap": "^4.5.2", "react": "^17.0.1", "react-dom": "^17.0.1", "react-redux": "^7.2.1", "react-router-dom": "^5.2.0", "react-scripts": "3.4.3", "react-validation": "^3.0.7", "redux": "^4.0.5", "redux-thunk": "^2.3.0", "axios": "^0.27.2", "validator": "^13.1.1" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject" }, "eslintConfig": { "extends": "react-app" }, "browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ] }, "devDependencies": { "redux-devtools-extension": "^2.13.8" }}
./services/user.service.js
import axios from "axios";import authHeader from "./auth-header";
const API_URL = "http://localhost:8080/api/test/";
class UserService { getPublicContent() { return axios.get(API_URL + "all"); }
getGreetings() { return axios.get(API_URL + "greeting", { headers: authHeader() }); }}
export default new UserService();
./services/auth.service.js
import axios from "axios";
const API_URL = "http://localhost:8080/api/auth/";
class AuthService { login(username, password) { return axios .post(API_URL + "signin", { username, password }) .then((response) => { if (response.data.accessToken) { localStorage.setItem("user", JSON.stringify(response.data)); }
return response.data; }); }
logout() { localStorage.removeItem("user"); }
register(username, email, password) { return axios.post(API_URL + "signup", { username, email, password, }); }}
export default new AuthService();
./services/auth-header.js
export default function authHeader() {
const user = JSON.parse(localStorage.getItem("user"));
if (user && user.accessToken) { return { Authorization: "Bearer " + user.accessToken };
} else { return {}; }}
./components/login.component.js
import React, { Component } from "react";import { Redirect } from 'react-router-dom';import Form from "react-validation/build/form";import Input from "react-validation/build/input";import CheckButton from "react-validation/build/button";import { connect } from "react-redux";import { login } from "../actions/auth";
const required = (value) => { if (!value) { return ( <div className="alert alert-danger" role="alert"> This field is required! </div> ); }};
class Login extends Component { constructor(props) { super(props); this.handleLogin = this.handleLogin.bind(this); this.onChangeUsername = this.onChangeUsername.bind(this); this.onChangePassword = this.onChangePassword.bind(this);
this.state = { username: "", password: "", loading: false, }; }
onChangeUsername(e) { this.setState({ username: e.target.value, }); }
onChangePassword(e) { this.setState({ password: e.target.value, }); }
handleLogin(e) { e.preventDefault();
this.setState({ loading: true, });
this.form.validateAll();
const { dispatch, history } = this.props;
if (this.checkBtn.context._errors.length === 0) { dispatch(login(this.state.username, this.state.password)) .then(() => { history.push("/profile"); window.location.reload(); }) .catch(() => { this.setState({ loading: false }); }); } else { this.setState({ loading: false, }); } }
render() { const { isLoggedIn, message } = this.props;
if (isLoggedIn) { return <Redirect to="/profile" />; }
return ( <div className="col-md-12"> <div className="card bg-light text-dark"> <h1><center>Login</center></h1>
<Form onSubmit={this.handleLogin} ref={(c) => { this.form = c; }} > <div className="form-group"> <label htmlFor="username">Username</label> <Input type="text" className="form-control" name="username" value={this.state.username} onChange={this.onChangeUsername} validations={[required]} /> </div>
<div className="form-group"> <label htmlFor="password">Password</label> <Input type="password" className="form-control" name="password" value={this.state.password} onChange={this.onChangePassword} validations={[required]} /> </div>
<div className="form-group"> <button className="btn btn-dark btn-block" disabled={this.state.loading} > {this.state.loading && ( <span className= "spinner-border spinner-border-sm"></span> )} <span>Login</span> </button> </div>
{message && ( <div className="form-group"> <div className="alert alert-danger" role="alert"> {message} </div> </div> )} <CheckButton style={{ display: "none" }} ref={(c) => { this.checkBtn = c; }} /> </Form> </div> </div> ); }}
function mapStateToProps(state) { const { isLoggedIn } = state.auth; const { message } = state.message; return { isLoggedIn, message };}
export default connect(mapStateToProps)(Login);
./components/register.component.js
import React, { Component } from "react";import Form from "react-validation/build/form";import Input from "react-validation/build/input";import CheckButton from "react-validation/build/button";import { isEmail } from "validator";import { connect } from "react-redux";import { register } from "../actions/auth";
const required = (value) => { if (!value) { return ( <div className="alert alert-danger" role="alert"> This field is required! </div> ); }};
const email = (value) => { if (!isEmail(value)) { return ( <div className="alert alert-danger" role="alert"> This is not a valid email. </div> ); }};
const vusername = (value) => { if (value.length < 3 || value.length > 20) { return ( <div className="alert alert-danger" role="alert"> The username must be between 3 and 20 characters. </div> ); }};
const vpassword = (value) => { if (value.length < 6 || value.length > 40) { return ( <div className="alert alert-danger" role="alert"> The password must be between 6 and 40 characters. </div> ); }};
class Register extends Component { constructor(props) { super(props); this.handleRegister = this.handleRegister.bind(this); this.onChangeUsername = this.onChangeUsername.bind(this); this.onChangeEmail = this.onChangeEmail.bind(this); this.onChangePassword = this.onChangePassword.bind(this);
this.state = { username: "", email: "", password: "", successful: false, }; }
onChangeUsername(e) { this.setState({ username: e.target.value, }); }
onChangeEmail(e) { this.setState({ email: e.target.value, }); }
onChangePassword(e) { this.setState({ password: e.target.value, }); }
handleRegister(e) { e.preventDefault();
this.setState({ successful: false, });
this.form.validateAll();
if (this.checkBtn.context._errors.length === 0) { this.props .dispatch( register(this.state.username, this.state.email, this.state.password) ) .then(() => { this.setState({ successful: true, }); }) .catch(() => { this.setState({ successful: false, }); }); } }
render() { const { message } = this.props;
return (
<div className="col-md-12"> <div className="card bg-light text-dark">
<h1><center>User Registration </center></h1>
<Form onSubmit={this.handleRegister} ref={(c) => { this.form = c; }} > {!this.state.successful && ( <div> <div className="form-group"> <label htmlFor="username">Username</label> <Input type="text" className="form-control" name="username" value={this.state.username} onChange={this.onChangeUsername} validations={[required, vusername]} /> </div>
<div className="form-group"> <label htmlFor="email">Email</label> <Input type="text" className="form-control" name="email" value={this.state.email} onChange={this.onChangeEmail} validations={[required, email]} /> </div>
<div className="form-group"> <label htmlFor="password">Password</label> <Input type="password" className="form-control" name="password" value={this.state.password} onChange={this.onChangePassword} validations={[required, vpassword]} /> </div>
<div className="form-group"> <button className="btn btn-dark btn-block"> Sign Up</button> </div> </div> )}
{message && ( <div className="form-group"> <div className={this.state.successful ? "alert alert-success" : "alert alert-danger"} role="alert"> {message} </div> </div> )} <CheckButton style={{ display: "none" }} ref={(c) => { this.checkBtn = c; }} /> </Form> </div> </div> ); }}
function mapStateToProps(state) { const { message } = state.message; return { message, };}
export default connect(mapStateToProps)(Register);
./components/profile.component.js
import React, { Component } from "react";import { Redirect } from 'react-router-dom';import { connect } from "react-redux";
class Profile extends Component {
render() {
const { user: currentUser } = this.props;
if (!currentUser) { return <Redirect to="/login" />; }
return ( <div class="card bg-light text-dark"> <h1>{currentUser.username}</h1> <p> <strong>Id:</strong> {currentUser.id} </p> <p> <strong>Email:</strong> {currentUser.email} </p> <a href="#"><i class="fa fa-dribbble"></i></a> <a href="#"><i class="fa fa-twitter"></i></a> <a href="#"><i class="fa fa-linkedin"></i></a> <a href="#"><i class="fa fa-facebook"></i></a> </div> ); }}
function mapStateToProps(state) { const { user } = state.auth; return { user, };}
export default connect(mapStateToProps)(Profile);
./components/user.component.js
import React, { Component } from "react";import UserService from "../services/user.service";import EventBus from "../common/EventBus";
export default class User extends Component { constructor(props) { super(props);
this.state = { content: "" }; }
componentDidMount() { UserService.getGreetings().then( response => { this.setState({ content: response.data.message }); }, error => { this.setState({ content: (error.response && error.response.data && error.response.data.message) || error.message || error.toString() });
if (error.response && error.response.status === 401) { EventBus.dispatch("logout"); } } ); }
render() { return (
<div class="card bg-light text-dark"> <h3>{this.state.content}</h3> <a href="#"><i class="fa fa-dribbble"></i></a> <a href="#"><i class="fa fa-twitter"></i></a> <a href="#"><i class="fa fa-linkedin"></i></a> <a href="#"><i class="fa fa-facebook"></i></a> </div> ); }}
./helpers/history.js
import { createBrowserHistory } from "history";
export const history = createBrowserHistory();
./reducers/auth.js
import { REGISTER_SUCCESS, REGISTER_FAIL, LOGIN_SUCCESS, LOGIN_FAIL, LOGOUT,} from "../actions/types";
const user = JSON.parse(localStorage.getItem("user"));
const initialState = user ? { isLoggedIn: true, user } : { isLoggedIn: false, user: null };
export default function (state = initialState, action) { const { type, payload } = action;
switch (type) { case REGISTER_SUCCESS: return { ...state, isLoggedIn: false, }; case REGISTER_FAIL: return { ...state, isLoggedIn: false, }; case LOGIN_SUCCESS: return { ...state, isLoggedIn: true, user: payload.user, }; case LOGIN_FAIL: return { ...state, isLoggedIn: false, user: null, }; case LOGOUT: return { ...state, isLoggedIn: false, user: null, }; default: return state; }}
./reducers/index.js
import { combineReducers } from "redux";import auth from "./auth";import message from "./message";
export default combineReducers({ auth, message,});
./reducers/message.js
import { SET_MESSAGE, CLEAR_MESSAGE } from "../actions/types";
const initialState = {};
export default function (state = initialState, action) { const { type, payload } = action;
switch (type) { case SET_MESSAGE: return { message: payload };
case CLEAR_MESSAGE: return { message: "" };
default: return state; }}
./comman/auth-verify.js
import React, { Component } from "react";import { history } from '../helpers/history';
const parseJwt = (token) => { try { return JSON.parse(atob(token.split('.')[1])); } catch (e) { return null; }};
class AuthVerify extends Component { constructor(props) { super(props);
history.listen(() => { const user = JSON.parse(localStorage.getItem("user"));
if (user) { const decodedJwt = parseJwt(user.accessToken);
if (decodedJwt.exp * 1000 < Date.now()) { props.logOut(); } } }); }
render() { return <div></div>; }}
export default AuthVerify;
./comman/EventBus.js
const eventBus = { on(event, callback) { document.addEventListener(event, (e) => callback(e.detail)); }, dispatch(event, data) { document.dispatchEvent(new CustomEvent(event, { detail: data })); }, remove(event, callback) { document.removeEventListener(event, callback); },};
export default eventBus;
./actions/auth.js
import { REGISTER_SUCCESS, REGISTER_FAIL, LOGIN_SUCCESS, LOGIN_FAIL, LOGOUT, SET_MESSAGE,} from "./types";
import AuthService from "../services/auth.service";
export const register = (username, email, password) => (dispatch) => {
return AuthService.register(username, email, password).then( (response) => { dispatch({ type: REGISTER_SUCCESS, });
dispatch({ type: SET_MESSAGE, payload: response.data.message, });
return Promise.resolve(); }, (error) => { const message = (error.response && error.response.data && error.response.data.message) || error.message || error.toString();
dispatch({ type: REGISTER_FAIL, });
dispatch({ type: SET_MESSAGE, payload: message, });
return Promise.reject(); } );};
export const login = (username, password) => (dispatch) => { return AuthService.login(username, password).then( (data) => { dispatch({ type: LOGIN_SUCCESS, payload: { user: data }, });
return Promise.resolve(); }, (error) => { const message = (error.response && error.response.data && error.response.data.message) || error.message || error.toString();
dispatch({ type: LOGIN_FAIL, });
dispatch({ type: SET_MESSAGE, payload: message, });
return Promise.reject(); } );};
export const logout = () => (dispatch) => { AuthService.logout();
dispatch({ type: LOGOUT, });};
./actions/message.js
import { SET_MESSAGE, CLEAR_MESSAGE } from "./types";
export const setMessage = (message) => ({ type: SET_MESSAGE, payload: message,});
export const clearMessage = () => ({ type: CLEAR_MESSAGE,});
./actions/types.js
export const REGISTER_SUCCESS = "REGISTER_SUCCESS";export const REGISTER_FAIL = "REGISTER_FAIL";export const LOGIN_SUCCESS = "LOGIN_SUCCESS";export const LOGIN_FAIL = "LOGIN_FAIL";export const LOGOUT = "LOGOUT";export const SET_MESSAGE = "SET_MESSAGE";export const CLEAR_MESSAGE = "CLEAR_MESSAGE";
store.js
import { createStore, applyMiddleware } from "redux";import { composeWithDevTools } from "redux-devtools-extension";import thunk from "redux-thunk";import rootReducer from "./reducers";
const middleware = [thunk];
const store = createStore( rootReducer, composeWithDevTools(applyMiddleware(...middleware)));
export default store;
App.js
import React, { Component } from "react";import { connect } from "react-redux";import { Router, Switch, Route, Link } from "react-router-dom";
import "bootstrap/dist/css/bootstrap.min.css";import "./App.css";import Login from "./components/login.component";import Register from "./components/register.component";import Profile from "./components/profile.component";import User from "./components/user.component";import { logout } from "./actions/auth";import { clearMessage } from "./actions/message";import { history } from './helpers/history';import EventBus from "./common/EventBus";
class App extends Component { constructor(props) { super(props); this.logOut = this.logOut.bind(this);
this.state = { currentUser: undefined, };
history.listen((location) => { props.dispatch(clearMessage()); }); }
componentDidMount() { const user = this.props.user;
if (user) { this.setState({ currentUser: user }); }
EventBus.on("logout", () => { this.logOut(); }); }
componentWillUnmount() { EventBus.remove("logout"); }
logOut() { this.props.dispatch(logout()); this.setState({ currentUser: undefined, }); }
render() { const { currentUser} = this.state;
return ( <Router history={history}> <div> <nav className="navbar navbar-expand navbar-dark bg-dark"> <Link to={"/"} className="navbar-brand"> Knowledgefactory </Link> <div className="navbar-nav mr-auto"> {currentUser && ( <li className="nav-item"> <Link to={"/user"} className="nav-link"> Resource </Link> </li> )} </div>
{currentUser ? ( <div className="navbar-nav ml-auto"> <li className="nav-item"> <Link to={"/profile"} className="nav-link"> Profile </Link> </li> <li className="nav-item"> <a href="/login" className="nav-link" onClick={this.logOut}> LogOut </a> </li> </div> ) : ( <div className="navbar-nav ml-auto"> <li className="nav-item"> <Link to={"/login"} className="nav-link"> Login </Link> </li>
<li className="nav-item"> <Link to={"/register"} className="nav-link"> Sign Up </Link> </li> </div> )} </nav>
<div className="container mt-3"> <Switch> <Route exact path={["/", "/register"]} component={Register} /> <Route exact path="/login" component={Login} /> <Route exact path="/register" component={Register} /> <Route exact path="/profile" component={Profile} /> <Route exact path="/user" component={User} /> </Switch> </div> </div> </Router> ); }}
function mapStateToProps(state) { const { user } = state.auth; return { user, };}
export default connect(mapStateToProps)(App);
App.css
label { display: block; margin-top: 10px;}
.card-container.card { max-width: 350px !important; padding: 40px 40px;}
.card { background-color: #f7f7f7; padding: 20px 25px 30px; margin: 0 auto 25px; margin-top: 50px; -moz-border-radius: 2px; -webkit-border-radius: 2px; border-radius: 2px;
}
.profile-img-card { width: 96px; height: 96px; margin: 0 auto 10px; display: block; -moz-border-radius: 50%; -webkit-border-radius: 50%; border-radius: 50%;}
index.js
import React from "react";import ReactDOM from "react-dom";import { Provider } from "react-redux";import store from "./store";import "./index.css";import App from "./App";import * as serviceWorker from "./serviceWorker";
ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById("root"));serviceWorker.unregister();
index.css
body { margin: 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;}
.env
PORT=9080
Download the complete source code - click here
Local Setup and Run the application
Step1: Download or clone the source code from GitHub to a local machine - Click here
Backend
Step 2: mvn clean install
Step 3: Run the Spring Boot application - mvn spring-boot:run
Frontend
Step 4: npm install or yarn install
Step 5: npm start or yarn start
From the browser call the endpoint http://localhost:9080/
Buy Now – Unlock Your Microservices Mastery for Only $9.22!
Get your copy now for just $9.22! and start building resilient and scalable microservices with the help of Microservices with Spring Boot 3 and Spring Cloud.