Hey guys in this post, we will discuss Spring security JWT token based authentication with mysql database.
Table of Contents
Complete Example
We will create this example step by step, follow this tutorial till the end
Read More:
- Check the Complete JavaServer Faces (JSF) Tutorial
- Check the Complete Spring Data JPA Tutorial
- Check the Complete Spring Security Tutorial
- Check the Javascript Projects for Beginners
- Check the Spring Boot JdbcTemplate Tutorials
Create spring boot project
There are many different ways to create a spring boot application, you can follow the below articles to create one –
>> Create spring boot application using Spring initializer
>> Create spring boot application in Spring tool suite [STS]
>> Create spring boot application in IntelliJ IDEA
Add maven dependencies
Open pom.xml
and add the following dependencies –
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>in.bushansirgur</groupId>
<artifactId>springsecurityjwt</artifactId>
<version>1.0.0</version>
<name>springsecurityjwt</name>
<description>Spring security jwt demo</description>
<properties>
<java.version>16</java.version>
</properties>
<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-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
spring-boot-starter-web
dependency for building web applications using Spring MVC. It uses the tomcat as the default embedded container. spring-boot-starter-security
dependency, which will help to implement spring security. spring-boot-devtools
dependency for automatic reloads or live reload of applications.
lombok
dependency is a java library that will reduce the boilerplate code that we usually write inside every entity class like setters, getters, and toString(). mysql-connector-java
dependency is to store the data into the database.
Configure the datasource
Open application.properties
file and add the following contents
spring.datasource.url=jdbc:mysql://localhost:3306/springsecurityjwt
spring.datasource.username=root
spring.datasource.password=scbushan05
spring.jpa.hibernate.ddl-auto=create
Create Configuration classes
Create CorsConfig.java
class inside in.bushansirgur.springsecurityjwt.config
package and add the following content
package in.bushansirgur.springsecurityjwt.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class CorsConfig {
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.allowedOriginPatterns("*")
.allowCredentials(true);
}
};
}
}
The above class is responsible for allowing requests from any application, it our spring boot application should not block those requests.
Create JwtAuthenticationEntityPoint.java
class inside in.bushansirgur.springsecurityjwt.config
package and add the following content
package in.bushansirgur.springsecurityjwt.config;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
@Component
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class JwtAuthenticationEntityPoint implements AuthenticationEntryPoint{
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
}
The above class is responsible for Authenticating the Jwt Tokens, if authentications fails, application throw Unauthorized error.
@EnableWebSecurity
annotation will enable the web security on this application.@EnableGlobalMethodSecurity
will allows us to add method level security on this application, we will setprePostEnabled
option totrue
Create JwtRequestFilter.java
class inside in.bushansirgur.springsecurityjwt.config
package and add the following content
package in.bushansirgur.springsecurityjwt.config;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
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 in.bushansirgur.springsecurityjwt.service.JwtService;
import in.bushansirgur.springsecurityjwt.util.JwtUtil;
import io.jsonwebtoken.ExpiredJwtException;
@Component
public class JwtRequestFilter extends OncePerRequestFilter{
@Autowired
private JwtUtil jwtUtil;
@Autowired
private JwtService jwtService;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
final String header = request.getHeader("Authorization");
String jwtToken = null;
String userName = null;
if (header != null && header.startsWith("Bearer ")) {
jwtToken = header.substring(7);
try {
userName = jwtUtil.getUserNameFromToken(jwtToken);
} catch (IllegalArgumentException e) {
System.out.println("Unable to get JWT token");
} catch (ExpiredJwtException e) {
System.out.println("Jwt token is expired");
}
} else {
System.out.println("Jwt token does not start with Bearer");
}
if (userName != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = jwtService.loadUserByUsername(userName);
if (jwtUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
filterChain.doFilter(request, response);
}
}
The above class is the custom filter, we will validate the Jwt token. We will extends this class with OncePerRequestFilter
provided by Spring security.
Create WebSecurityConfiguration.java
class inside in.bushansirgur.springsecurityjwt.config
package and add the following content
package in.bushansirgur.springsecurityjwt.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import in.bushansirgur.springsecurityjwt.service.JwtService;
@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter{
@Autowired
private JwtAuthenticationEntityPoint jwtAuthenticationEntityPoint;
@Autowired
private JwtRequestFilter jwtRequestFilter;
@Autowired
private UserDetailsService jwtService;
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors();
http.csrf().disable()
.authorizeRequests()
.antMatchers("/authenticate", "/users").permitAll()
.antMatchers(HttpHeaders.ALLOW).permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntityPoint)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder builder) throws Exception {
builder.userDetailsService(jwtService).passwordEncoder(passwordEncoder());
}
}
Anytime if we want to customize spring security then we need to create a configuration class by extending WebSecurityConfigurerAdapter
class.
We will override the configure()
method where we will add the custom filter by calling addFilterBefore()
and we will also configure URL mapping by calling antMatchers()
.
We will also create a bean for PasswordEncoder
because whenever we are dealing with user and user details spring expects us to encode the password.
Create Util class
Create JwtUtil.java
class inside in.bushansirgur.springsecurityjwt.util
package and add the following content
package in.bushansirgur.springsecurityjwt.util;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
@Component
public class JwtUtil {
private static final int TOKEN_VALIDITY = 3600 * 5;
public String getUserNameFromToken(String token) {
return getClaimFromToken(token, Claims::getSubject);
}
//it is a HOF, it takes function as argument and return the function
private <T> T getClaimFromToken(String token, Function<Claims, T> claimResolver) {
final Claims claims = getAllClaimsFromToken(token);
return claimResolver.apply(claims);
}
private Claims getAllClaimsFromToken(String token) {
return Jwts.parser().setSigningKey("b2tech").parseClaimsJws(token).getBody();
}
public boolean validateToken(String token, UserDetails userDetails) {
String userName = getUserNameFromToken(token);
return (userName.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
private boolean isTokenExpired(String token) {
final Date expirationDate = getExpirationDateFromToken(token);
return expirationDate.before(new Date());
}
private Date getExpirationDateFromToken(String token) {
return getClaimFromToken(token, Claims::getExpiration);
}
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
return Jwts.builder().setClaims(claims).setSubject(userDetails.getUsername())
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + TOKEN_VALIDITY * 1000))
.signWith(io.jsonwebtoken.SignatureAlgorithm.HS512, "b2tech")
.compact();
}
}
The above class is a Jwt util class, provides utility methods for validating, generating tokens and all.
Create entity class
Create JwtRequest.java
class in.bushansirgur.springsecurityjwt.entity
package and add the following content
package in.bushansirgur.springsecurityjwt.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class JwtRequest {
private String userName;
private String userPassword;
}
Create JwtResponse.java
class in.bushansirgur.springsecurityjwt.entity
package and add the following content
package in.bushansirgur.springsecurityjwt.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class JwtResponse {
private User user;
private String jwtToken;
}
Create Role.java
class in.bushansirgur.springsecurityjwt.entity
package and add the following content
package in.bushansirgur.springsecurityjwt.entity;
import javax.persistence.Entity;
import javax.persistence.Id;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Role {
@Id
private String roleName;
private String roleDescription;
}
Create User.java
class in.bushansirgur.springsecurityjwt.entity
package and add the following content
package in.bushansirgur.springsecurityjwt.entity;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
@Id
private String userName;
private String userFirstName;
private String userLastName;
private String userPassword;
@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinTable(name = "user_role",
joinColumns = {
@JoinColumn(name = "user_id")
},
inverseJoinColumns = {
@JoinColumn(name = "role_id")
}
)
private Set<Role> roles;
}
These are the entity classes, contains private fields, setters, getters and constructors.
Create Service class
Create JwtService.java
class in.bushansirgur.springsecurityjwt.service
package and add the following content
package in.bushansirgur.springsecurityjwt.service;
import java.util.HashSet;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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 in.bushansirgur.springsecurityjwt.entity.JwtRequest;
import in.bushansirgur.springsecurityjwt.entity.JwtResponse;
import in.bushansirgur.springsecurityjwt.entity.User;
import in.bushansirgur.springsecurityjwt.repository.UserRepository;
import in.bushansirgur.springsecurityjwt.util.JwtUtil;
@Service
public class JwtService implements UserDetailsService{
@Autowired
private UserRepository userReop;
@Autowired
private JwtUtil jwtUtil;
@Autowired
private AuthenticationManager authenticationManager;
public JwtResponse createJwtToken(JwtRequest jwtRequest) throws Exception{
String userName = jwtRequest.getUserName();
String userPassword = jwtRequest.getUserPassword();
authenticate(userName, userPassword);
final UserDetails userDetaisl = loadUserByUsername(userName);
String newGeneratedToken = jwtUtil.generateToken(userDetaisl);
User user = userReop.findById(userName).get();
return new JwtResponse(user, newGeneratedToken);
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userReop.findById(username).get();
if (user != null) {
return new org.springframework.security.core.userdetails.User(user.getUserName(), user.getUserPassword(), getAuthorities(user));
} else {
throw new UsernameNotFoundException("Username is not valid");
}
}
private Set<SimpleGrantedAuthority> getAuthorities(User user) {
Set<SimpleGrantedAuthority> authorities = new HashSet<>();
user.getRoles().forEach(role -> {
authorities.add(new SimpleGrantedAuthority("ROLE_"+role.getRoleName()));
});
return authorities;
}
private void authenticate(String userName, String userPassword) throws Exception{
try {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(userName, userPassword));
} catch (DisabledException e) {
throw new Exception("User is disabled");
} catch(BadCredentialsException e) {
throw new Exception("Bad credentials from user");
}
}
}
The above class is implements UserDetailsService
, which will override a single method loadUserByUsername()
which will load by the user based on the values requested by the user.
Create RoleService.java
class in.bushansirgur.springsecurityjwt.service
package and add the following content
package in.bushansirgur.springsecurityjwt.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import in.bushansirgur.springsecurityjwt.entity.Role;
import in.bushansirgur.springsecurityjwt.repository.RoleRepository;
@Service
public class RoleService {
@Autowired
private RoleRepository roleRepo;
public Role createNewRole(Role role) {
return roleRepo.save(role);
}
}
Create UserService.java
class in.bushansirgur.springsecurityjwt.service
package and add the following content
package in.bushansirgur.springsecurityjwt.service;
import java.util.HashSet;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import in.bushansirgur.springsecurityjwt.entity.Role;
import in.bushansirgur.springsecurityjwt.entity.User;
import in.bushansirgur.springsecurityjwt.repository.RoleRepository;
import in.bushansirgur.springsecurityjwt.repository.UserRepository;
@Service
public class UserService {
@Autowired
private UserRepository userRepo;
@Autowired
private RoleRepository roleRepo;
@Autowired
private PasswordEncoder passwordEncoder;
public User registerNewUser(User user) {
Role role = roleRepo.findById("User").get();
Set<Role> roles = new HashSet<>();
roles.add(role);
user.setRoles(roles);
user.setUserPassword(getEncodedPassword(user.getUserPassword()));
return userRepo.save(user);
}
public void initRolesAndUser() {
Role adminRole = new Role();
adminRole.setRoleName("Admin");
adminRole.setRoleDescription("Admin role");
roleRepo.save(adminRole);
Role userRole = new Role();
userRole.setRoleName("User");
userRole.setRoleDescription("Default role for newly created record");
roleRepo.save(userRole);
User adminUser = new User();
adminUser.setUserName("admin123");
adminUser.setUserFirstName("admin");
adminUser.setUserLastName("admin");
adminUser.setUserPassword(getEncodedPassword("admin@12345"));
Set<Role> adminRoles = new HashSet<>();
adminRoles.add(adminRole);
adminUser.setRoles(adminRoles);
userRepo.save(adminUser);
}
public String getEncodedPassword(String password) {
return passwordEncoder.encode(password);
}
}
Create Repository class
Create RoleRepository.java
class in.bushansirgur.springsecurityjwt.repository
package and add the following content
package in.bushansirgur.springsecurityjwt.repository;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import in.bushansirgur.springsecurityjwt.entity.Role;
@Repository
public interface RoleRepository extends CrudRepository<Role, String> {
}
Create UserRepository.java
class in.bushansirgur.springsecurityjwt.repository
package and add the following content
package in.bushansirgur.springsecurityjwt.repository;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import in.bushansirgur.springsecurityjwt.entity.User;
@Repository
public interface UserRepository extends CrudRepository<User, String> {
}
Create Controller class
Create JwtController.java
class in.bushansirgur.springsecurityjwt.controller
package and add the following content
package in.bushansirgur.springsecurityjwt.controller;
import org.springframework.beans.factory.annotation.Autowired;
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.RestController;
import in.bushansirgur.springsecurityjwt.entity.JwtRequest;
import in.bushansirgur.springsecurityjwt.entity.JwtResponse;
import in.bushansirgur.springsecurityjwt.service.JwtService;
@RestController
@CrossOrigin
public class JwtController {
@Autowired
private JwtService jwtService;
@PostMapping("/authenticate")
public JwtResponse createJwtToken(@RequestBody JwtRequest jwtRequest) throws Exception {
return jwtService.createJwtToken(jwtRequest);
}
}
Create RoleController.java
class in.bushansirgur.springsecurityjwt.controller
package and add the following content
package in.bushansirgur.springsecurityjwt.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import in.bushansirgur.springsecurityjwt.entity.Role;
import in.bushansirgur.springsecurityjwt.service.RoleService;
@RestController
public class RoleController {
@Autowired
private RoleService roleService;
@PostMapping("/roles")
public Role createNewRole(@RequestBody Role role) {
return roleService.createNewRole(role);
}
}
Create UserController.java
class in.bushansirgur.springsecurityjwt.controller
package and add the following content
package in.bushansirgur.springsecurityjwt.controller;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import in.bushansirgur.springsecurityjwt.entity.User;
import in.bushansirgur.springsecurityjwt.service.UserService;
@RestController
public class UserController {
@Autowired
private UserService userService;
@PostConstruct
public void initRolesAndUsers() {
userService.initRolesAndUser();
}
@PostMapping("/users")
public User registerNewUser(@RequestBody User user) {
return userService.registerNewUser(user);
}
@GetMapping("/forAdmin")
@PreAuthorize("hasRole('Admin')")
public String forAdmin() {
return "This url is only accessible to admin";
}
@GetMapping("/forUser")
@PreAuthorize("hasRole('User')")
public String forUser() {
return "This url is only accessible to user";
}
}
Here we are adding the spring security method level annotations @PreAuthorize
for authenticating the user to access the specific URIs
Run the app
Run the application using the below maven command –
mvn spring-boot:run
Open the Browser or Postman and enter the following URL –
localhost:8080/users
localhost:8080/authenticate
localhost:8080/forUser
localhost:8080/forAdmin
—
That’s it for this post, if you like this post, share this with your friends and colleagues or you can share this within your social media platform. Thanks, I will see you in our next post.
La Mejor Tienda de Camiseta Marcos Llorente
Encontrarás cada camiseta españa marcos llorente y ropa de entrenamiento
de españa.
Hi kindly share the refresh token link for jwt