Spring MVC - allowing requests from localhost only to specific controller
Asked Answered
D

5

18

I have a specific controller (among many other controllers). I would like to allow requests to this controller that are being invoked from localhost only. Whats the best way to do this?

here is the controller:

@Controller
public class LocalProvider {

@RequestMapping(value = "/someURL", method = RequestMethod.POST)
@ResponseBody
public responseDTO doSomethingForLocalRequest(@RequestBody ReqDTO reqDTO ) {

//do something
}

EDIT :

Succesffuly achieved that by adding the following to spring security.xml:

<intercept-url pattern="/someURL/*" access="hasIpAddress('127.0.0.1')" />
Dixiedixieland answered 23/4, 2014 at 8:31 Comment(0)
C
14

I would create a custom annotation @LocalhostOnly and a MVC interceptor that would check if handler method is annotated with @LocalhostOnly and in that case check if remote ip address fetched from the HttpServletRequest.getRemoteAddr() is indeed localhost.

If you're using spring security then, as NimChimpsky suggested, it might be better plug in remote ip check into that. You could define a custom permission evaluator that checks remote ip address.

You could also use servlet filter and do the localhost check there for a specific URL (e.g. /someURL**).

Lastly, be aware that if you'll be running the application behind a reverse proxy at some point, all the requests will look like they arrived from localhost (that is, if reverse proxy is installed at the same host). In that case you'll need to pick up the ip address from X-Forwarded-For header.

EDIT

Spring security actually has ip checking expression hasIpAddress('127.0.0.1') so NimChimpsky's answer is probably the best way to go.

Colon answered 23/4, 2014 at 8:48 Comment(2)
how would you implement "MVC interceptor that would check if handler method is annotated with @LocalhostOnly"?Psychoactive
You can check it using handler Object that is passed as the argument in the interceptor's preHandle method. Take a look at answer here for a similar scenario: #22931574Erlindaerline
I
8

spring-security provides @PreAuthorize annotation that can be used on type or method so an alternative to <intercept-url> can be @PreAuthorize("hasIpAddress('127.0.0.1')")

Imitable answered 22/12, 2015 at 8:36 Comment(0)
F
6

To restrict access to your whole webserveryou could use

<Connector port="8080" address="127.0.0.1" maxHttpHeaderSize="8192"

In server xml of tomcat (or similar in a different app server).

For one app use add allow="localhost" to the context :

<Context>
    <Valve className="org.apache.catalina.valves.RemoteHostValve" allow="localhost"/>
</Context>

But for specific controller methods, you'll be best using spring security.

Forenamed answered 23/4, 2014 at 8:37 Comment(0)
P
2

Solution is to use the following expression:

@PreAuthorize("#request.getRemoteAddr().equals(#request.getLocalAddr())")

As first comment suggested, Failed to evaluate expression error will show using #request if @P("request") HttpServletRequest parameter is missing.

Full solution:

@PreAuthorize("#request.getRemoteAddr().equals(#request.getLocalAddr())")
@PostMapping("/doSomething")
public void myMethod(@P("request") HttpServletRequest request) {
    ...
}
Pyrolysis answered 20/3, 2016 at 14:15 Comment(2)
This will only work if you have in your Spring Security Configuration EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true and if you pass HttpServletRequest as parameter @P("request") in the method i.e.@PreAuthorize("#request.getRemoteAddr().equals(#request.getLocalAddr())") @RequestMapping(value = "/") public String getMethod(@P("request") HttpServletRequest) { return ... ; }Flagitious
see also linkFlagitious
E
0

For people with limited knowledge of Spring or Spring Security, follow these steps (applicable for Spring Security v6):

#1. Add Spring Security into your project. Maven example:

<dependencies>
    <!-- ... other dependency elements ... -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
</dependencies>

#2. Add a Spring Security configuration class to your project, e.g. SecurityConfig. Put all your Spring security configurations there:

@Configuration
@EnableWebSecurity
@EnableMethodSecurity // this will enable the method security required for the @PreAuthorize annotation
public class SecurityConfig {    
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.httpBasic().disable(); // this will disable the login/pass requirement
        return http.build();
    }
}

#3. Use @mbaranauskas solution:

@PreAuthorize("#request.getRemoteAddr().equals(#request.getLocalAddr())")
@GetMapping(path="/test-local")
public @ResponseBody String testLocal(@P("request") HttpServletRequest request) {
    // body of your method
}
Elohist answered 22/7, 2023 at 12:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.