0

Suppose there are two type of roles in the application -

  1. Admin
  2. Zonal Manager

Admins can get all the office ids while the zonal managers can get only the office assigned under his zone. In the controller I want something like this

@RequestMapping(method = RequestMethod.GET)
Collection<Long> getOfficeIds(){
    // returns all office ids in system
}

@RequestMapping(method = RequestMethod.GET, value = "/{zoneId}")
Collection<Long> getOfficeIds(@RequestParam("zoneId") long zoneId){
    // returns all office ids in the zone
}

Now I want all my users to make request with the no-arg version only (the first method). The system should get user role before hitting controller and should call appropriate controller method (if admin then call the first method, if zonal manager call the second one with appropriate zone).

The question is , is it possible at all ? If yes then what would be the best way of doing this ? I could try to modify the request in a servlet filter. Is there a way using method argument resolver ?

Xcodian Solangi
  • 2,342
  • 5
  • 24
  • 52
dumb_terminal
  • 1,815
  • 1
  • 16
  • 27
  • Why not just put the logic of fetching all office ids or the ones the zonal manager owns (if they are one) in the first method body? You can get the context by adding a filter to the request that runs before it is handle. The filter would populate the ThreadLocal with the account context. – Andonaeus Oct 04 '17 at 20:17
  • I could do that, but there could be more method like this, i didn't want if else in all of the methods, thought it could produce cleaner controller if I could achieve this. any suggestion or alternative ways to eliminate the if-elses will be greatly appreciated. – dumb_terminal Oct 04 '17 at 20:20
  • Perhaps you can follow the same URL pattern you have laid out in all your controllers. Then, in a filter before the request is handled, you can check the role of the requesting user and set the context accordingly. Then, you can change the route of the request before it is handled (by appending the managed zone Id). This way, you can keep all the role-based logic in one place and have the controllers worry about their own flow. I have not tried this, but it is what I would do. – Andonaeus Oct 04 '17 at 20:31
  • That seems to me a great solution, the one I had in mind exactly. The question is how to change the route ? I hear HttpServletRequestWrapper is the way to go. Can you please shed some light here ? – dumb_terminal Oct 04 '17 at 20:34
  • 1
    I recall having used the before, but I do not have access to that codebase anymore. The steps laid out here seem to be pretty straightforward: https://stackoverflow.com/questions/2725102/how-to-use-a-servlet-filter-in-java-to-change-an-incoming-servlet-request-url – Andonaeus Oct 04 '17 at 20:36
  • 1
    Xtreme Biker's answer is basically what you would do (with your own calculated URL, the logic for which would precede the forward, obviously). – Andonaeus Oct 04 '17 at 20:39
  • Sounds promising. let me get back to you on this. :) – dumb_terminal Oct 04 '17 at 20:41
  • 1
    The way we implement filters using spring is by creating a bean which extends the OncePerRequest Filter, e.g. `@Component("accountContextFilter") public class AccountContextFilter extends OncePerRequestFilter { ... public void doFilterInternal(){ //logic } ... }` – Andonaeus Oct 04 '17 at 20:42
  • 1
    Spring Security provides a solution for this kind of situation, search for `@PreAuthorized` and `@PostAuthorized` but in your case, `@PreAuthorized` seems the good solution. – akuma8 Oct 05 '17 at 07:25
  • Yes trying out preauthorize, but for now went with writing a filter which will add a header and mapped the request according to header presence. Also had to write a method argument resolver to resolve the custom header to method parameters. – dumb_terminal Oct 05 '17 at 07:42
  • @Andonaeus as I am currently going your way, could you post it as an appropriate answer ? – dumb_terminal Oct 05 '17 at 07:43
  • You got it, and thanks! – Andonaeus Oct 05 '17 at 15:12

1 Answers1

1

Per comments, I am posting the answer below.

The best thing to do to achieve your goal is to add a filter which runs before the request is handled by the controller. In this filter, you can apply the appropriate logic to determine the requesting user's role and act accordingly. If you follow the same URL pattern in all of your controllers to handle these different cases, you can simply rewrite the internal URL after determining which case to apply so that it can be handled by the appropriate controller. In this way, you can keep all of your user-role logic in one location and your controller logic can handle their own, separate flows accordingly.

To create such a filter using spring, you may do something like the following:

@Component("accountContextFilter") public class AccountContextFilter extends OncePerRequestFilter { 

    public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException{
        //user role and routing logic
    }
}

The reference for the logic inside doFilterInternal can be found here: How to use a servlet filter in Java to change an incoming servlet request url?

Simply change the request path accordingly by appending to your route with the role-defined URLs and you're done.

Andonaeus
  • 872
  • 1
  • 5
  • 21