Spring Boot-Authentication and Authorization with Spring Security & JWT

Hello everyone, today we will learn how to handle authentication and authorization on RESTful APIs written with Spring Boot.GitHub repository link is provided at the end of this tutorial. You can download the source code.


Technologies used

  • Spring boot is used to develop REST web services and microservices. Spring Boot has taken the Spring framework to the next level. It has drastically reduced the configuration and setup time required for spring projects. We can set up a project with almost zero configuration and start building the things that actually matter to your application.
  • Spring Security is a Java/Java EE framework that provides authentication, authorization, and other security features for enterprise applications.
  • JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret or a public/private key pair.
  • Maven is a build automation tool used primarily for Java projects. Maven can also be used to build and manage projects written in C#, Ruby, Scala, and other languages.
  • Java 8 is a major feature release of JAVA programming language development. With the Java 8 release, Java provided fortifies for functional programming, incipient JavaScript engine, incipient APIs for date-time manipulation, incipient streaming API, etc.
  • MongoDB is a cross-platform document-oriented database program. Classified as a NoSQL database program, MongoDB uses JSON-like documents with optional schemas.
Flow Diagram



Project Directory


Maven[pom.xml] 

A Project Object Model or POM is the fundamental unit of work in Maven. It is an XML file that contains information about the project and configuration details utilized by Maven to build the project. It contains default values for most projects. Some of the configurations that can be designated in the POM is the project dependencies, the plugins or goals that can be executed, the build profiles, and so on. Other information such as the project version, description, developers, mailing lists, and such can withal be designated.

<?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.1.1.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.knf.dev</groupId>
<artifactId>spring-boot-security-jwt-mongodb</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-security-jwt-mongodb</name>
<description>Demo project for Spring Boot </description>

<properties>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</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>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-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>

Document[Employee.class]

package com.knf.dev.models;

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

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

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Document;

@Document(collection = "employees")
public class Employee {
@Id
private String id;

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

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

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

@DBRef
private Set<Role> roles = new HashSet<>();

public Employee() {
}

public Employee(String employeename, String email, String password) {
super();
this.employeename = employeename;
this.email = email;
this.password = password;
}

public String getEmployeename() {
return employeename;
}

public void setEmployeename(String employeename) {
this.employeename = employeename;
}

public String getId() {
return id;
}

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

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

Document[Role.class]

package com.knf.dev.models;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;

@Document(collection = "roles")
public class Role {
@Id
private String id;
@Indexed(unique = true)
private ERole name;

public Role() {

}

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

public String getId() {
return id;
}

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

public ERole getName() {
return name;
}

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

ERole.class

package com.knf.dev.models;

public enum ERole {
ROLE_EMPLOYEE, ROLE_ADMIN
}

Repository[EmployeeRepository.class]

package com.knf.dev.repository;

import java.util.Optional;

import org.springframework.data.mongodb.repository.MongoRepository;

import com.knf.dev.models.Employee;

public interface EmployeeRepository extends MongoRepository<Employee, String> {
Optional<Employee> findByEmployeename(String employeename);

Boolean existsByEmployeename(String employeename);

Boolean existsByEmail(String email);
}

Repository[RoleRepository.class]

package com.knf.dev.repository;

import java.util.Optional;

import org.springframework.data.mongodb.repository.MongoRepository;

import com.knf.dev.models.ERole;
import com.knf.dev.models.Role;

public interface RoleRepository extends MongoRepository<Role, String> {
Optional<Role> findByName(ERole name);
}

EmployeeDetailsImpl.class

package com.knf.dev.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.fasterxml.jackson.annotation.JsonIgnore;
import com.knf.dev.models.Employee;

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

private String id;

private String username;

private String email;

@JsonIgnore
private String password;

private Collection<? extends GrantedAuthority> authorities;

public EmployeeDetailsImpl(String 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 EmployeeDetailsImpl build(Employee user) {
List<GrantedAuthority> authorities = user.getRoles().stream()
.map(role -> new SimpleGrantedAuthority
(role.getName().name())).collect(Collectors.toList());

return new EmployeeDetailsImpl(user.getId(), user.getEmployeename(),
user.getEmail(), user.getPassword(),
authorities);
}

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

public String 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;
EmployeeDetailsImpl user = (EmployeeDetailsImpl) o;
return Objects.equals(id, user.id);
}
}

EmployeeDetailsServiceImpl.class

package com.knf.dev.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.knf.dev.models.Employee;
import com.knf.dev.repository.EmployeeRepository;

@Service
public class EmployeeDetailsServiceImpl implements UserDetailsService {
@Autowired
EmployeeRepository employeeRepository;

@Override
@Transactional
public UserDetails loadUserByUsername(String employeename)
throws UsernameNotFoundException {
Employee employee = employeeRepository.findByEmployeename(employeename)
.orElseThrow(() -> new UsernameNotFoundException
("Employee Not Found with username: " + employeename));

return EmployeeDetailsImpl.build(employee);
}

}

JwtUtils.class

package com.knf.dev.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.knf.dev.security.services.EmployeeDetailsImpl;

import io.jsonwebtoken.*;

@Component
public class JwtUtils {
private static final Logger logger = LoggerFactory.getLogger
(JwtUtils.class);
@Value("${knf.app.jwtExpirationMs}")
private int jwtExpirationMs;
@Value("${knf.app.jwtSecret}")
private String jwtSecret;

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

public String generateJwtToken(Authentication authentication) {

EmployeeDetailsImpl employeePrincipal = (EmployeeDetailsImpl)
authentication.getPrincipal();

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

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

}

AuthTokenFilter.class

package com.knf.dev.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.knf.dev.security.services.EmployeeDetailsServiceImpl;

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

@Autowired
private EmployeeDetailsServiceImpl employeeDetailsService;

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 employeename = jwtUtils.getEmployeeNameFromJwtToken(jwt);

UserDetails employeeDetails = employeeDetailsService.
loadUserByUsername(employeename);
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(
employeeDetails, null, employeeDetails.getAuthorities())
;
authentication.setDetails(new WebAuthenticationDetailsSource().
buildDetails(request));

SecurityContextHolder.getContext().setAuthentication
(authentication);
}
} catch (Exception e) {
logger.error("Cannot set employee 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;
}
}

AuthEntryPointJwt.class

package com.knf.dev.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");
}

}

WebSecurityConfig.class

package com.knf.dev.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.knf.dev.security.jwt.AuthEntryPointJwt;
import com.knf.dev.security.jwt.AuthTokenFilter;
import com.knf.dev.security.services.EmployeeDetailsServiceImpl;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private AuthEntryPointJwt unauthorizedHandler;
@Autowired
EmployeeDetailsServiceImpl employeeDetailsService;

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

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

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

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

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

}


LoginRequest.class

package com.knf.dev.request;

import javax.validation.constraints.NotBlank;

public class LoginRequest {
@NotBlank
private String employeename;

@NotBlank
private String password;

public String getEmployeename() {
return employeename;
}

public void setEmployeename(String employeename) {
this.employeename = employeename;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}
}

SignupRequest.class

package com.knf.dev.request;

import java.util.Set;

import javax.validation.constraints.*;

public class SignupRequest {
@NotBlank
@Size(min = 3, max = 20)
private String employeename;

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

private Set<String> roles;

@NotBlank
@Size(min = 6, max = 40)
private String password;

public String getEmployeename() {
return employeename;
}

public void setEmployeename(String employeename) {
this.employeename = employeename;
}

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<String> getRoles() {
return this.roles;
}

public void setRole(Set<String> roles) {
this.roles = roles;
}
}

JwtResponse.class

package com.knf.dev.response;

import java.util.List;

public class JwtResponse {
private String token;
private String type = "Bearer";
private String id;
private String employeename;
private String email;
private List<String> roles;

public JwtResponse(String accessToken, String id,
String employeename, String email, List<String> roles) {
this.token = accessToken;
this.id = id;
this.employeename = employeename;
this.email = email;
this.roles = roles;
}

public String getAccessToken() {
return token;
}

public void setAccessToken(String accessToken) {
this.token = accessToken;
}

public String getTokenType() {
return type;
}

public void setTokenType(String tokenType) {
this.type = tokenType;
}

public String getId() {
return id;
}

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

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

public String getEmployeename() {
return employeename;
}

public void setEmployeename(String employeename) {
this.employeename = employeename;
}

public List<String> getRoles() {
return roles;
}
}

MessageResponse.class

package com.knf.dev.response;

public class MessageResponse {
private String message;

public MessageResponse(String message) {
this.message = message;
}

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}
}

AuthController.class

package com.knf.dev.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.knf.dev.models.ERole;
import com.knf.dev.models.Role;
import com.knf.dev.models.Employee;
import com.knf.dev.repository.RoleRepository;
import com.knf.dev.repository.EmployeeRepository;
import com.knf.dev.request.LoginRequest;
import com.knf.dev.request.SignupRequest;
import com.knf.dev.response.JwtResponse;
import com.knf.dev.response.MessageResponse;
import com.knf.dev.security.jwt.JwtUtils;
import com.knf.dev.security.services.EmployeeDetailsImpl;

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

@Autowired
EmployeeRepository employeeRepository;

@Autowired
RoleRepository roleRepository;

@Autowired
PasswordEncoder encoder;

@Autowired
JwtUtils jwtUtils;

@PostMapping("/signin")
public ResponseEntity<?> authenticateEmployee
(@Valid @RequestBody LoginRequest loginRequest) {

Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(loginRequest.
getEmployeename(), loginRequest.getPassword()));

SecurityContextHolder.getContext().setAuthentication(authentication);
String jwt = jwtUtils.generateJwtToken(authentication);

EmployeeDetailsImpl employeeDetails = (EmployeeDetailsImpl)
authentication.getPrincipal();
List<String> roles = employeeDetails.getAuthorities().stream().
map(item -> item.getAuthority())
.collect(Collectors.toList());

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

@PostMapping("/signup")
public ResponseEntity<?> registerUser(@Valid @RequestBody
SignupRequest signUpRequest) {
if (employeeRepository.existsByEmployeename(signUpRequest.
getEmployeename())) {
return ResponseEntity.badRequest().body(new MessageResponse
("Error: Employeename is already taken!"));
}

if (employeeRepository.existsByEmail(signUpRequest.getEmail())) {
return ResponseEntity.badRequest().body(new MessageResponse
("Error: Email is already in use!"));
}

// Create new employee account
Employee employee = new Employee(signUpRequest.getEmployeename(),
signUpRequest.getEmail(),
encoder.encode(signUpRequest.getPassword()));

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

if (strRoles == null) {
Role employeeRole = roleRepository.findByName(ERole.ROLE_EMPLOYEE)
.orElseThrow(() -> new RuntimeException
("Error: Role is not found."));
roles.add(employeeRole);
} 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;
default:
Role defaultRole = roleRepository.findByName
(ERole.ROLE_EMPLOYEE)
.orElseThrow(() -> new RuntimeException
("Error: Role is not found."));
roles.add(defaultRole);
}
});
}

employee.setRoles(roles);
employeeRepository.save(employee);

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

EmployeeController.class

package com.knf.dev.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;

import com.knf.dev.response.MessageResponse;

@CrossOrigin(origins = "*", maxAge = 4800)
@RestController
@RequestMapping("/api/test")
public class EmployeeController {
@GetMapping("/all")
public MessageResponse allAccess() {
return new MessageResponse("Public ");
}

@GetMapping("/employee")
@PreAuthorize("hasRole('EMPLOYEE') ")
public MessageResponse employeeAccess() {

return new MessageResponse("Employee zone");
}

@GetMapping("/admin")
@PreAuthorize("hasRole('ADMIN')")
public MessageResponse adminAccess() {
return new MessageResponse("Admin zone");
}
}

Run & Test

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

Register employee with POST -http://localhost:8080/api/auth/signup API:



Employee Sign in to an account with POST-http://localhost:8080/api/auth/signin API:



Using accessToken access ROLE_EMPLOYEE resource with  GET http://localhost:8080/api/test/employee API:



Register Admin with POST -http://localhost:8080/api/auth/signup API:



Admin Sign in to an account with POST-http://localhost:8080/api/auth/signin API:


Using accessToken access ROLE_ADMIN resource with  http://localhost:8080/api/test/admin API:



Comments