Mastering JWT: Secure Token Authentication with Spring Boot and Angular

This article explains what JSON Web Tokens are, their structure and security considerations, introduces the JJWT Java library, and provides a complete Spring Boot and Angular example—including Maven setup, Java filters, controllers, and front‑end code—to demonstrate secure token‑based authentication.

MaGe Linux Operations
MaGe Linux Operations
MaGe Linux Operations
Mastering JWT: Secure Token Authentication with Spring Boot and Angular

What is JWT?

JWTs are a compact, URL‑safe representation of JSON objects that can be signed and optionally encrypted, allowing trustworthy transmission of claims such as user identity.

JWT Components

Header – contains metadata about the token, including the signing or encryption algorithm.

Claims – the payload with any information you wish to convey.

JSON Web Signature (JWS) – the digital signature generated using the algorithm specified in the header.

Example token creation:

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

Claims:
{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

Signature:
base64UrlEncode(Header) + "." + base64UrlEncode(Claims)

Encrypted token example:

How to Ensure JWT Security

Always verify the signature before trusting any data in a JWT. Reject tokens that use the "none" algorithm or have an invalid signature.

Keep the secret signing key confidential and never expose it outside the issuer and consumer.

Do not place sensitive information inside JWTs, as they are only base64‑encoded and can be decoded easily.

Mitigate replay attacks by including a nonce (jti claim), expiration time (exp claim), and issued‑at time (iat claim) as defined in the JWT specification.

JWT Framework: JJWT

JJWT is a free, open‑source (Apache 2.0) Java library that simplifies creating and verifying JWTs on the JVM with a fluent API that hides most of the complexity.

Provides an easy‑to‑use interface for JWT creation and validation.

Implements the JWT, JWS, JWE, JWK, and JWA RFC specifications.

Offers non‑standard conveniences such as JWT compression and mandatory claims.

Supported features include creating and parsing plain‑text compressed JWTs and handling all standard JWS algorithms:

HS256, HS384, HS512

RS256, RS384, RS512

PS256, PS384, PS512

ES256, ES384, ES512

Below is a GitHub demo that shows how to integrate JJWT into a Spring Boot + Angular project.

Application architecture (Spring Boot + Angular + JWT):

Maven Dependency

<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.nibado.example</groupId>
    <artifactId>jwt-angular-spring</artifactId>
    <version>0.0.2-SNAPSHOT</version>
    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <commons.io.version>2.4</commons.io.version>
        <jjwt.version>0.6.0</jjwt.version>
        <junit.version>4.12</junit.version>
        <spring.boot.version>1.5.3.RELEASE</spring.boot.version>
    </properties>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring.boot.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>${spring.boot.version}</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>${commons.io.version}</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>${jjwt.version}</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
        </dependency>
    </dependencies>
</project>

WebApplication.java

package com.nibado.example.jwtangspr;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@EnableAutoConfiguration
@ComponentScan
@Configuration
public class WebApplication {

    // Filter registration
    @Bean
    public FilterRegistrationBean jwtFilter() {
        final FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(new JwtFilter());
        registrationBean.addUrlPatterns("/api/*");
        return registrationBean;
    }

    public static void main(final String[] args) throws Exception {
        SpringApplication.run(WebApplication.class, args);
    }

}

JwtFilter.java

package com.nibado.example.jwtangspr;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.springframework.web.filter.GenericFilterBean;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureException;

public class JwtFilter extends GenericFilterBean {

    @Override
    public void doFilter(final ServletRequest req,
                         final ServletResponse res,
                         final FilterChain chain) throws IOException, ServletException {
        final HttpServletRequest request = (HttpServletRequest) req;

        // Token is expected in the Authorization header as "Bearer <token>"
        final String authHeader = request.getHeader("Authorization");
        if (authHeader == null || !authHeader.startsWith("Bearer ")) {
            throw new ServletException("Missing or invalid Authorization header.");
        }

        final String token = authHeader.substring(7);

        try {
            // Parse token and obtain claims
            final Claims claims = Jwts.parser().setSigningKey("secretkey")
                .parseClaimsJws(token).getBody();
            // Attach claims to request for downstream handlers
            request.setAttribute("claims", claims);
        } catch (final SignatureException e) {
            throw new ServletException("Invalid token.");
        }

        chain.doFilter(req, res);
    }

}

UserController.java

package com.nibado.example.jwtangspr;

import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;

import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

@RestController
@RequestMapping("/user")
public class UserController {

    // Simulated user database
    private final Map<String, List<String>> userDb = new HashMap<>();

    @SuppressWarnings("unused")
    private static class UserLogin {
        public String name;
        public String password;
    }

    public UserController() {
        userDb.put("tom", Arrays.asList("user"));
        userDb.put("wen", Arrays.asList("user", "admin"));
    }

    @RequestMapping(value = "login", method = RequestMethod.POST)
    public LoginResponse login(@RequestBody final UserLogin login)
            throws ServletException {
        if (login.name == null || !userDb.containsKey(login.name)) {
            throw new ServletException("Invalid login");
        }
        // Generate JWT token
        return new LoginResponse(Jwts.builder().setSubject(login.name)
            .claim("roles", userDb.get(login.name)).setIssuedAt(new Date())
            .signWith(SignatureAlgorithm.HS256, "secretkey").compact());
    }

    @SuppressWarnings("unused")
    private static class LoginResponse {
        public String token;
        public LoginResponse(final String token) {
            this.token = token;
        }
    }
}

ApiController.java

package com.nibado.example.jwtangspr;

import io.jsonwebtoken.Claims;

import java.util.List;

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

import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
public class ApiController {
    @SuppressWarnings("unchecked")
    @RequestMapping(value = "role/{role}", method = RequestMethod.GET)
    public Boolean login(@PathVariable final String role,
                        final HttpServletRequest request) throws ServletException {
        final Claims claims = (Claims) request.getAttribute("claims");
        return ((List<String>) claims.get("roles")).contains(role);
    }
}

index.html

<!doctype html>
<html ng-app="myApp">
<head>
    <meta charset="utf-8"/>
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
    <title>JSON Web Token / AngularJS / Spring Boot example</title>
    <meta name="description" content="">
    <meta name="viewport" content="width=device-width">
    <link rel="stylesheet" href="libs/bootstrap/css/bootstrap.css">
    <script src="libs/jquery/jquery.js"></script>
    <script src="libs/bootstrap/js/bootstrap.js"></script>
    <script src="libs/angular/angular.js"></script>
    <script src="app.js"></script>
</head>
<body>
<div class="container" ng-controller='MainCtrl'>
  <h1>{{greeting}}</h1>
  <div ng-show="!loggedIn()">
    Please log in (tom and sally are valid names)<br/>
    <form ng-submit="login()">
      Username: <input type="text" ng-model="userName"/>
      <input type="submit" value="Login"/>
    </form>
  </div>
  <div class="alert alert-danger" role="alert" ng-show="error.data.message">
    {{error.data.message}}
  </div>
  <div ng-show="loggedIn()">
    <div class="row">
      <div class="col-md-6">
        <h3><span class="label label-success">Success!</span> Welcome {{userName}}</h3>
        <a href ng-click="logout()">(logout)</a>
      </div>
      <div class="col-md-4">
        <div class="row header">
          <div class="col-sm-4">{{userName}} is a</div>
        </div>
        <div class="row">
          <div class="col-sm-2">User</div>
          <div class="col-sm-2"><span class="glyphicon glyphicon-ok" ng-show="roleUser"></span></div>
        </div>
        <div class="row">
          <div class="col-sm-2">Admin</div>
          <div class="col-sm-2"><span class="glyphicon glyphicon-ok" ng-show="roleAdmin"></span></div>
        </div>
        <div class="row">
          <div class="col-sm-2">Foo</div>
          <div class="col-sm-2"><span class="glyphicon glyphicon-ok" ng-show="roleFoo"></span></div>
        </div>
      </div>
    </div>
  </div>
</div>
</body>
</html>

app.js

var appModule = angular.module('myApp', []);

appModule.controller('MainCtrl', ['mainService','$scope','$http',
        function(mainService, $scope, $http) {
            $scope.greeting = 'Welcome to the JSON Web Token / AngularJR / Spring example!';
            $scope.token = null;
            $scope.error = null;
            $scope.roleUser = false;
            $scope.roleAdmin = false;
            $scope.roleFoo = false;

            $scope.login = function() {
                $scope.error = null;
                mainService.login($scope.userName).then(function(token) {
                    $scope.token = token;
                    $http.defaults.headers.common.Authorization = 'Bearer ' + token;
                    $scope.checkRoles();
                }, function(error){
                    $scope.error = error;
                    $scope.userName = '';
                });
            };

            $scope.checkRoles = function() {
                mainService.hasRole('user').then(function(user) {$scope.roleUser = user});
                mainService.hasRole('admin').then(function(admin) {$scope.roleAdmin = admin});
                mainService.hasRole('foo').then(function(foo) {$scope.roleFoo = foo});
            };

            $scope.logout = function() {
                $scope.userName = '';
                $scope.token = null;
                $http.defaults.headers.common.Authorization = '';
            };

            $scope.loggedIn = function() {
                return $scope.token !== null;
            };
        }]);

appModule.service('mainService', function($http) {
    return {
        login : function(username) {
            return $http.post('/user/login', {name: username}).then(function(response) {
                return response.data.token;
            });
        },
        hasRole : function(role) {
            return $http.get('/api/role/' + role).then(function(response){
                return response.data;
            });
        }
    };
});

Run the Application

Result

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Spring BootSecurityAuthenticationJWTAngular
MaGe Linux Operations
Written by

MaGe Linux Operations

Founded in 2009, MaGe Education is a top Chinese high‑end IT training brand. Its graduates earn 12K+ RMB salaries, and the school has trained tens of thousands of students. It offers high‑pay courses in Linux cloud operations, Python full‑stack, automation, data analysis, AI, and Go high‑concurrency architecture. Thanks to quality courses and a solid reputation, it has talent partnerships with numerous internet firms.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.