• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Tim Cooke
  • Ron McLeod
  • paul wheaton
  • Jeanne Boyarsky
Sheriffs:
  • Paul Clapham
  • Devaka Cooray
Saloon Keepers:
  • Tim Holloway
  • Roland Mueller
  • Himai Minh
Bartenders:

Why is an authenticated user failing to access protected URL resources in spring boot

 
Greenhorn
Posts: 14
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Dears,

I have a Spring Boot project that is using Spring Security and Jwt for authentication, but however the user is failing to access protected URL's even though the user has the right permissions.

The project is on the link below:

https://github.com/omudzingwa/jwt-security

Everything else is working fine.

Regards

Obert
 
Saloon Keeper
Posts: 2449
13
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
For your code : @PreAuthorize("hasAuthority('USER')"),
can you replace it by @PreAuthorize("hasRole('USER')") ?
 
Obert Mudzingwa
Greenhorn
Posts: 14
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hi, thanks. The use of @PreAuthorize("hasRole('USER')") or @PreAuthorize("hasAuthority('USER')") doesn't solve the problem because the code doesn't throw an error. In postman I am only getting the message: 401Unauthorized and no Errors are thrown in Spring Boot which is what is confusing.
 
Marshal
Posts: 80745
486
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Please avoid the quote button; it simply duplicates what has been written before and adds nothing new. Duplicated material is liable to removal.
Please don't edit posts after they have been replied to because that makes it difficult to follow the thread. Please post new material in a new post.
Please check your GitHub link in case it exposes internal policies you would want to keep hidden.
 
Himai Minh
Saloon Keeper
Posts: 2449
13
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I guess the user input credentials cannot be authenticated.
401 unauthorization error means authentication failing.
Are you sure you input the right credentials?
 
Obert Mudzingwa
Greenhorn
Posts: 14
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Yes the user credentials are correct and I am able to login and generate a Jwt token with them. but after I get the token and try to access a protected url, I just get the message 401 in postman and no other error is throw. See my code on: https://github.com/omudzingwa/jwt-security/


 
Himai Minh
Saloon Keeper
Posts: 2449
13
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Maybe, put some debuggers in your code and run it in debugging mode.
I guess what your code does is to create a JWT token for a user. When the user logins, a JWT token is generated.
See the JWT token generated for a new user. When the user submits a request , see if  the token matches with original token.
This is a good tutorial:
https://www.javainuse.com/spring/boot-jwt

You can also check if the information in your token correct by pasting your token here:
https://jwt.io/
Remember to select the right algorithm you are using in your code.
 
Obert Mudzingwa
Greenhorn
Posts: 14
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
on the JWT.IO everything is exactly as I want it to be including the correct user role and the signature is verified correctly. Even the logged in username and the user details name are all the same e.g if I login with username Obert the User details also returns Obert as the currently authenticated user..
 
Himai Minh
Saloon Keeper
Posts: 2449
13
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Try to put some debuggers in your code and   run your code in debugging mode.
The problem may be from a JWT token that can't be validated .
Or, try to find a simple example online and see how it works.
 
Himai Minh
Saloon Keeper
Posts: 2449
13
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
For example, put a debugger in line 43 of your JwtAuthenticationFilter class and see.
Or, put some more debuggers in this class to trace the code.
 
Obert Mudzingwa
Greenhorn
Posts: 14
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hi.

It seems that the Granted Authorities are coming out as an empty Array and I yet to figure out why: below is the response that I saw when debugging:

userDetails = {User@13907} "org.springframework.security.core.userdetails.User [Username=obert, Password=[PROTECTED], Enabled=true, AccountNonExpired=true, CredentialsNonExpired=true, AccountNonLocked=true, Granted Authorities=[]]"
password = "$2a$10$7KxM9fcU4eiG1vL2RAWISu4Fl1LbHix0RIS4ZD892ChN.iPNJVg1W"
username = "obert"
authorities = {Collections$UnmodifiableSet@18441}  size = 0
accountNonExpired = true
accountNonLocked = true
credentialsNonExpired = true
enabled = true
 
Himai Minh
Saloon Keeper
Posts: 2449
13
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
The empty authority may not be the cause.
The cause is your credentials cannot be authenticated. Try to put a debugger in line 16 or 17 of JwtAuthenticationEntryPoint. Run the code in the debug mode and see if this filter is passed through.
 
Obert Mudzingwa
Greenhorn
Posts: 14
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hi.

This filter is not passed through and am getting the messages:

((User)userDetails).password = Cannot find local variable 'userDetails'
message = "Invalid credentials, access denied"

but when I even check the password that I got through debugging against https://bcrypt-generator.com/ its matching and this puzzles me.
 
Himai Minh
Saloon Keeper
Posts: 2449
13
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
First of all, where is this error coming from : ((User)userDetails).password = Cannot find local variable 'userDetails'   ?

In the AuthService class,  can you put some debuggers in the login method  to see how it goes?
Especially inside the generateNewAccessToken method, put a debugger in this line:  UserDetails userDetails = ourUserDetailsService.loadUserByUsername(username);

Also, in the JwtAuthenticationFilter class, can you put some debuggers in this piece of code:


That way, you can see if your token is valid or invalid.
 
Himai Minh
Saloon Keeper
Posts: 2449
13
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Also, one more additional note to my above comment. I found a public free educational example Github https://github.com/springframeworkguru/ssc-brewery/blob/mtc-secure-read-beer-order/src/main/java/guru/sfg/brewery/security/JpaUserDetailsService.java
This example shows:



So, in your code, you can do this:
 

 
Himai Minh
Saloon Keeper
Posts: 2449
13
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Also, one more thing. From the above public free example, I notice the author encrypted the text password of the user:
https://github.com/springframeworkguru/ssc-brewery/blob/mtc-secure-read-beer-order/src/main/java/guru/sfg/brewery/bootstrap/DefaultBreweryLoader.java
https://github.com/springframeworkguru/ssc-brewery/blob/mtc-secure-read-beer-order/src/main/java/guru/sfg/brewery/config/SecurityConfig.java

I am not sure if you need to encrypt your raw text password before you convert it to JWT token.
 
Obert Mudzingwa
Greenhorn
Posts: 14
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hi.

NOTE: UPDATED CODE IS on the link: https://github.com/omudzingwa/jwt-security/

First, its necessary to encrypt the password.

Second: I updated my code using your recommendation and it didnt work

Tthirdly. Just a while back I was now able to access protected URL's when I changed my code for OurUserDetailsService as:

From:

   public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

       User user = userRepository.findByUsername(username)
               .orElseThrow(()-> new UsernameNotFoundException("OurUserDetailsService-: Username not found"));

       Set<GrantedAuthority> authorities = new HashSet<>();
   
       return new org.springframework.security.core.userdetails.User(
              user.getUsername(),
              user.getPassword(),
              authorities);
   }

To:

   public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

       return userRepository.findByUsername(username)
               .map(this::createUserDetails)
               .orElseThrow(()->new UsernameNotFoundException("Username not found"));
   }

   private UserDetails createUserDetails(com.netrork.pine.security.users.User user) {
       return new org.springframework.security.core.userdetails.User(
               user.getUsername(),
               user.getPassword(),
               user.getAuthorities());
   }

But then I later added some flesh to my CustomLogoutHandler as below and then everything went hay wire and I was failing to access protected URL's again. The CustomLogoutHandler which implements LogoutHandler is as below:

public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {

       String token = request.getHeader("refresh_token");
       log.info("Length for Token is : " + token.length());

       log.info("Fetching token from Database");
       String tokenFromDatabase = refreshTokenService.findTokenByValue(token)
               .orElseThrow(()-> new RuntimeException("Token not found"));

       log.info("Token from Database is : " + tokenFromDatabase);
       log.info("Token from Request  is : " + token);

       //1 - Check if token exists in database
       if(token.equals(tokenFromDatabase)){

           long userId=refreshTokenService.findUserIdFromTokenByTokenValue(token);

           String username= refreshTokenService.getUsernameForTokenByUserId(userId);

           refreshTokenService.deleteTokenByTokenValue(token);
           log.info("Deleted Refresh token for user : " + username);

           SecurityContextHolder.clearContext();

           try {
               request.logout();
               log.info("Logged out User : " + username);

           } catch (ServletException e) {
               throw new RuntimeException(e);
           }

NOTE: UPDATED CODE IS on the link: https://github.com/omudzingwa/jwt-security/

 
Himai Minh
Saloon Keeper
Posts: 2449
13
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
As I check your CustomLogoutHandler, it simply deletes the token, which was once used by the user and no longer needed.
It should not affect the authorities of the user.
So, when you log back in, you are denied again.
Maybe, put some debuggers in the JwtAuthenticationFilter and see.

I don't understand why you need to use the refreshToken like this:

 
Himai Minh
Saloon Keeper
Posts: 2449
13
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
One more additional note to my previous post.
Can you try to create a second user , maybe called John and a password for John?
Then, you, as Obert user,  log in and log out.
Then, you log in as John. See if John can be authenticated.
 
Himai Minh
Saloon Keeper
Posts: 2449
13
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
So far, I made some changes to your code:




In application.properties, use this property instead of create-drop to recreate the table every time I start the application:

I use port 8080 instead of 6666
I use Postman to do the following:
1. I first send a POST request at  localhost:8080/auth/register.
I submit these info in JSON
{
       
        "firstname" : "John",
         "lastname": "Smith",
         "username" : "jsmith3",
         "password": "smith3",
         "email" : "[email protected]"
       
   }
2. I then send a POST request at localhost:8080/auth/login
{       "username" : "jsmith3",
         "password": "smith3"
}
I got the access token
3. I create another GET request at localhost:8080/endpoints/user
Under the Authorization panel, copy and paste the token in the Bearer Token field.
4. So far, I get access to the endpoint
 
Himai Minh
Saloon Keeper
Posts: 2449
13
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Also, my next test is to use  @PreAuthorize("hasAuthority('USER')") for the get endpoint instead of @RolesAllowed.
It now works.
 
Obert Mudzingwa
Greenhorn
Posts: 14
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hi.

Thanks for the detailed help. The issue had nothing to do with all that I have been checking. The @PreAuthorize works however the real problem was on postman testing URL:

I was testing using:

GET: http://localhost:6666/endpoints/users

instead of:

http://localhost:6666/endpoints/user

So the /users was pointing to a link that was nonexistent in my Auth Controller. That's why this has been so difficult to debug. Now I am working on my logout the way I want it to work, but the logout is not working yet.
reply
    Bookmark Topic Watch Topic
  • New Topic