Spring Boot Microservices with JWT Issues

I try to implement an example of Spring Boot Microservices with JWT. I have some problems following: 1 ) I cannot run all integration tests of product service even if I defined bearer token 2 ) After login and get access token, I cannot send any request to product service. I got 500 Internal Server Error. How can I fix it? I hope you can help me? Here is the repo : https://github.com/Rapter1990/springbootmicroserviceswithsecurity
GitHub
GitHub - Rapter1990/springbootmicroserviceswithsecurity
Contribute to Rapter1990/springbootmicroserviceswithsecurity development by creating an account on GitHub.
108 Replies
JavaBot
JavaBot5mo ago
This post has been reserved for your question.
Hey @direct_x_34! Please use /close or the Close Post button above when your problem is solved. Please remember to follow the help guidelines. This post will be automatically closed after 300 minutes of inactivity.
TIP: Narrow down your issue to simple and precise questions to maximize the chance that others will reply in here.
dan1st
dan1st5mo ago
Can you show the stack trace?
JavaBot
JavaBot5mo ago
💤 Post marked as dormant
This post has been inactive for over 300 minutes, thus, it has been archived. If your question was not answered yet, feel free to re-open this post or create a new one. In case your post is not getting any attention, you can try to use /help ping. Warning: abusing this will result in moderative actions taken against you.
direct_x_34
direct_x_34OP5mo ago
@dan1st | Daniel I see the logs
c.s.a.filter.JwtAuthenticationFilter : Error validating token: block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-nio-3
c.s.a.filter.JwtAuthenticationFilter : Error validating token: block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-nio-3
When I send a request to another service through Api gateway.
dan1st
dan1st5mo ago
Stack Overflow
Wrapping blocking I/O in project reactor
I have a spring-webflux API which, at a service layer, needs to read from an existing repository which uses JDBC. Having done some reading on the subject, I would like to keep the execution of the
dan1st
dan1st5mo ago
don't call blocking methods in reactor
JavaBot
JavaBot5mo ago
userServiceClient.validateToken(jwt);
userServiceClient.validateToken(jwt);
direct_x_34
direct_x_34OP5mo ago
@dan1st | Daniel I asked someone and opened a pull request. https://github.com/Rapter1990/springbootmicroserviceswithsecurity/pull/1/files
GitHub
Refactor JwtAuthenticationFilter by hary-singh · Pull Request #1 · ...
Refactor JwtAuthenticationFilter to efficiently manage calls to external services in an asynchronous manner, ensuring the application remains responsive.
direct_x_34
direct_x_34OP5mo ago
@dan1st | Daniel I still couldn't fix the issue ?
No description
dan1st
dan1st5mo ago
this is blocking which you cannot use with reactive you could use Spring's WebClient instead I guess
direct_x_34
direct_x_34OP5mo ago
@dan1st | Daniel One guy opened a pull request but I still got the same error.
dan1st
dan1st5mo ago
Why would the PR change anything?
dan1st
dan1st5mo ago
that's not in reactive code Do you know the difference between synchronous/blocking and asynchronous/reactive/nonblocking code?
direct_x_34
direct_x_34OP5mo ago
I revise the JwtAuthenticationFilter in api gateway. I got 401 instead of 500 even if I use a token.
direct_x_34
direct_x_34OP5mo ago
GitHub
GitHub - Rapter1990/springbootmicroserviceswithsecurity
Contribute to Rapter1990/springbootmicroserviceswithsecurity development by creating an account on GitHub.
dan1st
dan1st5mo ago
Spring Security verbose logging?
direct_x_34
direct_x_34OP5mo ago
Here is the console result of API Gateway ?
No description
No description
direct_x_34
direct_x_34OP5mo ago
@dan1st | Daniel
No description
No description
direct_x_34
direct_x_34OP5mo ago
I just updated my repo again. Here is the results.
dan1st
dan1st5mo ago
Did you enable verbose logging in Spring Security?
direct_x_34
direct_x_34OP5mo ago
Yeah, I got this message after I enabled it. I just updated my repo again.
No description
No description
JavaBot
JavaBot5mo ago
💤 Post marked as dormant
This post has been inactive for over 300 minutes, thus, it has been archived. If your question was not answered yet, feel free to re-open this post or create a new one. In case your post is not getting any attention, you can try to use /help ping. Warning: abusing this will result in moderative actions taken against you.
direct_x_34
direct_x_34OP5mo ago
@Twala Hi, I think you can help me. I couldn't fix my issue. @dan1st | Daniel Hi, What's up? I couldn't still fix the issue. I also add some logs to check if token is valid or not. The token is valid from user service. Is it possible to look through it?
direct_x_34
direct_x_34OP5mo ago
No description
direct_x_34
direct_x_34OP5mo ago
Hi, I revised it again. I got this error in product service.
[UserServiceClient#getAuthentication(String)]: [{"time":"2024-07-18T14:26:57.2236457","httpStatus":"NOT_FOUND","header":"API ERROR","message":"Could not write JSON: Unable to convert claim 'iss' of type 'class java.lang.String' to URL.","isSuccess":false}]
[UserServiceClient#getAuthentication(String)]: [{"time":"2024-07-18T14:26:57.2236457","httpStatus":"NOT_FOUND","header":"API ERROR","message":"Could not write JSON: Unable to convert claim 'iss' of type 'class java.lang.String' to URL.","isSuccess":false}]
Here is the screenshot
No description
direct_x_34
direct_x_34OP5mo ago
I updated my repo again. I still got the error. @dan1st | Daniel
No description
No description
dan1st
dan1st5mo ago
Did you add verbose Spring Security logs yet? I don't see it in the image What's the code responsible for that?
direct_x_34
direct_x_34OP5mo ago
I added it into application.yml files of the services. Can you look through my latest post as I still couldn't fix it?
dan1st
dan1st5mo ago
which post? the repo is a bit too much to look through on my phone lol
direct_x_34
direct_x_34OP5mo ago
@dan1st | Daniel Here are the posts
dan1st
dan1st5mo ago
Is there a filter response for it? If so, can you debug that?
direct_x_34
direct_x_34OP5mo ago
Yeah, it comes from Custom filter in Product Service.
dan1st
dan1st5mo ago
Is the filter called?
direct_x_34
direct_x_34OP5mo ago
I debugged it. Filter is called.
dan1st
dan1st5mo ago
what happens then? in the debugger
direct_x_34
direct_x_34OP5mo ago
Just a moment. I'm currently using my PC.
direct_x_34
direct_x_34OP5mo ago
Here is the login
No description
direct_x_34
direct_x_34OP5mo ago
Here is the process of creating Product via access token
No description
direct_x_34
direct_x_34OP5mo ago
Here is the logs of api gateway
direct_x_34
direct_x_34OP5mo ago
No description
direct_x_34
direct_x_34OP5mo ago
Here are the logs of other services
No description
No description
No description
direct_x_34
direct_x_34OP5mo ago
I run product service as debug mode.
direct_x_34
direct_x_34OP5mo ago
No description
direct_x_34
direct_x_34OP5mo ago
No description
direct_x_34
direct_x_34OP5mo ago
Token is valid through filter defined in Product service. Here is result which I show
dan1st
dan1st5mo ago
What does getAuthentication() return?
direct_x_34
direct_x_34OP5mo ago
No description
No description
dan1st
dan1st5mo ago
seems like getAuthentication isn't doing something right
direct_x_34
direct_x_34OP5mo ago
I can get the right values from claims
direct_x_34
direct_x_34OP5mo ago
I look through it again. Here is the information
No description
dan1st
dan1st5mo ago
Can you step to the
return UsernamePasswordAuthenticationToken
.authenticated(jwt, null, authorities);
return UsernamePasswordAuthenticationToken
.authenticated(jwt, null, authorities);
? what is jwt and authorities there?
direct_x_34
direct_x_34OP5mo ago
There are the information
No description
direct_x_34
direct_x_34OP5mo ago
Here is the token info
No description
dan1st
dan1st5mo ago
So you are using UsernamePasswordAuthenticationToken.authenticated(jwt, null, authorities) Is the filter called once or twice per request?
direct_x_34
direct_x_34OP5mo ago
Here is authentication to be sent to product service.
No description
dan1st
dan1st5mo ago
Which service is that?
direct_x_34
direct_x_34OP5mo ago
User Service I use user service to validate token through product service
dan1st
dan1st5mo ago
Is that also the user service?
direct_x_34
direct_x_34OP5mo ago
Right
dan1st
dan1st5mo ago
so if you are there the jwt and authorities are filled but if you use step over/step out, you get an empty UsernamePasswordAuthenticationToken?
direct_x_34
direct_x_34OP5mo ago
Here is the filter in product service. Authentication information is empty
No description
dan1st
dan1st5mo ago
When you were debugging that, did it come from the filter or from somewhere else?
direct_x_34
direct_x_34OP5mo ago
It is UserController's method in User Service.
dan1st
dan1st5mo ago
oh so the getAuthentication method is making a call feign I think you can't transmit UsernamePasswordAuthenticationToken over the network like that
JavaBot
JavaBot5mo ago
final UsernamePasswordAuthenticationToken authentication = userServiceClient.getAuthentication(jwt);
final UsernamePasswordAuthenticationToken authentication = userServiceClient.getAuthentication(jwt);
direct_x_34
direct_x_34OP5mo ago
Where is the problem?
dan1st
dan1st5mo ago
Can you make getAuthentication return a custom object (ideally a record) storing just the information you need?
dan1st
dan1st5mo ago
you returning a UsernamePasswordAuthenticationToken there this is a network call that cannot properly transmit a UsernamePasswordAuthenticationToken instead, try to transmit a custom object both parties have that contains all the information for authentication
direct_x_34
direct_x_34OP5mo ago
GitHub
springbootmicroserviceswithsecurity/productservice/src/main/java/co...
Contribute to Rapter1990/springbootmicroserviceswithsecurity development by creating an account on GitHub.
dan1st
dan1st5mo ago
and then your CustomBearerTokenAuthenticationFilter can convert it to a UsernamePasswordAuthenticationToken oh I think you can also use that way but I think your mixin is not sufficient
dan1st
dan1st5mo ago
I think you'd need a custom serializer/deserializer for the UsernamePasswordAuthenticationToken Can you debug the deserializer? What is p? Does it have all the information you need? You might also need a serializer
dan1st
dan1st5mo ago
You just attempt to deserialize the principal and the credentials. You deserialize the principal as a string meaning you expect that to be a string. You don't deserialize the authorities. Are you sure about that? https://github.com/Rapter1990/springbootmicroserviceswithsecurity/blob/8bbb6a01c7799c5b1b87406336ab76a20a32bd5d/productservice/src/main/java/com/springbootmicroservices/productservice/serializer/UsernamePasswordAuthenticationTokenDeserializer.java#L15-L25
GitHub
springbootmicroserviceswithsecurity/productservice/src/main/java/co...
Contribute to Rapter1990/springbootmicroserviceswithsecurity development by creating an account on GitHub.
JavaBot
JavaBot5mo ago
@Override
public UsernamePasswordAuthenticationToken deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode node = p.getCodec().readTree(p);

// Assuming the response contains the necessary fields
String principal = node.get("principal").asText();
String credentials = node.get("credentials").asText();

return new UsernamePasswordAuthenticationToken(principal, credentials);
}
@Override
public UsernamePasswordAuthenticationToken deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode node = p.getCodec().readTree(p);

// Assuming the response contains the necessary fields
String principal = node.get("principal").asText();
String credentials = node.get("credentials").asText();

return new UsernamePasswordAuthenticationToken(principal, credentials);
}
direct_x_34
direct_x_34OP5mo ago
I check it now
dan1st
dan1st5mo ago
What the problem is: You serialize a token, send it, receive it, deserialize it and then you don't have the same token (but a token with pretty much no relevant information)
direct_x_34
direct_x_34OP5mo ago
Here is the information of p ?
No description
dan1st
dan1st5mo ago
oh that's good if you step over once, what is principal?
direct_x_34
direct_x_34OP5mo ago
Here is the result
No description
dan1st
dan1st5mo ago
ok so the node.get("principal").asText() seems to be the problem What should the principal be?
direct_x_34
direct_x_34OP5mo ago
No description
direct_x_34
direct_x_34OP5mo ago
public class UsernamePasswordAuthenticationTokenDeserializer extends JsonDeserializer<UsernamePasswordAuthenticationToken> {

@Override
public UsernamePasswordAuthenticationToken deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode node = p.getCodec().readTree(p);

// Extract the nested principal object
JsonNode principalNode = node.get("principal");
String principal = principalNode.toString(); // Converting the entire principal node to a JSON string

// Extracting the credentials
String credentials = node.get("credentials").isNull() ? null : node.get("credentials").asText();

return new UsernamePasswordAuthenticationToken(principal, credentials);
}
}
public class UsernamePasswordAuthenticationTokenDeserializer extends JsonDeserializer<UsernamePasswordAuthenticationToken> {

@Override
public UsernamePasswordAuthenticationToken deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode node = p.getCodec().readTree(p);

// Extract the nested principal object
JsonNode principalNode = node.get("principal");
String principal = principalNode.toString(); // Converting the entire principal node to a JSON string

// Extracting the credentials
String credentials = node.get("credentials").isNull() ? null : node.get("credentials").asText();

return new UsernamePasswordAuthenticationToken(principal, credentials);
}
}
I revised it but nothing changed. I still get the same error.
direct_x_34
direct_x_34OP5mo ago
No description
direct_x_34
direct_x_34OP5mo ago
@dan1st | Daniel I want to show the console output of it.
No description
dan1st
dan1st5mo ago
yeah ok ig that's better But why set the principal to that json?
direct_x_34
direct_x_34OP5mo ago
What should I do?
dan1st
dan1st5mo ago
What should the principal be?
direct_x_34
direct_x_34OP5mo ago
Here is the principal information
No description
dan1st
dan1st5mo ago
if you want that on the other side as well, you need to deserialize it to a Jwt or equivalent object not a String
direct_x_34
direct_x_34OP5mo ago
I did it but I got this error message.
No description
dan1st
dan1st5mo ago
ok it seems you cannot deserialize that jwt class so just create a record with the same components
direct_x_34
direct_x_34OP5mo ago
Ok. I'll try it.
public record JwtRecord(String tokenValue, Map<String, Object> headers, Map<String, Object> claims) {
// You can add any necessary methods or constructors here if needed
}


public class UsernamePasswordAuthenticationTokenDeserializer extends JsonDeserializer<UsernamePasswordAuthenticationToken> {

private final ObjectMapper objectMapper = new ObjectMapper();

@Override
public UsernamePasswordAuthenticationToken deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode node = p.getCodec().readTree(p);

// Extract the nested principal object and deserialize it into a JwtRecord object
JsonNode principalNode = node.get("principal");
JwtRecord principal = objectMapper.treeToValue(principalNode, JwtRecord.class);

// Extracting the credentials
String credentials = node.get("credentials").isNull() ? null : node.get("credentials").asText();

return new UsernamePasswordAuthenticationToken(principal, credentials);
}
}
public record JwtRecord(String tokenValue, Map<String, Object> headers, Map<String, Object> claims) {
// You can add any necessary methods or constructors here if needed
}


public class UsernamePasswordAuthenticationTokenDeserializer extends JsonDeserializer<UsernamePasswordAuthenticationToken> {

private final ObjectMapper objectMapper = new ObjectMapper();

@Override
public UsernamePasswordAuthenticationToken deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode node = p.getCodec().readTree(p);

// Extract the nested principal object and deserialize it into a JwtRecord object
JsonNode principalNode = node.get("principal");
JwtRecord principal = objectMapper.treeToValue(principalNode, JwtRecord.class);

// Extracting the credentials
String credentials = node.get("credentials").isNull() ? null : node.get("credentials").asText();

return new UsernamePasswordAuthenticationToken(principal, credentials);
}
}
I implemented it.
direct_x_34
direct_x_34OP5mo ago
No description
direct_x_34
direct_x_34OP5mo ago
Here is the error
No description
No description
direct_x_34
direct_x_34OP5mo ago
Caused by: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "issuedAt" (class com.springbootmicroservices.productservice.model.auth.JwtRecord), not marked as ignorable (3 known properties: "tokenValue", "headers", "claims"])
at [Source: UNKNOWN; byte offset: #UNKNOWN] (through reference chain: com.springbootmicroservices.productservice.model.auth.JwtRecord["issuedAt"])
Caused by: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "issuedAt" (class com.springbootmicroservices.productservice.model.auth.JwtRecord), not marked as ignorable (3 known properties: "tokenValue", "headers", "claims"])
at [Source: UNKNOWN; byte offset: #UNKNOWN] (through reference chain: com.springbootmicroservices.productservice.model.auth.JwtRecord["issuedAt"])
dan1st
dan1st5mo ago
it means the JWT has an issuedAt field but your record doesn't you could add that to the record or if you don't need it, configure jackson to ignore missing fields
direct_x_34
direct_x_34OP5mo ago
I get the same error again .
No description
direct_x_34
direct_x_34OP5mo ago
Here are console result
No description
No description
direct_x_34
direct_x_34OP5mo ago
Is it possible to test my example as I'm stuck there?
JavaBot
JavaBot5mo ago
💤 Post marked as dormant
This post has been inactive for over 300 minutes, thus, it has been archived. If your question was not answered yet, feel free to re-open this post or create a new one. In case your post is not getting any attention, you can try to use /help ping. Warning: abusing this will result in moderative actions taken against you.
💤 Post marked as dormant
This post has been inactive for over 300 minutes, thus, it has been archived. If your question was not answered yet, feel free to re-open this post or create a new one. In case your post is not getting any attention, you can try to use /help ping. Warning: abusing this will result in moderative actions taken against you.
💤 Post marked as dormant
This post has been inactive for over 300 minutes, thus, it has been archived. If your question was not answered yet, feel free to re-open this post or create a new one. In case your post is not getting any attention, you can try to use /help ping. Warning: abusing this will result in moderative actions taken against you.
Want results from more Discord servers?
Add your server