-
Notifications
You must be signed in to change notification settings - Fork 38.5k
Resolve Principal argument only when not annotated #25780
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
ServletRequestMethodArgumentResolver happend early in the `ArgumentResolver` chain. One of this ability is to resolve `Principal`, that's great and it works well. But most of the time when a parametre of type `Principal` is annotated we don't want to get the `Principal` from the `HttpServletRequest.getUserPrincipal()`. This feature is actually conflicting with the **spring-security** documentation and the `@AuthenticationPrincipal` annotation which is supposed to resolve the Principal from Authentication.getPrincipal(). Fix #4151
@anthonyraymond Please sign the Contributor License Agreement! Click here to manually synchronize the status of this Pull Request. See the FAQ for frequently asked questions. |
@anthonyraymond Thank you for signing the Contributor License Agreement! |
return (WebRequest.class.isAssignableFrom(paramType) || | ||
ServletRequest.class.isAssignableFrom(paramType) || | ||
MultipartRequest.class.isAssignableFrom(paramType) || | ||
HttpSession.class.isAssignableFrom(paramType) || | ||
(pushBuilder != null && pushBuilder.isAssignableFrom(paramType)) || | ||
Principal.class.isAssignableFrom(paramType) || | ||
(Principal.class.isAssignableFrom(paramType) && parameterAnnotations.length == 0) || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Merely checking for the absence of an annotation would not be sufficient, since the parameter could be annotated with annotations other than Spring Security's @AuthenticationPrincipal
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the Spring Security issue would need to be addressed within Spring Security, potentially making use of RequestMappingHandlerAdapter.setArgumentResolvers(List<HandlerMethodArgumentResolver>)
(as opposed to setCustomArgumentResolvers(...)
).
@rstoyanchev and @rwinch, thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think setting the default resolvers is a good way to go.
We have some precedent with arguments of type Map
where we do check that it isn't annotated in some way, see 5a3ff35. We could so something similar here given that Principal
is also a very high level interface with many possible uses and implementations. What I'm wondering though is why this problem surfaces only now, was there some change in Spring Security or did it never work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or does it depend on the user Object of the application?
Under spring-projects/spring-security#4151 it says one option is for UserDetails to not inherit Principal but from what I see UserDetails doesn't .
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have some precedent with arguments of type Map where we do check that it isn't annotated in some way, see 5a3ff35.
OK. I wasn't aware of that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or does it depend on the user Object of the application?
Yes, it depends on the user-specific implementation of UserDetails
.
Under spring-projects/spring-security#4151 it says one option is for UserDetails to not inherit Principal but from what I see UserDetails doesn't .
Right. The UserDetails
interface does not extend Principal
by default, though a developer may of course choose to implement Principal
if they want (or need) to.
I have implemented in UserDetails
in several applications of the years, and I have never had it implement Principal
as well. I also have never seen anyone do that. But... as I mentioned above, it's certainly an option if it makes sense in a given application.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi guys thanks for your investment in this issue.
to answer @rstoyanchev
What I'm wondering though is why this problem surfaces only now, was there some change in Spring Security or did it never work?
My guess is that it's never worked, people use the @AuthenticationPrincipal
and they get what they asks for: a Principal
. It's not the expected one, because it's not coming from the right place, but it's a principal and it's good enough after 3 hours of tweaking the Auth process to get what you want🤝. All Most peoples HATES having to deal with auth 😨, and if the framework is handling this good enough that's a deal 😄 .
But this behavior has always been a problem:
- 2019 SO post:
HttpServletRequest.getUserPrincipal()
contains theAuthentication
(which extends Principal) so it returns it, and it does not extract the Principal from the Authentication. There are multiple solutions for this around SO and most of them say : Useorg.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken
in place ofKeycloakPrincipal
(which is an implementation ofAuthentication
) and magically it starts to work, but not for the good reasons. - 2015 SO post: Because
HttpServletRequest.getUserPrincipal()
contains theAuthentication
(which extends Principal).
Basically what you can get with the Google search "Current user principal is not of type" is related to this issue.
Both of these issues come from the fact that the Principal resolved is from HttpServletRequest.getUserPrincipal()
instead of SecurityContext.getAuthentication.getPrincipal()
.
The nasty thing about that is that the @AuthenticationPrincipal
has not only become not relevant but also missleading, because all class extending Principal can be injected into a Principal
type (thanks to the spring-framework, the annotation takes no part in this), but as soon as you want to get your implementation of Principal
you are doomed.
A side question that i can't figure out on my own (no sarcasm, that's a real question):
- Why is the
spring-framework
trying to inject a Principal ? Isn'tjava.security.Principal
informaly but well accepted as an authentication class? IMHO it makes more sense to delegate that behaviour to thesecurity
part of spring.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
great job
Thanks for the extra detail. This is scheduled for 5.3 now.
It is one of many request properties that |
Thanks for the quick reply. Since this will most likely get merged, i added a test to ensure it wont break in the future. |
...ngframework/web/servlet/mvc/method/annotation/ServletRequestMethodArgumentResolverTests.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good
ServletRequestMethodArgumentResolver
happend early in theArgumentResolver
chain. One of his ability is to resolvePrincipal
, that's great and it works well.But when a parameter of type
Principal
is annotated we don't want to get thePrincipal
from theHttpServletRequest.getUserPrincipal()
.This feature is even in conflict with the spring-security documentation and the
@AuthenticationPrincipal
annotation which is supposed to resolve the Principal from Authentication.getPrincipal().Having the
ServletRequestMethodArgumentResolver
resolving annotatedPrincipal
makes the@AuthenticationPrincipal
andAuthenticationPrincipalArgumentResolver
useless and missleading.Fix spring-projects/spring-security#4151