Spring MVC Interceptor exclude paths on HTTP method - java

I have some interceptors that need to check the headers and the authorization in some requests done to my API. For example, some requests should require user authentication (for example, to alter user details from the database) and some don't require authentication (for example, to create a user). Unfortunately, the methods for excluding paths from the interceptors don't depend on the request method.
Right now I have created an Util class that receives an array of strings of paths and methods that should be excluded from the verifications. For example: "POST /api/users" should not be intercepted by my authentication interceptor because it is for creating a user, but "PUT /api/users" should be intercepted because it is for altering an existing user (who should be logged in).
public static Boolean skipVerification(HttpServletRequest request, String... skipRequests) {
for (String string : skipRequests) {
String[] split = string.split(" ");
if(split[0].equals(request.getMethod()) && split[1].equals(request.getRequestURI()))
return true;
}
return false;
}
In the constructor of my interceptor, I add the requests that should be skipped and in the PreHandle method I return true if the request matches any one of them as they didn't need to me intercepted in the first place.
public AuthenticationHeaderInterceptor(String...skipWhen) {
this.skipWhen = skipWhen;
}
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
if(InterceptorUtils.skipVerification(request, skipWhen))
return true;
///Do Authentication Logic
}
I know that changing the URI for creating and editing users would solve this, but I prefer not to clutter my API with too many URIs and was wondering if there was a cleaner way to solve this.

Looking at the code, I was checking if you would be able to extend InterceptorRegistry, InterceptorRegistration, and MappedInterceptor. You would also have to extend PathMatcher and incorporate code to check methods as well as paths, which PathMatcher isn't meant for. So overall, the way you are doing it currently is probably best. I would also point out the code in Spring's AntPathMatcher has always been very messy and buggy so you probably wouldn't want to take on any task that involved dealing with it.

Related

Spring boot #PreAuthorize (is it possible to modify the input to the method in #PreAuthorize then pass into the method)

I have a method annotated with #PreAuthorize(...) with some logic that goes away and queries an API for some information about what the user can view. However, I have this endpoint that I need to add this #PreAuthorize annotation into which receives in a more "complex" object which I want to transform slightly (the object contains an array that is some cases I want to add/remove data from).
#PostMapping("/search")
#PreAuthorize("#Service.isAuth(#searchParam)")
public ResponseEntity<Response> search(SearchParams searchParam) {
return service.getSearchResult(searchParam);
}
Is there a way I can modify searchParam inside the #PreAuthorize annotation then have it passed into the method body, I know that this is probably is not the correct way to do this and maybe isn't something that #PreAuthorize wasn't designed for but is there any way of doing this even with a different type of annotation. Obviously worst case I can move the logic into the method body but I would prefer to use an annotation-based solution like #PreAuthorize offers if possible. Thanks for any help even links to other relevant things would be useful I've not found much on google related to this.
I think the best solution is to make a handler/interceptor and then annotate it with #PreAuthorize. So I think you are in the right track but you need to make sure that you modify your code to implement the HandlerMapping interface to create the interceptor and then override the prehandle method. After you need to annotate it with #PreAuthorize programatically. The last thing will be to use a wrapper to modify the HttpWrapper, it cannot be done manually. Here links to the relevant resources in order:
Creating a Handler/Interceptor: https://www.baeldung.com/spring-mvc-handlerinterceptor
Using PreAuthorise in the interceptor: How can I require that all request handlers in my Spring application have #PreAuthorize
To modify the HttpServlet request you will need a wrapper: How to modify HttpServletRequest body in java?
Have a try, hopefully that works.
Snippet of code taken from second link uses a programatic PreAuthorize rather than annotation:
public class PreAuthorizeChecker implements HandlerInterceptor {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod hm = (HandlerMethod) handler;
PreAuthorize annotation = AnnotationUtils.findAnnotation(hm.getMethod(), PreAuthorize.class);
//TODO use the technique shown on the third link to wrap and modify the HttpServletRequest
if (annotation == null) {
// prevent access to method wihout security restrictions
throw new RuntimeException("Rights are not defined for this handler");
}
}
return true;
}
.....

RESTEasy: How to fall back to other matching resource methods?

In a RESTEasy application I need to determine at runtime if a certain path can be handled by a specific resource method, based on it's path/query parameters. If it can't be handled I want the request to fallback to other methods. E.g.:
#Path("/")
class MyResource {
#GET
#Path("{path : .*}")
public Response handleRedirects(#PathParam("path") String path) {
if (hasRedirectFor(path)) {
return redirectTo(path);
}
else {
//somehow pretend that this method didn't exist and fallback to the next best match
}
}
#GET
#Path("img/{image}")
public Response handleImage(#PathParam("image") String someParam) {
return createImageResponse(image);
}
#GET
#Path("{template : .*\\.html}")
public Response handleTemplate(#PathParam("template") String template) {
return createTemplateResponse(template);
}
}
Is this somehow possible, without having to use a RequestFilter? (I don't want to use a RequestFilter since then I need to implement URL matching for handleSometimes myself).
EDIT: The comments requested to provide more details, so I changed the example to match closer to my real world situation. I have a typical webserver that handles all sorts of requests, in the above example I reduced that to just images and templates. For legacy reasons there are some incoming links from 3rd parties to URLs that don't (or no longer) exists, but which we don't want to break. We therefore want to serve redirects on those URLs, which is what the handleRedirects function is supposed to do.
Unfortunately there is overlap in the patterns for legacy and supported urls, which prevents me from writing #Path annotations to statically route to the correct method. Moreover, I only know if I can actually generate a redirect from legacy -> new when I examine the path at runtime, if this fails I want to fall back to the other methods. I therefore want to be able to determine in the method itself whether I can handle the request or not, and let RESTEasy fallback to the next matching resource if it can't. Usually this is something were filters would come in handy, but then I loose the ability to automatically extract path parameters or to route to different methods, which I really like to keep.

MVC interceptor vs Spring security filter vs something else...?

I'm using Spring-MVC with Spring Security for my web application. It includes user registration pages and private user panel. I have it set up currently with the following URL patterns:
whatever/myapp/login user log in
whatever/myapp/register?step=1 start registration
whatever/myapp/account/** private area views (pages)
whatever/myapp/pending view shown while post-registration processes complete
whatever/myapp/blocked account blocked view
whatever/myapp/register/retry if registration failed, allow retry
Essentially, these URLs below should require user authentication, i.e. require log-in:
whatever/myapp/account/** (private area pages)
whatever/myapp/pending (this page has a timer set to redirect to /account/home)
whatever/myapp/register/retry
This is quite straightforward to achieve using Spring security. However, regardless of user authentication through Spring security, private area pages should be accessible or not, depending on user's current account status (stored in my DB).
More specifically: if a user tries to access anything in the private area (/account/**), he should be shown the appropriate view (redirected to appropriate page), according to the status. I have these statuses defined:
suspended - relates to pending view
enabled - allow full access
disabled - not relevant here
retry_allowed- relates to retry view
blocked - relates to account-blocked view
Currently, I have a MVC interceptor setup to /account/**, that checks user status, and redirects to appropriate pages, but somehow I get the sense that this is not really the ideal or appropriate solution here, since I'm facing strange behavior, like multiple controller invocation... and also I'm not quite certain when to return true / false within preHandle() method. Here's the code snippet from the interceptor:
#Override
public boolean preHandle(
HttpServletRequest request,
HttpServletResponse response,
Object arg2)
throws Exception {
IPanelUser pUser = (IPanelUser) SecurityContextHolder.getContext()
.getAuthentication().getPrincipal();
// check principal first and then load from DB
// "suspended" is initial status upon registration
if(pUser.getCustomer().getStatus() == CustomerStatus.Suspended.getCode()) {
// if suspended, load from DB and update status
Customer customer = this.customerService.getUserByUsername(pUser.getUsername());
if(customer != null)
pUser.getCustomer().setStatus(customer.getStatus());
// still suspended? redirect to pending
if(pUser.getCustomer().getStatus() == CustomerStatus.Suspended.getCode()) {
response.sendRedirect("../pending");
return false;
}
}
if(pUser.getCustomer().getStatus() == CustomerStatus.Blocked.getCode()) {
// redirect to blocked page
response.sendRedirect("../blocked");
SecurityContextHolder.clearContext();
return false;
}
if(pUser.getCustomer().getStatus() == CustomerStatus.AllowRetry.getCode()) {
// redirect to CC submission page
response.sendRedirect("../register/retry");
return false;
}
if(pUser.getCustomer().getStatus() == CustomerStatus.Enabled.getCode() ||
pUser.getCustomer().getStatus() == CustomerStatus.Disabled.getCode()) {
// do nothing
}
return true;
}
.
Is this a valid approach ? Any alternative suggestions ?
All options are valid, it depends on the level of abstraction you want.
In a Filter, you only have access to HttpServletRequest and HttpServletResponse objects, so you are very much coupled with the Servlet API. You also don't (directly) have access to all the great Spring functionality like returning a view to be rendered or a ResponseEntity.
In a HandlerInterceptor, it's again more of the same. You can do your redirection or request handling directly in the preHandle() where you don't have access to the ModelAndView or set a flag which you check in postHandle(). You would have access to the ModelAndView but not to some other Spring MVC functionality.
Spring Security is a good alternative, but I find it has a lot of configuration that I don't like too much.
One final alternative, that I like the most, is to use AOP (you can do this with Spring Security or Shiro as well). You create an annotation like #Private and you annotate your #Controller handler methods. You use AOP to advise these methods. The advice basically checks some session or request attribute for a flag (authorized or not). If you are allowed, you continue executing the handler method, if not, you throw an UnauthorizedException (or similar). You then also declare an #ExceptionHandler for that exception where you have pretty much complete control over how the response is generated: a ModelAndView (and related), a ResponseEntity, annotate the handler with #ResponseBody, write the response directly, etc. I feel like you have much more control, if you want it.

How should I pass subject/principals/roles between application layers in Java?

I currently have a number of web applications which access a common service running in JBoss 5.0. The service is very simple, using Guice and POJOs. The web applications are authenticated and know who the user is and what roles they have. When calling the service how should I pass this authentication information to the service?
It would seem the simple approach is to simply add a parameter to the interface to take the user information. Possibly a Subject. But this has the downside of cluttering up the interface with contextual information that isn't specific to the job in hand.
void doSomething(Subject subject, ...) {
}
The alternative I have seen is to use ThreadLocal storage, put the user information in there before making the call and make this accessible via some utility class that the service can use. This cleans up the interface but hides the fact that the client of the service has to set the user information before making the call.
Is there another way of doing this? I get the feeling the AOP may be of use here too but can't quite see how. Is there some "best practice" I am missing? Would EJB help?
This cleans up the interface but hides the fact that the client of the
service has to set the user information before making the call.
True, but if you need to pass something to a particular method across the application then you are defeating the purpose of using Dependency Injection. It's there so that you don't have to pass a bunch of services and objects to other services and objects and so forth, they are created with everything they need.
Is there another way of doing this? I get the feeling the AOP may be
of use here too but can't quite see how. Is there some "best practice"
I am missing? Would EJB help?
The other way of doing this would be to use a single filter on every Servlet that calls the services that need the Subject / User. Set the user in the filter, and clear the user at the end in a try-finally block. In fact, OWASP Esapi uses this style when setting their ThreadLocalUser, it allows the User to be available in every part of the application.
Something like this:
#Singleton
public MyUserFilter extends FilterOfTheMonth {
private final Provider<Authenticator> authProvider;
#Inject
MyUserFilter(Provider<Authenticator> auth) {
this.authProvider = auth;
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws java.io.IOException, ServletException {
try {
// Authenticate and SET the current user utilizing the request and/or
// session objects
authProvider.get().authenticateUser(HttpRequest currentRequest);
// Continue on here along the servlet chain
... other processing
} finally {
authProvider.get().getRidOfCurrentUser();
}
}
}
Have you considered moving the authentication process to the common service? Then you only need the session ID in the common service to identify all information about the user the request is coming from.

The indestructibles - HTTP Parameters

I always wondered why there exists no removeParameters() method in Servlet API.
What could be the motive behind this design?
Here is a scenario: I am posed with a challenge in a proprietary MVC framework that I am compelled to use. This framework uses a Controller Servlet that hosts an algorithm in it's post method:
doPost() {
//create instance of action - just like struts action
action.init
action.preexecution
if(redirection state is not set)
action.process
action.postprocess
action.finish
}
The only way I can skip process of any particular action would be by setting a redirection url. The Controller Servlet is FINAL. Now, when I do a requestdispatcher.forward from say the preexecution method of an action, the controller will go ahead and execute the rest of the methods and not skip the rest. I cannot change this behavior, neither can I set the redirect, coz I need to do a forward. It works fine as long as I am not forwarding request to the same action. When a request is forwarded to the same action, the http parameters are all the same. This would take it into a never ending loop. Hence, I am compelled to add extra parameters indicating that it is a repeat request and should be treated differently.
Not sure if my problem made sense, but thought this is a good forum to post the same.
Umm... because it would serve no purpose? Request parameters are sent by the client to the server. The server is free to ignore them, but what practical effect would you expect such a removeParameter() method to have?
Edit: Request parameters are meant for the communication between server and client. For server-internal communication, you can use request attributes, which can be set and removed.
EDIT: McDowell reminded me of HttpServletRequestWrapper, so I'm changing the below to make it a little less work... Thanks McD!
You can decorate the request to "hide" parameters you don't want and/or add extra parameters.
Something like (off the top of me head -- no compiling so the API might be a tweak off...)
public class MyParameterHider extends HttpServletRequestWrapper {
public MyParameterHider(HttpServletRequest request) {
super(request);
}
public String getParameter(String name) {
if ("parameterToHide".equals(name))
return null;
return realRequest.getParameter(name);
}
// similar for getParameterNames and getParameterMap - don't include the hidden parm
// all other methods are strictly pass-through and are automatically
// handled by HttpServletRequestWrapper
}
In your forward, just wrap the request in a ParameterHider when calling doFilter:
dispatcher.forward(new MyParameterHider(request), response);
Patterns FTW!
Hope this helps!

Categories

Resources