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.
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
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
