Spring Boot Vue.js JWT Authentication Example

Author :Sai K

In this tutorial, we will create a user authentication system using JSON Web Tokens (JWT) with Spring Boot 3 for the backend and Vue.js 3.4 for the frontend. We will use Spring Security 6 to secure the application and Vue.js to handle the user interface.

Prerequisites

Before we start, ensure you have the following:

  • Java Development Kit (JDK) installed

  • Apache Maven installed

  • Apache Maven installed

  • An IDE (such as IntelliJ IDEA, Eclipse, or VS Code) installed

Step 1: Setting Up the Spring Boot Project

1.1 Create a Spring Boot Project

Go to Spring Initializr in your web browser.

2.Configure Project Metadata:

  • Project: Maven Project

  • Language: Java

  • Spring Boot: Select the latest version of Spring Boot 3

  • Group: com.example

  • Artifact: jwt-authentication

  • Name: jwt-authentication

  • Description: JWT Authentication with Spring Boot and Vue.js

  • Package Name:com.example.jwtauthentication

  • Packaging: Jar

  • Java Version: 17 (or your preferred version)

  • Click Next.

3.Select Dependencies:

  • On the Dependencies screen, select the dependencies you need:
    • Spring Web

    • Spring Data JPA

    • H2 Database

    • Spring Boot DevTools

    • Spring Security

    • Spring Boot Starter Mail

    • JWT (io.jsonwebtoken:jjwt-api, io.jsonwebtoken:jjwt-impl, io.jsonwebtoken:jjwt-jackson)

  • Click Next.
  • 4.Generate the Project:

    • Click Generate to download the project zip file.

    • Extract the zip file to your desired location.

    5.Open the Project in Your IDE:

    • Open your IDE and import the project as a Maven project.

    1.2 Maven Dependencies

    Here's a pom.xml file for the Spring Boot and JWT authentication tutorial:

    
                        <?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>
    
        <groupId>com.example</groupId>
        <artifactId>jwt-authentication</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>jwt-authentication</name>
        <description>Full Stack Application with Spring Boot and Vue.js for JWT Authentication</description>
        <packaging>jar</packaging>
    
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>3.0.6</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
    
        <properties>
            <java.version>17</java.version>
            <jjwt.version>0.11.5</jjwt.version>
        </properties>
    
        <dependencies>
            <!-- Spring Boot dependencies -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-jpa</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-security</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <scope>runtime</scope>
                <optional>true</optional>
            </dependency>
    
            <!-- H2 Database -->
            <dependency>
                <groupId>com.h2database</groupId>
                <artifactId>h2</artifactId>
                <scope>runtime</scope>
            </dependency>
    
            <!-- JWT dependencies -->
            <dependency>
                <groupId>io.jsonwebtoken</groupId>
                <artifactId>jjwt-api</artifactId>
                <version>${jjwt.version}</version>
            </dependency>
            <dependency>
                <groupId>io.jsonwebtoken</groupId>
                <artifactId>jjwt-impl</artifactId>
                <version>${jjwt.version}</version>
                <scope>runtime</scope>
            </dependency>
            <dependency>
                <groupId>io.jsonwebtoken</groupId>
                <artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if preferred -->
                <version>${jjwt.version}</version>
                <scope>runtime</scope>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    </project>
                    

    I suggest using the same JWT dependency versions as the ones used in this tutorial.

    1.3 Update application.properties

    Open the application.properties file located in the src/main/resources directory and add the following configuration:

    spring.datasource.url=jdbc:h2:mem:testdb
                        spring.datasource.driverClassName=org.h2.Driver
                        spring.datasource.username=sa
                        spring.datasource.password=password
                        
                        spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
                        spring.h2.console.enabled=true
                        spring.jpa.hibernate.ddl-auto=update
                        
                        # JWT Configuration
                        jwt.secret=secret
                        jwt.expiration=3600000
                        

    1.4 Create the User Entity

    In the com.example.jwtauthentication.model package, create a new Java class named User:

    
                        package com.example.jwtauthentication.model;
    
    import jakarta.persistence.Entity;
    import jakarta.persistence.GeneratedValue;
    import jakarta.persistence.GenerationType;
    import jakarta.persistence.Id;
    
    @Entity
    public class User {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        private String firstName;
        private String lastName;
        private String email;
        private String password;
    
        // Getters and Setters
    }

    1.5 Create the UserRepository Interface

    In the com.example.jwtauthentication.repository package, create a new Java interface named UserRepository:

    
    package com.example.jwtauthentication.repository;
    
    import com.example.jwtauthentication.model.User;
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.stereotype.Repository;
    
    @Repository
    public interface UserRepository extends JpaRepository {
        User findByEmail(String email);
    }
    

    1.6 Create the UserService Class

    In the com.example.jwtauthentication.service package, create a new Java class named UserService:

    
        package com.example.jwtauthentication.service;
    
    import com.example.jwtauthentication.model.User;
    import com.example.jwtauthentication.repository.UserRepository;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.stereotype.Service;
    
    @Service
    public class UserService {
    
        private final UserRepository userRepository;
        private final BCryptPasswordEncoder passwordEncoder;
    
        @Autowired
        public UserService(UserRepository userRepository, BCryptPasswordEncoder passwordEncoder) {
            this.userRepository = userRepository;
            this.passwordEncoder = passwordEncoder;
        }
    
        public User saveUser(User user) {
            user.setPassword(passwordEncoder.encode(user.getPassword()));
            return userRepository.save(user);
        }
    
        public User findByEmail(String email) {
            return userRepository.findByEmail(email);
        }
    }
    

    1.7 Create JWT Utility Class

    In the com.example.jwtauthentication.util package, create a new Java class named JwtUtil:

    
        package com.example.jwtauthentication.util;
    
    import io.jsonwebtoken.Claims;
    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.SignatureAlgorithm;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Component;
    
    import java.util.Date;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.function.Function;
    
    @Component
    public class JwtUtil {
    
        @Value("${jwt.secret}")
        private String secret;
    
        @Value("${jwt.expiration}")
        private long expiration;
    
        public String extractUsername(String token) {
            return extractClaim(token, Claims::getSubject);
        }
    
        public  T extractClaim(String token, Function claimsResolver) {
            final Claims claims = extractAllClaims(token);
            return claimsResolver.apply(claims);
        }
    
        private Claims extractAllClaims(String token) {
            return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
        }
    
        public String generateToken(String username) {
            Map claims = new HashMap<>();
            return createToken(claims, username);
        }
    
        private String createToken(Map claims, String subject) {
            return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis()))
                    .setExpiration(new Date(System.currentTimeMillis() + expiration))
                    .signWith(SignatureAlgorithm.HS256, secret).compact();
        }
    
        public boolean validateToken(String token, String username) {
            final String extractedUsername = extractUsername(token);
            return (extractedUsername.equals(username) && !isTokenExpired(token));
        }
    
        private boolean isTokenExpired(String token) {
            return extractExpiration(token).before(new Date());
        }
    
        public Date extractExpiration(String token) {
            return extractClaim(token, Claims::getExpiration);
        }
    }
    

    1.8 Create JWT Authentication Filter

    In the com.example.jwtauthentication.filter package, create a new Java class named JwtAuthenticationFilter:

    
        package com.example.jwtauthentication.filter;
    
    import com.example.jwtauthentication.service.UserService;
    import com.example.jwtauthentication.util.JwtUtil;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
    import org.springframework.stereotype.Component;
    import org.springframework.web.filter.OncePerRequestFilter;
    
    import javax.servlet.FilterChain;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    @Component
    public class JwtAuthenticationFilter extends OncePerRequestFilter {
    
        @Autowired
        private JwtUtil jwtUtil;
    
        @Autowired
        private UserService userService;
    
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
            final String authorizationHeader = request.getHeader("Authorization");
    
            String username = null;
            String jwt = null;
    
            if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
                jwt = authorizationHeader.substring(7);
                username = jwtUtil.extractUsername(jwt);
            }
    
            if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                UserDetails userDetails = this.userService.loadUserByUsername(username);
    
                if (jwtUtil.validateToken(jwt, userDetails.getUsername())) {
                    UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
                            userDetails, null, userDetails.getAuthorities());
                    usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
                }
            }
            filterChain.doFilter(request, response);
        }
    }
    

    1.9 Create Security Configuration

    Create a new Java class named SecurityConfig in the com.example.jwtauthentication.config package:

    
        package com.example.jwtauthentication.config;
    
    import com.example.jwtauthentication.filter.JwtAuthenticationFilter;
    import com.example.jwtauthentication.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
    import org.springframework.security.crypto.b
    
    crypt.BCryptPasswordEncoder;
    import org.springframework.security.web.SecurityFilterChain;
    import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
    
    @Configuration
    @EnableWebSecurity
    public class SecurityConfig {
    
        @Autowired
        private JwtAuthenticationFilter jwtAuthenticationFilter;
    
        @Autowired
        private UserService userService;
    
        @Bean
        public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
            http
                .csrf(AbstractHttpConfigurer::disable)
                .authorizeHttpRequests(auth -> auth
                    .requestMatchers("/auth/**").permitAll()
                    .anyRequest().authenticated()
                )
                .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
    
            return http.build();
        }
    
        @Bean
        public BCryptPasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    
        @Bean
        public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
            return authenticationConfiguration.getAuthenticationManager();
        }
    }
    

    1.10 Update UserService to Implement UserDetailsService

    Update the UserService class to implement UserDetailsService:

    
        package com.example.jwtauthentication.service;
    
    import com.example.jwtauthentication.model.User;
    import com.example.jwtauthentication.repository.UserRepository;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.stereotype.Service;
    
    import java.util.Collections;
    
    @Service
    public class UserService implements UserDetailsService {
    
        private final UserRepository userRepository;
        private final BCryptPasswordEncoder passwordEncoder;
    
        @Autowired
        public UserService(UserRepository userRepository, BCryptPasswordEncoder passwordEncoder) {
            this.userRepository = userRepository;
            this.passwordEncoder = passwordEncoder;
        }
    
        public User saveUser(User user) {
            user.setPassword(passwordEncoder.encode(user.getPassword()));
            return userRepository.save(user);
        }
    
        public User findByEmail(String email) {
            return userRepository.findByEmail(email);
        }
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            User user = userRepository.findByEmail(username);
            if (user == null) {
                throw new UsernameNotFoundException("User not found");
            }
            return new org.springframework.security.core.userdetails.User(user.getEmail(), user.getPassword(), Collections.emptyList());
        }
    }
    

    1.11 Create the AuthController Class

    In the com.example.jwtauthentication.controller package, create a new Java class named AuthController:

    
        package com.example.jwtauthentication.controller;
    
    import com.example.jwtauthentication.model.User;
    import com.example.jwtauthentication.service.UserService;
    import com.example.jwtauthentication.util.JwtUtil;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.web.bind.annotation.*;
    
    @RestController
    @RequestMapping("/auth")
    public class AuthController {
    
        private final UserService userService;
        private final AuthenticationManager authenticationManager;
        private final JwtUtil jwtUtil;
    
        @Autowired
        public AuthController(UserService userService, AuthenticationManager authenticationManager, JwtUtil jwtUtil) {
            this.userService = userService;
            this.authenticationManager = authenticationManager;
            this.jwtUtil = jwtUtil;
        }
    
        @PostMapping("/register")
        public User registerUser(@RequestBody User user) {
            return userService.saveUser(user);
        }
    
        @PostMapping("/login")
        public String loginUser(@RequestBody User user) {
            Authentication authentication = authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(user.getEmail(), user.getPassword())
            );
            SecurityContextHolder.getContext().setAuthentication(authentication);
            UserDetails userDetails = userService.loadUserByUsername(user.getEmail());
            return jwtUtil.generateToken(userDetails.getUsername());
        }
    }
    package com.example.jwtauthentication.controller;
    
    import com.example.jwtauthentication.model.User;
    import com.example.jwtauthentication.service.UserService;
    import com.example.jwtauthentication.util.JwtUtil;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.web.bind.annotation.*;
    
    @RestController
    @RequestMapping("/auth")
    public class AuthController {
    
        private final UserService userService;
        private final AuthenticationManager authenticationManager;
        private final JwtUtil jwtUtil;
    
        @Autowired
        public AuthController(UserService userService, AuthenticationManager authenticationManager, JwtUtil jwtUtil) {
            this.userService = userService;
            this.authenticationManager = authenticationManager;
            this.jwtUtil = jwtUtil;
        }
    
        @PostMapping("/register")
        public User registerUser(@RequestBody User user) {
            return userService.saveUser(user);
        }
    
        @PostMapping("/login")
        public String loginUser(@RequestBody User user) {
            Authentication authentication = authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(user.getEmail(), user.getPassword())
            );
            SecurityContextHolder.getContext().setAuthentication(authentication);
            UserDetails userDetails = userService.loadUserByUsername(user.getEmail());
            return jwtUtil.generateToken(userDetails.getUsername());
        }
    }
    

    Step 2: Creating the Frontend with Vue.js

    2.1 Set Up Vue Project

    1.Open a terminal and navigate to your workspace directory.

    2.Create a new Vue project using Vue CLI:

    
    npm install -g @vue/cli
    vue create vue-frontend
    

    3.Navigate to the project directory:

    cd vue-frontend

    2.2 Install Axios and Bootstrap

    Install Axios to make HTTP requests and Bootstrap for styling:

    npm install axios bootstrap

    2.3 Create Components

    Create the necessary components for user registration and login.

    2.3.1 Create AuthService.js

    Create a new file AuthService.js in the src directory to handle API requests for authentication:

    
        import axios from 'axios';
    
    const API_BASE_URL = "http://localhost:8080/auth";
    
    class AuthService {
        register(user) {
            return axios.post(`${API_BASE_URL}/register`, user);
        }
    
        login(user) {
            return axios.post(`${API_BASE_URL}/login`, user);
        }
    }
    
    export default new AuthService();
    

    2.3.2 Create RegisterComponent.vue

    Create a new file RegisterComponent.vue in the src/components directory:

    
        
          
                
    

    2.3.3 Create LoginComponent.vue

    Create a new file LoginComponent.vue in the src/components directory:

    
        <template>
      <div class="container mt-5">
        <div class="row justify-content-center">
          <div class="col-md-6">
            <div class="card">
              <div class="card-header">Login Form</div>
              <div class="card-body">
                <form @submit.prevent="loginUser">
                  <div class="form-group">
                    <label>Email</label>
                    <input v-model="user.email" class="form-control" type="email" placeholder="Email" />
                  </div>
                  <div class="form-group">
                    <label>Password</label>
                    <input v-model="user.password" class="form-control" type="password" placeholder="Password" />
                  </div>
                  <button type="submit" class="btn btn-primary">Login</button>
                </form>
                <div class="mt-3">
                  <span>Not registered? <router-link to="/register">Register/SignUp Here</router-link></span>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </template>
    
    <script>
    import AuthService from '../AuthService';
    
    export default {
      data() {
        return {
          user: {
            email: '',
            password: ''
          }
        };
      },
      methods: {
        loginUser() {
          AuthService.login(this.user).then(response => {
            console.log(response.data);
          });
        }
      }
    };
    </script>
    

    2.3.4 Create App.vue

    Modify the App.vue file to include routing for the components:

    
        <template>
      <div id="app">
        <nav class="navbar navbar-expand-lg navbar-light bg-light">
          <a class="navbar-brand" href="#">JWT Authentication System</a>
        </nav>
        <router-view></router-view>
      </div>
    </template>
    
    <script>
    export default {
      name: 'App'
    };
    </script>
    
    <style>
    nav {
      margin-bottom: 20px;
    }
    </style>
    

    2.3.5 Update main.js

    Ensure the main.js file is set

    up correctly:

    
        import { createApp } from 'vue';
    import App from './App.vue';
    import { createRouter, createWebHistory } from 'vue-router';
    import RegisterComponent from './components/RegisterComponent.vue';
    import LoginComponent from './components/LoginComponent.vue';
    import 'bootstrap/dist/css/bootstrap.min.css';
    
    const routes = [
      { path: '/', component: RegisterComponent },
      { path: '/register', component: RegisterComponent },
      { path: '/login', component: LoginComponent }
    ];
    
    const router = createRouter({
      history: createWebHistory(),
      routes
    });
    
    const app = createApp(App);
    app.use(router);
    app.mount('#app');
    

    Step 3: Running the Application

    3.1 Run the Spring Boot Application

    1.Open the JwtAuthenticationApplication class in the src/main/java/com/example/jwtauthentication directory.

    2.Click the green Run button in your IDE or use the terminal to run the application

    ./mvnw spring-boot:run

    3.2 Run the Vue.js Application

    1. Open a terminal and navigate to the vue-frontend directory.

    2.Start the Vue application:

    npm run serve

    3.Open your web browser and navigate to http://localhost:8080.

    You should now be able to register and log in users using the Vue.js frontend and Spring Boot backend. The

    backend will issue a JWT token upon successful login, which can be used to access secured endpoints.


    Conclusion

    In this tutorial, we created a full-stack application using Spring Boot for the backend and Vue.js for the frontend.

    We implemented JWT authentication and handled the necessary configurations to connect the two parts of the

    application. This setup provides a solid foundation for developing more secure full-stack applications.

    Related Spring and Spring Boot Tutorials/Guides: