I am using Retrofit 2.3.0 to talk to an API that uses JWT for authentication from a Spring Boot application.
To make it work, I created an Interceptor
implementation:
private static class JwtAuthenticationInterceptor implements Interceptor {
private Supplier<String> jwtTokenSupplier;
private JwtAuthenticationInterceptor(Supplier<String> jwtTokenSupplier) {
this.jwtTokenSupplier = jwtTokenSupplier;
}
@Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
Request.Builder builder = original.newBuilder()
.header("Authorization",
String.format("Bearer %s", jwtTokenSupplier.get()));
Request request = builder.build();
return chain.proceed(request);
}
}
In my Spring service, I let Retrofit create an instance of the API interface in the constructor:
public MySringServiceImpl() {
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(createLoggingInterceptor())
.addInterceptor(new JwtAuthenticationInterceptor(this::createJwtToken))
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://my.remoteapi.com/api/")
.addConverterFactory(JacksonConverterFactory.create())
.client(client)
.build();
api = retrofit.create(MyRemoteApi.class);
}
So in the actual methods of my service, I use something like this:
public List<Stuff> getStuffFromApi() {
try {
List<Stuff> response = api.getStuff().execute().body();
if (response != null) {
return response;
} else {
return new ArrayList<>();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
The createJwtToken
method create JWT token (Using the Java JWT library)
private String createJwtToken() {
return Jwts.builder()
.setIssuer("http://my.remoteapi.com/api/")
.setId("my-test-id")
.setIssuedAt(new Date())
.setExpiration(new Date(ZonedDateTime.now().plusSeconds(60).toEpochSecond() * 1000))
.claim("uid", "123")
.signWith(SignatureAlgorithm.HS512,
"my-very-secret-key"
.getBytes())
.compact();
}
The actual problem:
The uid
claim needs to contain the id of the current user (instead of being hardcoded like it is now). I am well aware on how to get the Spring principal in the RestController and than pass that down to the service, but how would I instruct the interceptor to use the id of that principal for the call that is happening? Should I create a new Retrofit instance for each call or are there better ways to handle this?