2

Is it possible to implement simple JWT authentication (not caring about invalidating tokens - I'll do it in cache) without database calls to load user into Security Context? I see my current implementation hits database with every call to api (to load user into security context). Below you can see part of implementation of JwtAuthenticationFilter extending OncePerRequestFilter:

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    try {
        String jwt = getJwtFromRequest(request);
        if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {
            Long userId = tokenProvider.getUserIdFromJWT(jwt);
            UserDetails userDetails = customUserDetailsService.loadUserById(userId);
            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }
    } catch (Exception ex) {
        logger.error("Could not set user authentication in security context", ex);
    }
    filterChain.doFilter(request, response);
}

And here is the call to database, which I would like to avoid (with every authenticated call to api):

@Service
public class CustomUserDetailsService implements UserDetailsService {

    @Autowired
    UserRepository userRepository;

    // This method is used by JWTAuthenticationFilter
    @Transactional
    public UserDetails loadUserById(Long id) {
        User user = userRepository.findById(id).orElseThrow(
            () -> new UsernameNotFoundException("User not found with id : " + id)
        );
        return UserPrincipal.create(user);
    }
}

I found some kind of solution of problem to build UserPrincipal object (it implements UserDetails interface) with only user id, username and granted authorities, and without e.g. password, which I cannot read from JWT token itself (the rest I can), but I am not sure if it's secure and and considered as a good-practice solution (UserDetails class requires password field and storing it JWT would not be wise I think). I need UserPrincipal instance (implementing UserDetails interface) to support as argument to UsernamePasswordAuthenticationToken, as you can see in the first paragraph.

3
  • it is better to provide a demo code repo Commented Mar 7, 2019 at 1:33
  • You have to provide more code. In the code you provided there is nowhere a database call to be seen. And that is how JWT is supposed to work. Without any call to the database. But provide more code or just like @clevertension said. And I'll try to help you Commented Mar 8, 2019 at 16:39
  • I supplied more code, tell me if you need more details. Any help I will much appreciate. Commented Mar 11, 2019 at 16:30

1 Answer 1

1

One approach can be having two filter as follows.

  1. CustomAuthenticationFilter that serves only login request/endpoint. You can do the following in this filter,

    • Call the db and validate the credential and retrieve the roles of the user
    • Generate the JWT token and you can store the user id or email along with roles in the subject of the JWT token. As we are adding user specific details I would recommend to encrypt the JWT token.
  2. CustomAuthroizationFilter that serves all other requests/endpoints. You can do the following in this filter,

    • Validate JWT token
    • Retrieve the user id or email along with roles of the user from the subject of the JWT token.
    • Build spring authentication (UsernamePasswordAuthenticationToken) and set it in SecurityContextHolder like you did.

This way you will be calling db only during the login request not for all other api endpoints.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.