“This is my 32nd day of participating in the First Challenge 2022. For details: First Challenge 2022”
One, foreword
Microservices need to communicate with each other, which belongs to internal communication, and there are some security problems:
- The internal interface cannot be accessed externally.
- In traditional micro services, internal communication is secure by default and authentication is not required.
At work, an external request transforms an internal request as shown below:
All requests passapi
When the gateway enters and converts to an internal request, the following scenarios occur:
- External request passing
api
Gateway for authentication: for example, user logintoken
mechanism - External request passing
api
Gateway without authentication: For example, to obtain platform public information, add the corresponding interface address to the gatewayignore-urls
In the configuration - Internal requests, trusted by default, generally do not pass through the gateway, by which the call service for load balancing to directly call other services
For example: the administrator passedcms
Delete user
- Normal process: The call is made first
cms
Service, the administrator authentication after the calluser
Service, delete user B. - Exception process: After user A logs in to user B, user A directly deletes user B through the interface.
This article will discuss how to prevent illegal indirect internal access.
Let’s review the service invocationfeign
Usage:
- Service A definition
controller
@RestController
@RequestMapping(path = "/test")
public class TestController {
@GetMapping("/feign")
public String test(a) {
return "test feign success"; }}Copy the code
- Service B definition
feign
@FeignClient(value = "test")
public interface TestClient {
@GetMapping(value = "/test/feign")
String test(a);
}
Copy the code
Service B invokes service A’s interface through feign.
Second, the design
In order to be directly provided by internal interfaces, the following directions can be considered:
api
The gateway translates external requests with labels (or deletes corresponding ones)tag
)- Each invocation of a service carries a corresponding identifier: for example, in
HTTP
中header
Add special parameters - Microservices define interceptors for internal interfaces: they can only be accessed if they are identified as internal interfaces
(1)feign
configuration
Each internal request carries an identifier: X-inner-request :true.
@Component
public class FeignInnerRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
template.header("x-inner-request".true); }}Copy the code
Simply place the configuration in a file specified by a third-party JAR and the user will load it automatically to avoid code intrusion:
- Create a directory under the resource directory
META-INF
- in
META-INF
Create a file in the directoryspring.factories
- Add the following configuration to the file:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
cn.test.config.InternalApiInterceptor, \
cn.test.config.FeignInnerRequestInterceptor, \
cn.test.config.InternalApiConfig
Copy the code
(2) Gateway filtering
Each external request to an internal request carries an identifier: X-inner-request :false.
Gateway is used here:
@Configuration
public class InternalApiGatewayFilter {
@Bean
@Order(1)
public GlobalFilter filterInteralApi(a) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
request = exchange.getRequest().mutate().header("x-inner-request".false).build();
returnchain.filter(exchange.mutate().request(request).build()); }; }}Copy the code
(3) Internal request interceptor for microservices
There are two parts to this:
- Annotation: Used to indicate that methods or classes are accessed internally.
- Interceptor: Used to process interception requests uniformly
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface InternalApi {
/** * Indicates whether the interface is internal. The default value is true *@returnWhether * /
boolean value(a) default true;
}
Copy the code
The interceptor:
@Slf4j
@Component
public class InternalApiInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
InternalApi internalApi = getAnnotation(handler);
if (Objects.isNull(internalApi)) {
return true;
}
boolean isInternalApi = Boolean.valueOf(request.getHeader("x-inner-request"));
if(! isInternalApi) {throw new Exception(); // Throw an exception and no access is given
}
return true;
}
private InternalApi getAnnotation(Object handler) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
InternalApi internalApi = handlerMethod.getMethodAnnotation(InternalApi.class);
if (Objects.isNull(internalApi)) {
internalApi = handlerMethod.getMethod()
.getDeclaringClass().getAnnotation(InternalApi.class);
}
return internalApi;
}
private <A extends Annotation> A getAnnotation(HandlerMethod handlerMethod, Class annotationType) {
A annotation = handlerMethod.getMethodAnnotation(annotationType);
if (Objects.isNull(annotation)) {
return handlerMethod.getMethod()
.getDeclaringClass().getAnnotation(annotationType);
}
returnannotation; }}Copy the code