The tutorial is Part 1 of the series: Angular Spring Boot JWT Authentication example | Angular 6 + Spring Security + MySQL Full Stack. In this part, we show you Overview and Architecture of the System (from Angular frontend to SpringBoot backend). You will see the combination of big components and what you need to do for the security part (authentication & authorization) of full-stack web development.
– Part 2: Build Spring Boot Backend
– Part 3: Build Angular Frontend
JSON Web Token
JSON Web Token (JWT) defines a compact and self-contained way for securely transmitting information between parties as a JSON object.
Why using JWT?
JSON Web Token (JWT) is a popular way to secure APIs and other web-based services by providing a way to authenticate requests and transmit information securely. Some reasons why JWT might be used include:
Stateless authentication: JWT allows you to authenticate users without maintaining session state on the server. This can be useful for scalability and for building distributed systems, as it allows you to decouple authentication from the rest of your application.
Compact and self-contained: JWT is a compact, self-contained format that can be easily transmitted over the network. This makes it convenient for transmitting user information and other data between services or between the client and server.
Easy to implement: JWT is relatively easy to implement, as it only requires a shared secret or public/private key pair to sign and verify tokens. This makes it a good choice for APIs and other web-based services where simplicity and ease of implementation are important.
Wide adoption: JWT has gained wide adoption and is supported by many popular libraries and frameworks, making it easy to integrate into your application.
Overall, JWT is a useful tool for securing APIs and other web-based services by providing a way to authenticate requests and transmit information securely. It is particularly well-suited for stateless authentication and for building distributed systems.
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
Angular Spring Boot JWT Authentication example
Goal
We will build an application, from frontend (Angular) 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:
The diagram below show how our system handles User Registration and User Login processes:
Demo
Full Stack Architecture
Spring Boot back-end with Spring Security
This is diagram for Spring Security/JWT classes that are separated into 3 layers:
– HTTP
– Spring Security
– REST API
– 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.
For more details, please visit: Spring Security – JWT Authentication Architecture | Spring Boot
Angular front-end with Interceptor
– 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
.
For more details about:
- Interceptor: Angular 6 Http Interceptor – with Node.js RestAPIs
- Routing: Angular 6 Routing/Navigation – with Angular Router Service
- Form Validation: Angular 6 Form Validation example – Template-driven Forms
- Angular 6 HttpClient example: Angular 6 HttpClient – Get/Post/Put/Delete requests + SpringBoot RestAPIs + Bootstrap 4
Where should we store jwt with Angular Application
There are several options for storing JSON Web Tokens (JWTs) in an Angular application. Here are a few common approaches:
In a cookie: One option is to store the JWT in an HTTP cookie, which is a small piece of data that is sent from the server to the client and stored on the client’s device. This allows the JWT to be sent with each request to the server, so the server can authenticate the user. You can use the ngx-cookie-service library to manage cookies in your Angular application.
In local storage: Another option is to store the JWT in the browser’s local storage, which is a client-side storage mechanism that allows you to store data on the client’s device. This allows the JWT to persist even after the user closes the browser or navigates away from the application. You can use the localStorage or sessionStorage objects to store data in local storage in your Angular application.
In a custom store: If you don’t want to use cookies or local storage, you can also create your own custom store for storing the JWT. For example, you could create a service that uses the InMemoryDbService from the @angular/in-memory-web-api library to store the JWT in memory.
Ultimately, the choice of where to store the JWT will depend on your specific needs and requirements. You should consider factors such as the security of the storage mechanism, the duration for which the JWT needs to be stored, and the performance implications of storing the JWT.
How to encode and decode JWT with SpringBoot
Here is an example of how you can encode and decode JSON Web Tokens (JWTs) in a Spring Boot application:
First, you will need to add the jjwt library to your project’s dependencies. You can do this by adding the following dependency to your build.gradle file:
compile group: 'io.jsonwebtoken', name: 'jjwt', version: '0.9.1'
To encode a JWT, you can use the Jwts.builder() method to create a JWT builder, and then set the claims and other metadata for the JWT using the builder’s methods. Once you have configured the builder, you can use the signWith() method to sign the JWT using a secret or private key, and then call the compact() method to serialize the JWT as a compact, URL-safe string:
import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; // ... String jwt = Jwts.builder() .setSubject("user") .setExpiration(new Date(System.currentTimeMillis() + 86400000)) // set expiration to 1 day .claim("roles", "user") .signWith(SignatureAlgorithm.HS256, "secret") .compact();
To decode a JSON Web Token (JWT) in a Spring Boot application, you can use the Jwts.parser() method to create a JWT parser and then pass the JWT and the secret or public key used to sign it to the parseClaimsJws() method. This will return a Jws object that you can use to access the claims and other metadata contained in the JWT:
import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.security.Keys; // ... String jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyIiwiZXhwIjoxNTc4NzEwNDM1LCJyb2xlcyI6WyJ1c2VyIl19.lD8W8W2hKJR3iOzif5v5C-_5EYdzgU0jXUZv6UZpWgM"; Jwsclaims = Jwts.parser() .setSigningKey(Keys.hmacShaKeyFor("secret".getBytes())) .parseClaimsJws(jwt); String subject = claims.getBody().getSubject(); Date expiration = claims.getBody().getExpiration(); List roles = claims.getBody().get("roles", List.class);
You can then use the subject, expiration, and roles variables to access the claims contained in the JWT.
It is important to note that you will need to use the same secret or public key that was used to sign the JWT in