This tutorial discusses how to implement user login and logout functionality using Vue.js and Spring Boot Security. The primary purpose of this article is to guide developers through creating a secure login system using Spring Boot and Vue.js. We will implement a demo application to showcase how the login and logout functionalities work together.
In this article, we are modifying the application developed for Vuejs and Spring Boot CRUD example by adding login/logout functionality.
1. Prerequisites
To follow this tutorial, we should have a basic understanding of the following:
- Vue.js
- Spring Boot
- JWT (JSON Web Token)
2. Maven
To configure Spring Boot Security, we need to add the following Maven dependencies to our pom.xml
file:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
- jjwt-api: is the JSON Web Token (JWT) API for Java which provides a simple way to generate and parse JWT tokens.
- jjwt-impl: is the implementation of the JWT API and provides the core functionality for generating and parsing JWT tokens.
- jjwt-jackson: is a JSON library for working with JSON Web Tokens. It supports reading and writing the tokens using the Jackson JSON library.
- spring-boot-starter-security: is a starter for using security in a Spring Boot project. It provides all the necessary dependencies to use Spring Security, including the core library, configuration, and other features. It can be used to add authentication and authorization to our spring boot application.
3. JWT Authentication Flow with Spring Security
Before digging deep into the tiny details, let us first understand the authentication process at a high level.
3.1. Login Workflow
The following diagram depicts the process of login when a user submits the username/password into the login screen and the server responds with the JWT token to be sent in all subsequent requests.

Here is how the login flow works:
- The client makes a POST request to the
/login
endpoint with a JSON payload that contains the user’s username and password (AuthenticationRequest). - The
authenticateUser()
method in theAuthController
class receives this request and uses theAuthenticationManager
to authenticate the user’s credentials. TheAuthenticationManager
verifies the user’s identity by checking if the username and password match a record in the backend/database. - If the user’s credentials are valid, an
Authentication
object is returned. This object contains information about the authenticated user, such as their username and granted authorities. - The
SecurityContextHolder
is updated with the authenticatedAuthentication
object so it can be obtained anywhere in the application. - The
JwtTokenProvider
uses the Authentication to create a JWT token for the authenticated user. This token contains the user’s username, authorities, and expiration time. - The server responds to the client with a 200 OK status code and a JSON payload containing the JWT token (AuthenticationResponse). The client must store this token and include it in the headers of future requests to access protected endpoints.
3.2. Authentication Workflow
The following diagram depicts the process when a user requests a protected resource and the request contains the Jwt token. The spring security extracts the token and validates the user’s identity before sending the response.

Here’s a summary of what happens when you send a request to /employees
:
- The JwtTokenFilter intercepts the request before it reaches the
/employees
endpoint. - It retrieves the JWT token from the Authorization header of the request using
JwtTokenProvider
.resolveToken()
. - If the token is found, it is validated using the
validateToken()
. - If the token is valid, the filter loads the user details associated with the token using the
loadUserByUsername
method ofUserDetailsService
. - It then creates an authentication object of type
UsernamePasswordAuthenticationToken
and sets it in theSecurityContextHolder
. - Finally, the filter chain is called to pass the request and response to the next filter or to the endpoint.
4. Spring Security Implementation
Let us deep dive into changes in each class and how they help the whole process. It is highly recommended first to read how Spring security works with UserDetailsService.
4.1. Security Configuration
The SecurityConfig defines important beans such as AuthenticationManager
, AuthenticationProvider
, UserDetailsService
and SecurityFilterChain
which are essential for implementing authentication and authorization in a Spring Security application.
@EnableWebSecurity
@Configuration
public class SecurityConfig {
private final JwtTokenFilter jwtAuthenticationFilter;
private final UserDetailsService userDetailsService;
private final DaoAuthenticationProvider daoAuthenticationProvider;
public SecurityConfig(JwtTokenFilter jwtAuthenticationFilter,
UserDetailsService userDetailsService,
DaoAuthenticationProvider daoAuthenticationProvider) {
this.jwtAuthenticationFilter = jwtAuthenticationFilter;
this.userDetailsService = userDetailsService;
this.daoAuthenticationProvider = daoAuthenticationProvider;
}
@Bean
public AuthenticationProvider authenticationProvider() {
daoAuthenticationProvider.setUserDetailsService(userDetailsService);
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
return daoAuthenticationProvider;
}
@Bean
PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
@Bean
AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
httpSecurity.headers().frameOptions().disable();
httpSecurity.cors().and().csrf().disable();
//@formatter:off
httpSecurity
.authorizeHttpRequests()
.requestMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling()
.authenticationEntryPoint(
(request, response, authException)
-> response.sendError(
HttpServletResponse.SC_UNAUTHORIZED,
authException.getLocalizedMessage()
)
)
.and()
.authenticationProvider(authenticationProvider())
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
//@formatter:on
return httpSecurity.build();
}
}
Let us note down a few essential details.
AuthenticationManager
The AuthenticationManager coordinates and manages the authentication flow, allowing the application to delegate the authentication process to multiple providers. Each provider can have authentication mechanisms, such as username/password, social login, or multi-factor authentication.
When a user attempts to log in, the application delegates the authentication to the AuthenticationManager. The AuthenticationManager then selects the appropriate AuthenticationProvider based on the request type and forwards the request to the configured provider.
AuthenticationProvider
The AuthenticationProvider interface exposes only two functions:
authenticate
() performs authentication.supports
() checks if the provider supports the indicated authentication type.
One important interface implementation is DaoAuthenticationProvider, which retrieves user details from a UserDetailsService. The UserDetailsService interface contains a single method loadUserByUsername(), which takes a username as a parameter and returns a UserDetails object. The UserDetails object contains the user’s security-related information such as password, authorities, and account status.
SecuritFilterChain
The SecurityFilterChain
is a crucial component in Spring Security and is responsible for applying various security filters, such as authentication and authorization filters, to incoming HTTP requests.
In the securityFilterChain()
method, the HttpSecurity
object is used to configure various security settings, such as CORS, CSRF protection, session management, and exception handling. It also adds a custom authentication provider and a JWT authentication filter to the filter chain.
The
SpringSecurityConfigurerAdapter
class has been deprecated since Spring Security 5.3, and instead, it is recommended to use theSecurityFilterChain
bean to configure Spring Security. TheSecurityFilterChain
bean allows for more fine-grained control over the configuration of the filter chain and provides better customization options.
4.2. Authentication Filter
To implement our authentication and authorization logic, we are using three classes:
JwtTokenProvider
JwtTokenFilter
JwtAuthenticationEntryPoint
JwtTokenProvider
JwtTokenProvider is responsible for generating a JWT token for an authenticated user. It creates a token using the user’s username, current time, and expiration time. JwtTokenProvider also provides methods for resolving and validating a token.
@Component
@Slf4j
public class JwtTokenProvider {
Key key = Keys.secretKeyFor(SignatureAlgorithm.HS512);
public String createToken(Authentication authentication) {
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
Date now = new Date();
Date expiryDate = new Date(now.getTime() + 3600000);
return Jwts.builder()
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date())
.setExpiration(expiryDate)
.signWith(SignatureAlgorithm.HS512, key)
.compact();
}
public String resolveToken(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
// Check if the token is valid and not expired
public boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(key).parseClaimsJws(token);
return true;
} catch (MalformedJwtException ex) {
log.error("Invalid JWT token");
} catch (ExpiredJwtException ex) {
log.error("Expired JWT token");
} catch (UnsupportedJwtException ex) {
log.error("Unsupported JWT token");
} catch (IllegalArgumentException ex) {
log.error("JWT claims string is empty");
} catch (SignatureException e) {
log.error("there is an error with the signature of you token ");
}
return false;
}
// Extract the username from the JWT token
public String getUsername(String token) {
return Jwts.parser()
.setSigningKey(key)
.parseClaimsJws(token)
.getBody()
.getSubject();
}
}
JwtTokenFilter
JwtTokenFilter
is an implementation of the OncePerRequestFilter
abstract class, which ensures that the filter is only executed once per request.
JwtTokenFilter is a filter that intercepts every request and checks whether it contains a valid JWT token in the Authorization header. If a valid token is found, JwtTokenFilter retrieves the user details from the token using JwtTokenProvider, creates an Authentication object, and sets it in the security context.
@Component
@AllArgsConstructor
public class JwtTokenFilter extends OncePerRequestFilter {
private JwtTokenProvider jwtTokenProvider;
private UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String token = jwtTokenProvider.resolveToken(request);
if (token != null && jwtTokenProvider.validateToken(token)) {
UserDetails userDetails = userDetailsService.loadUserByUsername(jwtTokenProvider.getUsername(token));
UsernamePasswordAuthenticationToken authentication
= new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}
}
In the JwtTokenFilter, if the token is not valid/exist , then the code simply continues to the next filter in the filter chain without setting any Authentication object. You may ask why we don’t return a 401 error directly if the JWT token doesn’t exist or is invalid. That’s a great question!
This behavior is intentional because not all endpoints might require authentication, and in some cases, it might be valid for the user not to be authenticated to access public APIs.
When the JwtTokenFilter sets an Authentication object in the SecurityContextHolder, Spring Security automatically handles the authentication process for subsequent requests in the same thread.
JwtAuthenticationEntryPoint
JwtAuthenticationEntryPoint is an authentication entry point that handles authentication errors. If a user tries to access a protected resource without a valid token, JwtAuthenticationEntryPoint is triggered and sends a 401 Unauthorized response.
@Component
public class JwtAuthenticationEntryPoint extends BasicAuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, IOException {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType("application/json");
response.getWriter().write("{ \"message\": \"" + authException.getMessage() + "\" }");
}
@Override
public void afterPropertiesSet() {
setRealmName("JWT Authentication");
super.afterPropertiesSet();
}
}
4.3. Authentication Provider
In our implementation, the DaoAuthenticationProvider
uses the UserDetailsService
interface that retrieves UserDetails from the database using the UserRepository
. The UserRepository
is responsible for interacting with the database and retrieving user information.
UserRepository
@Repository
public interface UserRepository extends JpaRepository<User , Long> {
Optional<User> findByEmail(String email);
}
UserDetailsService Implementation
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
try {
return userRepository.findByUsername(username)
.orElseThrow(() -> new Exception("user Not found "));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
5. Login Endpoint
Now let us understand what happens when application invokes the /login with username/password.
5.1. Controller
The AuthController is a standard REST Controller. The login
method first authenticates the user using the AuthenticationManager
and sets the authentication object in the security context.
@RestController
@CrossOrigin
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtTokenProvider jwtTokenProvider;
@PostMapping("/login")
public ResponseEntity<?> authenticateUser(@RequestBody AuthenticationRequest authenticationRequest) {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
authenticationRequest.getUsername(),
authenticationRequest.getPassword()
)
);
SecurityContextHolder.getContext().setAuthentication(authentication);
String jwt = jwtTokenProvider.createToken(authentication);
return ResponseEntity.ok(new AuthenticationResponse(jwt));
}
@GetMapping("/test")
public ResponseEntity<?> test() {
return ResponseEntity.ok(" you have access now ");
}
}
5.2. Test
Now if we try to access one of our endpoints, we will get the unauthorized message:

Send a request to /api/auth/login with the username and password in the request body, and we will get an access token.

Add the access token in the Authorization header to access now the /employees endpoint.

6. Front-end with Vue.js
The following diagram depicts the login flow on the client application side.

At a high level, login and logout functionality is implemented as follows:
- The user requests a login endpoint (with username & password).
- Spring Boot Security authenticates the user, generates a JWT token containing the user’s information, and sends it back.
- The application stores the token in the browser’s local storage.
- For subsequent requests, the user sends the token along with the request headers (using Axios interceptors).
- Spring Boot Security verifies the token’s signature and extracts the user’s information to authorize the request.
- The token is invalidated and removed from the browser’s local storage when the user logs out.
6.1. Login

LoginForm
To implement the login form, we can create a LoginForm.vue
component that contains a form with username and password fields. When the user clicks the “Login” button, the login()
method is called.
The login()
method sends a POST
request to the /auth/login
endpoint of the backend using the Axios
. If the server responds with a successful login, the response will contain an accessToken
property, which the login()
method stores in the browser’s localStorage
under the key "jwtToken"
.
Finally, the method redirects the user to the home page (“/”) using window.location.href
.
<template>
<div>
<input type="text" v-model="user.username" placeholder="Username" />
<input type="password" v-model="user.password" placeholder="Password" />
<button @click="login">Login</button>
</div>
</template>
<script>
import AuthService from "@/services/AuthService";
import router from "@/router";
export default {
data() {
return {
user: {
username: "",
password: "",
},
};
},
methods: {
login() {
AuthService.login(this.user).then((response) => {
if (response.data.accessToken) {
window.localStorage.clear();
window.localStorage.setItem("jwtToken", response.data.accessToken);
}
window.location.href = "/";
});
},
},
};
</script>
//CSS code removed for brevity
AuthService
import apiClient from "@/utils/apiClient"; // apiClient is an axios instance
class AuthService {
login = async (user: any): Promise<any> => {
return await apiClient.post("/auth/login", {
username: user.username,
password: user.password,
});
};
logout() {
window.localStorage.removeItem("jwtToken");
router.push("/login");
}
}
export default new AuthService();
6.2. Including the Token in the Authorization Header
To include a token with each request, we use Axios interceptors which intercept the request and add the token to the Authorization header as a bearer token before sending it.
import axios, { AxiosInstance } from "axios";
const API_URL = "http://localhost:9090/api";
const axiosInstance: AxiosInstance = axios.create({
baseURL: API_URL,
headers: {
"Content-Type": "application/json",
},
withCredentials: true,
});
const token = localStorage.getItem("jwtToken");
axiosInstance.interceptors.request.use(
(config) => {
config.headers.Authorization = `Bearer ${token}`;
return config;
},
(error) => {
return Promise.reject(error);
}
);
export default axiosInstance;
6.3. Logout
we can remove the JWT token from the browser’s localStorage
. This will log the user out of their session until they request a fresh token from the server.
window.localStorage.removeItem('jwtToken');
router.push('/login');
7. Conclusion
Implementing the JWT authentication in a Vue.js and Spring Security application is a secure and efficient way to protect your users’ data and resources specially the REST APIs called by single page applications (SPA). In this article, we’ve explored the fundamental concepts behind JWT authentication, including token-based authentication.
We’ve also demonstrated how to integrate JWT authentication in the front end and the backend side, covering everything from retrieving and validating tokens and processing success/error scenarios.
Happy Learning !!