Angular 11 SpringBoot JWT Authentication Example

Angular 11 SpringBoot Jwt Authentication Example

Tutorial: Angular 11 SpringBoot JWT Authentication Example with Angular 11 + MySQl/PostgreSQL + Spring Security

In tutorial ‘Angular 11 Spring Boot JWT Token Based Authentication Example’, I guide you very clearly how to implement full stack example to demonistrade an jwt token based authentication flow from frontend Angular 11 to backend: SpringBoot and MySQL.

* Technology Stack: Angular11, SpringBoot, Jwt, SpringBoot, MySQL, PostgreSQL, Spring Security, Spring JPA

– I give you an Epic of the application, a fullstack excutive flow from frontend Angular 11 to backend jwt SpringBoot Security to database (MySQL/PostgreSQL) with overall architecture diagram.
– I give you a layer diagram of Angular 11 Jwt Authentication application with localStorage and Angular HttpClient (plus Interceptor)
– I guide you detail-steps how to implement a security SpringBoot Jwt Token Authentication.
– I guide you step by step how to develop a Angular 11 Jwt Authentication application.
– Finally, I do an integrative testing from Angular 11 to Jwt Based Token SpringBoot Security RestApis.

Related posts:


Overview Angular 11 Spring Boot JWT Authentication example

We will build an application, from frontend (Angular 11) to backend (Spring Boot), which allows users to register, login account. This application is secured with JWT (JSON Web Token) authentication and Spring Security. Then, depending on the role of current User (user, pm or admin), this system accepts what he can access:

Angular 11 Jwt Authentication Login Form
Angular Login Form
Angular 11 Jwt Authentication Register Form
Register Form
Angular 11 Jwt Authentication Home Page of Jack User
Home Page of Jack User
Angular 11 SpringBoot Jwt Authentication Example Content of Jack User
Content of Jack User

The diagram below show how our system handles User Registration and User Login processes:

Angular 11 Spring Boot Security Jwt Authentication Work Process Diagram
Angular Spring Boot Security Jwt Authentication Work Process Diagram

This is diagram for SpringBoot Token based authentication Security/JWT classes that are separated into 3 layers:
– HTTP
– Spring Security
– REST API

Spring Boot Angular Spring Security Jwt Authentication Architecture Diagram Back End Server
Spring Boot Angular Spring Security Jwt Authentication Architecture Diagram Back End Server

SecurityContextHolder provides access to the SecurityContext.
SecurityContext holds the Authentication and possibly request-specific security information.
Authentication represents the principal which includes GrantedAuthority that reflects the application-wide permissions granted to a principal.
UserDetails contains necessary information to build an Authentication object from DAOs or other source of security data.
UserDetailsService helps to create a UserDetails from a String-based username and is usually used by AuthenticationProvider.
JwtAuthTokenFilter (extends OncePerRequestFilter) pre-processes HTTP request, from Token, create Authentication and populate it to SecurityContext.
JwtProvider validates, parses token String or generates token String from UserDetails.
UsernamePasswordAuthenticationToken gets username/password from login Request and combines into an instance of Authentication interface.
AuthenticationManager uses DaoAuthenticationProvider (with help of UserDetailsService & PasswordEncoder) to validate instance of UsernamePasswordAuthenticationToken, then returns a fully populated Authentication instance on successful authentication.
SecurityContext is established by calling SecurityContextHolder.getContext().setAuthentication(…​) with returned authentication object above.
AuthenticationEntryPoint handles AuthenticationException.
– Access to Restful API is protected by HTTPSecurity and authorized with Method Security Expressions.

In the tutorial, “Angular 11 Spring Boot JWT Authentication Example”, we need the Angular HTTP Interceptor to add JWT Authentication Token Based for Security:

SpringBoot Angular Spring Security Jwt Authentication Architecture Diagram Front End Client
SpringBoot Angular Spring Security Jwt Authentication Architecture Diagram Front End Client

app.component is the parent component that contains routerLink and router-outlet for routing. It also has an authority variable as the condition for displaying items on navigation bar.
user.component, pm.component, admin.component correspond to Angular Components for User Board, PM Board, Admin Board. Each Board uses user.service to access authority data.
register.component contains User Registration form, submission of the form will call auth.service.
login.component contains User Login form, submission of the form will call auth.service and token-storage.service.

user.service gets access to authority data from Server using Angular HttpClient ($http service).
auth.service handles authentication and signup actions with Server using Angular HttpClient ($http service).
– every HTTP request by $http service will be inspected and transformed before being sent to the Server by auth-interceptor (implements HttpInterceptor).
auth-interceptor check and get Token from token-storage.service to add the Token to Authorization Header of the HTTP Requests.

token-storage.service manages Token inside Browser’s sessionStorage.

JSON Web Token

In the tutorial: ‘Angular 11 SpringBoot JWT Authentication Example’, we use JWT token based to security our website, so the question is: What is JSON Web Token, JWT?
JSON Web Token (JWT) defines a compact and self-contained way for securely transmitting information between parties as a JSON object.

Scenarios where JSON Web Tokens are useful:

  • Authorization: the most common scenario for using JWT. Single Sign On is a feature that widely uses JWT
  • Information Exchange: Because JWTs can be signed, JSON Web Tokens are a good way of securely transmitting information between parties.

JSON Web Tokens consist of 3 parts:

  • Header
  • Payload
  • Signature

-> JWT looks like Header-Base64-String.Payload-Base64-String.Signature-Base64-String

Header consists of two parts:

  • token type.
  • hashing algorithm.

-> Example:

{
  "alg": "HS256",
  "typ": "JWT"
}

Payload contains the claims. Claims are statements about an entity and additional information.
There are 3 types of claims ->

  • Registered claims -> These are a set of predefined claims: iss (issuer), exp (expiration time), sub (subject)
  • Public claims
  • Private claims

Example:

{
  "sub": "thomasgkz",
  "iat": 1537603195,
  "exp": 1537689595
}

Signature -> To create the signature part you have to take the encoded header, the encoded payload, a secret, the algorithm specified in the header, and sign that.

Example:

HMACSHA512(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  your-256-bit-secret
)

Combine all together, we get 3 Base64-URL strings separated by dots,

-> Example:

eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0aG9tYXNna3oiLCJpYXQiOjE1Mzc2MDMxOTUsImV4cCI6MTUzNzY4OTU5NX0.m2YMjTYmOnfR7nnVNxqCzWbQ2FhKRe1eiizxnC2TF4eAoEzKlwo7PheVkKcxj08ST3vB-ZOIhiORvYVfSgzcog

When accessing a protected route or resource, the user agent should send the JWT, typically in the Authorization header using the Bearer schema.

Example:

Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0aG9tYXNna3oiLCJpYXQiOjE1Mzc2MDMxOTUsImV4cCI6MTUzNzY4OTU5NX0.m2YMjTYmOnfR7nnVNxqCzWbQ2FhKRe1eiizxnC2TF4eAoEzKlwo7PheVkKcxj08ST3vB-ZOIhiORvYVfSgzcog

Video Guide – Angular 11 SpringBoot JWT Authentication Example

Build SpringBoot JWT Authentication Example RestAPIs

Overview SpringBoot jwt authentication

Look back to the diagram for Spring Security/JWT classes that are separated into 3 layers:
– HTTP
– Spring Security
– REST API

Angular Spring Boot Jwt Authentication Example Architecture Diagram Back End Server
Spring Boot Angular Spring Security Jwt Authentication Architecture Diagram Back End Server

For more details about this Architecture, please please back to SPRING BOOT BACK-END WITH SPRING SECURITY.

SpringBoot Generate/Validate Token

We use a class named JwtProvider. It gets username from Authentication object, then builds JWT Token with username, Date() object, secretKey. JwtProvider can also be used to validate JWT Token:

class JwtProvider {
    @Value("${grokonez.app.jwtSecret}")
    private String jwtSecret;
 
    @Value("${grokonez.app.jwtExpiration}")
    private int jwtExpiration;
 
    public String generateJwtToken(Authentication authentication) {
        UserPrinciple userPrincipal = (UserPrinciple) authentication.getPrincipal();
 
        return Jwts.builder()
                    .setSubject((userPrincipal.getUsername()))
                    .setIssuedAt(new Date())
                    .setExpiration(new Date((new Date()).getTime() + jwtExpiration*1000))
                    .signWith(SignatureAlgorithm.HS512, jwtSecret)
                    .compact();
    }
    
    public boolean validateJwtToken(String authToken) {
        Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken);
        return ...;
    }
    
    public String getUserNameFromJwtToken(String token) {
        return Jwts.parser()
                      .setSigningKey(jwtSecret)
                      .parseClaimsJws(token)
                      .getBody().getSubject();
    }
}

SpringBoot Jwt Authentication RestAPI Filter the Request

We add our JwtAuthTokenFilter (that extends Spring OncePerRequestFilter abstract class) to the chain of filters.

public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
 
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        ...
        http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
    }
}

JwtAuthTokenFilter validates the Token using JwtProvider:

class JwtAuthTokenFilter extends OncePerRequestFilter {
    @Autowired
    private JwtProvider tokenProvider;
 
    @Override
    protected void doFilterInternal(...) {
        String jwt = getJwt(request);
        if (jwt!=null && tokenProvider.validateJwtToken(jwt)) {
            ...
        }
        filterChain.doFilter(request, response);
    }
}

Now we have 2 cases:
– Login/SignUp: RestAPI with non-protected APIs -> authenticate Login Request with AuthenticationManager, if error occurs, handle AuthenticationException with AuthenticationEntryPoint.
– With protected Resources:
+ jwt token is null/invalid -> if Authenticated Error occurs, handle AuthenticationException with AuthenticationEntryPoint.
+ jwt token is valid -> from token, get User information, then create AuthenticationToken.

SpringBoot Create Jwt Authentication from Token

JwtAuthTokenFilter extracts username/password from the received token using JwtProvider, then based on the extracted data, JwtAuthTokenFilter:
– creates a AuthenticationToken (that implements Authentication)
– uses the AuthenticationToken as Authentication object and stores it in the SecurityContext for future filter uses (e.g: Authorization filters).

In this tutorial, we use UsernamePasswordAuthenticationToken:

// extract user information
String username = tokenProvider.getUserNameFromJwtToken(jwt);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
 
// create AuthenticationToken
UsernamePasswordAuthenticationToken authentication
        = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

SpringBoot Store Jwt Authentication object in SecurityContext

SecurityContextHolder.getContext().setAuthentication(authentication);

SecurityContextHolder is the most fundamental object where we store details of the present security context of the application (includes details of the principal). Spring Security uses an Authentication object to represent this information and we can query this Authentication object from anywhere in our application:

Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
// currently authenticated user
Object principal = authentication.getPrincipal();

getContext() returns an instance of SecurityContext interface that holds the Authentication and possibly request-specific security information.

SpringBoot Security authenticate with AuthenticationProvider

These are some authentication providers that Spring Framework provides, in this example, we use >DaoAuthenticationProvider. This Provider works well with form-based logins or HTTP Basic authentication which submits a simple username/password authentication request.
It authenticates the User simply by comparing the password submitted in a UsernamePasswordAuthenticationToken against the one loaded by the UserDetailsService (as a DAO):

@Autowired
AuthenticationManager authenticationManager;
...
Authentication authentication = 
        authenticationManager.authenticate(
            new UsernamePasswordAuthenticationToken(loginRequest.username, loginRequest.password)
        );

Configuring this provider is simple with AuthenticationManagerBuilder:

class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    UserDetailsServiceImpl userDetailsService;
 
    @Override
    public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
        authenticationManagerBuilder
                .userDetailsService(userDetailsService)
                .passwordEncoder(passwordEncoder());
    }
 
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

SpringBoot Retrieve User details with UserDetailsService

We can obtain a principal from the Authentication object. This principal can be cast into a UserDetails object to lookup the username, password and GrantedAuthoritys.

Therefore, after authenticating is successful, we can simply get UserDetails from Authentication object:

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

DaoAuthenticationProvider also uses UserDetailsService for getting UserDetails object. This is the common approach in which we only pass a String-based ‘username’ argument and returns a UserDetails:

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

It is simple to implement UserDetailsService and easy for us to retrieve authentication information using a persistence strategy:

@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 or email : " + username));
 
      return UserPrinciple.build(user); // UserPrinciple implements UserDetails
    }
}

SpringBoot Protect Resources with HTTPSecurity & Method Security Expressions

To help Spring Security know when we want to require all users to be authenticated, which Exception Handler to be chosen, which filter and when we want it to work. We implement WebSecurityConfigurerAdapter and provide a configuration in the configure(HttpSecurity http) method:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable().
                authorizeRequests()
                .antMatchers("/api/auth/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
                ...;
        
        http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
    }
}

Spring Security provides some annotations for pre and post-invocation authorization checks, filtering of submitted collection arguments or return values: @PreAuthorize, @PreFilter, @PostAuthorize and @PostFilter.

To enable Method Security Expressions, we use @EnableGlobalMethodSecurity annotation:

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

In the code below, we use the most useful annotation @PreAuthorize to decide whether a method can actually be invoked or not:

@RestController
public class TestRestAPIs {
  
    @GetMapping("/api/test/user")
    @PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
    public String userAccess() {
      return ">>> User Contents!";
    }
 
    @GetMapping("/api/test/pm")
    @PreAuthorize("hasRole('PM') or hasRole('ADMIN')")
    public String projectManagementAccess() {
      return ">>> Project Management Board";
    }
  
    @GetMapping("/api/test/admin")
    @PreAuthorize("hasRole('ADMIN')")
    public String adminAccess() {
        return ">>> Admin Contents";
    }
}

SpringBoot Handle AuthenticationException – AuthenticationEntryPoint

If the user requests a secure HTTP resource without being authenticated, AuthenticationEntryPoint will be called. At this time, an AuthenticationException is thrown, commence() method on the entry point is triggered:

@Component
public class JwtAuthEntryPoint implements AuthenticationEntryPoint {
   
    @Override
    public void commence(HttpServletRequest request,
                         HttpServletResponse response,
                         AuthenticationException e) 
                            throws IOException, ServletException {
      
        logger.error("Unauthorized error. Message - {}", e.getMessage());
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Error -> Unauthorized");
    }
}

SpringBoot Jwt Authentication Project Overview

Goal

The diagram below show how our system handles User Registration and User Login processes:

Angular Spring Security Jwt Authentication Work Process Diagram
Angular Spring Security Jwt Authentication Work Process Diagram

We expose 2 RestAPIs to signup and signin:
– SignUp API: /api/auth/signup

Springboot Jwt Authentication Register User Phrase
Springboot Jwt Authentication Register User Phrase

– SignIn API: /api/auth/signin

Springboot Jwt Authentication Register User Phrase Signin
Springboot Jwt Authentication Register User Phrase Signin

– In the tutorial, “Angular 10 + Spring Boot JWT Token Based Authentication Example”, We expose 3 RestAPIs to test protected resources:

@GetMapping("/api/test/user")
@PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
public String userAccess() {
  return ">>> User Contents!";
}
 
@GetMapping("/api/test/pm")
@PreAuthorize("hasRole('PM') or hasRole('ADMIN')")
public String projectManagementAccess() {
  return ">>> Board Management Project";
}
 
@GetMapping("/api/test/admin")
@PreAuthorize("hasRole('ADMIN')")
public String adminAccess() {
  return ">>> Admin Contents";
}

Access Successfully:

Access Content Successfully
Access Content Successfully

Unauthorized:

Access PM Content Fail
Access PM Content Fail

Technologies – Angular 11 SpringBoot Jwt Authentication Example

– Spring Boot 2
– jjwt – 0.9.0
– Spring Security
– Spring JPA
– MySQL/PostgreSQL

Project Structure – SpringBoot jwt token based authentication example

SpringBoot Structure
SpringBoot Structure

model package defines 2 entities User & Role that have many-to-many relationship:

spring-security-jwt-json-web-token-authentication-springboot-spring-jpa-postgresql-uml-modeling

repository package contains interfaces that use Hibernate JPA to store/retrieve data from MySQL database.
controller package defines RestAPIs for user signup/signin and testing protected resources that is secured with JWT.
message package defines payload data transferred from user agents (Browser/RestClient…) to RestAPIs and message back.
security package is the main part of the project that implements JWT security.

Create SpringBoot Jwt Authentication Project

Dependency for the Project:

<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>
 
<!-- For Working with Json Web Tokens (JWT) -->
<dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt</artifactId>
  <version>0.9.0</version>
</dependency>

If you are using MySQL database, add the below dependency:

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

If you use PostgreSQL database, add more the below dependency:

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

Create SpringBoot Java Models

User model includes 5 attributes:

  • id
  • name
  • username
  • email
  • password

model/User.java

package com.loizenjava.jwtauthentication.model;
 
import java.util.HashSet;
import java.util.Set;
 
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 javax.persistence.UniqueConstraint;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
 
import org.hibernate.annotations.NaturalId;
 
@Entity
@Table(name = "users", uniqueConstraints = {
        @UniqueConstraint(columnNames = {
            "username"
        }),
        @UniqueConstraint(columnNames = {
            "email"
        })
})
public class User{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
 
    @NotBlank
    @Size(min=3, max = 50)
    private String name;
 
    @NotBlank
    @Size(min=3, max = 50)
    private String username;
 
    @NaturalId
    @NotBlank
    @Size(max = 50)
    @Email
    private String email;
 
    @NotBlank
    @Size(min=6, max = 100)
    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 name, String username, String email, String password) {
        this.name = name;
        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 getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    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;
    }
}

Role model with 2 attributes:

  • id
  • rolename

model/Role.java

package com.loizenjava.jwtauthentication.model;
 
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
 
import org.hibernate.annotations.NaturalId;
 
@Entity
@Table(name = "roles")
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
 
    @Enumerated(EnumType.STRING)
    @NaturalId
    @Column(length = 60)
    private RoleName name;
 
    public Role() {}
 
    public Role(RoleName name) {
        this.name = name;
    }
 
    public Long getId() {
        return id;
    }
 
    public void setId(Long id) {
        this.id = id;
    }
 
    public RoleName getName() {
        return name;
    }
 
    public void setName(RoleName name) {
        this.name = name;
    }
}

model/RoleName.java

package com.loizenjava.jwtauthentication.model;
 
public enum  RoleName {
    ROLE_USER,
    ROLE_PM,
    ROLE_ADMIN
}

Implement SpringBoot JPA Repository

repository/UserRepository.java

package com.loizenjava.jwtauthentication.repository;
 
import java.util.Optional;
 
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
 
import com.grokonez.jwtauthentication.model.User;
 
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByUsername(String username);
    Boolean existsByUsername(String username);
    Boolean existsByEmail(String email);
}

repository/UserRepository.java

package com.loizenjava.jwtauthentication.repository;
 
import java.util.Optional;
 
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
 
import com.grokonez.jwtauthentication.model.Role;
import com.grokonez.jwtauthentication.model.RoleName;
 
@Repository
public interface RoleRepository extends JpaRepository<Role, Long> {
    Optional<Role> findByName(RoleName roleName);
}

Implement SpringBoot JWT Security

security/WebSecurityConfig.java

package com.loizenjava.jwtauthentication.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.grokonez.jwtauthentication.security.jwt.JwtAuthEntryPoint;
import com.grokonez.jwtauthentication.security.jwt.JwtAuthTokenFilter;
import com.grokonez.jwtauthentication.security.services.UserDetailsServiceImpl;
 
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(
    prePostEnabled = true
)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    UserDetailsServiceImpl userDetailsService;
 
    @Autowired
    private JwtAuthEntryPoint unauthorizedHandler;
 
    @Bean
    public JwtAuthTokenFilter authenticationJwtTokenFilter() {
        return new JwtAuthTokenFilter();
    }
 
    @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().
                authorizeRequests()
                .antMatchers("/api/auth/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        
        http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
    }
}

Create SpringBoot jwt Authetication UserDetails Service

UserDetailsServiceImpl implements UserDetailsService and overrides loadUserByUsername() method.

loadUserByUsername method finds a record from users database tables to build a UserDetails object for authentication.

security/services/UserDetailsServiceImpl.java

package com.loizenjava.jwtauthentication.security.services;
 
import com.grokonez.jwtauthentication.model.User;
import com.grokonez.jwtauthentication.repository.UserRepository;
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;
 
@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 or email : " + username));
 
    return UserPrinciple.build(user);
  }
}

UserPrinciple will implement UserDetails.
UserPrinciple is not used directly by Spring Security for security purposes.
It simply stores user information which is later encapsulated into Authentication objects. This allows non-security related user information (such as email addresses, telephone numbers etc) to be stored.

security/services/UserPrinciple.java

package com.loizenjava.jwtauthentication.security.services;
 
import com.grokonez.jwtauthentication.model.User;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
 
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
 
public class UserPrinciple implements UserDetails {
  private static final long serialVersionUID = 1L;
 
  private Long id;
 
    private String name;
 
    private String username;
 
    private String email;
 
    @JsonIgnore
    private String password;
 
    private Collection authorities;
 
    public UserPrinciple(Long id, String name, 
              String username, String email, String password, 
              Collection authorities) {
        this.id = id;
        this.name = name;
        this.username = username;
        this.email = email;
        this.password = password;
        this.authorities = authorities;
    }
 
    public static UserPrinciple build(User user) {
        List authorities = user.getRoles().stream().map(role ->
                new SimpleGrantedAuthority(role.getName().name())
        ).collect(Collectors.toList());
 
        return new UserPrinciple(
                user.getId(),
                user.getName(),
                user.getUsername(),
                user.getEmail(),
                user.getPassword(),
                authorities
        );
    }
 
    public Long getId() {
        return id;
    }
 
    public String getName() {
        return name;
    }
 
    public String getEmail() {
        return email;
    }
 
    @Override
    public String getUsername() {
        return username;
    }
 
    @Override
    public String getPassword() {
        return password;
    }
 
    @Override
    public Collection getAuthorities() {
        return authorities;
    }
 
    @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;
        
        UserPrinciple user = (UserPrinciple) o;
        return Objects.equals(id, user.id);
    }
}

SpringBoot JWT Authentication Classes

JwtAuthTokenFilter extends OncePerRequestFilter.

org.springframework.web.filter.OncePerRequestFilter executes once per request. This is a filter base class that is used to guarantee a single execution per request dispatch. It provides a doFilterInternal method with HttpServletRequest and HttpServletResponse arguments.

Inside JwtAuthTokenFilter class, the doFilterInternal method will:

  • get JWT token from header
  • validate JWT
  • parse username from validated JWT
  • load data from users table, then build an authentication object
  • set the authentication object to Security Context

security/jwt/JwtAuthTokenFilter.java

package com.loizenjava.jwtauthentication.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.web.filter.OncePerRequestFilter;
 
import com.grokonez.jwtauthentication.security.services.UserDetailsServiceImpl;
 
public class JwtAuthTokenFilter extends OncePerRequestFilter {
 
  @Autowired
  private JwtProvider tokenProvider;
 
  @Autowired
  private UserDetailsServiceImpl userDetailsService;
 
  private static final Logger logger = LoggerFactory.getLogger(JwtAuthTokenFilter.class);
 
  @Override
  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
      throws ServletException, IOException {
    try {
 
      String jwt = getJwt(request);
      if (jwt != null && tokenProvider.validateJwtToken(jwt)) {
        String username = tokenProvider.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("Can NOT set user authentication -> Message: {}", e);
    }
 
    filterChain.doFilter(request, response);
  }
 
  private String getJwt(HttpServletRequest request) {
    String authHeader = request.getHeader("Authorization");
 
    if (authHeader != null && authHeader.startsWith("Bearer ")) {
      return authHeader.replace("Bearer ", "");
    }
 
    return null;
  }
}

JwtAuthEntryPoint is used to handle Error exception when having unauthorized requests.
security/jwt/JwtAuthEntryPoint.java

package com.loizenjava.jwtauthentication.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 JwtAuthEntryPoint implements AuthenticationEntryPoint {
 
    private static final Logger logger = LoggerFactory.getLogger(JwtAuthEntryPoint.class);
    
    @Override
    public void commence(HttpServletRequest request,
                         HttpServletResponse response,
                         AuthenticationException e) 
                             throws IOException, ServletException {
      
        logger.error("Unauthorized error. Message - {}", e.getMessage());
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Error -> Unauthorized");
    }
}

JwtProvider is an util class -> it implements useful functions:

  • generate a JWT token
  • valiate a JWT token
  • parse username from JWT token

security/jwt/JwtProvider.java

package com.loizenjava.jwtauthentication.security.jwt;
 
import io.jsonwebtoken.*;
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.grokonez.jwtauthentication.security.services.UserPrinciple;
 
import java.util.Date;
 
@Component
public class JwtProvider {
 
    private static final Logger logger = LoggerFactory.getLogger(JwtProvider.class);
 
    @Value("${grokonez.app.jwtSecret}")
    private String jwtSecret;
 
    @Value("${grokonez.app.jwtExpiration}")
    private int jwtExpiration;
 
    public String generateJwtToken(Authentication authentication) {
 
        UserPrinciple userPrincipal = (UserPrinciple) authentication.getPrincipal();
 
        return Jwts.builder()
                    .setSubject((userPrincipal.getUsername()))
                    .setIssuedAt(new Date())
                    .setExpiration(new Date((new Date()).getTime() + jwtExpiration*1000))
                    .signWith(SignatureAlgorithm.HS512, jwtSecret)
                    .compact();
    }
    
    public boolean validateJwtToken(String authToken) {
        try {
            Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken);
            return true;
        } catch (SignatureException e) {
            logger.error("Invalid JWT signature -> Message: {} ", e);
        } catch (MalformedJwtException e) {
            logger.error("Invalid JWT token -> Message: {}", e);
        } catch (ExpiredJwtException e) {
            logger.error("Expired JWT token -> Message: {}", e);
        } catch (UnsupportedJwtException e) {
            logger.error("Unsupported JWT token -> Message: {}", e);
        } catch (IllegalArgumentException e) {
            logger.error("JWT claims string is empty -> Message: {}", e);
        }
        
        return false;
    }
    
    public String getUserNameFromJwtToken(String token) {
        return Jwts.parser()
                      .setSigningKey(jwtSecret)
                      .parseClaimsJws(token)
                      .getBody().getSubject();
    }
}

Implement SpringBoot Jwt Authentication RestControllers

Create SpringBoot jwt Payload Message

LoginForm with username & password.

message/request/LoginForm.java

package com.loizenjava.jwtauthentication.message.request;
 
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
 
public class LoginForm {
    @NotBlank
    @Size(min=3, max = 60)
    private String username;
 
    @NotBlank
    @Size(min = 6, max = 40)
    private String password;
 
    public String getUsername() {
        return username;
    }
 
    public void setUsername(String username) {
        this.username = username;
    }
 
    public String getPassword() {
        return password;
    }
 
    public void setPassword(String password) {
        this.password = password;
    }
}

SignUpForm includes:

  • name
  • username
  • email
  • role
  • password

message/request/SignUpForm.java

package com.loizenjava.jwtauthentication.message.request;
 
import java.util.Set;
 
import javax.validation.constraints.*;
 
public class SignUpForm {
    @NotBlank
    @Size(min = 3, max = 50)
    private String name;
 
    @NotBlank
    @Size(min = 3, max = 50)
    private String username;
 
    @NotBlank
    @Size(max = 60)
    @Email
    private String email;
    
    private Set role;
    
    @NotBlank
    @Size(min = 6, max = 40)
    private String password;
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    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 getRole() {
      return this.role;
    }
    
    public void setRole(Set role) {
      this.role = role;
    }
}

JwtResponse object will be returned by SpringBoot server once an authentication is successful, it contains:

  • JWT Token
  • Schema Type of Token
  • Username
  • Array of User’s Authorities

message/response/JwtResponse.java

package com.loizenjava.jwtauthentication.message.response;
 
import java.util.Collection;
 
import org.springframework.security.core.GrantedAuthority;
 
public class JwtResponse {
  private String token;
  private String type = "Bearer";
  private String username;
  private Collection authorities;
 
  public JwtResponse(String accessToken, String username, Collection authorities) {
    this.token = accessToken;
    this.username = username;
    this.authorities = authorities;
  }
 
  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 getUsername() {
    return username;
  }
 
  public void setUsername(String username) {
    this.username = username;
  }
  
  public Collection<? extends GrantedAuthority> getAuthorities() {
    return authorities;
  }
}

ResponseMessage object is just a message object.

message/response/ResponseMessage.java

package com.loizenjava.jwtauthentication.message.response;
 
public class ResponseMessage {
  private String message;
 
  public ResponseMessage(String message) {
    this.message = message;
  }
 
  public String getMessage() {
    return message;
  }
 
  public void setMessage(String message) {
    this.message = message;
  }
}

SpringBoot Jwt Authentication RestAPIs Controller

AuthRestAPIs defines 2 APIs:

/api/auth/signup: sign up
-> check username/email is already in use.
-> create User object
-> store to database

/api/auth/signin: sign in
-> attempt to authenticate with AuthenticationManager bean.
-> add authentication object to SecurityContextHolder
-> Generate JWT token, then return JWT to client

controller/AuthRestAPIs.java

package com.loizenjava.jwtauthentication.controller;
 
import java.util.HashSet;
import java.util.Set;
 
import javax.validation.Valid;
 
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.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
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.grokonez.jwtauthentication.message.request.LoginForm;
import com.grokonez.jwtauthentication.message.request.SignUpForm;
import com.grokonez.jwtauthentication.message.response.JwtResponse;
import com.grokonez.jwtauthentication.message.response.ResponseMessage;
import com.grokonez.jwtauthentication.model.Role;
import com.grokonez.jwtauthentication.model.RoleName;
import com.grokonez.jwtauthentication.model.User;
import com.grokonez.jwtauthentication.repository.RoleRepository;
import com.grokonez.jwtauthentication.repository.UserRepository;
import com.grokonez.jwtauthentication.security.jwt.JwtProvider;
 
@CrossOrigin(origins = "*", maxAge = 3600)
@RestController
@RequestMapping("/api/auth")
public class AuthRestAPIs {
 
  @Autowired
  AuthenticationManager authenticationManager;
 
  @Autowired
  UserRepository userRepository;
 
  @Autowired
  RoleRepository roleRepository;
 
  @Autowired
  PasswordEncoder encoder;
 
  @Autowired
  JwtProvider jwtProvider;
 
  @PostMapping("/signin")
  public ResponseEntity authenticateUser(@Valid @RequestBody LoginForm loginRequest) {
 
    Authentication authentication = authenticationManager.authenticate(
        new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword()));
 
    SecurityContextHolder.getContext().setAuthentication(authentication);
 
    String jwt = jwtProvider.generateJwtToken(authentication);
    UserDetails userDetails = (UserDetails) authentication.getPrincipal();
 
    return ResponseEntity.ok(new JwtResponse(jwt, userDetails.getUsername(), userDetails.getAuthorities()));
  }
 
  @PostMapping("/signup")
  public ResponseEntity registerUser(@Valid @RequestBody SignUpForm signUpRequest) {
    if (userRepository.existsByUsername(signUpRequest.getUsername())) {
      return new ResponseEntity<>(new ResponseMessage("Fail -> Username is already taken!"),
          HttpStatus.BAD_REQUEST);
    }
 
    if (userRepository.existsByEmail(signUpRequest.getEmail())) {
      return new ResponseEntity<>(new ResponseMessage("Fail -> Email is already in use!"),
          HttpStatus.BAD_REQUEST);
    }
 
    // Creating user's account
    User user = new User(signUpRequest.getName(), signUpRequest.getUsername(), signUpRequest.getEmail(),
        encoder.encode(signUpRequest.getPassword()));
 
    Set strRoles = signUpRequest.getRole();
    Set roles = new HashSet<>();
 
    strRoles.forEach(role -> {
      switch (role) {
      case "admin":
        Role adminRole = roleRepository.findByName(RoleName.ROLE_ADMIN)
            .orElseThrow(() -> new RuntimeException("Fail! -> Cause: User Role not find."));
        roles.add(adminRole);
 
        break;
      case "pm":
        Role pmRole = roleRepository.findByName(RoleName.ROLE_PM)
            .orElseThrow(() -> new RuntimeException("Fail! -> Cause: User Role not find."));
        roles.add(pmRole);
 
        break;
      default:
        Role userRole = roleRepository.findByName(RoleName.ROLE_USER)
            .orElseThrow(() -> new RuntimeException("Fail! -> Cause: User Role not find."));
        roles.add(userRole);
      }
    });
 
    user.setRoles(roles);
    userRepository.save(user);
 
    return new ResponseEntity<>(new ResponseMessage("User registered successfully!"), HttpStatus.OK);
  }
}

TestRestAPIs define 3 RestAPIs:

/api/test/user: access by users has USER_ROLE or ADMIN_ROLE
/api/test/pm: access by users has USER_PM or ADMIN_ROLE
/api/test/admin: access by users has ADMIN_ROLE

controller/TestRestAPIs.java

package com.loizenjava.jwtauthentication.controller;
 
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.RestController;
 
@CrossOrigin(origins = "*", maxAge = 3600)
@RestController
public class TestRestAPIs {
  
  @GetMapping("/api/test/user")
  @PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
  public String userAccess() {
    return ">>> User Contents!";
  }
 
  @GetMapping("/api/test/pm")
  @PreAuthorize("hasRole('PM') or hasRole('ADMIN')")
  public String projectManagementAccess() {
    return ">>> Project Management Board";
  }
  
  @GetMapping("/api/test/admin")
  @PreAuthorize("hasRole('ADMIN')")
  public String adminAccess() {
    return ">>> Admin Contents";
  }
}

Configure SpringBoot Datasource, JPA and define App Properties

– Configure for MySQL database:

spring.datasource.url=jdbc:mysql://localhost:3306/loizenjavadb
spring.datasource.username=root
spring.datasource.password=12345
spring.jpa.generate-ddl=true

# App Properties
loizenjava.app.jwtSecret=jwtLoizenai.comSecretKey
loizenjava.app.jwtExpiration=86400

– Configure for PostgreSQL database:

## PostgreSQL
spring.datasource.url=jdbc:postgresql://localhost:5432/loizenjavadb
spring.datasource.username=postgres
spring.datasource.password=123
#drop & create table again, good for testing, comment this in production
spring.jpa.hibernate.ddl-auto=create

Run SpringBoot Jwt Authentication Project & Check Results

Start SpringBoot Jwt Authentication Example project

– Start Springboot server by commandline mvn spring-boot:run

– Check database tables:

springboot jwt authentication - database tables schema
springboot jwt authentication – database tables schema

– Insert data to roles table :

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

SpringBoot Jwt Authentication SignUp RestAPI

Sign-Up 3 users:

  • Jack has ROLE_USER role
  • Adam has ROLE_PM & ROLE_USER roles
  • Thomas has ROLE_ADMIN role
springboot jwt authentication - register user phrase - thomas admin role
springboot jwt authentication – register user phrase – thomas admin role

– Check database’s tables:

springboot jwt authentication - check database tables after insert data
springboot jwt authentication – check database tables after insert data

SignIn and Access Protected Resources

– Jack can access api/test/user url, can NOT access others.

-> Sign In:

Jack sign In
Jack sign In

Access Protected Resources:

Jack accesses User content successfully
Jack accesses User content successfully
Jack can NOT access PM content
Jack can NOT access PM content

– Adam can access api/test/user and api/test/pm url.
Can NOT access /api/test/admin url.

-> Sign In:

Adam sign in successfully
Adam sign in successfully

-> Access Protected Resources:

Adam accesses user content
Adam accesses user content
Adam accessess PM content
Adam accessess PM content
Adam can NOT access Admin content
Adam can NOT access Admin content

– Thomas can access all URLs.

-> Sign In:

Thomas sign in successfully
Thomas sign in successfully
Thomas accesses Admin content successfully
Thomas accesses Admin content successfully

How to build Angular 11 JWT Authentication Example

Angular 11 Send Jwt Token Authentication Requests to Server

Angular 11 Add Jwt Authentication Token to Header with Angular HttpInterceptor

We use Angular HttpInterceptor with intercept() method to inspect and transform HTTP requests (before they are sent to server):

import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';
 
const TOKEN_HEADER_KEY = 'Authorization';
 
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
    ...
    intercept(req: HttpRequest, next: HttpHandler) {
        let authReq = req;
        const token = ...;
        if (token != null) {
            authReq = req.clone({ headers: req.headers.set(TOKEN_HEADER_KEY, 'Bearer ' + token) });
        }
        return next.handle(authReq);
    }
}
 
export const httpInterceptorProviders = [
    { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }
];

– The HTTPRequest object will be inspected and forwarded to handle() method of the HttpHandler object.
handle() method transforms HTTPRequest object into an Observable of HttpEvents which includes the server’s response.

What is next: HttpHandler object?
-> This object represents the next interceptor in the chain of interceptors. The final ‘next’ in the chain is the Angular HttpClient handler.

Angular 11 do http requests using Angular HttpClient

We send HTTP Requests (signin/signup) using Angular HttpClient:

import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
 
const httpOptions = {
  headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
 
export class AuthService {
 
  private loginUrl = 'http://localhost:8080/api/auth/signin';
  private signupUrl = 'http://localhost:8080/api/auth/signup';
 
  constructor(private http: HttpClient) {
  }
 
  // JwtResponse(accessToken,type,username,authorities)
  attemptAuth(credentials: AuthLoginInfo): Observable {
    return this.http.post(this.loginUrl, credentials, httpOptions);
  }
 
  // SignUpInfo(name,username,email,role,password)
  signUp(info: SignUpInfo): Observable {
    return this.http.post(this.signupUrl, info, httpOptions);
  }
}

AuthLoginInfo fields & SignUpInfo fields are validated using Angular template-driven Form.

Once Token is saved, we can access protected resources simply:

export class UserService {
  private userUrl = 'http://localhost:8080/api/test/user';
  private pmUrl = 'http://localhost:8080/api/test/pm';
  private adminUrl = 'http://localhost:8080/api/test/admin';
 
  constructor(private http: HttpClient) { }
 
  getUserBoard(): Observable {
    return this.http.get(this.userUrl, { responseType: 'text' });
  }
 
  getPMBoard(): Observable {
    return this.http.get(this.pmUrl, { responseType: 'text' });
  }
 
  getAdminBoard(): Observable {
    return this.http.get(this.adminUrl, { responseType: 'text' });
  }
}

Angular 11 Handle Http JWT Authentication Responses

Using AuthService to work with Observable object:

Angular 11 Signup Response

export class RegisterComponent implements OnInit {
  ...
  constructor(private authService: AuthService) { }
 
  onSubmit() {
    ...
    this.authService.signUp(this.signupInfo).subscribe(
      data => {
        ...
      },
      error => {
        ...
      }
    );
  }
}

Angular 11 Login Response

In addition to using AuthService to work with Observable object, we also call TokenStorageService methods to save Token, >Username, Authorities:

export class LoginComponent implements OnInit {
  ...
  constructor(private authService: AuthService, private tokenStorage: TokenStorageService) { }
 
  ngOnInit() {
    if (this.tokenStorage.getToken()) {
      this.isLoggedIn = true;
      this.roles = this.tokenStorage.getAuthorities();
    }
  }
 
  onSubmit() {
    ...
    this.authService.attemptAuth(this.loginInfo).subscribe(
      data => {
        this.tokenStorage.saveToken(data.accessToken);
        this.tokenStorage.saveUsername(data.username);
        this.tokenStorage.saveAuthorities(data.authorities);
 
        this.roles = this.tokenStorage.getAuthorities();
        ...
        reloadPage();
      },
      error => {
        ...
      }
    );
  }
}

Angular 11 Manage Jwt Authentication Token & User Logout

We use TokenStorageService to manage Token inside Browser’s sessionStorage:

export class TokenStorageService {
  private roles: Array<string> = [];
 
  public saveToken(token: string) {
    window.sessionStorage.removeItem(TOKEN_KEY);
    window.sessionStorage.setItem(TOKEN_KEY, token);
  }
 
  public getToken(): string {
    return sessionStorage.getItem(TOKEN_KEY);
  }
 
  public saveUsername(username: string) {
    window.sessionStorage.removeItem(USERNAME_KEY);
    window.sessionStorage.setItem(USERNAME_KEY, username);
  }
 
  public getUsername(): string {
    return sessionStorage.getItem(USERNAME_KEY);
  }
 
  public saveAuthorities(authorities: string[]) {
    window.sessionStorage.removeItem(AUTHORITIES_KEY);
    window.sessionStorage.setItem(AUTHORITIES_KEY, JSON.stringify(authorities));
  }
 
  public getAuthorities(): string[] {
    this.roles = [];
 
    if (sessionStorage.getItem(TOKEN_KEY)) {
      JSON.parse(sessionStorage.getItem(AUTHORITIES_KEY)).forEach(authority => {
        this.roles.push(authority.authority);
      });
    }
 
    return this.roles;
  }
}

For Logout action, we only need to clear Browser’s sessionStorage:

export class TokenStorageService {
  ...
  signOut() {
    window.sessionStorage.clear();
  }
}

Angular 11 JWT Authentication Example Overview

Angular Jwt Authentication Goal

We will build Angular Client which allows users to register, login account. And depending on the role of current User (user, pm or admin), this system accepts what he can access:

Angular Spring Boot Security Jwt Authentication Work Process Diagram
Angular Spring Boot Security Jwt Authentication Work Process Diagram

Look back below session for more information: Overview Goal

Angular 11 Technologies

– Angular 11
– RxJS 6

Angular 11 Jwt Authentication Project Structure

Angular Project Structure
Angular Jwt Authentication Example Project Structure

Create Angular 11 Services & Components

Run commands below:
ng g s auth/auth
ng g s auth/token-storage
ng g s services/user

ng g c login
ng g c register
ng g c home
ng g c user
ng g c pm
ng g c admin

Angular 11 AppModule

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { HttpClientModule } from '@angular/common/http';
import { FormsModule } from '@angular/forms';
 
import { AppComponent } from './app.component';
import { LoginComponent } from './login/login.component';
import { UserComponent } from './user/user.component';
import { RegisterComponent } from './register/register.component';
import { HomeComponent } from './home/home.component';
import { AdminComponent } from './admin/admin.component';
import { PmComponent } from './pm/pm.component';
 
import { httpInterceptorProviders } from './auth/auth-interceptor';
 
@NgModule({
  declarations: [
    AppComponent,
    LoginComponent,
    UserComponent,
    RegisterComponent,
    HomeComponent,
    AdminComponent,
    PmComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    FormsModule,
    HttpClientModule
  ],
  providers: [httpInterceptorProviders],
  bootstrap: [AppComponent]
})
export class AppModule { }

Angular Jwt Authentication Services

Angular Jwt Authentication Models

auth/login-info.ts

export class AuthLoginInfo {
    username: string;
    password: string;
 
    constructor(username: string, password: string) {
        this.username = username;
        this.password = password;
    }
}

auth/sigup-info.ts

export class SignUpInfo {
    name: string;
    username: string;
    email: string;
    role: string[];
    password: string;
 
    constructor(name: string, username: string, email: string, password: string) {
        this.name = name;
        this.username = username;
        this.email = email;
        this.password = password;
        this.role = ['user'];
    }
}

auth/jwt-response.ts

export class JwtResponse {
    accessToken: string;
    type: string;
    username: string;
    authorities: string[];
}

Angular Jwt Authentication Service

auth/auth.service.ts

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
 
import { JwtResponse } from './jwt-response';
import { AuthLoginInfo } from './login-info';
import { SignUpInfo } from './signup-info';
 
const httpOptions = {
  headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
 
@Injectable({
  providedIn: 'root'
})
export class AuthService {
 
  private loginUrl = 'http://localhost:8080/api/auth/signin';
  private signupUrl = 'http://localhost:8080/api/auth/signup';
 
  constructor(private http: HttpClient) {
  }
 
  attemptAuth(credentials: AuthLoginInfo): Observable {
    return this.http.post(this.loginUrl, credentials, httpOptions);
  }
 
  signUp(info: SignUpInfo): Observable {
    return this.http.post(this.signupUrl, info, httpOptions);
  }
}

Angular 11 Token Authentication Storage Service

auth/token-storage.service.ts

import { Injectable } from '@angular/core';
 
const TOKEN_KEY = 'AuthToken';
const USERNAME_KEY = 'AuthUsername';
const AUTHORITIES_KEY = 'AuthAuthorities';
 
@Injectable({
  providedIn: 'root'
})
export class TokenStorageService {
  private roles: Array = [];
  constructor() { }
 
  signOut() {
    window.sessionStorage.clear();
  }
 
  public saveToken(token: string) {
    window.sessionStorage.removeItem(TOKEN_KEY);
    window.sessionStorage.setItem(TOKEN_KEY, token);
  }
 
  public getToken(): string {
    return sessionStorage.getItem(TOKEN_KEY);
  }
 
  public saveUsername(username: string) {
    window.sessionStorage.removeItem(USERNAME_KEY);
    window.sessionStorage.setItem(USERNAME_KEY, username);
  }
 
  public getUsername(): string {
    return sessionStorage.getItem(USERNAME_KEY);
  }
 
  public saveAuthorities(authorities: string[]) {
    window.sessionStorage.removeItem(AUTHORITIES_KEY);
    window.sessionStorage.setItem(AUTHORITIES_KEY, JSON.stringify(authorities));
  }
 
  public getAuthorities(): string[] {
    this.roles = [];
 
    if (sessionStorage.getItem(TOKEN_KEY)) {
      JSON.parse(sessionStorage.getItem(AUTHORITIES_KEY)).forEach(authority => {
        this.roles.push(authority.authority);
      });
    }
 
    return this.roles;
  }
}

Angular 11 Jwt Authentication User Service

services/user.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
 
@Injectable({
  providedIn: 'root'
})
export class UserService {
 
  private userUrl = 'http://localhost:8080/api/test/user';
  private pmUrl = 'http://localhost:8080/api/test/pm';
  private adminUrl = 'http://localhost:8080/api/test/admin';
 
  constructor(private http: HttpClient) { }
 
  getUserBoard(): Observable {
    return this.http.get(this.userUrl, { responseType: 'text' });
  }
 
  getPMBoard(): Observable {
    return this.http.get(this.pmUrl, { responseType: 'text' });
  }
 
  getAdminBoard(): Observable {
    return this.http.get(this.adminUrl, { responseType: 'text' });
  }
}

Angular 11 Jwt Authentication Http Interceptor

auth/auth-interceptor.ts

import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';
 
import { TokenStorageService } from './token-storage.service';
 
const TOKEN_HEADER_KEY = 'Authorization';
 
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
 
    constructor(private token: TokenStorageService) { }
 
    intercept(req: HttpRequest, next: HttpHandler) {
        let authReq = req;
        const token = this.token.getToken();
        if (token != null) {
            authReq = req.clone({ headers: req.headers.set(TOKEN_HEADER_KEY, 'Bearer ' + token) });
        }
        return next.handle(authReq);
    }
}
 
export const httpInterceptorProviders = [
    { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }
];

Angular Jwt Authentication Components

Angular Jwt Home Component

home.component.ts

import { Component, OnInit } from '@angular/core';
 
import { TokenStorageService } from '../auth/token-storage.service';
 
@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
  info: any;
 
  constructor(private token: TokenStorageService) { }
 
  ngOnInit() {
    this.info = {
      token: this.token.getToken(),
      username: this.token.getUsername(),
      authorities: this.token.getAuthorities()
    };
  }
 
  logout() {
    this.token.signOut();
    window.location.reload();
  }
}

home.component.html

<div *ngIf="info.token; else loggedOut">
  <h5 class="text-primary">Your Information</h5>
  <p>
    <strong>Username:</strong> {{info.username}}<br/>
    <strong>Roles:</strong> {{info.authorities}}<br />
    <strong>Token:</strong> {{info.token}}.
  </p>
  <button class="btn btn-secondary" (click)="logout()">Logout</button>
</div>
 
<ng-template #loggedOut>
  Please login.
</ng-template>
Login Component

login.component.ts

import { Component, OnInit } from '@angular/core';
 
import { AuthService } from '../auth/auth.service';
import { TokenStorageService } from '../auth/token-storage.service';
import { AuthLoginInfo } from '../auth/login-info';
 
@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
  form: any = {};
  isLoggedIn = false;
  isLoginFailed = false;
  errorMessage = '';
  roles: string[] = [];
  private loginInfo: AuthLoginInfo;
 
  constructor(private authService: AuthService, private tokenStorage: TokenStorageService) { }
 
  ngOnInit() {
    if (this.tokenStorage.getToken()) {
      this.isLoggedIn = true;
      this.roles = this.tokenStorage.getAuthorities();
    }
  }
 
  onSubmit() {
    console.log(this.form);
 
    this.loginInfo = new AuthLoginInfo(
      this.form.username,
      this.form.password);
 
    this.authService.attemptAuth(this.loginInfo).subscribe(
      data => {
        this.tokenStorage.saveToken(data.accessToken);
        this.tokenStorage.saveUsername(data.username);
        this.tokenStorage.saveAuthorities(data.authorities);
 
        this.isLoginFailed = false;
        this.isLoggedIn = true;
        this.roles = this.tokenStorage.getAuthorities();
        this.reloadPage();
      },
      error => {
        console.log(error);
        this.errorMessage = error.error.message;
        this.isLoginFailed = true;
      }
    );
  }
 
  reloadPage() {
    window.location.reload();
  }
}

login.component.html

<div *ngIf="isLoggedIn; else loggedOut">
  Logged in as {{roles}}.
</div>
 
<ng-template #loggedOut>
  <div class="row col-sm-6" style="max-width:350px;">
    <form name="form" (ngSubmit)="f.form.valid && onSubmit()" #f="ngForm" novalidate>
      <div class="form-group">
        <label for="username">Username</label>
        <input type="text" class="form-control" name="username" [(ngModel)]="form.username" #username="ngModel"
          required />
        <div *ngIf="f.submitted && username.invalid">
          <div *ngIf="username.errors.required">Username is required</div>
        </div>
      </div>
      <div class="form-group">
        <label for="password">Password</label>
        <input type="password" class="form-control" name="password" [(ngModel)]="form.password" #password="ngModel"
          required minlength="6" />
        <div *ngIf="f.submitted && password.invalid">
          <div *ngIf="password.errors.required">Password is required</div>
          <div *ngIf="password.errors.minlength">Password must be at least 6 characters</div>
        </div>
      </div>
      <div class="form-group">
        <button class="btn btn-primary">Login</button>
        <div *ngIf="f.submitted && isLoginFailed" class="alert alert-danger">
          Login failed: {{errorMessage}}
        </div>
      </div>
    </form>
    <hr />
    <p>Don't have an account?</p>
    <a href="signup" class="btn btn-success">Sign Up</a>
  </div>
</ng-template>

Angular 11 Jwt Authentication Login Component

register.component.ts

import { Component, OnInit } from '@angular/core';
 
import { AuthService } from '../auth/auth.service';
import { SignUpInfo } from '../auth/signup-info';
 
@Component({
  selector: 'app-register',
  templateUrl: './register.component.html',
  styleUrls: ['./register.component.css']
})
export class RegisterComponent implements OnInit {
  form: any = {};
  signupInfo: SignUpInfo;
  isSignedUp = false;
  isSignUpFailed = false;
  errorMessage = '';
 
  constructor(private authService: AuthService) { }
 
  ngOnInit() { }
 
  onSubmit() {
    console.log(this.form);
 
    this.signupInfo = new SignUpInfo(
      this.form.name,
      this.form.username,
      this.form.email,
      this.form.password);
 
    this.authService.signUp(this.signupInfo).subscribe(
      data => {
        console.log(data);
        this.isSignedUp = true;
        this.isSignUpFailed = false;
      },
      error => {
        console.log(error);
        this.errorMessage = error.error.message;
        this.isSignUpFailed = true;
      }
    );
  }
}

register.component.html

<div *ngIf="isSignedUp; else signupForm">
  Your registration is successful. Please login!
</div>
 
<ng-template #signupForm>
  <div class="row col-sm-6" style="max-width:350px;">
    <form name="form" (ngSubmit)="f.form.valid && onSubmit()" #f="ngForm" novalidate>
      <div class="form-group">
        <label for="name">Your name</label>
        <input type="text" class="form-control" name="name" [(ngModel)]="form.name" #name="ngModel" required />
        <div *ngIf="f.submitted && name.invalid">
          <div *ngIf="name.errors.required">Name is required</div>
        </div>
      </div>
      <div class="form-group">
        <label for="username">Username</label>
        <input type="text" class="form-control" name="username" [(ngModel)]="form.username" #username="ngModel"
          required />
        <div *ngIf="f.submitted && username.invalid">
          <div *ngIf="username.errors.required">Username is required</div>
        </div>
      </div>
      <div class="form-group">
        <label for="email">Email</label>
        <input type="text" class="form-control" name="email" [(ngModel)]="form.email" #email="ngModel" required email />
        <div *ngIf="f.submitted && email.invalid">
          <div *ngIf="email.errors.required">Email is required</div>
          <div *ngIf="email.errors.email">Email must be a valid email address</div>
        </div>
      </div>
      <div class="form-group">
        <label for="password">Password</label>
        <input type="password" class="form-control" name="password" [(ngModel)]="form.password" #password="ngModel"
          required minlength="6" />
        <div *ngIf="f.submitted && password.invalid">
          <div *ngIf="password.errors.required">Password is required</div>
          <div *ngIf="password.errors.minlength">Password must be at least 6 characters</div>
        </div>
      </div>
      <div class="form-group">
        <button class="btn btn-primary">Register</button>
        <div *ngIf="f.submitted && isSignUpFailed" class="alert alert-warning">
          Signup failed!<br/>{{errorMessage}}
        </div>
      </div>
    </form>
  </div>
</ng-template>

Angular 11 jwt Authentication Register Component

register.component.ts

import { Component, OnInit } from '@angular/core';
 
import { AuthService } from '../auth/auth.service';
import { SignUpInfo } from '../auth/signup-info';
 
@Component({
  selector: 'app-register',
  templateUrl: './register.component.html',
  styleUrls: ['./register.component.css']
})
export class RegisterComponent implements OnInit {
  form: any = {};
  signupInfo: SignUpInfo;
  isSignedUp = false;
  isSignUpFailed = false;
  errorMessage = '';
 
  constructor(private authService: AuthService) { }
 
  ngOnInit() { }
 
  onSubmit() {
    console.log(this.form);
 
    this.signupInfo = new SignUpInfo(
      this.form.name,
      this.form.username,
      this.form.email,
      this.form.password);
 
    this.authService.signUp(this.signupInfo).subscribe(
      data => {
        console.log(data);
        this.isSignedUp = true;
        this.isSignUpFailed = false;
      },
      error => {
        console.log(error);
        this.errorMessage = error.error.message;
        this.isSignUpFailed = true;
      }
    );
  }
}

register.component.html

<div *ngIf="isSignedUp; else signupForm">
  Your registration is successful. Please login!
</div>
 
<ng-template #signupForm>
  <div class="row col-sm-6" style="max-width:350px;">
    <form name="form" (ngSubmit)="f.form.valid && onSubmit()" #f="ngForm" novalidate>
      <div class="form-group">
        <label for="name">Your name</label>
        <input type="text" class="form-control" name="name" [(ngModel)]="form.name" #name="ngModel" required />
        <div *ngIf="f.submitted && name.invalid">
          <div *ngIf="name.errors.required">Name is required</div>
        </div>
      </div>
      <div class="form-group">
        <label for="username">Username</label>
        <input type="text" class="form-control" name="username" [(ngModel)]="form.username" #username="ngModel"
          required />
        <div *ngIf="f.submitted && username.invalid">
          <div *ngIf="username.errors.required">Username is required</div>
        </div>
      </div>
      <div class="form-group">
        <label for="email">Email</label>
        <input type="text" class="form-control" name="email" [(ngModel)]="form.email" #email="ngModel" required email />
        <div *ngIf="f.submitted && email.invalid">
          <div *ngIf="email.errors.required">Email is required</div>
          <div *ngIf="email.errors.email">Email must be a valid email address</div>
        </div>
      </div>
      <div class="form-group">
        <label for="password">Password</label>
        <input type="password" class="form-control" name="password" [(ngModel)]="form.password" #password="ngModel"
          required minlength="6" />
        <div *ngIf="f.submitted && password.invalid">
          <div *ngIf="password.errors.required">Password is required</div>
          <div *ngIf="password.errors.minlength">Password must be at least 6 characters</div>
        </div>
      </div>
      <div class="form-group">
        <button class="btn btn-primary">Register</button>
        <div *ngIf="f.submitted && isSignUpFailed" class="alert alert-warning">
          Signup failed!<br/>{{errorMessage}}
        </div>
      </div>
    </form>
  </div>
</ng-template>

Angular Jwt [UserRole] Components

user/pm/admin.component.html

<h4>Content from Server</h4>
{{board}}
{{errorMessage}}

user.component.ts

import { Component, OnInit } from '@angular/core';
import { UserService } from '../services/user.service';
 
@Component({
  selector: 'app-user',
  templateUrl: './user.component.html',
  styleUrls: ['./user.component.css']
})
export class UserComponent implements OnInit {
  board: string;
  errorMessage: string;
 
  constructor(private userService: UserService) { }
 
  ngOnInit() {
    this.userService.getUserBoard().subscribe(
      data => {
        this.board = data;
      },
      error => {
        this.errorMessage = `${error.status}: ${JSON.parse(error.error).message}`;
      }
    );
  }
}

pm.component.ts

import { Component, OnInit } from '@angular/core';
import { UserService } from '../services/user.service';
 
@Component({
  selector: 'app-pm',
  templateUrl: './pm.component.html',
  styleUrls: ['./pm.component.css']
})
export class PmComponent implements OnInit {
  board: string;
  errorMessage: string;
 
  constructor(private userService: UserService) { }
 
  ngOnInit() {
    this.userService.getPMBoard().subscribe(
      data => {
        this.board = data;
      },
      error => {
        this.errorMessage = `${error.status}: ${JSON.parse(error.error).message}`;
      }
    );
  }
}

admin.component.ts

import { Component, OnInit } from '@angular/core';
import { UserService } from '../services/user.service';
 
@Component({
  selector: 'app-admin',
  templateUrl: './admin.component.html',
  styleUrls: ['./admin.component.css']
})
export class AdminComponent implements OnInit {
  board: string;
  errorMessage: string;
 
  constructor(private userService: UserService) { }
 
  ngOnInit() {
    this.userService.getAdminBoard().subscribe(
      data => {
        this.board = data;
      },
      error => {
        this.errorMessage = `${error.status}: ${JSON.parse(error.error).message}`;
      }
    );
  }
}

Angular 11 Jwt Authentication App Routing

app-routing.module.ts

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
 
import { RegisterComponent } from './register/register.component';
import { LoginComponent } from './login/login.component';
import { HomeComponent } from './home/home.component';
import { UserComponent } from './user/user.component';
import { PmComponent } from './pm/pm.component';
import { AdminComponent } from './admin/admin.component';
 
const routes: Routes = [
    {
        path: 'home',
        component: HomeComponent
    },
    {
        path: 'user',
        component: UserComponent
    },
    {
        path: 'pm',
        component: PmComponent
    },
    {
        path: 'admin',
        component: AdminComponent
    },
    {
        path: 'auth/login',
        component: LoginComponent
    },
    {
        path: 'signup',
        component: RegisterComponent
    },
    {
        path: '',
        redirectTo: 'home',
        pathMatch: 'full'
    }
];
 
@NgModule({
    imports: [RouterModule.forRoot(routes)],
    exports: [RouterModule]
})
export class AppRoutingModule { }

app.component.ts

import { Component, OnInit } from '@angular/core';
import { TokenStorageService } from './auth/token-storage.service';
 
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  private roles: string[];
  private authority: string;
 
  constructor(private tokenStorage: TokenStorageService) { }
 
  ngOnInit() {
    if (this.tokenStorage.getToken()) {
      this.roles = this.tokenStorage.getAuthorities();
      this.roles.every(role => {
        if (role === 'ROLE_ADMIN') {
          this.authority = 'admin';
          return false;
        } else if (role === 'ROLE_PM') {
          this.authority = 'pm';
          return false;
        }
        this.authority = 'user';
        return true;
      });
    }
  }
}

app.component.html

<nav class="navbar navbar-inverse">
  <div class="container-fluid">
    <div class="navbar-header">
      <a class="navbar-brand" href="#">grokonez</a>
    </div>
    <ul class="nav navbar-nav" routerLinkActive="active">
      <li class="nav-item"><a class="nav-link" routerLink="home">Home</a></li>
      <li *ngIf="authority === 'user'" class="nav-item">
        <a class="nav-link" routerLink="user">User Board</a>
      </li>
      <li *ngIf="authority === 'admin'" class="nav-item">
        <a class="nav-link" routerLink="admin">Admin Board</a>
      </li>
      <li *ngIf="authority === 'pm'" class="nav-item">
        <a class="nav-link" routerLink="pm">PM Board</a>
      </li>
      <li *ngIf="!authority" class="nav-item">
        <a class="nav-link" routerLink="auth/login">Login</a>
      </li>
    </ul>
  </div>
</nav>
<div class="container">
  <router-outlet></router-outlet>
</div>

Further Reading

Related posts:


Sourcecode – Angular 11 SpringBoot Jwt Authentication Example

I includes 2 sourcecodes for the tutorial: ‘Angular 11 SpringBoot JWt Authentication Example’

  • SpringBoot Backend
  • Angular Frontend

1. SpringBoot Sourcecode:

Nodejs JWT Authentication Example

– Github Sourcecode:

SpringBoot Angular 11 Jwt Authentication Examples – Backend RestAPIs GitHub

2. Angular 11 sourcecode:

Angular 11 SpringBoot Jwt Authentication Example – Frontend

– GitHub Sourcecode:

Angular 11 SpringBoot Jwt Authentication Example – Frontend GitHub

Thanks for reading!

Leave a Reply

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