How can I set global context variables in Spring? - spring

So I have a Spring boot application with many api requests.
For a large number of these requests I know want to log the "user-agent" part of the header from the request.
One solution is to do this.
In my controllers I could just put #RequestHeader("user-agent") String userAgent and pass it on to the service layer to be logged.
But it would be much handier if I could add the user agent as a global variable, just like the username is added as a global variable through the SecurityContextHolder.
So my question is, is it possible to add custom global context variables like the authentication details from the Authentication filter class? And if so how?

If you are using Spring MVC then you can Autowire HttpServletRequest and get the request headers from it.
#Service
public class HelloService {
#Autowired
private HttpServletRequest httpServletRequest;
public void print() {
System.out.println(httpServletRequest.getHeader("x-test"));
}
}
Alternatively you can also get hold of request instance from RequestContextHolder:
((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest().getHeader("x-test");

Related

Where do the Spring Security Authentication and Principal objects come from in a controller's methods?

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

HTTP Basic Authentication in Spring Web Services Integration Tests

I have a SOAP web service written in Spring Web Services that I would like to integration test.
I would like to use spring-ws-test as the reference documentation points to. So, the test code is similar to the example in the reference, something like that:
#Autowired
private ApplicationContext applicationContext;
private MockWebServiceClient mockClient;
#Before
public void createClient() {
mockClient = MockWebServiceClient.createClient(applicationContext);
}
#Test
public void customerEndpoint() throws Exception {
Source requestEnvelope = new ResourceSource(new ClassPathResource("request.xml"));
Source responsePayload = new ResourceSource(new ClassPathResource("response.xml"));
mockClient.sendRequest(withSoapEnvelope(requestPayload)).
andExpect(payload(responsePayload));
}
However, the endpoint I am testing is using basic authentication and it expects to read values in the Authorization header. It is not using spring-security for that task but it has custom logic that gets the HTTP headers by getting the HttpServletResponse from the TransportContextHolder. So, the request triggers the endpoint but it fails to retrieve the basic authentication base64 token.
The question is, how may I pass HTTP headers in that situation? Is it possible at all? If not what is the preferred alternative?
I have read the javadoc and I cannot find a way to pass the headers. Also, I have found this question which is similar but it doesn't help me much.

How to handle Privilege Escalation in Spring application

We have an application with 3 different user roles as User, Author & Admin. Each access role is having different set of menus and screens.
Issue is even though Menu item is hidden if we capture the admin URL and past it in user login it is opening the page. All action on the page will not work but still we are planning to restrict the page opening as well.
Only way I could see how we can handle is write a condition in each action to validate the access before opening the page. But with this approach we should touch many files, is there any best way to handle this situation.
Our application is written using Spring MVC framework.
Thanks.
You asked:
But with this approach we should touch many files, is there any best
way to handle this situation.
From Spring MVC HandlerInterceptor javadoc:
"Applications can register any number of existing or custom
interceptors for certain groups of handlers, to add common
preprocessing behavior without needing to modify each handler
implementation."
What you may do:
Write a custom HandlerInterceptor which should extend
HandlerInterceptorAdapter.
Override the boolean preHandle() method. This method is invoked just before the handler is invoked. So you can check access of logged in user (maybe from session). You can write a custom response from within this method.
Register the interceptor in your dispatcher-servlet.xml.
For example:
public class AuthInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
String uri = request.getRequestURI();
User user = (User) request.getSession().getAttribute("foo"); //for example
if (...) { //check access to this uri, if access fails
response.sendRedirect("/to/some/url");
return false;
}
return true;
}
}
And register this HandlerInterceptor to dispatcher-servlet.xml:
<mvc:interceptors>
<bean class="your.package.AuthInterceptor"/>
</mvc:interceptors>
You can configure this interceptor to be more url-specific. See Spring Reference and Spring javadoc.

How can I extend the parameters of the OAuth2 authorization endpoint?

I'm having some trouble regarding the authorization endpoint of my Spring based OAuth2 provider. I need more information from the client than there is currently possible. This is what I want to achieve:
I need the custom parameter in the authentication process later on. Is there any simple way to extend the default parameters with my custom one or do I need to implement a certain class myself?
Did some research on how the authentication endpoint works in the current Spring code. I found that the Authorization Endpoint uses a method named authorize that takes all the parameter that are being set and converts then into an AuthorizationRequest. While looking further into the AuthorizationRequest class I found that it holds a map with extensions that is being filled throughout the authorization process. But it does not seem to get filled with my custom parameter (as shown above). This is in fact by only looking at the code, so I might be wrong.
Would it be a good idea to extend the AuthorizationEndpoint with my custom implementation or is there a better and cleaner way to do this?
Update #1 (07-10-2015)
The place where I'd like to use the custom parameter is in my own implementation of the AuthenticationProvider. I need to information to be available inside the authenticate method of this class.
Update #2 (07-10-2015)
It seems that the AuthorizationProvider gets called before the AuthorizationEndpoint. This means that the custom parameter is obtained after the class where I need it (so that's too late).
Maybe I can get the referral Url by either extending part of the Spring security classes or by obtaining it in the HTML through JavaScript. Is this a good idea or should I use another approach?
So I managed to fix the problem myself by searching some more on Google.
What you need to do is speak to the HttpSessionRequestCache to get the referral URL. This is how I solved it in my own implementation of the AuthenticationProvider
#Component
public class CustomProvider implements AuthenticationProvider {
#Autowired
private HttpServletRequest httpRequest;
#Autowired
private HttpServletResponse httpResponse;
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
SavedRequest savedRequest = new HttpSessionRequestCache().getRequest(httpRequest, httpResponse);
logger.info("Referral URL: " + savedRequest.getRedirectUrl());
logger.info("Parameters: " + savedRequest.getParameterMap().keySet().toString());
}
}
This will print out the URL of the request that was called before heading to the login page of spring security. The second log method prints out the parameters that where found in this URL. This question and answer helped me in creating a solution for my problem.

How to get source address / ip from inside ContainerResponseFilter

I'm writing a logging filter that logs all HTTP requests / responses for a web app running in Jersey. ContainerResponseFilter seems to a straight forward solution and I've managed to get it to work.
Next step is to log the IP of the requests. Is there a way to do that from inside the ContainerResponseFilter ?
Short answer:
#Provider
public class YourContextFilter implements ContainerRequestFilter {
#Context
private HttpServletRequest sr;
#Override
public synchronized void filter(ContainerRequestContext request) throws IOException {
/*
* Returns the Internet Protocol (IP) address of the client or
* last proxy that sent the request. For HTTP servlets, same as
* the value of the CGI variable REMOTE_ADDR.
*/
String ip = sr.getRemoteAddr();
// ... log it ...
}
}
EDIT
(regarding the wish for a more detailed answer)
Afaig:
The #Context annotation allows to inject JAX-RS–specific components (one might say you are able to inject contextual information objects). JAX-RS itself is a Java based specification for RESTful Web Services over HTTP protocol. So we are able to inject stuff like:
javax.ws.rs.core.UriInfo
javax.ws.rs.core.Request
javax.ws.rs.core.SecurityContext
and also
javax.servlet.http.HttpServletRequest
In the IOC Chapter of the Jersey docs, you will find these notes:
[...] Jersey implementation allows you to directly inject HttpServletRequest instance into your JAX-RS components [...] - https://jersey.java.net/nonav/documentation/latest/user-guide.html#d0e2401
[...] The exception exists for specific request objects which can injected even into constructor or class fields. For these objects the runtime will inject proxies which are able to simultaneously server more request. These request objects are HttpHeaders, Request, UriInfo, SecurityContext. These proxies can be injected using the #Context annotation. [...]
[...] When deploying a JAX-RS application using servlet then ServletConfig, ServletContext, HttpServletRequest and HttpServletResponse are available using #Context. [...]
And if you do so, you inject in fact a Proxy named org.apache.catalina.connector.RequestFacade (link). This proxy functioned as your direct hotline to your Coyote (HTTP Connector) and thereby to the Coyote request object (link).
Hope this was helpful somehow :) - Have a nice day.

Resources