#Spring Boot Microservices with JWT Issues

139 messages · Page 1 of 1 (latest)

tacit grail
#

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

Contribute to Rapter1990/springbootmicroserviceswithsecurity development by creating an account on GitHub.

velvet ventureBOT
#

This post has been reserved for your question.

Hey @tacit grail! 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.

woeful smelt
#

Can you show the stack trace?

velvet ventureBOT
#

💤 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.

tacit grail
#

@woeful smelt 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
When I send a request to another service through Api gateway.

woeful smelt
#

don't call blocking methods in reactor

velvet ventureBOT
tacit grail
#

@woeful smelt I still couldn't fix the issue ?

woeful smelt
#

this is blocking

#

which you cannot use with reactive

#

you could use Spring's WebClient instead I guess

tacit grail
#

@woeful smelt One guy opened a pull request but I still got the same error.

woeful smelt
#

Why would the PR change anything?

tacit grail
woeful smelt
#

that's not in reactive code

#

Do you know the difference between synchronous/blocking and asynchronous/reactive/nonblocking code?

tacit grail
#

I revise the JwtAuthenticationFilter in api gateway. I got 401 instead of 500 even if I use a token.

woeful smelt
#

Spring Security verbose logging?

tacit grail
#

Here is the console result of API Gateway ?

tacit grail
#

@woeful smelt

#

I just updated my repo again. Here is the results.

woeful smelt
#

Did you enable verbose logging in Spring Security?

tacit grail
#

Yeah, I got this message after I enabled it. I just updated my repo again.

velvet ventureBOT
#

💤 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.

tacit grail
#

@iron wedge Hi, I think you can help me. I couldn't fix my issue.

tacit grail
#

@woeful smelt 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?

tacit grail
tacit grail
#

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}]

Here is the screenshot

tacit grail
#

I updated my repo again. I still got the error. @woeful smelt

woeful smelt
# tacit grail

Did you add verbose Spring Security logs yet? I don't see it in the image

woeful smelt
tacit grail
tacit grail
woeful smelt
#

the repo is a bit too much to look through on my phone lol

tacit grail
#

Here are the posts

woeful smelt
#

Is there a filter response for it?

#

If so, can you debug that?

tacit grail
woeful smelt
#

Is the filter called?

tacit grail
woeful smelt
#

in the debugger

tacit grail
#

Just a moment. I'm currently using my PC.

#

Here is the login

#

Here is the process of creating Product via access token

#

Here is the logs of api gateway

#

Here are the logs of other services

#

I run product service as debug mode.

#

Token is valid through filter defined in Product service.

tacit grail
woeful smelt
tacit grail
woeful smelt
#

seems like getAuthentication isn't doing something right

tacit grail
#

I can get the right values from claims

#

I look through it again. Here is the information

woeful smelt
#

what is jwt and authorities there?

tacit grail
#

There are the information

#

Here is the token info

woeful smelt
#

So you are using UsernamePasswordAuthenticationToken.authenticated(jwt, null, authorities)

#

Is the filter called once or twice per request?

tacit grail
#

Here is authentication to be sent to product service.

woeful smelt
tacit grail
#

I use user service to validate token through product service

woeful smelt
tacit grail
woeful smelt
tacit grail
#

Here is the filter in product service. Authentication information is empty

woeful smelt
woeful smelt
#

oh so the getAuthentication method is making a call feign

#

I think you can't transmit UsernamePasswordAuthenticationToken over the network like that

velvet ventureBOT
woeful smelt
#

Can you make getAuthentication return a custom object (ideally a record) storing just the information you need?

woeful smelt
#

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

woeful smelt
#

and then your CustomBearerTokenAuthenticationFilter can convert it to a UsernamePasswordAuthenticationToken

woeful smelt
woeful smelt
#

I think you'd need a custom serializer/deserializer for the UsernamePasswordAuthenticationToken

woeful smelt
#

You might also need a serializer

#

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

Contribute to Rapter1990/springbootmicroserviceswithsecurity development by creating an account on GitHub.

velvet ventureBOT
# woeful smelt You just attempt to deserialize the principal and the credentials. You deseriali...
    @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);
    }

woeful smelt
#

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)

tacit grail
#

Here is the information of p ?

woeful smelt
#

if you step over once, what is principal?

tacit grail
#

Here is the result

woeful smelt
#

ok so the node.get("principal").asText() seems to be the problem

#

What should the principal be?

tacit grail
#
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.

#

@woeful smelt I want to show the console output of it.

woeful smelt
#

yeah ok ig that's better

#

But why set the principal to that json?

tacit grail
woeful smelt
#

What should the principal be?

tacit grail
#

Here is the principal information

woeful smelt
#

if you want that on the other side as well, you need to deserialize it to a Jwt or equivalent object

#

not a String

tacit grail
#

I did it but I got this error message.

woeful smelt
#

ok it seems you cannot deserialize that jwt class

#

so just create a record with the same components

tacit grail
#

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);
    }
}

I implemented it.

#

Here is the error

#
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"])
woeful smelt
#

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

tacit grail
#

I get the same error again .

#

Here are console result

#

Is it possible to test my example as I'm stuck there?

velvet ventureBOT
#

💤 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.

velvet ventureBOT
#

💤 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.