How to check user status on each request in spring security
Asked Answered
S

2

6

I am using spring security and I implemented UserDetailsService which perfectly handles my user login process.

The question is this: Is there a standard way to check the user status from database on each request such that if the user's account status changes to "locked" or his roles get changed while he is STILL signed-in, the application prevents him from continuing his work.

The issue here is that "public UserDetails loadUserByUsername(String arg0)" in my custom UserDetailsService class is only called at the sign-in process and the userDetails object keeps data since the sign-in process was carried-out and userDetails information is not fresh.

I could solve this by some workaround like getting the user object from database and check its status by a listener. But I feel that spring security might have a general solution for this situation.

Please help. Thanks.

Sanjiv answered 3/11, 2014 at 23:42 Comment(3)
You can write a filter which maps all the requests (except login request) and check if the user is locked or not.Backward
Thanks for the solution. Actually it also came to my mind but I was thinking that maybe with a flag or property set in spring security I can force the application to load user details on demand.Sanjiv
I dont think you can manage it with just a flag, You need to add a filter to the filter-chain. You may get some idea if you can explore a bit on how the remember me filter is handled.Backward
L
1

The basic idea is to use a custom SecurityContextRepository which discards user details that are saved in the http session.

For a full example and a working project please take a look at my answer to a similar question here: Spring Security logoff a user while runtime

Lang answered 7/11, 2014 at 19:19 Comment(0)
O
0

This solution worked for me on Spring-boot 3.x (and Spring Security 6.x):

Whenever you use @EnableWebSecurity, you'll probably need to setup the standard UserDetailsService bean... so you should have something like this:

@Bean
public UserDetailsService userDetailsService() {
    return username -> userRepository.findByUsername(username)
         .orElseThrow(() -> new UsernameNotFoundException("User not found"));
}

You can change it to support your additional filters (in my case, I added a Status enum per each User entity to check if the user is Status.Active before serving the request):

@Bean
public UserDetailsService userDetailsService() {
    return username -> userRepository.findByUsernameAndStatus(username, Status.ACTIVE)
         .orElseThrow(() -> new UsernameNotFoundException("User not found or not Active"));
}

Needless to say that JPA comes in very handy when implementing the findBy queries with multiple fields as in my UserRepository:

@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
    
    // Old method:
    Optional<User> findByUsername(String username);
    
    // New method:
    Optional<User> findByUsernameAndStatus(String username, Status status);
}

One last touch:

Since I implemented my own custom UserService, I modified the findBy in my loadUserByUsername method also as below:

@Service
public class UserService implements UserDetailsService {

    @Autowired
    UserRepository userRepository;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) {
        User user = userRepository.findByUsernameAndStatus(username, Status.ACTIVE)
                .orElseThrow(() -> new UsernameNotFoundException("User not found or not Active"));
        return user;
    }

...
...

Goodluck!

Olcott answered 10/1 at 11:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.