I recently had to create a (micro-) service, offering APIs to a user frontend and to other internal services. The frontend apis were supposed to require authentication by the user/browser and the internal apis were to work without authentication. But because the internal apis represented sensitive functionality, they should only by available to internal services.
When creating a HTTP/REST microservice with Spring, the service usually binds to one tcp port. So every client which is able to connect to this port is also able call all the endpoints of the services. This poses a problem, when one needs to offer private and publics apis on the same service.
There are several options how to control access to different endpoints. One could create different services and bind them to different hosts or ports and have a firewall control the access. Or one might have a reverse proxy in front of the service, and regulate access with it.
One easy way is to have the same service offer different apis on different ports. It requires some more advanced configuration of spring, but has the advantage of keeping all functionality in one service. The only external requirement for limitting access to the private apis is some kind of firewalling to restrict access to the private tcp port.
Here is how it’s done with Spring.
Different path-prefixes for internal apis
It all starts with two controller methods, one offering an internal endpoint, the other one offering an external endpoint.
1 2 3 4 5 6 7 |
|
1 2 3 4 5 6 7 |
|
Note, that one controller serves under a path starting with /external
, the other under /internal
. This will be used later to systematically distinguish requests.
By default, these two endpoints are available at the same port and therefor it’s difficult to restrict access to the internal endpoint.
Listen on multiple ports
To change this, we first have to make spring listen on a second port. The internal tomcat server allows to listen on additional ports through
a WebServerFactoryCustomizer
, which can be provided as a bean.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
|
With this configuration, the internal tomcat listens on two ports, and all endpoints are available on them. Half way there. All that’s left to do is restricting endpoints to a certain port.
Filter requests based on path and port
With spring, all incoming requests can be filtered, before they reach a controller. Filtering means, requests can be modified and answered, and the filter can decide, if the request either should be passed on for further processing, or if processing ends with it.
To write a filter, one must implement Filter
and provide public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
.
A Response can be generated and the request can be analyzed. If the processing of the request should continue, filterChain.doFilter
must be called.
Otherwise, the filter just returns.
Here’s one way how to implement a filter in order to filter requests to a internal api based on internal/external ports.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
|
The filter decides based on a configurable prefix of the request path, if this request is for an internal or an external endpoint
and checks, if it arrived on the right port. If so, the request is passed on. If not, it is rejected with a status 404
.
To activate the filter, it must be provided as a bean.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Configuration
Finally, some configuration has to be provided, to define ports and the paths.
1 2 3 |
|
All requests to a path starting with server.trustedPathPrefix
are served on server.trustedPort
and all other requests are served on server.port
.
Conclusion
Spring is incredible flexible and allows quite sophisticated configurations. It is easily possible to configure it to serve different endpoints on different tcp ports. With that one can serve internal apis and external apis using the same spring server instance and control access to them using firewalling.
You can find the complete example at my Github.