spring-boot-authentication-jwt-spring-security-feature-image

Spring Boot Token based Authentication with Spring Security & JWT

In this tutorial, we’re gonna build a Spring Boot Application that supports Token based Authentication with JWT. You’ll know:

  • Appropriate Flow for User Signup & User Login with JWT Authentication
  • Spring Boot Application Architecture with Spring Security
  • How to configure Spring Security to work with JWT
  • How to define Data Models and association for Authentication and Authorization
  • Way to use Spring Data JPA to interact with PostgreSQL/MySQL Database

Lots of interesting things ahead, let’s explore together.


– Related Post: Spring Boot, Spring Data JPA โ€“ Building Rest CRUD API example
– More Practice:

– Fullstack:

Deployment: Deploy Spring Boot App on AWS โ€“ Elastic Beanstalk

Overview of Spring Boot JWT Authentication example

We will build a Spring Boot application in that:

  • User can signup new account, or login with username & password.
  • By User’s role (admin, moderator, user), we authorize the User to access resources

These are APIs that we need to provide:

MethodsUrlsActions
POST/api/auth/signupsignup new account
POST/api/auth/signinlogin an account
GET/api/test/allretrieve public content
GET/api/test/useraccess User’s content
GET/api/test/modaccess Moderator’s content
GET/api/test/adminaccess Admin’s content

The database we will use could be PostgreSQL or MySQL depending on the way we configure project dependency & datasource.

Spring Boot Signup & Login with JWT Authentication Flow

The diagram shows flow of how we implement User Registration, User Login and Authorization process.

spring-boot-authentication-jwt-spring-security-flow

A legal JWT must be added to HTTP Authorization Header if Client accesses protected resources.

Spring Boot Server Architecture with Spring Security

You can have an overview of our Spring Boot Server with the diagram below:

spring-boot-authentication-spring-security-architecture

Now I will explain it briefly.

Spring Security

WebSecurityConfigurerAdapter is the crux of our security implementation. It provides HttpSecurity configurations to configure cors, csrf, session management, rules for protected resources. We can also extend and customize the default configuration that contains the elements below.

UserDetailsService interface has a method to load User by username and returns a UserDetails object that Spring Security can use for authentication and validation.

UserDetails contains necessary information (such as: username, password, authorities) to build an Authentication object.

UsernamePasswordAuthenticationToken gets {username, password} from login Request, AuthenticationManager will use it to authenticate a login account.

AuthenticationManager has a DaoAuthenticationProvider (with help of UserDetailsService & PasswordEncoder) to validate UsernamePasswordAuthenticationToken object. If successful, AuthenticationManager returns a fully populated Authentication object (including granted authorities).

OncePerRequestFilter makes a single execution for each request to our API. It provides a doFilterInternal() method that we will implement parsing & validating JWT, loading User details (using UserDetailsService), checking Authorizaion (using UsernamePasswordAuthenticationToken).

AuthenticationEntryPoint will catch authentication error.

Repository contains UserRepository & RoleRepository to work with Database, will be imported into Controller.

Controller receives and handles request after it was filtered by OncePerRequestFilter.

AuthController handles signup/login requests

TestController has accessing protected resource methods with role based validations.

Technology

  • Java 8
  • Spring Boot 2.1.8 (with Spring Security, Spring Web, Spring Data JPA)
  • jjwt 0.9.1
  • PostgreSQL/MySQL
  • Maven 3.6.1

Project Structure

This is folders & files structure for our Spring Boot application:

spring-boot-authentication-spring-security-project-structure

security: we configure Spring Security & implement Security Objects here.

  • WebSecurityConfig extends WebSecurityConfigurerAdapter
  • UserDetailsServiceImpl implements UserDetailsService
  • UserDetailsImpl implements UserDetails
  • AuthEntryPointJwt implements AuthenticationEntryPoint
  • AuthTokenFilter extends OncePerRequestFilter
  • JwtUtils provides methods for generating, parsing, validating JWT

controllers handle signup/login requests & authorized requests.

  • AuthController: @PostMapping(‘/signin’), @PostMapping(‘/signup’)
  • TestController: @GetMapping(‘/api/test/all’), @GetMapping(‘/api/test/[role]’)

repository has intefaces that extend Spring Data JPA JpaRepository to interact with Database.

  • UserRepository extends JpaRepository<User, Long>
  • RoleRepository extends JpaRepository<Role, Long>

models defines two main models for Authentication (User) & Authorization (Role). They have many-to-many relationship.

  • User: id, username, email, password, roles
  • Role: id, name

payload defines classes for Request and Response objects

We also have application.properties for configuring Spring Datasource, Spring Data JPA and App properties (such as JWT Secret string or Token expiration time).

Setup new Spring Boot project

Use Spring web tool or your development tool (Spring Tool Suite, Eclipse, Intellij) to create a Spring Boot project.

Then open pom.xml and add these dependencies:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
</dependency>

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
	<groupId>io.jsonwebtoken</groupId>
	<artifactId>jjwt</artifactId>
	<version>0.9.1</version>
</dependency>

We also need to add one more dependency.
– If you want to use PostgreSQL:

<dependency>
	<groupId>org.postgresql</groupId>
	<artifactId>postgresql</artifactId>
	<scope>runtime</scope>
</dependency>

– or MySQL is your choice:

<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<scope>runtime</scope>
</dependency>

Configure Spring Datasource, JPA, App properties

Under src/main/resources folder, open application.properties, add some new lines.

For PostgreSQL

spring.datasource.url= jdbc:postgresql://localhost:5432/testdb
spring.datasource.username= postgres
spring.datasource.password= 123

spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation= true
spring.jpa.properties.hibernate.dialect= org.hibernate.dialect.PostgreSQLDialect

# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto= update

# App Properties
bezkoder.app.jwtSecret= bezKoderSecretKey
bezkoder.app.jwtExpirationMs= 86400000

For MySQL

spring.datasource.url= jdbc:mysql://localhost:3306/testdb?useSSL=false
spring.datasource.username= root
spring.datasource.password= 123456

spring.jpa.properties.hibernate.dialect= org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.ddl-auto= update

# App Properties
bezkoder.app.jwtSecret= bezKoderSecretKey
bezkoder.app.jwtExpirationMs= 86400000

Create the models

We’re gonna have 3 tables in database: users, roles and user_roles for many-to-many relationship.

Let’s define these models.
In models package, create 3 files:

ERole enum in ERole.java.
In this example, we have 3 roles corresponding to 3 enum.

package com.bezkoder.springjwt.models;

public enum ERole {
	ROLE_USER,
    ROLE_MODERATOR,
    ROLE_ADMIN
}

Role model in Role.java

package com.bezkoder.springjwt.models;

import javax.persistence.*;

@Entity
@Table(name = "roles")
public class Role {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Integer id;

	@Enumerated(EnumType.STRING)
	@Column(length = 20)
	private ERole name;

	public Role() {

	}

	public Role(ERole name) {
		this.name = name;
	}

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public ERole getName() {
		return name;
	}

	public void setName(ERole name) {
		this.name = name;
	}
}

User model in User.java.
It has 5 fields: id, username, email, password, roles.

package com.bezkoder.springjwt.models;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.*;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;

@Entity
@Table(	name = "users", 
		uniqueConstraints = { 
			@UniqueConstraint(columnNames = "username"),
			@UniqueConstraint(columnNames = "email") 
		})
public class User {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;

	@NotBlank
	@Size(max = 20)
	private String username;

	@NotBlank
	@Size(max = 50)
	@Email
	private String email;

	@NotBlank
	@Size(max = 120)
	private String password;

	@ManyToMany(fetch = FetchType.LAZY)
	@JoinTable(	name = "user_roles", 
				joinColumns = @JoinColumn(name = "user_id"), 
				inverseJoinColumns = @JoinColumn(name = "role_id"))
	private Set<Role> roles = new HashSet<>();

	public User() {
	}

	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 Set<Role> getRoles() {
		return roles;
	}

	public void setRoles(Set<Role> roles) {
		this.roles = roles;
	}
}

Implement Repositories

Now, each model above needs a repository for persisting and accessing data. In repository package, let’s create 2 repositories.

UserRepository

There are 3 necessary methods that JpaRepository supports.

package com.bezkoder.springjwt.repository;

import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.bezkoder.springjwt.models.User;

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
	Optional<User> findByUsername(String username);

	Boolean existsByUsername(String username);

	Boolean existsByEmail(String email);
}

RoleRepository

This repository also extends JpaRepository and provides a finder method.

package com.bezkoder.springjwt.repository;

import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.bezkoder.springjwt.models.ERole;
import com.bezkoder.springjwt.models.Role;

@Repository
public interface RoleRepository extends JpaRepository<Role, Long> {
	Optional<Role> findByName(ERole name);
}

Configure Spring Security

In security package, create WebSecurityConfig class that extends WebSecurityConfigurerAdapter.

WebSecurityConfig.java

package com.bezkoder.springjwt.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import com.bezkoder.springjwt.security.jwt.AuthEntryPointJwt;
import com.bezkoder.springjwt.security.jwt.AuthTokenFilter;
import com.bezkoder.springjwt.security.services.UserDetailsServiceImpl;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(
		// securedEnabled = true,
		// jsr250Enabled = true,
		prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
	@Autowired
	UserDetailsServiceImpl userDetailsService;

	@Autowired
	private AuthEntryPointJwt unauthorizedHandler;

	@Bean
	public AuthTokenFilter authenticationJwtTokenFilter() {
		return new AuthTokenFilter();
	}

	@Override
	public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
		authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
	}

	@Bean
	@Override
	public AuthenticationManager authenticationManagerBean() throws Exception {
		return super.authenticationManagerBean();
	}

	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.cors().and().csrf().disable()
			.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);
	}
}

Let me explain the code above.

@EnableWebSecurity allows Spring to find and automatically apply the class to the global Web Security.

@EnableGlobalMethodSecurity provides AOP security on methods. It enables @PreAuthorize, @PostAuthorize, it also supports JSR-250. You can find more parameters in configuration in Method Security Expressions.

– We override the configure(HttpSecurity http) method from WebSecurityConfigurerAdapter interface. It tells Spring Security how we configure CORS and CSRF, when we want to require all users to be authenticated or not, which filter (AuthTokenFilter) and when we want it to work (filter before UsernamePasswordAuthenticationFilter), which Exception Handler is chosen (AuthEntryPointJwt).

– Spring Security will load User details to perform authentication & authorization. So it has UserDetailsService interface that we need to implement.

– The implementation of UserDetailsService will be used for configuring DaoAuthenticationProvider by AuthenticationManagerBuilder.userDetailsService() method.

– We also need a PasswordEncoder for the DaoAuthenticationProvider. If we don’t specify, it will use plain text.

Implement UserDetails & UserDetailsService

If the authentication process is successful, we can get User’s information such as username, password, authorities from an Authentication object.

Authentication authentication = 
        authenticationManager.authenticate(
            new UsernamePasswordAuthenticationToken(username, password)
        );

UserDetails userDetails = (UserDetails) authentication.getPrincipal();
// userDetails.getUsername()
// userDetails.getPassword()
// userDetails.getAuthorities()

If we want to get more data (id, email…), we can create an implementation of this UserDetails interface.

security/services/UserDetailsImpl.java

package com.bezkoder.springjwt.security.services;

import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import com.bezkoder.springjwt.models.User;
import com.fasterxml.jackson.annotation.JsonIgnore;

public class UserDetailsImpl implements UserDetails {
	private static final long serialVersionUID = 1L;

	private Long id;

	private String username;

	private String email;

	@JsonIgnore
	private String password;

	private Collection<? extends GrantedAuthority> authorities;

	public UserDetailsImpl(Long id, String username, String email, String password,
			Collection<? extends GrantedAuthority> authorities) {
		this.id = id;
		this.username = username;
		this.email = email;
		this.password = password;
		this.authorities = authorities;
	}

	public static UserDetailsImpl build(User user) {
		List<GrantedAuthority> authorities = user.getRoles().stream()
				.map(role -> new SimpleGrantedAuthority(role.getName().name()))
				.collect(Collectors.toList());

		return new UserDetailsImpl(
				user.getId(), 
				user.getUsername(), 
				user.getEmail(),
				user.getPassword(), 
				authorities);
	}

	@Override
	public Collection<? extends GrantedAuthority> getAuthorities() {
		return authorities;
	}

	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);
	}
}

Look at the code above, you can notice that we convert Set<Role> into List<GrantedAuthority>. It is important to work with Spring Security and Authentication object later.

As I have said before, we need UserDetailsService for getting UserDetails object. You can look at UserDetailsService interface that has only one method:

public interface UserDetailsService {
    UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}

So we implement it and override loadUserByUsername() method.

security/services/UserDetailsServiceImpl.java

package com.bezkoder.springjwt.security.services;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.bezkoder.springjwt.models.User;
import com.bezkoder.springjwt.repository.UserRepository;

@Service
public class UserDetailsServiceImpl implements UserDetailsService {
	@Autowired
	UserRepository userRepository;

	@Override
	@Transactional
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		User user = userRepository.findByUsername(username)
				.orElseThrow(() -> new UsernameNotFoundException("User Not Found with username: " + username));

		return UserDetailsImpl.build(user);
	}

}

In the code above, we get full custom User object using UserRepository, then we build a UserDetails object using static build() method.

Filter the Requests

Let’s define a filter that executes once per request. So we create AuthTokenFilter class that extends OncePerRequestFilter and override doFilterInternal() method.

security/jwt/AuthTokenFilter.java

package com.bezkoder.springjwt.security.jwt;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import com.bezkoder.springjwt.security.services.UserDetailsServiceImpl;

public class AuthTokenFilter extends OncePerRequestFilter {
	@Autowired
	private JwtUtils jwtUtils;

	@Autowired
	private UserDetailsServiceImpl userDetailsService;

	private static final Logger logger = LoggerFactory.getLogger(AuthTokenFilter.class);

	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {
		try {
			String jwt = parseJwt(request);
			if (jwt != null && jwtUtils.validateJwtToken(jwt)) {
				String username = jwtUtils.getUserNameFromJwtToken(jwt);

				UserDetails userDetails = userDetailsService.loadUserByUsername(username);
				UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
						userDetails, null, userDetails.getAuthorities());
				authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

				SecurityContextHolder.getContext().setAuthentication(authentication);
			}
		} catch (Exception e) {
			logger.error("Cannot set user authentication: {}", e);
		}

		filterChain.doFilter(request, response);
	}

	private String parseJwt(HttpServletRequest request) {
		String headerAuth = request.getHeader("Authorization");

		if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) {
			return headerAuth.substring(7, headerAuth.length());
		}

		return null;
	}
}

What we do inside doFilterInternal():
– get JWT from the Authorization header (by removing Bearer prefix)
– if the request has JWT, validate it, parse username from it
– from username, get UserDetails to create an Authentication object
– set the current UserDetails in SecurityContext using setAuthentication(authentication) method.

After this, everytime you want to get UserDetails, just use SecurityContext like this:

UserDetails userDetails =
	(UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();

// userDetails.getUsername()
// userDetails.getPassword()
// userDetails.getAuthorities()

Create JWT Utility class

This class has 3 funtions:

  • generate a JWT from username, date, expiration, secret
  • get username from JWT
  • validate a JWT

security/jwt/JwtUtils.java

package com.bezkoder.springjwt.security.jwt;

import java.util.Date;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;

import com.bezkoder.springjwt.security.services.UserDetailsImpl;
import io.jsonwebtoken.*;

@Component
public class JwtUtils {
	private static final Logger logger = LoggerFactory.getLogger(JwtUtils.class);

	@Value("${bezkoder.app.jwtSecret}")
	private String jwtSecret;

	@Value("${bezkoder.app.jwtExpirationMs}")
	private int jwtExpirationMs;

	public String generateJwtToken(Authentication authentication) {

		UserDetailsImpl userPrincipal = (UserDetailsImpl) authentication.getPrincipal();

		return Jwts.builder()
				.setSubject((userPrincipal.getUsername()))
				.setIssuedAt(new Date())
				.setExpiration(new Date((new Date()).getTime() + jwtExpirationMs))
				.signWith(SignatureAlgorithm.HS512, jwtSecret)
				.compact();
	}

	public String getUserNameFromJwtToken(String token) {
		return Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token).getBody().getSubject();
	}

	public boolean validateJwtToken(String authToken) {
		try {
			Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken);
			return true;
		} catch (SignatureException e) {
			logger.error("Invalid JWT signature: {}", e.getMessage());
		} catch (MalformedJwtException e) {
			logger.error("Invalid JWT token: {}", e.getMessage());
		} catch (ExpiredJwtException e) {
			logger.error("JWT token is expired: {}", e.getMessage());
		} catch (UnsupportedJwtException e) {
			logger.error("JWT token is unsupported: {}", e.getMessage());
		} catch (IllegalArgumentException e) {
			logger.error("JWT claims string is empty: {}", e.getMessage());
		}

		return false;
	}
}

Remember that we’ve added bezkoder.app.jwtSecret and bezkoder.app.jwtExpirationMs properties in application.properties file.

Handle Authentication Exception

Now we create AuthEntryPointJwt class that implements AuthenticationEntryPoint interface. Then we override the commence() method. This method will be triggerd anytime unauthenticated User requests a secured HTTP resource and an AuthenticationException is thrown.

security/jwt/AuthEntryPointJwt.java

package com.bezkoder.springjwt.security.jwt;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

@Component
public class AuthEntryPointJwt implements AuthenticationEntryPoint {

	private static final Logger logger = LoggerFactory.getLogger(AuthEntryPointJwt.class);

	@Override
	public void commence(HttpServletRequest request, HttpServletResponse response,
			AuthenticationException authException) throws IOException, ServletException {
		logger.error("Unauthorized error: {}", authException.getMessage());
		response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Error: Unauthorized");
	}

}

HttpServletResponse.SC_UNAUTHORIZED is the 401 Status code. It indicates that the request requires HTTP authentication.

We’ve already built all things for Spring Security. The next sections of this tutorial will show you how to implement Controllers for our RestAPIs.

Define payloads for Spring RestController

Let me summarize the payloads for our RestAPIs:
– Requests:

  • LoginRequest: { username, password }
  • SignupRequest: { username, email, password }

– Responses:

  • JwtResponse: { token, type, id, username, email, roles }
  • MessageResponse: { message }

To keep the tutorial not so long, I don’t show these POJOs here.
You can find details for payload classes in source code of the project on Github.

Create Spring RestAPIs Controllers

Controller for Authentication

This controller provides APIs for register and login actions.

/api/auth/signup

  • check existing username/email
  • create new User (with ROLE_USER if not specifying role)
  • save User to database using UserRepository

/api/auth/signin

  • authenticate { username, pasword }
  • update SecurityContext using Authentication object
  • generate JWT
  • get UserDetails from Authentication object
  • response contains JWT and UserDetails data

controllers/AuthController.java

package com.bezkoder.springjwt.controllers;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.bezkoder.springjwt.models.ERole;
import com.bezkoder.springjwt.models.Role;
import com.bezkoder.springjwt.models.User;
import com.bezkoder.springjwt.payload.request.LoginRequest;
import com.bezkoder.springjwt.payload.request.SignupRequest;
import com.bezkoder.springjwt.payload.response.JwtResponse;
import com.bezkoder.springjwt.payload.response.MessageResponse;
import com.bezkoder.springjwt.repository.RoleRepository;
import com.bezkoder.springjwt.repository.UserRepository;
import com.bezkoder.springjwt.security.jwt.JwtUtils;
import com.bezkoder.springjwt.security.services.UserDetailsImpl;

@CrossOrigin(origins = "*", maxAge = 3600)
@RestController
@RequestMapping("/api/auth")
public class AuthController {
	@Autowired
	AuthenticationManager authenticationManager;

	@Autowired
	UserRepository userRepository;

	@Autowired
	RoleRepository roleRepository;

	@Autowired
	PasswordEncoder encoder;

	@Autowired
	JwtUtils jwtUtils;

	@PostMapping("/signin")
	public ResponseEntity<?> authenticateUser(@Valid @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();		
		List<String> roles = userDetails.getAuthorities().stream()
				.map(item -> item.getAuthority())
				.collect(Collectors.toList());

		return ResponseEntity.ok(new JwtResponse(jwt, 
												 userDetails.getId(), 
												 userDetails.getUsername(), 
												 userDetails.getEmail(), 
												 roles));
	}

	@PostMapping("/signup")
	public ResponseEntity<?> registerUser(@Valid @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's account
		User user = new User(signUpRequest.getUsername(), 
							 signUpRequest.getEmail(),
							 encoder.encode(signUpRequest.getPassword()));

		Set<String> strRoles = signUpRequest.getRole();
		Set<Role> roles = new HashSet<>();

		if (strRoles == null) {
			Role userRole = roleRepository.findByName(ERole.ROLE_USER)
					.orElseThrow(() -> new RuntimeException("Error: Role is not found."));
			roles.add(userRole);
		} else {
			strRoles.forEach(role -> {
				switch (role) {
				case "admin":
					Role adminRole = roleRepository.findByName(ERole.ROLE_ADMIN)
							.orElseThrow(() -> new RuntimeException("Error: Role is not found."));
					roles.add(adminRole);

					break;
				case "mod":
					Role modRole = roleRepository.findByName(ERole.ROLE_MODERATOR)
							.orElseThrow(() -> new RuntimeException("Error: Role is not found."));
					roles.add(modRole);

					break;
				default:
					Role userRole = roleRepository.findByName(ERole.ROLE_USER)
							.orElseThrow(() -> new RuntimeException("Error: Role is not found."));
					roles.add(userRole);
				}
			});
		}

		user.setRoles(roles);
		userRepository.save(user);

		return ResponseEntity.ok(new MessageResponse("User registered successfully!"));
	}
}

Controller for testing Authorization

There are 4 APIs:
/api/test/all for public access
/api/test/user for users has ROLE_USER or ROLE_MODERATOR or ROLE_ADMIN
/api/test/mod for users has ROLE_MODERATOR
/api/test/admin for users has ROLE_ADMIN

Do you remember that we used @EnableGlobalMethodSecurity(prePostEnabled = true) for WebSecurityConfig class?

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { ... }

Now we can secure methods in our Apis with @PreAuthorize annotation easily.

controllers/TestController.java

package com.bezkoder.springjwt.controllers;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@CrossOrigin(origins = "*", maxAge = 3600)
@RestController
@RequestMapping("/api/test")
public class TestController {
	@GetMapping("/all")
	public String allAccess() {
		return "Public Content.";
	}
	
	@GetMapping("/user")
	@PreAuthorize("hasRole('USER') or hasRole('MODERATOR') or hasRole('ADMIN')")
	public String userAccess() {
		return "User Content.";
	}

	@GetMapping("/mod")
	@PreAuthorize("hasRole('MODERATOR')")
	public String moderatorAccess() {
		return "Moderator Board.";
	}

	@GetMapping("/admin")
	@PreAuthorize("hasRole('ADMIN')")
	public String adminAccess() {
		return "Admin Board.";
	}
}

Run & Test

Run Spring Boot application with command: mvn spring-boot:run

Tables that we define in models package will be automatically generated in Database.
If you check PostgreSQL for example, you can see things like this:

\d users
                                  Table "public.users"
  Column  |          Type          |                     Modifiers
----------+------------------------+----------------------------------------------------
 id       | bigint                 | not null default nextval('users_id_seq'::regclass)
 email    | character varying(50)  |
 password | character varying(120) |
 username | character varying(20)  |
Indexes:
    "users_pkey" PRIMARY KEY, btree (id)
    "uk6dotkott2kjsp8vw4d0m25fb7" UNIQUE CONSTRAINT, btree (email)
    "ukr43af9ap4edm43mmtq01oddj6" UNIQUE CONSTRAINT, btree (username)
Referenced by:
    TABLE "user_roles" CONSTRAINT "fkhfh9dx7w3ubf1co1vdev94g3f" FOREIGN KEY (user_id) REFERENCES users(id)


\d roles;
                                Table "public.roles"
 Column |         Type          |                     Modifiers
--------+-----------------------+----------------------------------------------------
 id     | integer               | not null default nextval('roles_id_seq'::regclass)
 name   | character varying(20) |
Indexes:
    "roles_pkey" PRIMARY KEY, btree (id)
Referenced by:
    TABLE "user_roles" CONSTRAINT "fkh8ciramu9cc9q3qcqiv4ue8a6" FOREIGN KEY (role_id) REFERENCES roles(id)


\d user_roles
   Table "public.user_roles"
 Column  |  Type   | Modifiers
---------+---------+-----------
 user_id | bigint  | not null
 role_id | integer | not null
Indexes:
    "user_roles_pkey" PRIMARY KEY, btree (user_id, role_id)
Foreign-key constraints:
    "fkh8ciramu9cc9q3qcqiv4ue8a6" FOREIGN KEY (role_id) REFERENCES roles(id)
    "fkhfh9dx7w3ubf1co1vdev94g3f" FOREIGN KEY (user_id) REFERENCES users(id)

We also need to add some rows into roles table before assigning any role to User.
Run following SQL insert statements:

INSERT INTO roles(name) VALUES('ROLE_USER');
INSERT INTO roles(name) VALUES('ROLE_MODERATOR');
INSERT INTO roles(name) VALUES('ROLE_ADMIN');

Then check the tables:

> SELECT * FROM roles;

 id |      name
----+----------------
  1 | ROLE_USER
  2 | ROLE_MODERATOR
  3 | ROLE_ADMIN
(3 rows)

Register some users with /signup API:

  • admin with ROLE_ADMIN
  • mod with ROLE_MODERATOR and ROLE_USER
  • zkoder with ROLE_USER

spring-boot-authentication-jwt-spring-security-signup-mod

Our tables after signup could look like this.

> SELECT * FROM users;

 id |       email        |                           password                           | username
----+--------------------+--------------------------------------------------------------+----------
  1 | admin@bezkoder.com | $2a$10$4K8Vq5mw.nwxl.WRmuYCfevme82c73uGkEcnPbmm/3/YJ3UToie7m | admin
  2 | mod@bezkoder.com   | $2a$10$1dCKuQoQqbBNCK.Rb8XQSemwqdHdVAcCTb1kUQLg2key/4VX./TvS | mod
  3 | user@bezkoder.com  | $2a$10$e9Mgd/63paPL0VBj232BH.tQvIgQu0/tBg/rwfyDVMUcQc8djEPle | zkoder
(3 rows)


> SELECT * FROM roles;

 id |      name
----+----------------
  1 | ROLE_USER
  2 | ROLE_MODERATOR
  3 | ROLE_ADMIN
(3 rows)


>SELECT * FROM user_roles;

 user_id | role_id
---------+---------
       1 |       3
       2 |       1
       2 |       2
       3 |       1
(4 rows)

Access public resource: GET /api/test/all

spring-boot-authentication-jwt-spring-security-get-public-resource

Access protected resource: GET /api/test/user

spring-boot-authentication-jwt-spring-security-get-user-resource-unauthorized

Login an account: POST /api/auth/signin

spring-boot-authentication-jwt-spring-security-login-mod

Access ROLE_USER resource: GET /api/test/user

spring-boot-authentication-jwt-spring-security-get-user-resource

Access ROLE_MODERATOR resource: GET /api/test/mod

spring-boot-authentication-jwt-spring-security-get-mod-resource

Access ROLE_ADMIN resource: GET /api/test/admin

spring-boot-authentication-jwt-spring-security-get-admin-resource-unauthorized

Conclusion

Congratulation!

Today we’ve learned so many interesting things about Spring Security and JWT Token based Authentication in just a Spring Boot example.
Despite we wrote a lot of code, I hope you will understand the overall architecture of the application, and apply it in your project at ease.

You can also know how to deploy Spring Boot App on AWS (for free) with this tutorial.

Happy learning! See you again.

Further Reading

Source Code

You can find the complete source code for this tutorial on Github.

160 thoughts to “Spring Boot Token based Authentication with Spring Security & JWT”

  1. Hello, im a french dev student, thank you very much for your work! I have a probleme with this method in UserDetailsImpl.

    => public static UserDetailsImpl build(User user) {
    		List authorities = user.getRoles().stream()
    				.map(role -> new SimpleGrantedAuthority(role.getName().name()))
    				.collect(Collectors.toList());
    
    		return new UserDetailsImpl(
    				user.getId(), 
    				user.getUsername(), 
    				user.getEmail(),
    				user.getPassword(), 
    				authorities);
    	}
    =>  getName() "cannot resolve method"
    

    Im not very strong with stream map etc, can you explain me this block a bit more?

    Thank you for your attention and continue to display this wonderful content.

    Mathieu

    1. Hi Mathieu,

      user.getRoles() returns a Set<Role>, we convert this Set to a Stream of Role. Imagine that the stream is a collection of Role that releases items in turn.

      map() changes every Role item above to a SimpleGrantedAuthority object, the parameter for the constructor could be one of these Strings: ‘ROLE_USER’, ‘ROLE_MODERATOR’, ‘ROLE_ADMIN’.

      So, what does role.getName().name() do?
      role.getName() returns ERole enum object, let’s call it erole_object. Look back to this:

      public enum ERole {
      	ROLE_USER,
          ROLE_MODERATOR,
          ROLE_ADMIN
      }
      

      erole_object.name() returns the String corresponding to the enum (‘ROLE_MODERATOR’ for example).

      Please make sure that you define Role model in Role.java correctly.

      Regards,
      bezkoder.

      1. Thank you a lot for your explanation! I will follow your site and youtube channel studiously and recommend it to others students!

  2. Amazing article! It really helped me with a school project.
    Only one question, how can I add a server side logout feature? So the token is immediately invalid

  3. I am regular reader of your site. All of Spring tutorials are really good. This Spring Boot Authentication with JWT tutorial is the best.

  4. Thank you for good writing. But I don’t understand what’s the difference between database and repository. I don’t know exactly the difference between the two, so I’m asking you a question.

    1. Hi,

      A Repository is a database that stores the metadata for Designer objects. In other words, the data associated with objects like Entities, Modules, Table Definitions, etc. is stored as rows in tables that are part of the Repository.

      The Repository also includes a PL/SQL Application Programming Interface (API) for maintaining this data.

      A Repository would essentially be linked to at least one database, but in general, it is linked to many databases.

      A Database is just a place to store data, or an application database is a place to store the data for a particular computer application.

      The Repository is itself an application database, but it also stores data about (ie. metadata – data about data) other application databases. These other databases might be co-located with the Repository, but normally are stored somewhere else.

      A Repository is a central common point of file storage of a related nature, a Database is a structured file or set of files that store and organize data.


      I found this great answer here: https://qr.ae/TeQcol.

  5. Hello, I’m a beginner, so I have a lot of questions. I hope you will answer this question. I’m so frustrated because I don’t understand.
    In the repository, we divided the user and the role. What function does the user and the role ? For example, if you type id/pass and then go to the repository, I don’t understand why you use id/email in the user. I know the reason why I want to use the role in the repository for automation, but I don’t know why I use the repository user.

    1. Hi, I don’t really understand your question.
      But if you want to know why we use the repository user, you can find where we call its methods:

      • userRepository.existsByUsername()
      • userRepository.existsByEmail
      • userRepository.save()
      • userRepository.findByUsername()
  6. Hello, I would like to ask you something.

    The spring security oauth 2.0 resource serve has been deprecated.

    Is this tutorial the way that it should be done now that the EnableResourceServer is deprecated?

    I would like to migrate my project that is using it, but in their migration guide there are not explanation about the resource server since they say that is out of the scope in the migration guide.

    Is this the new way or can you write a tutorial with the new way about implementing oauth 2.0 ?

    thanks

    1. Hi, I think that EnableResourceServer will not affect the code in this tutorial.
      For Spring Security OAuth 2.0, I will write the tutorial in near future.

  7. Hello,
    Thank you very much for your step by step tutorial.

    I got the following error: “cannot resolve method getName()” in the following method:

    public static UserDetailsImpl build(User user) {
            List authorities = user.getRoles().stream()
                    .map(role -> new SimpleGrantedAuthority(role.getName().name()))
                    .collect(Collectors.toList());
    
            return new UserDetailsImpl(
                    user.getId(),
                    user.getUsername(),
                    user.getEmail(),
                    user.getPassword(),
                    authorities);
        }
    

    Initially, I thought it happened maybe I did some mistake during my typing. But I copy-paste your code for ERole, User, Role Model and also the UserDetailsImpl.java but it still shows that error.

    How can I fix that?

    1. Hi, I’m sorry, syntax highlight plugin causes the Typo.

      List<GrantedAuthority> authorities = ...
      

      You can check Role and User class again to make sure that all the fields are correct.
      Another way is to download the source code.

  8. Great tutorial but my database table was not generated at startup

    spring.datasource.url= jdbc:mysql://localhost:3306/testdb?useSSL=false
    spring.datasource.username= root
    spring.datasource.password= 123456
    
    spring.jpa.properties.hibernate.dialect= org.hibernate.dialect.MySQL5InnoDBDialect
    spring.jpa.hibernate.ddl-auto= update
    
    # App Properties
    bezkoder.app.jwtSecret= bezKoderSecretKey
    bezkoder.app.jwtExpirationMs= 86400000
    

    and any pointer on how to generate refresh token. thanks

    1. Hi, you must create testdb database with username & password first.
      Our Hibernate configuration only generates tables.

      For refresh token, I will write a tutorial when having time. ๐Ÿ™‚

  9. Hi bezkoder! thank you very much for this awesome tutorial! I have a little question though.. It all worked perfectly, but when i create an user with 2 roles (as input I wrote: ‘ “role”: [“ROLE_MODERATOR”, “ROLE_USER]’ the app only associates the user with user role, but not with moderator role. I tried with my own code (following ur instructions) but when it didnt work, i cloned your repo, but i still have the same problem.. do u know what it could be?

    Thanks again for this amazing tutorial!

    1. Hello, can you help me to resorlve this problem, i can’t assign ROLE_ADMIN or ROLE_MODERATOR to user.when i add a user even i specify admin or role in dataBase it’s user role !!

        1. i did but i have the some problem :'( .in user_roles table i have:
          user_id role_id
          1 1
          2 1
          3 1
          4 1
          5 1
          THX U for your answer (f)

          1. We have 3 tables, and each row in user_roles was generated automatically ๐Ÿ™‚

            So you should create user by sending HTTP request with payload containing role array.

    1. Please make sure that you’ve configured datasource in application.properties correctly.
      If the issue still exists, please show me your console log error.

  10. here is an overview of its code application.properties:

    spring.datasource.url= jdbc:mysql://localhost:3306/testdb?useSSL=false
    spring.datasource.username= root
    spring.datasource.password=

    spring.jpa.hibernate.ddl-auto= update
    spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect

    # App Properties
    example.app.jwtSecret= secretkey
    example.app.jwtExpirationMs= 123456789

  11. Hi bezkoder,

    Thank you for the tutorial!

    I have followed your instructions. However, for some reason in my WebSecurityConfig.java, I get an error on the following code block:

      @Override
      public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
        authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
      }
    

    which underlines the userDetailsService in the brackets and says:

    userDetailsService (T) in AuthenticationManagerBuilderย cannot be applied to
    (com.globomatics.portalproject.services.UserDetailsServiceImpl)
    reason: no instance(s) of type variable(s) exist so that UserDetailsServiceImpl conforms to UserDetailsService

    Do you have an idea why I might be getting this one and how could I fix it?

    Thank you in advance!

  12. Hi, great Tutorial. But if I want to register a user i get this Error:
    “timestamp”: “2020-02-06T15:14:41.823+0000”,
    “status”: 405,
    “error”: “Method Not Allowed”,
    “message”: “Request method ‘GET’ not supported”,
    “path”: “/api/auth/signup”

    The same for “path”: “/api/auth/signin”.
    Could you pls help me. Thank you very much

  13. Hi Bezkoden,

    Thank you for the great tutorial. I do have a question, though. The AuthenticationManager in the AuthController is not able to be autowired as there is no bean found for the AuthenticationManager. Do you have any idea as how to solve it?

    Thanks in advance!

    1. Hi, we have a bean in WebSecurityConfig:

      public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
        ...
        @Bean
        @Override
        public AuthenticationManager authenticationManagerBean() throws Exception {
          return super.authenticationManagerBean();
        }
        ...
      
  14. Hello, many thanks for these great tutorials!

    I think I’ll go this way to implement a simple app, but I have some questions.
    Is there a way to encrypt password in the POST request? I can see it is encrypted in database but requests can be intercepted and I’m new to angular.
    Do you plan to make a tutorial on deployment of a Spring boot app? I heard of solutions like heroku, azure, amazon.. but it’s still a bit confusing, I’d really like a guide made by you on this (you make things look really simple!).

    1. Definitely look into Heroku, which is part of Salesforce Inc apparently. They are by far the easiest and most user friendly hosting platform for Spring boot applications that I know of.

    2. You encrypt POST requests by using HTTPS with a valid certificate. If you do that, the whole POST request body will be encrypted. In order for your requests to not be intercepted, you should never send them over HTTP. This tutorial is good for setting it up in development, but when you go into live environment, it’s necessary to look into HTTPS as well.

  15. Hello, I am a beginner in the code do you create the fields of the database tables? or do you do manual in mysql?

      1. Thank you for your response, and if I have a database already created with the same data, should your code work well? Or do I have to skip somewhere?

        1. If you have the same database table, you don’t need to run SQL queries for inserting records to roles table ๐Ÿ™‚

  16. Hello,

    Thanks for the great tutorial, but i have one problem. When sending a request to the API with the roles “mod”, “user” it always throws an exception that it could not find a role.

    This even happens when using your source code.

    {
      "username": "henk",
      "email": "someone@gmail.com",
      "password": "keukentafel",
      "role": ["mod", "user"]
    }
    
    1. Nevermind, as I was writing this comment I found the problem. Forgot to put the roles in the postgres database

  17. Hi, thanks for the tutorial I have this error that the postman shows me what do you know?

    “error”: “Bad Request”,
    “message”: “Invalid JSON input: Cannot deserialize instance of `java.util.HashSet` out of VALUE_STRING token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.util.HashSet` out of VALUE_STRING token\n at [Source: (PushbackInputStream); line: 5, column: 10] (through reference chain: com.ramajudicial.Solicitud.de.Informacion.payload.requet.SignupRequest[\”role\”])”

      1. Hi, thanks for answering if I had something wrong with the POST, now this error is generating, I am working with a sql server database

        Error: Cannot insert the value NULL into column ‘userId’, table ‘lramajudicialpru.dbo.user’; column does not allow nulls. INSERT fails.

        Do you know why you are sending me null?

        1. Hi, this Spring Boot Application is configured to work with MySQL or PostgreSQL database, so you need to modify application.properties file for SQL Server Database.

          1. Do you have any idea how to configure it? I have this configuration connected to the database but I don’t know why it gives the previous error

            spring.datasource.url=
            spring.datasource.username=
            spring.datasource.password=
            spring.datasource.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver
            spring.jpa.show-sql=true
            spring.jpa.hibernate.dialect=org.hibernate.dialect.SQLServer2012Dialect
            spring.jpa.hibernate.ddl-auto = validate
            spring.jpa.hibernate.naming_strategy = org.hibernate.cfg.EJB3NamingStrategy

            thanks.

          2. You should fill url, username and password.
            If you did it but the error still occurs, I’m so sorry because I’ve not worked with SQL Server now.
            I will check it and write another tutorial when having time ๐Ÿ™‚

  18. Hello Bezkoder thank you , you did a great job , but hasRole(..) it should be changed to hasAuthority right? because at first hasRole doesn’t work after that i tried with hasAuthority and it works..

    1. Hi, Roles and Authorities are similar in Spring.
      The difference is that the 'ROLE_' prefix gets added automatically for hasRole. That means, hasAuthority('ROLE_USER') is similar to hasRole('USER'). ๐Ÿ™‚

      1. thank you bezkoder , i have a question i didn’t add the prefix ROLE_ when i added my roles (because i didn’t use enumarted strings ), so i think that’s why she doesn’t work in the first time with hasRole so did you think if that will causes a problems??

          1. ok thank you , what about the logout endpoint
            the method should have parametres?

            //tokenStore.removeAccessToken(accessToken);
            //tokenStore.removeRefreshToken(refreshToken);

          2. We’re gonna implement logout on frontend side (by removing token from localStorage/sessionStorage for example). So we don’t need logout endpoint here.

  19. Hi, im new with java language and this helped me a lot. Are there any way to support and
    appreciate your work?

    Thanks so much !!

  20. Hi bezkoder i have a problem when i create a user and give him some roles on angular when i’m back in springBoot the role array is empty can you help please!!!

    Best regards !!

  21. Hi bezkoder, thank you very much, you’ve done a great job!
    I have one problem with Postman.
    After signing in as “mod” I try to access resource: GET http://localhost:8080/api/test/user.
    I send value: Bearer {my token} with key Authorization (in section Header) and I receive this error:
    {
    “timestamp”: “2020-04-04T23:51:06.706+0000”,
    “status”: 401,
    “error”: “Unauthorized”,
    “message”: “Error: Unauthorized”,
    “path”: “/api/test/user”
    }
    What am I doing wrong?

    1. Hi, I’ve checked GET http://localhost:8080/api/test/user with ‘mod’ role and it works.
      Please make sure that you use the token correctly.

  22. Ok, now I’ve got it. Testing this endpoint I tried to send token from Headers tab, instead using Authorisation tab. Everything is ok ๐Ÿ™‚

  23. Hi BezKoder,

    Thank you so much for you posting such superb tutorial. And could you explain a little how this JWT token has secured in this api.

    1. Hi, JWT does not hide, obscure, secure data at all.
      You can see that the process of generating JWT (Header, Payload, Signature) only encode & hash data, not encrypt data.

      The purpose of JWT is to prove that the data is generated by an authentic source.
      If you want to know more details about JWT, you can visit:
      In-depth Introduction to JWT-JSON Web Token

  24. Excellent! It worked perfectly. That’s not the first time I found here exactly just what I looking for. Thanks for the good work.

  25. I loved this Spring Boot Auth tutorial! I actually read your blog fairly often, and youโ€™re constantly coming out with some great tutorials here.

  26. Hello!
    it’s giving me this: “message”: “Error: Role is not found.” to refer to this one: case “admin”:
    Role adminRole = roleRepository.findByName(ERole.ROLE_ADMIN)
    .orElseThrow(() -> new RuntimeException(“Error: Role is not found.”));
    roles.add(adminRole);

    break;
    Can you tell me what’s the problem please? Thanks.

    1. {
      “username”:”alphabank”,
      “password”:”alphabank”,
      “role”:[“mod”, “user”]
      }
      this is the request i am sending, and the header is content-type-
      application/json

  27. Hello,
    I’m getting the following error, whenever I try to create a signup POST request:
    ERROR: insert or update on table “user_roles” violates foreign key constraint “fkrhfovtciq1l558cw6udg0h0d3”
    Detail: Key (role_id)=(2) is not present in table “role”.
    I’ve already inserted the 3 roles you mentioned.
    Do you know why I’m getting this error? Thank you for your help! ๐Ÿ™‚

    1. Hi, I think you can drop user_roles table, then drop users and roles table.
      Just do everything from the beginning. Maybe you miss or make mistake in a certain step.

  28. Great tutorial!
    I have a question, How can I modify it to authenticate a user using its email and password instead of username and password ?

  29. Thx for this tutorial, I looked through a lot of tutorials and couldn’t understand any of them until I found yours.

    Thank you so much

  30. Thanks for this amazing course. It was really easy to understand it.
    I have one question, given this example code, how could I implement an oauth2 login as well?

    E.g. : when the user visits /signin , I would like to provide the options of logging in either with jwt or oauth2 (github/google). Can you give me a hint?

  31. Really great tutorial, it helped me a lot, thank you very much! ๐Ÿ™‚

    But one question:

    Why can you write @PreAuthorize(“hasRole(‘ADMIN’)”)
    and not @PreAuthorize(“hasRole(‘ROLE_ADMIN’)”)

    We neved used the string ADMIN anywhere, only ROLE_ADMIN as part of the enum.

    1. Hi, the ‘ROLE_’ prefix gets added automatically for hasRole. If you want to work with ROLE_ADMIN, just use hasAuthority('ROLE_ADMIN').

      1. ah, thank you so much, very simple explanation and it makes sense! I didn’t take a course on Spring Security yet, so I didn’t know that.

    1. Hi, it depends on your use cases. The roles in this tutorial is used for illustrating authority ๐Ÿ™‚

  32. Hello,

    I’m trying to follow your tutorial but I’d like to have a connexion without username (just email/password), what is the workaround for this? I’m in trouble when I need to implement the UserDetailsService because there is no loadByEmail method or something like this.

    1. Hi, you can treat ‘username’ as ’email’. That means, when saving user information to the database, the username column stores email.

  33. Hello Sir
    Thank you so much for such a detailed tutorial. You made life very easy for more in learning spring authentication.

    However, I am facing two challenges; 1. I cant add more than one users because the in the user_roles table the ids for both user and the role are unique. I get an exception: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry ‘1’ for key ‘UK_5q4rc4fh1on6567qk69uesvyf’

    2. After registering a user I get another exception on trying to login: 2020-05-07 13:05:36.007 WARN 12224 — [nio-8080-exec-7] o.s.s.c.bcrypt.BCryptPasswordEncoder : Encoded password does not look like BCrypt
    2020-05-07 13:05:36.008 ERROR 12224 — [nio-8080-exec-7] z.o.m.security.jwt.AuthEntryPointJwt : Unauthorized error: Bad credentials

    How best can I avoid roles column in the user_roles table to be non unique or is there a different approach.

    1. Hi, I don’t really understand your question.
      “I cant add more than one users because the in the user_roles table the ids for both user and the role are unique.”

      You can add user by sending HTTP request to /api/auth/signup endpoint (with role array in the payload).

      1. its about the relationship table. the one that has two columns for either ids (role_id and user_id). Since we created a table with 3 roles that have ids 1,2, and 3 in the table the ids are unique say if my first user has id=1 and role is ROLE_USER which has id=1 then if i wish to add another user with id=2 and role ROLE_USER again when added to the relationship table where role_id = 1 already exists. this will create a scenario like:

        user_id |role_id
        1 | 1
        2 | 1
        which then complains of constraint violation issues since role_id must be unique. well i had to create new roles with new ids.

        my question number 2
        After registering a user I get another exception on trying to login: 2020-05-07 13:05:36.007 WARN 12224 โ€” [nio-8080-exec-7] o.s.s.c.bcrypt.BCryptPasswordEncoder : Encoded password does not look like BCrypt
        2020-05-07 13:05:36.008 ERROR 12224 โ€” [nio-8080-exec-7] z.o.m.security.jwt.AuthEntryPointJwt : Unauthorized error: Bad credentials
        What could be the cause?

        1. 1. The relationship between User entity and Role entity is Many to Many.
          We don’t add any record to user_roles table directly. Please look at how we signup user in AuthController.
          I will write a tutorial for Spring JPA Many to Many Association when having time.

          2. I don’t really know what was wrong. You can debug yourself and notice these lines:

          User user = new User(signUpRequest.getUsername(), 
          					 signUpRequest.getEmail(),
          					 encoder.encode(signUpRequest.getPassword()));
          
  34. Apart from component scanning , if you missed web-starter dependency , it could cause a ripple , that is what i’ve done and failed me many times.

  35. main] o.s.b.web.embedded.tomcat.TomcatStarter : Error starting Tomcat context. Exception: org.springframework.beans.factory.UnsatisfiedDependencyException.

    M I the only one ?

  36. Hello!

    Amazing work, I used this implementation and everything works fine. Thanks for sharing.
    However, I have a question because I want to add the option remember me to login and I am not sure how to implement it in this case.

    I have API in spring boot and frontend in react. I tried to add rememberMe() function at configure(HttpSecurity http) function at WebSecurityConfig class, but after that i don’t see a remember-me cookie in the browser.

    Do you have any advice?

    Greetings
    Adrian

  37. Hello, thank you for the tutorial. I keep getting “Error: Unauthorized” when I go to /user , /mod, or /admin and /all. Everything else seems to work okay. Do you have any idea as to why I might be getting this error?

      1. Hey there can u please help me with error ….
        “internal server error”

        when i m passing the jason body it is showing error as: Internal server Error
        and error message as : “Role not found”

  38. Hello, Thank you so much for this great tutorial !

    I have implemented this tutorial, but I have a problem.
    When localhost:8080/api/expense/all called, it’s successfully as expected. But when localhost:8080/api/expense/76 (76 is ID for an expense), the expenseService always returns null (500 server error).

    What could be the cause? and how to fix it?

  39. Hey. Thank for the tutorial but I think there might be one problem or it’s only happening for me. But maybe you can reproduce it. I’m using the full tutorial of Angular and Spring Boot and if I register and directly login with this account I get 2020-05-12 23:20:09.452 ERROR 13476 — [nio-8080-exec-7] d.example.project.security.jwt.JwtUtil : Invalid JWT token: JWT strings must contain exactly 2 period characters. Found: 0
    The JWT token is undefined. So the typical Angular behaviour if something is not loaded. Is that a special case and should somehow be validated in Spring Boot? Or is anything wrong in Angular? The method doFilterInternal() in AuthTokenFilter.java is causing this.

    1. Okay I could fix it in a not very acceptable manner. In the authInterceptor in Angular you are using the line const token = this.token.getToken(); and this is giving me undefined and is not null and gives it to the Spring Boot backend. The only way to handle it is if (token != null && token != “undefined”) {

  40. how can i implement the same in oracle database, i have some issues with this. if you can help me, i’ll be very greatful. i hear that the versions are the most current issue.

  41. Hello, Thank you so much for this great tutorial !
    I have implemented this tutorial, but I have a problem.
    when i call:
    POST: http://localhost:8080/api/auth/signin
    I filled out :
    {
    “username”:”user”,
    “password”:”123456″
    }

    and postman response:
    {
    “timestamp”: “2020-05-14T13:30:02.827+0000”,
    “status”: 401,
    “error”: “Unauthorized”,
    “message”: “Error: Unauthorized”,
    “path”: “/api/auth/signin”
    }
    and eclipse response: “Unauthorized error:User account is locked”
    Although I have filled in the correct information.
    How can I fix it? thanks

    1. Hi, “User account is locked” happens after a number of failed authentication events. So you need to check the issue why you couldn’t login with this account.

    2. In UserDetailsImpl.java:
      Switch from false to true by the following methods:
      public boolean isAccountNonExpired()
      public boolean isAccountNonLocked()
      public boolean isCredentialsNonExpired()
      public boolean isEnabled()
      Because if you automatically generated them by default they are false.

      PD: Sorry, I’m really not very good at English

  42. I’ve implemented jwt with spring security and work’s fine, but when I’m trying to access the API those have @preAuthorize it’s giving me unauthorized when hitting from angular(v9) but it’s working fine in postman!
    I have ensured that the login role and preAuthorize api roles are same.
    Why is this happening? I have no idea…
    Please help…

  43. Hi, how can I add a cascading dropdown in the signup page , and the data’s in the dropdown from the database. How it is possible in the backend and frontend?

  44. You’re the best teacher that I’ve ever come across in my coding journey who can explain in detail what spring security and jwt are all about. I don’t know how I can describe you but you’re genius. This is an inspirational tutorial and I look forward to see more powerful tutorial of this type. Once again, I will say big thank you

Leave a Reply

Your email address will not be published. Required fields are marked *