Search code examples
javaspringspring-mvcspring-security

How does Spring Security inject principal into Controller?


I can get the user principal as the below code, but I am puzzled how Spring Security knows to inject the correct principal. Usually, we would pass the args to call a method with parameters. So, where does Spring call the Controller Method with Principal args? Thanks for any help.

@ResponseBody
@RequestMapping({"/api/user"})
public Principal user(Principal principal) {
    return principal;
}

Solution

  • As the comment says, HandlerMethodArgumentResolver is a strategy interface for resolving method parameters into argument values in the context of a given request. Actually, Principal argument would be resolved in ServletRequestMethodArgumentResolver. Talk is cheap, and show the source code.

    @Override
    public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
    
        Class<?> paramType = parameter.getParameterType();
    
        // WebRequest / NativeWebRequest / ServletWebRequest
        if (WebRequest.class.isAssignableFrom(paramType)) {
            if (!paramType.isInstance(webRequest)) {
                throw new IllegalStateException(
                        "Current request is not of type [" + paramType.getName() + "]: " + webRequest);
            }
            return webRequest;
        }
    
        // ServletRequest / HttpServletRequest / MultipartRequest / MultipartHttpServletRequest
        if (ServletRequest.class.isAssignableFrom(paramType) || MultipartRequest.class.isAssignableFrom(paramType)) {
            return resolveNativeRequest(webRequest, paramType);
        }
    
        // HttpServletRequest required for all further argument types
        return resolveArgument(paramType, resolveNativeRequest(webRequest, HttpServletRequest.class));
    }
    

    Now you can see the the key code as Principal.class.isAssignableFrom(paramType), and if you look for furtherly, you can see the code SecurityContextHolder.getContext().getAuthentication() to get the actual argument. Ok, that's all, thanks for @chrylis -on strike- comments.

    @Nullable
    private Object resolveArgument(Class<?> paramType, HttpServletRequest request) throws IOException {
        //omitted......
        else if (Principal.class.isAssignableFrom(paramType)) {
            Principal userPrincipal = request.getUserPrincipal();
            if (userPrincipal != null && !paramType.isInstance(userPrincipal)) {
                throw new IllegalStateException(
                        "Current user principal is not of type [" + paramType.getName() + "]: " + userPrincipal);
            }
            return userPrincipal;
        }
        //omitted......
    }