Fetch Current Logged In User in Controller
Introduction
You have integrated spring security in you project.
Using keycloak as authentication and authorization server.
Backend as resource server guarded by jwks-keys
After every successful authentication you get all user details inside Controller but it is still pointing to
Authentication or Jwt Object from spring security hence you need to get sub and then again get user from resource server db.
So, instead of repeating auth-fetch-user flow we can write custom spring component to get current logged in user inside controller.
Remember, we are no authenticating user so we dont have UserDetails object.
Solution
Using standard spring component called HandlerMethodArgumentResolver in conjunction with annotation.
Here is how i have done it.
Define custom annotation
1package in.silentsudo.storyteller.config.resolver;
2
3import java.lang.annotation.ElementType;
4import java.lang.annotation.Retention;
5import java.lang.annotation.RetentionPolicy;
6import java.lang.annotation.Target;
7
8@Target(ElementType.PARAMETER) // Available to method params only
9@Retention(RetentionPolicy.RUNTIME) // Available at runtime for processing
10public @interface CurrentUser {
11 boolean required() default true;
12}
Create custom argument resolver
1package in.silentsudo.springboot.tips.config.resolver;
2
3import in.silentsudo.springboot.tips.persistence.models.User;
4import in.silentsudo.springboot.tips.services.UserService;
5import lombok.RequiredArgsConstructor;
6import org.jspecify.annotations.NonNull;
7import org.springframework.core.MethodParameter;
8import org.springframework.http.HttpStatus;
9import org.springframework.security.authentication.AnonymousAuthenticationToken;
10import org.springframework.security.core.Authentication;
11import org.springframework.security.core.context.SecurityContextHolder;
12import org.springframework.security.oauth2.jwt.Jwt;
13import org.springframework.stereotype.Component;
14import org.springframework.web.bind.support.WebDataBinderFactory;
15import org.springframework.web.context.request.NativeWebRequest;
16import org.springframework.web.method.support.HandlerMethodArgumentResolver;
17import org.springframework.web.method.support.ModelAndViewContainer;
18import org.springframework.web.server.ResponseStatusException;
19
20@Component
21@RequiredArgsConstructor
22public class CurrentUserArgumentResolver implements HandlerMethodArgumentResolver {
23
24 private final UserService userService;
25
26 @Override
27 public boolean supportsParameter(MethodParameter parameter) {
28 // Only trigger for our custom User entity
29 return parameter.getParameterType().equals(User.class) &&
30 parameter.hasParameterAnnotation(CurrentUser.class);
31 }
32
33 @Override
34 public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
35 @NonNull NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
36
37 CurrentUser annotation = parameter.getParameterAnnotation(CurrentUser.class);
38 boolean isRequired = annotation != null && annotation.required();
39
40 Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
41
42 // Handle Anonymous/Public requests
43 if (authentication == null || authentication instanceof AnonymousAuthenticationToken ||
44 !(authentication.getPrincipal() instanceof Jwt jwt)) {
45 if (isRequired) {
46 throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "User context required");
47 }
48 return null; // Return null safely for public registration
49 }
50
51 String oidcSub = jwt.getSubject();
52 return userService.findByOidcSub(oidcSub)
53 .orElseGet(() -> {
54 if (isRequired) throw new ResponseStatusException(HttpStatus.NOT_FOUND, "User record missing");
55 return null;
56 });
57 }
58}
Attach to WebMvcConfigurer
1package in.silentsudo.springboot.tips.config;
2
3import in.silentsudo.springboot.tips.config.resolver.CurrentUserArgumentResolver;
4import lombok.RequiredArgsConstructor;
5import org.springframework.context.annotation.Configuration;
6import org.springframework.web.method.support.HandlerMethodArgumentResolver;
7import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
8
9import java.util.List;
10
11@Configuration
12@RequiredArgsConstructor
13public class WebConfig implements WebMvcConfigurer {
14
15 private final CurrentUserArgumentResolver currentUserArgumentResolver;
16
17 @Override
18 public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
19 resolvers.add(currentUserArgumentResolver);
20 }
21}
How to use in Controller?
1public ResponseEntity<Video> createVideo(
2 @RequestBody VideoRequest request,
3 @CurrentUser User user) {
4
5 Video createdVideo = videoService.initiateVideoGeneration(request, user);
6
7 return new ResponseEntity<>(createdVideo, HttpStatus.CREATED);
8 }
Thanks