I have a controller method that handles REST requests as well as STOMP/websocket messages. Something like:
#RequestMapping(value="/test")
#MessageMapping(value=".test")
#SendTo(value="/topic/testresponse")
public ResponseEntity<?> test(Principal principal)
{
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
...
return ResponseEntity.ok(principal.getName());
}
When I invoke this method from a REST client, the Principal and Authentication objects are both populated correctly. However, when I invoke this method from a STOMP/websocket client, the Principal object is populated but the Authentication object is null.
Clearly, Spring is able to get the Authentication object somehow, even in the websocket case, because it populated the Principal object. How can I do it in my code?
The reason I want to know this is that I need to do custom authorization checks in code that is NOT invoked as a controller method.
I am using Spring Boot 1.2.2.
Thanks.
Related
I have some methods in a Spring Boot #RestController that magically seem to have access to the Spring Security Authentication or Principal objects when I add them as arguments. I am wondering, how do my methods work with these arguments? Where do they come from?
Here is an example:
#GetMapping("/someEndpoint")
public ObjectNode someEndpoint(Authentication authentication) {
...
CustomAuthentication customAuthentication = (CustomAuthentication) authentication;
logger.debug("Name: {}", customAuthentication.getName());
...
}
or
#GetMapping("/anotherEndpoint)
public ObjectNode anotherEndpoint(Principal principal) {
logger.debug(principal.getName());
...
}
It isn't just these Authentication and Principal objects either. I've also seen HttpServletRequest and other arguments sometimes in these controller endpoints. Where do they come from and why are they optional? Is there a list somewhere of these objects I can get in my methods?
Answer to all your questions are available in the Spring Reference Documentation
For Authentication related question do read through to understand on a highlevel how it is made available in the session. Read on AbstractAuthenticationProcessingFilter
Read through the documentation for the list of supported controller method arguments here
Spring MVC Reference documentation
Spring Security Reference
My SpringBoot application is a packaged software application, to customize it I want to manipulate the authentication object when users first login, and I expect this object would be pushed back to the user's session for subsequent connection.
I managed to use an Around advice to intercept a REST endpoint that will be triggered when first login:
#Around("execution( * com.myproject.CurrentUser.get(..)))"
public ResponseEntity getCurrentUser(ProceedingJoinPoint pjp) throws Exception {
SecurityContextHolder.getContext().setAuthentication(getNewAuthentication());
((ServletRequestAttributes) RequestContextController.currentRequestAttributes())
.getRequest().getSession().setAttribute(HttpSessionSecurityContextRepository.SPRING.SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext());
ResponseEntity response = (ResponseEntity) pjp.proceed();
return response;
}
The getNewAuthentication() method is confirmed OK, it returns a PreAuthenticatedAuthenticationToken that includes additional authorities.
However in the subsequent REST calls when I check the Security Context object the authentication is still the original one.
May I know what would be the proper way to do this? I need to manipulate the authentication object at the very beginning and make sure the subsequent calls will make use of it.
Any idea?
I have a Spring Boot application that is setup as a Service Provider. My end goal is to be able to call the AWS STS Assume Role with SAML service to generate AWS temporary credentials on behalf of the user with the SAML response used to initially authenticate users of my application.
I found this other question. With that answer I am able to get only the assertion, not the entire response. From my testing, the AWS API call linked above wants the entire response, not just the assertion piece.
I used this Chrome Extension to view the SAML response. When I include everything (outline below)
<samlp:Response>
...
<saml:Assertion>
...
</saml:Assertion>
</samlp:Response>
The AWS STS Assume Role with SAML works. The other related question's answer only provides me the
<saml:Assertion>...</saml:Assertion>
block and the AWS STS Assume Role with SAML fails.
So my question is how do I get the entire SAML Response XML object back in a controller of my Spring Boot application?
I don't know any direct way in spring-security-saml, but maybe you could try to implement your own SAMLProcessingFilter ie simply extending the existing one and overriding the method attemptAuthentication().
Principle:
In this method, you have access to the response returned from the IdP and post back to the SP (at least in a Redirect-POST profile)
You probably have a way to extract what you need from the httpRequest
Then you can store (session, ThreadLocal variable, ...)
And finally you delegate the authentication process to the parent (by calling super.attemptAuthentication())
`
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if ("POST".equalsIgnoreCase(request.getMethod())) {
String samlResponse = request.getParameter("SAMLResponse");
System.out.println("Original SAML Response (base64 decoded) : " + new
String(Base64.getDecoder().decode(samlResponse), StandardCharsets.UTF_8));
}
return super.attemptAuthentication(request, response);
}
`
I am using SpringBoot 2.0.5 and Spring Security OAuth to implement an OAuth 2.0 server and a set of client microservices.
In the AuthServer:
I have implemented the UserDetailsService so I can provide my custom enriched principal.
For the userInfoUri controller endpoint, I return user (my principal) and authorities as a map.
In the Client:
I have implemented PrincipalExtractor to extract and create my custom principal.
For each of the methods I require the principal, I use the following notation:
public List<Message> listMessages(#AuthenticationPrincipal MyPrincipal user)
This works (and I hope it's the right way) but now I'm having an issue to secure methods using scopes.
For example, if I want to have a controller method which is only accessible by another server (using client_credentials), I mark the method with the following annotation:
#PreAuthorize("#oauth2.hasScope('trust')")
But this results in an access error as I think the scope is not being transferred. I have added the scope to the userInfoUri endpoint but am unsure what I need to do on the client side so the scope is picked up.
Any pointers or example code would be very much appreciated.
I've wrote a web app with its brave controllers and handler mapping, everything with Spring 3.0 and controller annotations. Now turns out that I need simple and custom autentication. I don't want to use ACEGI for the moment, because I've no time to learn it. I'd like ideally that I could have a routine that gets called before every mapped handler, gets from the HttpSession the userId, checks if he is logged in and the session key and if not redirects to a login page. I've been thinking about an interceptor... the problem is that you have to use HandlerInterceptorAdapter, which has the following method:
public boolean preHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
that won't let me access the HttpSession associated with the request. How do I solve this?
Are you sure? You should be able to obtain the session through request.getSession().