Parse jwt token java

Декодировать токен JWT в Java

Веб -токен JSON (JWT) часто используется в безопасности REST API. Несмотря на то, что токен может быть проанализирован такими фреймворками, как Spring Security OAuth , мы можем захотеть обработать токен в нашем собственном коде.

2. Структура JWT​

Во-первых, давайте разберемся со структурой JWT :

Подпись необязательна. Действительный JWT может состоять только из разделов заголовка и полезной нагрузки. Однако мы используем раздел подписи для проверки содержимого заголовка и полезной нагрузки для авторизации безопасности .

Разделы представлены в виде строк в кодировке base64url, разделенных точкой (‘.’) в качестве разделителя. По замыслу любой может декодировать JWT и читать содержимое разделов заголовка и полезной нагрузки. Но нам нужен доступ к секретному ключу, используемому для создания подписи, чтобы проверить целостность токена.

Чаще всего JWT содержит «заявки» пользователя. Они представляют данные о пользователе, которые API может использовать для предоставления разрешений или отслеживания пользователя, предоставившего токен. Декодирование маркера позволяет приложению использовать данные, а проверка позволяет приложению доверять тому, что JWT был создан надежным источником.

Давайте посмотрим, как мы можем декодировать и проверять токен в Java.

3. Расшифровка JWT​

Мы можем декодировать токен, используя встроенные функции Java.

Читайте также:  List not contains java

Во-первых, давайте разделим токен на его разделы:

 String[] chunks = token.split("\\."); 

Следует отметить, что регулярное выражение, передаваемое в String.split , использует экранированный символ «.». символ, чтобы избежать ‘.’ означает «любой символ».

Наш массив кусков теперь должен иметь два или три элемента, соответствующих разделам JWT.

Затем давайте декодируем части заголовка и полезной нагрузки, используя декодер base64url:

 Base64.Decoder decoder = Base64.getUrlDecoder();    String header = new String(decoder.decode(chunks[0]));   String payload = new String(decoder.decode(chunks[1])); 

Давайте запустим этот код с помощью JWT (мы можем декодировать онлайн , чтобы сравнить результаты):

 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkJhZWxkdW5nIFVzZXIiLCJpYXQiOjE1MTYyMzkwMjJ9.qH7Zj_m3kY69kxhaQXTa-ivIpytKXXjZc1ZSmapZnGE 

На выходе мы получим декодированный заголовок любой полезной нагрузки:

 "alg":"HS256","typ":"JWT">"sub":"1234567890","name":"ForEach User","iat":1516239022> 

Если в JWT определены только разделы заголовка и полезной нагрузки, мы закончили и успешно декодировали информацию.

4. Проверка JWT​

Затем мы можем проверить целостность заголовка и полезной нагрузки, чтобы убедиться, что они не были изменены с помощью раздела подписи.

4.1. Зависимости​

Для проверки мы можем добавить jjwt в наш pom.xml :

 dependency>   groupId>io.jsonwebtoken/groupId>   artifactId>jjwt/artifactId>   version>0.7.0/version>   /dependency> 

Следует отметить, что нам нужна версия этой библиотеки, начиная с версии 0.7.0 .

4.2. Настройка алгоритма подписи и спецификации ключа​

Чтобы начать проверку полезной нагрузки и заголовка, нам нужен как алгоритм подписи, который изначально использовался для подписи токена, так и секретный ключ:

 SignatureAlgorithm sa = HS256;   SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(), sa.getJcaName()); 

В этом примере мы жестко запрограммировали наш алгоритм подписи на HS256 . Однако мы могли бы расшифровать JSON заголовка и прочитать поле alg , чтобы получить это значение.

Следует также отметить, что переменная secretKey является строковым представлением секретного ключа. Мы можем предоставить это нашему приложению через его конфигурацию или через REST API, предоставляемый службой, которая выдает JWT.

4.3. Выполнение проверки​

Теперь, когда у нас есть алгоритм подписи и секретный ключ, мы можем приступить к проверке.

Давайте рекомбинируем заголовок и полезную нагрузку в неподписанный JWT, соединив их с помощью ‘.’ разделитель:

 String tokenWithoutSignature = chunks[0] + "." + chunks[1];   String signature = chunks[2]; 

Теперь у нас есть неподписанный токен и предоставленная подпись. Мы можем использовать библиотеку для проверки:

 DefaultJwtSignatureValidator validator = new DefaultJwtSignatureValidator(sa, secretKeySpec);    if (!validator.isValid(tokenWithoutSignature, signature))    throw new Exception("Could not verify JWT token integrity!");   > 

Сначала мы создаем валидатор с выбранным алгоритмом и секретом. Затем мы предоставляем ему неподписанные данные токена и предоставленную подпись.

Затем валидатор генерирует новую подпись и сравнивает ее с предоставленной подписью. Если они равны, мы проверили целостность заголовка и полезной нагрузки.

5. Вывод​

В этой статье мы рассмотрели структуру JWT и способы ее декодирования в JSON.

Затем мы использовали библиотеку для проверки целостности токена, используя его подпись, алгоритм и секретный ключ.

Как всегда, примеры кода из этой статьи можно найти на GitHub .

Источник

Working with JWT (Json Web Tokens)

In this blog we will understand what a JWT is, what it can contain, where it is used and how can you create and parse a JWT in Java.

Table of Contents

1.0 What is JWT

Before we look into how to create and parse JWT in Java, lets first understand a little bit more about JWT in this section.

JWT (pronounced as jots) is short of Json Web Tokens.

As per jwt.io, JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object.

The first question that comes to mind is what is so special about JWT? How is it different from any other token that can be for authentication?

The primary difference lies in following

  • The payload contains JSON data that contains various claims, which are nothing but key/value pairs that carry information about the subject
  • It is digital signed and can be verified. The tokens are signed either using a private secret or a public/private key.

A very common scenario where JWT is used is for authentication/auth, and the workflow would look like this

2.0 JWT Structure

Any JWT might look like this sample given below.

eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ZWY2ZGU5MC1hMzljLTRhMjgtOTBmYy0xY2MyZmViZjFhMzciLCJzdWIiOiJzdW5pdEBnbWFpbC5jb20iLCJpc3MiOiJEZW1vIEFwcCIsImlhdCI6MTU5NzYwNDYwNywiZXhwIjoxMDIzNzUxODIwNywiZmlyc3ROYW1lIjoiU3VuaXQiLCJsYXN0TmFtZSI6IkNoYXR0ZXJqZWUiLCJpc0FkbWluIjp0cnVlfQ.VvvfRsZ75erZ6-k1-BeAI3IS6yXCM_3p5jSsxPCFkfI

If you look closely it has three parts separated by dot ( . ) in format of xxxx . yyyy . zzzz

Let’s look at below image on how a given sample JWT looks like when broken into its parts.

We will look at each part one be one in below sections.

2.1 JWT Header & Signing Algorithms

Header of an JWT contains two information

  • Type of token. e.g JWT
  • Signing algorithm used. The most common signing alogrithm is HMAC + SHA256 which is also known as HS256 .

Typically the header json would look like this.

The most common signing algorithm HMAC + SHA , which uses a secret key for signing the JWT.

Algorithm Detail Minimum Size of Secret Key
HS256 HMAC + SHA256 256 bits = 32 bytes
HS384 HMAC + SHA384 384 bits = 48 bytes
HS512 HMAC + SHA512 512 bits = 64 bytes

However you can also use asymmetric key based algorithms like RSA or Elliptic Curve .

2.2 JWT Claims

Claims are key/value pairs that contains information about the subject.
A claim payload JSON might look like this

There are two types of claims

  • Reserved claims or standard claims
  • Custom claims (which can be any keys/value pair the server wants to send to client)

In above example jti , sub , iss , iat , exp are reserved claims and firstName , lastName , isAdmin are custom claims.

Some of the reserved claims are

Claim Claim Information Claim Description
iss Issuer Who issued this JWT
sub Subject Typically the user
aud Audience Who the JWT recipient
iat Issued At When JWT was created/issued
exp Expiration Time Time after which JWT expires
nbf Not Before Time Time before which JWT cannot be used
jti JWT ID Unique identified that prevents a JWT to be used again.

Could you think of a typical scenario where you may need all three claim – iat , exp and nbf ?
A simple use case can be that we are giving a 4 hour access of an API to a client at 12pm next day. In that case

  • iat – would be set to current time
  • nbf – would beset to 12pm next day
  • exp – would be set to 4 pm next day

2.3 JWT Signature

The signature is primarily used to verify that the JWT was not tampered in any way.

Typically Signature is created using encoded Header and encoded Payload and a Secret Key.

HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

3.0 Creating & Parsing JWT

We have spent lot of time at theory. Now lets get our hands dirtly and write code to generate and parse a JWT

3.1 Adding library dependencies

If you are using Gradle, the add following dependencies

compile 'io.jsonwebtoken:jjwt-api:0.11.2' runtime 'io.jsonwebtoken:jjwt-impl:0.11.2' runtime 'io.jsonwebtoken:jjwt-jackson:0.11.2'

If you intend to use RSASSA-PSS (PS256, PS384, PS512) algorithms, then add following dependency

runtime 'org.bouncycastle:bcprov-jdk15on:1.60'

If you intend to use GSON instead of Jackson for Json Parsing then add following dependency

runtime 'io.jsonwebtoken:jjwt-gson:0.11.2'

3.2 Creating JWT

We will use HS256 Algorithm to create our JWT and it only requires a same SecretKey to be used both for creating and parsing the JWT.

First we need to generate a HMAC-SHA key for signing the JWT.
For this we will need a 256 bit Secret ( String ) and then generate the SecretKey

String secret = "9aaa4aba-6c85-4524-a72a-1021caf2e025"; byte[] secretInBytes = secret.getBytes("UTF-8"); SecretKey key = Keys.hmacShaKeyFor(secretInBytes);

The above method automatically chooses the right algorithm HS256 / HS384 / HS512 based on the length of secret byte array.

In case you want to generate a random Secret Key based on HMAC algorithm, you can use below code for it

SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS256);

If you provided a secret that is smaller than required size of the SignatureAlgorithm , then you will get a WeakKeyException .

Now that secret key is created lets create a JWT and add some claims to it.

We will do this using Jwts.builder() to create the JWT and use its setter methods to set the claims.

//Generate secret key as shown above SecretKey secretKey = Keys.hmacShaKeyFor(. ); Date currentTime = Date.from(Instant.now()); Date expiryTime = Date.from(Instant.now().plus(Duration.ofSeconds(60))); String jwt = Jwts.builder() .setId(UUID.randomUUID().toString()) //random jti .setSubject("sunit@gmail.com") .setIssuer("Demo App") .setIssuedAt(currentTime) .setExpiration(expiryTime) .claim("firstName", "Sunit") .claim("lastName", "Chatterjee") .claim("isAdmin", true) .signWith(secretKey) .compact();

Above code will generate a JWT with below payload.

3.3 Parsing JWT and Extracting claims

We will use the same class io.jsonwebtoken.Jwts to parse a JWT token and get claims.

Jws headerClaimsJwt = Jwts.parserBuilder() .setSigningKey(secretKey) .build() .parseClaimsJws(jwtString); Claims claims = headerClaimsJwt.getBody();

In above code we need to use the same SecretKey that was used to create the JWT.

We can get claim information from the Claims object as shown below

// Get Reserved Claims String issuer = claims.getIssuer(); String subject = claims.getSubject(); // Get Custom claims String firstName = claims.get("firstName", String.class); boolean isAdmin = claims.get("isAdmin", Boolean.class);

You can also get some exceptions while parsing the JWT.

  • SignatureException – If different key is used to parse the JWT, than what was used to create it.
  • ExpiredJwtException – If JWT is expired.
  • MalformedJwtException – If JWT structure is incorrect.
  • IllegalArgumentException – If JWT is null or empty string.

With this we come to end of our current blog.

The code for this blog can be found at – JWTDemo.java and JWTDemoTest.java.

Источник

Оцените статью