Home > Software design >  How to make Spring auto-inject user entity in each RequestMapping from Spring Security Authenticatio
How to make Spring auto-inject user entity in each RequestMapping from Spring Security Authenticatio

Time:01-18

I'm new to Spring development and I use Spring Security for JWT authentication in my application.

It is already configured and works fine, but the only messy thing is unpacking the Principal in each API request mapping. I only encode the user UUID in a JWT payload, but I need the entire User entity fetched from database in each request mapping.

Currently my code looks like:

@GetMapping("/something")
public SomeResponse someMethod(Authentication authentication) {
    CustomUserDetails userDetails = (CustomUserDetails) authentication.getPrincipal();
    MyUserEntity user = userService.findByUuid(userDetails.getUuid());

    // ...
}

But I want to create some kind of a middleware so I'll be able to call findByUuid before the controller receives the request and then pass the entity to Spring to inject it, so the mapping code will look like:

@GetMapping("/some")
public SomeResponse someMethod(MyUserEntity user) {
    // ...
}

I've searched for the same problem and the only idea I found was creating a filter which performs the user lookup by their UUID and setting the request attribute:

@Component
public class UserFilter extends OncePerRequestFilter {
  @Override
  protected void doFilterInternal(
      HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
      throws ServletException, IOException {

    request.setAttribute("user", new User("Jerry"));
    filterChain.doFilter(request, response);
  }
}

And then injecting the WebRequest into each mapping:

@GetMapping("/current-user")
public String getCurrentUser(WebRequest request) {
  var user = (User) request.getAttribute("user", WebRequest.SCOPE_REQUEST);
  return user.getUsername();
}

But it still doesn't look like a good approach as it forces me to repeat the same line for each of my 50 API methods.

Is there a way to manipulate the arguments injected to a request mapping by Spring?

CodePudding user response:

Thanks to @M.Deinum, I was able to set up my own HandlerMethodArgumentsResolver component which can provide the required argument:

@Component
@RequiredArgsConstructor
public class AuthenticatedUserArgumentResolver implements HandlerMethodArgumentResolver {
    private final UserService userService;

    @Override
    public boolean supportsParameter(@NonNull MethodParameter parameter) {
        return parameter.getParameterType().equals(MyUserEntity.class);
    }

    @Override
    public Object resolveArgument(@NonNull MethodParameter parameter, ModelAndViewContainer mavContainer, @NonNull NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();

        CustomUserDetails userDetails = (CustomUserDetails) auth.getPrincipal();

        return userService.findByUuid(userDetails.getUuid());
    }
}

And use it as expected:

@GetMapping("/some")
public SomeResponse someMethod(MyUserEntity user) {
    // ...
}
  • Related