spring boot中统一对响应做处理
场景:
现有一组对外提供服务的http api,返回一个Result,序列化后json如下
{"code":200,"message":"成功","data":null}其中data为业务数据,为了避免在返回给终端用户之前,被非法劫持篡改,需要在返回之前,通过一个约定的算法,生成一个签名,将该签名放入响应的header中。前端接受到返回后,需要比较body和签名是否匹配。
解决办法:
实现ResponseBodyAdvice接口。
实现思路:
我们查看ResponseBodyAdvice的源码。
/*
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.servlet.mvc.method.annotation;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.lang.Nullable;
/**
* Allows customizing the response after the execution of an {@code @ResponseBody}
* or a {@code ResponseEntity} controller method but before the body is written
* with an {@code HttpMessageConverter}.
*
* <p>Implementations may be registered directly with
* {@code RequestMappingHandlerAdapter} and {@code ExceptionHandlerExceptionResolver}
* or more likely annotated with {@code @ControllerAdvice} in which case they
* will be auto-detected by both.
*
* @author Rossen Stoyanchev
* @since 4.1
* @param <T> the body type
*/
public interface ResponseBodyAdvice<T> {
/**
* Whether this component supports the given controller method return type
* and the selected {@code HttpMessageConverter} type.
* @param returnType the return type
* @param converterType the selected converter type
* @return {@code true} if {@link #beforeBodyWrite} should be invoked;
* {@code false} otherwise
*/
boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType);
/**
* Invoked after an {@code HttpMessageConverter} is selected and just before
* its write method is invoked.
* @param body the body to be written
* @param returnType the return type of the controller method
* @param selectedContentType the content type selected through content negotiation
* @param selectedConverterType the converter type selected to write to the response
* @param request the current request
* @param response the current response
* @return the body that was passed in or a modified (possibly new) instance
*/
@Nullable
T beforeBodyWrite(@Nullable T body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response);
}可以发现其有两个方法。
1.supports。此方法可以写我们自己的逻辑,判断哪些方法需求增强。
2.beforeBodyWrite。此方法是给客户端响应之前执行,我们就在其中添加一个名为sign的参数。
实现:
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice<Result> {
/**
* 控制器增强配置类
*/
@Autowired
private ControllerAdviceConfig config;
@Override
public boolean supports(MethodParameter methodParameter, Class aClass) {
//读取配置,判断哪些方法不需要生成签名
String methodName=methodParameter.getMethod().getName();
List list = config.getIgnore();
return !list.contains(methodName);
}
private String generateSign(String body){
//根据body计算签名。。。
return body;
}
@Override
public Result beforeBodyWrite(Result result, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if (result == null) {
return result;
}
String bodyStr = result.toString();
String sign = generateSign(bodyStr);
response.getHeaders().add("sign", sign);
return result;
}
}需要注意的是Result的toString()方法已经重写,重写的规则需要与客户端约定好,最终生成一致的字符串,再对字符串用一致的哈希算法,计算签名。

浙公网安备 33010602011771号