I got the task to implement jwks on the project. On our project, we have implemented a token validation check with oauth2. We use a jks format certificate to obtain a public key. the private key is not used in our project, since we need to check the validity of the token. Our goal is to get rid of the .jks file. There are too few resources for jwks and therefore some points are not clear. If I understand correctly, then jwks mean that there is a jwks.json file in the resources with keys inside, which we select by kid from the token header. Based on the documentation, it is not clear what kind of file it is and how it is loaded for checking by kid, that is, at what moment it happens.Does anyone have a project that can be used as an example? thanks in advance
-
who is creating the JWT token? is it created on your project or on an external authentication server? – Adil Karaöz Jun 10 '21 at 17:36
-
on external server, my server only check token validity – Самир Шахмурадлы Jun 10 '21 at 17:37
2 Answers
You can use spring-boot resource server implementation.
First, what you need is to add the following dependency to your project
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
Second, you need to add an authentication server configuration. The JSON file that you mentioned has to be located on the authentication server or you can use JWKs URL of the authentication server. You should have a configuration in your properties file like this.
spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https:/example.com/.well-known/openid-configuration/jwks
spring.security.oauth2.resourceserver.jwt.issuer-uri=https:/example.com
Finally, you need to follow the natural spring-security API configuration. What you need is like the following.
@Configuration
@EnableWebSecurity
public class SecureSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Value("${spring.security.oauth2.resourceserver.jwt.jwk-set-uri}")
private String jwtSetUri;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.requiresChannel().anyRequest().requiresInsecure().and().cors()
.and().csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.GET, "some path1").permitAll()
.antMatchers(HttpMethod.POST, "some path2").permitAll()
.antMatchers(HttpMethod.GET, "some path3").permitAll()
.antMatchers("/**").hasAuthority("some scope") // if you need this scope.
.anyRequest()
.authenticated()
.and()
.oauth2ResourceServer()
.jwt().decoder(jwtDecoder());
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration().applyPermitDefaultValues();
config.addAllowedMethod("PUT");
config.addAllowedMethod("DELETE");
source.registerCorsConfiguration("/**", config);
return source;
}
private JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withJwkSetUri(jwtSetUri)
.jwtProcessorCustomizer(p -> p.setJWSTypeVerifier(
new DefaultJOSEObjectTypeVerifier<>(new JOSEObjectType("at+jwt")))).build();
}
}
After this, each request to your APIs should be verified automatically by the Spring by using the authentication server.

- 30,962
- 25
- 85
- 135

- 216
- 1
- 11
-
spring.security.oauth2.resourceserver.jwt.jwk-set-uri how i can use my .json? I do not understand where this file comes from. – Самир Шахмурадлы Jun 10 '21 at 18:02
-
You don't need any JSON files. You need to learn the jwk-set-uri of the authentication server. That's all. – Adil Karaöz Jun 10 '21 at 18:03
-
do you mean that my service has to apply for json to the service that generates the token every time? – Самир Шахмурадлы Jun 10 '21 at 18:05
-
of course, after each request of the users to your APIs, Spring has to communicate with the authentication server and verify the token – Adil Karaöz Jun 10 '21 at 18:19
-
-
is it really impossible to implement without referring to .json? I have in the project in the resources folder – Самир Шахмурадлы Jun 10 '21 at 18:30
If you have a JWKS URL
, that is basically sets of keys in a JSON response, the following code might help to implement JWKS validation of a JWT access token.
package com.example.project;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.configurationprocessor.json.JSONArray;
import org.springframework.boot.configurationprocessor.json.JSONException;
import org.springframework.boot.configurationprocessor.json.JSONObject;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.net.URL;
import java.net.URLConnection;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPublicKeySpec;
import java.util.Base64;
import static java.nio.charset.StandardCharsets.UTF_8;
@Component
public class JWKSValidation {
@Value("${jwks.url}")
private String jwksURL;
private static PublicKey getPublicKey(String modulus, String exponent) {
try {
byte[] exponentB = Base64.getUrlDecoder().decode(exponent);
byte[] modulusB = Base64.getUrlDecoder().decode(modulus);
BigInteger bigExponent = new BigInteger(1, exponentB);
BigInteger bigModulus = new BigInteger(1, modulusB);
PublicKey publicKey;
publicKey = KeyFactory.getInstance("RSA").generatePublic(new RSAPublicKeySpec(bigModulus, bigExponent));
return publicKey;
} catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
public JSONObject decodeJWT(String jwtToken) throws JSONException {
String[] split_string = jwtToken.split("\\.");
String base64EncodedBody = split_string[0];
Base64.Decoder decoder = Base64.getDecoder();
byte[] bytes = decoder.decode(base64EncodedBody);
String decodedString = new String(bytes, UTF_8);
return new JSONObject(decodedString);
}
private JsonObject jwksResponse() throws IOException {
URL url = new URL(jwksURL);
URLConnection request = url.openConnection();
request.connect();
JsonElement response = JsonParser.parseReader(new InputStreamReader((InputStream) request.getContent()));
return response.getAsJsonObject();
}
private String signedData(String jwtToken) {
return jwtToken.substring(0, jwtToken.lastIndexOf("."));
}
private byte[] signature(String jwtToken) {
String signatureB64u = jwtToken.substring(jwtToken.lastIndexOf(".") + 1);
return Base64.getUrlDecoder().decode(signatureB64u);
}
public boolean verifySignature(String jwtToken, String kid) throws Exception {
JsonObject jwkresponse = jwksResponse();
String jsonString = jwkresponse.toString();
JSONObject jwks = new JSONObject(jsonString);
JSONArray jsonArray = jwks.getJSONArray("keys");
String modulus = null, exponent = null;
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject result = jsonArray.getJSONObject(i);
if (result.getString("kid").equals(kid)) {
modulus = result.getString("n");
exponent = result.getString("e");
}
}
PublicKey publicKey = getPublicKey(modulus, exponent);
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initVerify(publicKey);
String signatureData = signedData(jwtToken);
byte[] signatures = signature(jwtToken);
signature.update(signatureData.getBytes());
return signature.verify(signatures);
}
}
You can autowire the above class and pass the JWT access token
in the verifySignature
method and the kid
of the access token and works in those algorithm whose JWT header
is RSA
based. And the JWKS URL
should be mentioned in application.properties
file. Similar approach might be implemented for other algorithms.

- 11
- 3