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

  • 相关阅读:
    Java中@Override注解的作用
    JAVA8 双冒号::的作用
    JAVA8 Stream
    梯度下降法推导,凸函数,收敛性推导
    Maven基础
    Openflow协议详解
    KeepAlive--高可用解决方案
    Spring Cloud Alibaba:Seata基础知识
    Spring Cloud Alibaba:Sentinel初始化监控和流控
    Spring Cloud Alibaba:Nacos配置中心
  • 原文地址:https://www.cnblogs.com/wugang/p/14232345.html
Copyright © 2020-2023  润新知