Spring MVC custom authorization/security for controller methods - java

Looking to create a custom implementation to authorize requests.
I am evaluating options to avoid using annotations on every method in the controller.
Instead, I am exploring the possibility centralizing this feature via a filter that checks if the logged in user has the required permission for the URL
We are using Spring 3.2.5.RELEASE
I intend to create a database table that has a mapping between a permission and the relevant URL.
I am looking for a way to get the request mapping information for the current URL.
For E-g :
In my database, I have:
URL=/view/{id} , permission=VIEW_USER
If the current URL is :
/app/user/view/1
, a method annotated with
#RequestMapping(value = "/view/{id}", method = RequestMethod.GET)
will be invoked. But before this happens, I need to verify if the logged in user has the permission to view user details.
In my filter, I can get the current URL (/app/user/view/1) , how do I get the corresponding mapping (/view/{id}) ? Is there a mechanism to match a URL to its corresponding MVC request mapping ?
I have looked/am looking at related posts on SO and also looked at Spring code but am yet to find a way.

If you want to do it that way, you could register MVC interceptor instead of servlet filter.
Create a class that extends HandlerInterceptorAdapter and in preHandle method you will have access to controller's method and it's annotation. Prehandle method of your interceptor could look something like this:
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod method = (HandlerMethod) handler;
if (method.getMethod().isAnnotationPresent(RequestMapping.class)) {
RequestMapping rqm = method.getMethodAnnotation(RequestMapping.class);
String[] urlMappings = rqm.value();
//do security logic
}
}
...
}
Then you need to register the interceptor. If you use xml config it's done like this:
<mvc:interceptors>
<bean class="com.example.MySecurityInterceptor" />
...
</mvc:interceptors>
Please note that your approach will be difficult, you'll need to handle all the request mapping cases that spring supports. For example, #RequestMapping that's annotated on class level. Or #RequestMapping annotated on parent class of the controller etc..

Related

How to modify query param in postfilter in Jersey 2

I am trying to modify the query param in a post-matching filter. I cannot do this in pre-matching filter as the filter logic depends on the resource class and method annotations. I am using Jersey 2.x and Spring Boot.
I cannot modify the query param by calling setRequestUri as it is allowed only in pre-matching filter (reference).
I do not want to set an attribute, as that would require changing all existing services to read from the attribute. If no other option is available, I might need to go with the attribute approach.
Filter is registered by calling ResourceConfig#register(cls, priority)
The filter class implements ContainerRequestFilter.
public class MyFilter implements ContainerRequestFilter {
#Override
public void filter(final ContainerRequestContext requestContext) {
// modifiedUri is the uri with modified query params
// requestContext.setRequestUri(modifiedUri) ==> Throws IllegalStateException - "Method could be called only in pre-matching request filter."
}
}
I tried searching online and found solutions that sets request attributes.
I found another solution which suggests creating a wrapper (HttpServletRequestWrapper). But in the above filter, I do not have access to the filterChain. Since the filter requires access to the resource class and resource method annotations, I believe I need to use a Jersey filter and cannot use the Servlet filter (Reference).

Spring GET and POST mappings in separate classes

We are attempting to separate our GET and POST #RequestMapping methods in our Spring controllers into two separate classes.
The reason is that we want the POST calls to have an exception handler which serializes responses as JSON payloads, while the GET calls should be bubbled up through the Spring stack.
However, when we attempt to separate these, we receive errors suggesting that the mappings are being registered twice:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping#0' defined in OSGi resource[classpath:/dispatcher-servlet.xml|bnd.id=21|bnd.sym=com.company.application]: Initialization of bean failed; nested exception is java.lang.IllegalStateException: Cannot map handler 'settingsController' to URL path [/settings.html]: There is already handler of type [class com.company.application.controller.SettingsModelAndViewController$$EnhancerBySpringCGLIB$$54324809] mapped.
Is it possible to separate GET and POST request mappings into two different classes? Basically we want (excuse the pseudo-naming conventions):
class PostHandler {
#ExceptionHandler
public void handleException(...) { // Serialize to JSON }
#RequestMapping(value = "/settings.html", method = RequestMethod.POST)
public void saveChanges() { ... }
}
class GetHandler {
#RequestMapping(value = "/settings.html", method = RequestMethod.GET)
public ModelAndView getSettings() { ... }
}
But currently are unable to find a way around Spring's double-mapping complaints.
Looking at the design and code for DispatcherServlet that routes a URL to a Controller (actually to a HandlerAdapter interface), it certainly seems possible, but not easy and not by existing HandlerMapping classes (look at existing classes implementing this interface at https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/HandlerMapping.html). You would have to write a HandlerMapping class (existing handler mappings' code can guide you with this), which would return the right controller based on the URL and HTTP method and configure it (this link should help with HandlerMapping configuration: http://www.baeldung.com/spring-handler-mappings). None of the current HandlerMapping classes look at the HTTP method when choosing a controller for a URL.
You may be able to tweak the GET and POST request mapping, by adding let's say a wildcard to one of the HTTP method handlers (e.g. How do I set priority on Spring MVC mapping?), but not by using the exact same URL in 2 different controllers.

Why doesn't DispatcherServlet invoke my HandlerInterceptor?

I know that in JavaEE, filters can intercept any request to a servlet. But Interceptors in Spring MVC are not exactly the same. If you look at the diagram below, you will see that Interceptors come after Dispatcher Servlet.
Let me give you an example before I ask my question.
I have a controller, which has 2 methods in it that are mapped to two different requests. One accepts GET requests and other accepts POST requests. Now if I add an interceptor in my web application, that interceptor will sit before Controller. Meaning that before controller method is hit, first a request will hit my interceptor's preHandle method.
Now say that in my app, two controllers methods look like this:
#Controller
public class myController{
#RequestMapping(value = "/test", method = RequestMethod.GET)
public String test1(){
return "abc";
}
#RequestMapping(value = "/login", method = RequestMethod.POST)
public String test1(){
return "xyz";
}
And lets say I have a simple interceptor like this:
public class URLInterceptors extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("REQUESTED SERVLET PATH IS: " + request.getServletPath());
return true;
}
}
Now, if I make a GET request to /test, my interceptor is hit and it prints the servlet path, but when I make a GET request to /login, I know it will fail because my method that handles /login mapping accepts only POST requests, however before it throws '405 Request method 'GET' not supported' error, it should at least hit first my interceptor? It doesn't. I I don't want to change POST to GET. So the question is why?
Part of this is explained in
Why does Spring MVC respond with a 404 and report "No mapping found for HTTP request with URI [...] in DispatcherServlet"?
In summary, the DispatcherServlet attempts to find an appropriate handler for your request by using a HandlerMapping (see your graphic). These handlers are actually adapters that wrap the actual handler method (a #RequestMapping annotated method in this case) with the interceptors you've registered. If this handler is found, then the DispatcherServlet can proceed, invoke interceptors, and, if required, invoke your handler method.
In your case, because your #RequestMapping is restricted to POST requests and your request is a GET, the DispatcherServlet fails to find an appropriate handler and therefore returns an error before it's had a chance to invoke any interceptors.
Note that the javadoc states
A HandlerInterceptor gets called before the appropriate HandlerAdapter
triggers the execution of the handler itself.
but your DispatcherServlet never found a handler to begin with.
You might want to consider using a Servlet Filter instead.

Spring boot - Controller catching all URLs

I am building a Spring boot web app and am using annotations for controller/url mapping.
I have several controllers annotated with #RequestMapping with the url value set (both empty strings and specific URLs) which are working fine e.g.
#Controller
#RequestMapping("/accounts")
class SignInController {
#Autowired PartyService partyService
#RequestMapping(value="", method = RequestMethod.GET )
public String signinPage( Model model) {
Navigating to /accounts renders the sign-in page correctly.
However, if I add a controller with no RequestMapping values e.g.
#Controller
class CustomController {
#RequestMapping
public String transform( Model model ) {
Then any URL I enter that doesn't match any other specific controller is getting handled by this controller (so pages I would expect to 404 all just renderthis page). Is this expected behaviour? I was not expecting this, and as the RequestMapping value defaults to empty and is an antMatcher I wouldn't have thought it would handle all other URLs.
The reason I have this controller with out RequestMapping defined is because I want to also have a SimpleUrlMappingHandler defined with some explicit URLs going to that controller, and if I don't include the #Controller & #RequestMapping annotations to that controller then I get an error about not being able to find the handler method (maybe the problem is that I have mis-understood the implementation details of that).
Should my custom controller be handling all URLs? If so, is there something I can do so it doesnt and only gets called for the explicit SimpleUrlMappingHandler I have defined?
As mentioned in the comments - I just removed the #Controller annotation from the class, and then explicitly defined the controller as a #Bean in my config class and explicitly assigned that to the mapping in the SimpleUrlMappingHandler configuration

Doing pre-checks before calling controller in spring MVC

I recently started working on a spring MVC project which uses spring security.
I had to do some pre-checks before user's request gets to the controller.
This is what I want to achieve, like I have worked a lot in struts and in struts we can extend all the action classes to a superclass let's say BaseAction and then write some validation here so that they gets called before calling the sub class methods.
I would like to achieve same thing here but don't know how to start.
I cannot use filters as i need to make database calls and web-service calls in pre checks.
I just need the pointers .
You can implement an interceptor using HandlerInterceptorAdapter.
http://static.springsource.org/spring/docs/3.0.x/reference/mvc.html#mvc-handlermapping-interceptor
Configuring the applicationContext in XML.
<mvc:interceptors>
<bean class="my.package.MyInterceptor" />
</mvc:interceptors>
The interceptor.
public class MyInterceptor extends HandlerInterceptorAdapter {
public boolean preHandle(
HttpServletRequest request,
HttpServletResponse response, Object handler) {
// your logic
return true;
}
}
Returns true if the execution chain should proceed with the next interceptor or the handler itself. Else, DispatcherServlet assumes that this interceptor has already dealt with the response itself.

Categories