Registration and Login with JWT Authentication Example in Spring Boot.
In this article, we will learn Authentication using Spring Security and JWT. Here, we will know how to create a register API with the password stored in encrypted form. Along with that, we will see how to handle the global exception. And last but not least we will see Authentication using spring security and JWT.
Authentication is one of the essential modules of any application. Earlier developers have written lots of code on their own for doing Authentication. As we are into Spring Boot, it has provided us with Spring Security which provides us with the Authentication mechanism. So, let us take a deep dive into how we can use this dependency along with the JWT concept and perform Authentication.
What is JWT?
JWT stands for JSON Web Token. It is basically used for securing the transmitting information between client and server. It consists of three parts:
- Header
- Payload
- Signature
It typically looks like this separated by two dots:
eyJhbGciOiSIUzUxOiJ9.eyJzdWIiOiJzdXplbmVAZ21haWwuY29tIiwiZXhwIjoxNjY0NzQ2NTU1LCJpYXQiOjE2NjQ3Mjg1NTV9.69zUKr0Yzlnqe7wDUmh3G94VXeYY-H9JjSVqvidN_ojeWgZmw3iu8bPMhWT50afOaSf1hGKNWaJjpiOu7lmtOQ
Tools and Technologies
- Spring Tool Suite
- Java 8
- Spring Boot Application with Spring Security, Spring Web, and Spring Data JPA dependency.
- JWT IO dependency
- MYSQL
Let us now go step by step and implement the following flow:
Project Structure
After the completion of the project, your project structure would look like the following.
Create Spring Boot Project using STS
Using STS, create Spring Boot Project. Go to File > New > Spring Starter Project. Add the Project Name, other details and click on Next and add dependencies.
Create Database
Go to MYSQL Workbench and create a database using the “create database jwtdb ” command.
Configure application.properties File
This file will contain the entire configuration required by our application to run. Like, as a connection for the MYSQL database, changing server ports and etc.
#server port server.port=8888 #configure database spring.datasource.url=jdbc:mysql://localhost:3306/jwtdb spring.datasource.username=root spring.datasource.password=root spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect spring.jpa.hibernate.ddl-auto=update #show sql queries spring.jpa.show-sql=true #format sql queries spring.jpa.properties.hibernate.format_sql=true
Create a Model Class
Here, we will create two classes. One is a User entity, and the other is a Role entity. In the database, three tables will be created. One is for storing user details another table will be created to store Role details and the last table would be created where we will have the user_roles table i.e. which user belongs to which role. I have used the Lombok library to remove boilerplate code.
User.java
package com.codebun.entities; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.Table; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import lombok.Getter; import lombok.Setter; @Entity @Table(name = "user") @Setter @Getter public class User implements UserDetails { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String firstName; private String lastName; private String login; private String password; // ManyToMany Relationship: i.e one user can // have many roles and vice versa @ManyToMany(fetch = FetchType.EAGER) @JoinTable(name = "user_roles", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id")) private Set<Role> roles = new HashSet<Role>(); @Override public Collection<? extends GrantedAuthority> getAuthorities() { // return collection of granted authorities List<SimpleGrantedAuthority> authorities = roles.stream().map((role)->new SimpleGrantedAuthority(role.getRoleName())).collect(Collectors.toList()); return authorities; } @Override public String getUsername() { // TODO Auto-generated method stub return login; } @Override public boolean isAccountNonExpired() { // TODO Auto-generated method stub return true; } @Override public boolean isAccountNonLocked() { // TODO Auto-generated method stub return true; } @Override public boolean isCredentialsNonExpired() { // TODO Auto-generated method stub return true; } @Override public boolean isEnabled() { // TODO Auto-generated method stub return true; } }
Role.java
package com.codebun.entities; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; import lombok.Getter; import lombok.Setter; @Entity @Table(name = "role") @Setter @Getter public class Role { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String roleName; }
- The User model is created with 5 fields, id, login(userName), firstName, lastName, and password in the same way the Role Model is created with 2 fields id and roleName.
- After authentication, we will need some more information about users like their authorities, and isEnabled for that we have to implement the UserDetails interface.
- These classes are marked with @Entity to specify that this class will be persisted in the database.
- A many-to-many relationship is shown using @ManyToMany annotation.
Create DTO
Ideally while creating the Restful APIs we should not expose our entities through endpoints as it can lead to security issues. So, to transmit data through endpoints we can create Data Transfer Objects(DTOs). It is typically used to transfer data between two parties. Also, it avoids multiple calls to remote servers. So, now we will create UserDTO, JWTRequest, and JWTResponse inside dtos package.
UserDTO.java
package com.codebun.dtos; import java.util.HashSet; import java.util.Set; import com.codebun.entities.Role; import lombok.Getter; import lombok.Setter; @Setter @Getter public class UserDTO { private Long id; private String firstName; private String lastName; private String login; private String password; private Set<Role> roles = new HashSet<Role>(); }
JWTRequest.java
package com.codebun.dtos; import lombok.Getter; import lombok.Setter; @Setter @Getter public class JWTRequest { private String login; private String password; }
JWTResponse.java
package com.codebun.dtos; import lombok.Getter; import lombok.Setter; @Setter @Getter public class JWTResponse { private String token; private String login; }
- UserDTO class will contain all the fields that you want to expose through the endpoints.
- JWTRequest class contains the login and password field required for authentication and JWTResponse class is used to send the token and login id of the user in the response.
Create Repositories
Now, will create a DAO layer that would interact with the database. For each model, we will have the repository classes.
UserRepository.java
package com.codebun.repositories; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import com.codebun.entities.User; @Repository public interface UserRepository extends JpaRepository<User, Long>{ User findByLogin(String login); }
RoleRepository.java
package com.codebun.repositories; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import com.codebun.entities.Role; @Repository public interface RoleRepository extends JpaRepository<Role, Long>{ Optional<Role> findByRoleName(String name); }
- These classes are annotated with @Repository annotation that would specify that the class would mechanism to perform CRUD on objects.
- Also, in UserRepository, we have a custom method called findByLogin(String login) that would fetch user details based on the login id.
- In the same way, we used the findByRoleName(String name) method to fetch the roles from the table based on roleName.
Create Service Layer
Now, let us create a service layer that would handle all the logic for authentication and registration.
UserService.java
package com.codebun.services; import java.util.List; import com.codebun.dtos.UserDTO; public interface UserService { public UserDTO register(UserDTO userDTO); public List<UserDTO> listAllUsers(); }
UserServiceImpl.java
package com.codebun.services; import java.util.Arrays; import java.util.List; import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; import com.codebun.dtos.UserDTO; import com.codebun.entities.Role; import com.codebun.entities.User; import com.codebun.exception.DuplicateRecordException; import com.codebun.repositories.RoleRepository; import com.codebun.repositories.UserRepository; @Service public class UserServiceImpl implements UserService { @Autowired private UserRepository userRepository; @Autowired private BCryptPasswordEncoder passwordEncoder; @Autowired private RoleRepository roleRepository; @Override public UserDTO register(UserDTO userDTO) { // check whether email id is already present or not User duplicateLogin = userRepository.findByLogin(userDTO.getLogin()); if(duplicateLogin!=null) throw new DuplicateRecordException("Login already exist"); // we need to convert it into User Entity User user = new User(); user.setFirstName(userDTO.getFirstName()); user.setLastName(userDTO.getLastName()); user.setPassword(passwordEncoder.encode(userDTO.getPassword())); user.setLogin(userDTO.getLogin()); // now add role info Role role = roleRepository.findByRoleName("ROLE_USER").get(); user.getRoles().add(role); UserDTO dto = new UserDTO(); dto.setFirstName(user.getFirstName()); User registedUser = userRepository.save(user); dto.setId(registedUser.getId()); dto.setFirstName(registedUser.getFirstName()); dto.setLastName(registedUser.getLastName()); dto.setPassword(registedUser.getPassword()); dto.setLogin(registedUser.getLogin()); dto.setRoles(registedUser.getRoles()); return dto; } @Override public List<UserDTO> listAllUsers() { List<User> users = userRepository.findAll(); ModelMapper mm = new ModelMapper(); List<UserDTO> userList = Arrays.asList(mm.map(users, UserDTO[].class)); return userList; } }
- In the UserServiceImpl.java class, we have implemented UserService Interface. Also, autowired UserRepository & RoleRepository.
- Inside the registration method, first, we are checking whether the login id is unique. If the login Id is already present then DuplicateRecordException is thrown. Else, next, we will convert the UserDTO into a User entity and will set the details to the User entity.
- Now, in order to set the Role Id, first, Just make sure in the Role Table you already have your roles defined. And then with the help of findByRoleName(String name), we will fetch the value and add that specific role to the user.
- At last, Just called the save() method and persist the data and return the UserDTO by converting the User entity to UserDTO class.
- Also, we have created listAllUsers() method, here just call the findAll() method that would return the list of all users. Now, this would be in the User entity, and now need to convert it into UserDTO. So, for that, we have used the ModelMapper class. This class has a map() method that would convert the source class to the destination.
- For ModelMapper to work, just you have to add the following dependency:
<!-- https://mvnrepository.com/artifact/org.modelmapper/modelmapper --> <dependency> <groupId>org.modelmapper</groupId> <artifactId>modelmapper</artifactId> <version>3.1.0</version> </dependency>
Create Exception Class: Handle Global Exception in Spring Boot
It is important for a developer to handle exceptions that would arise in the program. We need to show the proper message to the client about the error that occurred instead of showing ‘InternalServerError’. So, Spring Boot has a mechanism to handle Global Exceptions that we would see here.
As we know, inside the UserServiceImpl class we are throwing DuplicateRecordException. For that, we have created this custom exception inside the exception package of our project.
DuplicateRecordException.java
package com.codebun.exception; public class DuplicateRecordException extends RuntimeException { public DuplicateRecordException(String msg) { super(msg); } }
ResourceNotFoundException.java
package com.codebun.exception; public class ResourceNotFoundException extends RuntimeException { public ResourceNotFoundException(String message) { super(message); // TODO Auto-generated constructor stub } }
Now, create a class called MyControllerAdvice that would handle the Exception globally.
MyControllerAdvice.java
package com.codebun.exception; import java.net.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.context.request.WebRequest; import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; @ControllerAdvice public class MyControllerAdvice extends ResponseEntityExceptionHandler { @ExceptionHandler(DuplicateRecordException.class) public ResponseEntity<String> handleLoginException(DuplicateRecordException duplicateRecordException){ return new ResponseEntity<String>("Login Id already Exist",HttpStatus.BAD_REQUEST); } @ExceptionHandler(ResourceNotFoundException.class) public ResponseEntity<String> handleLoginException(ResourceNotFoundException duplicateRecordException){ return new ResponseEntity<String>("Login does not Exist",HttpStatus.BAD_REQUEST); } @ExceptionHandler(BadCredentialsException.class) public ResponseEntity<String> handleLoginException(BadCredentialsException duplicateRecordException){ return new ResponseEntity<String>("Incorrect Username & Password",HttpStatus.BAD_REQUEST); } @Override protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex, org.springframework.http.HttpHeaders headers, HttpStatus status, WebRequest request) { return new ResponseEntity<Object>("Please enter right method type",HttpStatus.NOT_FOUND); } }
Any exception occurs in the application that would be handled by a class annotated with @ControllerAdvice.
Now, let’s start with the JWT Authentication process in Spring Boot Application 🙂
Create JWT Authentication Exception
Now, we will create JWTAuthenticationEntryPoint that implements AuthenticationEntryPoint. If any unauthorized user requests for resources the commence() method would be called and the exception would be thrown.
JWTAuthenticationEntryPoint.java
package com.codebun.jwt; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.stereotype.Component; @Component public class JWTAuthenticationEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { response.sendError(HttpServletResponse.SC_UNAUTHORIZED,"Unauthorized Access"); } }
Create JWTUtil class
This class contains a method to generate tokens, validate tokens, and get the username from JWT.
package com.codebun.jwt; 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; import io.jsonwebtoken.SignatureAlgorithm; @Component public class JWTUtil { public static final long JWT_TOKEN_VALIDITY = 5 * 60 * 60; private String secret="mysecret"; //retrieve username from jwt token public String getUsernameFromToken(String token) { return getClaimFromToken(token, Claims::getSubject); } //retrieve expiration date from jwt token public Date getExpirationDateFromToken(String token) { return getClaimFromToken(token, Claims::getExpiration); } public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) { final Claims claims = getAllClaimsFromToken(token); return claimsResolver.apply(claims); } //for retrieveing any information from token we will need the secret key private Claims getAllClaimsFromToken(String token) { return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody(); } //check if the token has expired private Boolean isTokenExpired(String token) { final Date expiration = getExpirationDateFromToken(token); return expiration.before(new Date()); } //generate token for user public String generateToken(UserDetails userDetails) { Map<String, Object> claims = new HashMap<>(); return doGenerateToken(claims, userDetails.getUsername()); } //while creating the token - //1. Define claims of the token, like Issuer, Expiration, Subject, and the ID //2. Sign the JWT using the HS512 algorithm and secret key. //3. According to JWS Compact Serialization(https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#section-3.1) // compaction of the JWT to a URL-safe string private String doGenerateToken(Map<String, Object> claims, String subject) { return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis())) .setExpiration(new Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY * 1000)) .signWith(SignatureAlgorithm.HS512, secret).compact(); } //validate token public Boolean validateToken(String token, UserDetails userDetails) { final String username = getUsernameFromToken(token); System.out.println("JWTUtil.validateToken(): "+isTokenExpired(token)); return (username.equals(userDetails.getUsername()) && !isTokenExpired(token)); } }
Create UserDetailsService Class
Create MyUserDetailsService class which implements UserDetails. Inside this class override the loadUserByUsername(String username) method.
package com.codebun.security; 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.Component; import com.codebun.entities.User; import com.codebun.exception.ResourceNotFoundException; import com.codebun.repositories.UserRepository; @Component public class MyUserDetailsService implements UserDetailsService { @Autowired private UserRepository userRepository; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User findByLogin = userRepository.findByLogin(username); if(findByLogin==null) throw new ResourceNotFoundException("Login doesn't exist"); return findByLogin; } }
- First, we will autowire the UserRepository interface.
- Next, call the findByLogin() method and get the user data. If the user is null then throw ResourceNotFoundException else return the user data.
Create JWTAuthenticationFilter class
Create a class JWTAuthenticationFilter that extends OncePerRequestFilter and override doFilterInternal() method.
JWTAuthenticationFilter.java
package com.codebun.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.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; import com.codebun.security.MyUserDetailsService; import io.jsonwebtoken.ExpiredJwtException; @Component public class JWTAuthenicationFilter extends OncePerRequestFilter { @Autowired private UserDetailsService userDetailsService; @Autowired private JWTUtil jwtUtil; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // get header String header = request.getHeader("Authorization"); // get token from the header: Bearer jokmolkmk String username = null; String token = null; if (header != null && header.startsWith("Bearer")) { // fetch token from header token = header.substring(7); // fetch user from token try { username = jwtUtil.getUsernameFromToken(token); } catch (IllegalArgumentException e) { System.out.println("Unable to get JWT"); } catch (ExpiredJwtException e) { System.out.println("Token Expired"); } } else { System.out.println("JWT does not starts with Bearer"); } // once get the token, time to validate it if(username!=null && SecurityContextHolder.getContext().getAuthentication()==null) { UserDetails loadUserByUsername = userDetailsService.loadUserByUsername(username); if(jwtUtil.validateToken(token, loadUserByUsername)) { UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loadUserByUsername,null,loadUserByUsername.getAuthorities()); authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authenticationToken); }else { System.out.println("Invalid JWT"); } }else { System.out.println("Username is null"); } filterChain.doFilter(request, response); } }
- The doFilterInternal() method intercepts the request and then it will check the Authorization Header. It will call the getUsernameFromToken() method to get username from the token passed.
- And, once it get the token, now we will validate the token and also the token is set to Security Context.
Create Security Configuration Class
Create SecurityConfiguration.java class to configure Spring Security. This class will extend WebSecurityConfigurerAdapter class.
package com.codebun.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.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.codebun.jwt.JWTAuthenicationFilter; import com.codebun.jwt.JWTAuthenticationEntryPoint; @Configuration @EnableWebSecurity public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Autowired private MyUserDetailsService myUserDetailsService; @Autowired private JWTAuthenticationEntryPoint authenticationEntryPoint; @Autowired private JWTAuthenicationFilter authenicationFilter; @Bean public BCryptPasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(HttpSecurity http) throws Exception { http .cors() .and() .csrf() .disable() .authorizeRequests() .antMatchers("/api/auth/**").permitAll() .anyRequest().authenticated() .and() .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint) .and().sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS); http.addFilterBefore(authenicationFilter,UsernamePasswordAuthenticationFilter.class); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(myUserDetailsService).passwordEncoder(passwordEncoder()); } @Bean @Override protected AuthenticationManager authenticationManager() throws Exception { // TODO Auto-generated method stub return super.authenticationManager(); } }
- First, we will override configure(HttpSecurity http) method. With the help of this method, we will tell Spring security to configure CORS and CSRF.
- In this method, which requests we want to authorize will mention here. So, here we have passed the endpoints “/api/auth/**” to permit. And any other request that needs to be authenticated.
- Also, if any exception occurs then it would be handled by AuthenticationEntryPoint class and the Session Policy we will set is STATELESS.
- The MyUserDetailsService will be used for configuring DaoAuthenticationProvider by AuthenticationManagerBuilder.userDetailsService() method
- Also, we need a BCryptPasswordEncoder to store passwords in encrypted form.
Create Controller Class
The client request is sent to the controller which acts as an API layer that will have the endpoints for REST API. Here, first, we will create a registration API.
AuthController.java
package com.codebun.controllers; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.DisabledException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; 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.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.codebun.dtos.JWTRequest; import com.codebun.dtos.JWTResponse; import com.codebun.dtos.UserDTO; import com.codebun.exception.BadCredentialsException; import com.codebun.jwt.JWTUtil; import com.codebun.security.MyUserDetailsService; import com.codebun.services.UserService; @RestController @RequestMapping("/api/auth") public class AuthController { @Autowired private UserService userService; @Autowired private JWTUtil jwtUtil; @Autowired private UserDetailsService userDetailsService; @Autowired private AuthenticationManager authenticationManager; @PostMapping("/register") public ResponseEntity<UserDTO> register(@RequestBody UserDTO userDTO){ UserDTO register = userService.register(userDTO); return new ResponseEntity<UserDTO>(register,HttpStatus.CREATED); } @PostMapping("/login") public ResponseEntity<JWTResponse> createToken(@RequestBody JWTRequest jwtRequest) throws Exception{ authenticate(jwtRequest.getLogin(),jwtRequest.getPassword()); UserDetails userDetails = userDetailsService.loadUserByUsername(jwtRequest.getLogin()); String generateToken = jwtUtil.generateToken(userDetails); JWTResponse response = new JWTResponse(); response.setToken(generateToken); String username = userDetails.getUsername(); response.setLogin(username); return new ResponseEntity<JWTResponse>(response,HttpStatus.OK); } private void authenticate(String login, String password) throws Exception { UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(login,password); try { authenticationManager.authenticate(authenticationToken); } catch (BadCredentialsException e) { System.out.println("Incorrect Username & Password"); throw new BadCredentialsException("Incorrect Username & Password"); } } }
- In AuthController class, first, we will autowire the UserService Interface.
- Next, create the API for registration with the endpoints “/api/auth/register” that would return user information and HTTP Status OK(201).
- In login API, generateToken() is invoked with UserDetails object where we have users’s JWTRequest object. Once we get the Token, we would set it to JWTResponse and send the token and login details of the user in the response object.
UserController.java
package com.codebun.controllers; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.codebun.dtos.UserDTO; import com.codebun.services.UserService; @RestController @RequestMapping("/api/v1/") public class UserController { @Autowired private UserService userService; @GetMapping("/users") public ResponseEntity<List<UserDTO>> list(){ List<UserDTO> listAllUsers = userService.listAllUsers(); return new ResponseEntity<List<UserDTO>>(listAllUsers,HttpStatus.OK); } }
- “/api/v1/list” is created to get the list of users in the database. Here, we need to pass the token to access this API.
- If you don’t pass the token you will get 401 UnAuthorized Response Status.
Run and Test the Registration & Login Endpoints
Run the Spring Boot Application and test it using POSTMAN.
Now, try to use the same Login and see the response.
And hence, in the database, we can see the user data added to the user table and which user belongs to which role is also added. Now, run the login API and you will get a token as a response.
Now, try to access resources with the help of the token. Go to Postman, inside Header Section select Authorization from the dropdown, and Pass Bearer + “token” as shown below.
Thus, in this way, we learn how to create registration and login with JWT Authentication using Spring Boot and Spring Security.
Thank you for reading this article!