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()方法已经重写,重写的规则需要与客户端约定好,最终生成一致的字符串,再对字符串用一致的哈希算法,计算签名。

原文地址:https://www.cnblogs.com/wugang/p/14232345.html