How does Spring Security inject principal into Controller?

Viewed 802

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;
}
1 Answers

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......
}
Related