为了账号安全,请及时绑定邮箱和手机立即绑定

我可以强制要求在 Spring 中至少包含两个标头之一吗?

我可以强制要求在 Spring 中至少包含两个标头之一吗?

蝴蝶不菲 2023-06-04 17:40:07
我的一个标题拼写错误,我想在向后兼容的同时更改它。@RequestHeader(value = "Custmer-Key") String customerKey我想添加一个拼写正确的标题Customer-Key,并至少强制其中一个。有任何想法吗?
查看完整描述

3 回答

?
慕的地8271018

TA贡献1796条经验 获得超4个赞

我会在这里做一些假设。在您的具体情况下,每一个都可能正确,也可能不正确,但目的是提供更好的背景信息,说明此类解决方案何时可行并且使用起来有意义。

  1. 你需要保持向后兼容性(这个很简单......你写的)

  2. 您有一个相当大的代码库,可能基于微服务并由多个开发人员维护,并且您希望避免跨越多个团队的大量提交,将修复集中在一个所有服务都打算使用的公共共享库中

  3. 您的标头不仅使用 Spring 获取,有时还通过直接访问请求来获取

  4. 你在一个生产应用程序中工作,你希望尽可能少地更改代码,因为它的一些内部工作很难理解

该解决方案包括连接自定义过滤器及其配置。过滤器会将HttpServletRequest实例与另一个允许操作标头的实例交换。


首先,创建自己的过滤器,如下所示:

import org.springframework.context.annotation.Configuration;

import org.springframework.core.Ordered;

import org.springframework.core.annotation.Order;


import javax.servlet.*;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletRequestWrapper;

import javax.servlet.http.HttpServletResponse;

import java.io.IOException;

import java.util.*;


@Configuration

@Order(Ordered.HIGHEST_PRECEDENCE)

public class HeadersFilter implements Filter {


    private static final String WRONG_HEADER = "Custmer-Key";

    private static final String RIGHT_HEADER = "Customer-Key";


    @Override

    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {


        HttpServletRequest request = (HttpServletRequest) servletRequest;

        HttpServletResponse response = (HttpServletResponse) servletResponse;


        String newHeaderValue = request.getHeader(RIGHT_HEADER);

        String headerValue;

        if(newHeaderValue != null) {

            headerValue = newHeaderValue;

        }

        else {

            headerValue = request.getHeader(WRONG_HEADER);

        }


        HeadersRewriteHttpServletRequestWrapper requestWrapper = new HeadersRewriteHttpServletRequestWrapper(request);

        requestWrapper.setCustomHeader(WRONG_HEADER, headerValue);


        filterChain.doFilter(requestWrapper, response);

    }


    public static class HeadersRewriteHttpServletRequestWrapper extends HttpServletRequestWrapper {


        private Map<String, String> customHeaders;


        HeadersRewriteHttpServletRequestWrapper(HttpServletRequest request) {

            super(request);

            customHeaders = new HashMap<>();

        }


        void setCustomHeader(String name, String value) {

            customHeaders.put(name, value);

        }


        private String getCustomHeader(String name) {

            return customHeaders.get(name);

        }


        @Override

        public String getHeader(String name) { // not needed by spring but useful if someone uses this method directly

            String header = super.getHeader(name);

            if(header != null) {

                return header;

            }

            return getCustomHeader(name);

        }


        @Override

        public Enumeration<String> getHeaderNames() {

            Set<String> names = new HashSet<>(Collections.list(super.getHeaderNames()));

            names.addAll(customHeaders.keySet());

            return Collections.enumeration(names);

        }


        @Override

        public Enumeration<String> getHeaders(String name) {

            List<String> headers = Collections.list(super.getHeaders(name));

            String customHeader = getCustomHeader(name);

            if(headers.isEmpty() && customHeader != null) {

                headers.add(customHeader);

            }

            return Collections.enumeration(headers);

        }

    }

}

其次,连接 Spring 配置以创建此过滤器的实例并在必要时注入它。


import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;


@Configuration

public class FilterConfiguration {

    @Bean

    public HeadersFilter headersFilterBean() {

        return new HeadersFilter();

    }

}

就是这样。Customer-Key假设您的应用程序没有阻止它工作的怪癖(在这种情况下祝您调试顺利),此代码将采用和的内容,优先考虑Custmer-Key并将Customer-Key它们写入假Custmer-Key标头中。这样你就不必触摸任何控制器,它们应该继续透明地工作。


查看完整回答
反对 回复 2023-06-04
?
人到中年有点甜

TA贡献1895条经验 获得超7个赞

下一个方法是创建一个注释 OneOf 或其他东西。我使用了一种比使用 Aspect 更简单的方法。使用这种方法,您可以验证请求参数、Requestbody 和 RequestHeader


@Target({TYPE, ANNOTATION_TYPE})

@Retention(RUNTIME)

@Constraint(validatedBy = OneOfValidator.class)

@Documented

public @interface OneOf {


    String message() default "";

    String[] value();

}

创建如下所示的验证器类。


public class OneOfValidator implements ConstraintValidator<OneOf, Object> {


    private String[] fields;

    private String fieldList;


    public void initialize(OneOf annotation) {

        this.fields = annotation.value();

        fieldList = Arrays.toString(fields);

    }


    public boolean isValid(Object value, ConstraintValidatorContext context) {


        BeanWrapper wrapper = PropertyAccessorFactory.forBeanPropertyAccess(value);

        int matches = countNumberOfMatches(wrapper);

        if (matches > 1) {

            setErrorMessage(context, <your message>);

            return false;

        } else if (matches == 0) {

            setErrorMessage(context, <your message>);

            return false;

        }


        return true;

    }


    private int countNumberOfMatches(BeanWrapper wrapper) {

        int matches = 0;

        for (String field : fields) {

            Object value = wrapper.getPropertyValue(field);

            boolean isPresent = detectOptionalValue(value);


            if (value != null && isPresent) {

                matches++;

            }

        }

        return matches;

    }


    private boolean detectOptionalValue(Object value) {

        if (value instanceof Optional) {

            return ((Optional)value).isPresent();

        }

        if (value instanceof String) {

            return StringUtils.hasText((String)value);

        }

        return true;

    }

    private void setErrorMessage(ConstraintValidatorContext context, String template) {

        context.disableDefaultConstraintViolation();

        context

                .buildConstraintViolationWithTemplate(template)

                .addNode(fieldList)

                .addConstraintViolation();

    }


在控制器中,您可以创建如下所示的内容。


    @GetMapping(value = "your path")

    public ResponseEntity<HeaderDataDTO> getBuildDetails(@RequestHeader(value = "Custmer-Key") String custmerKey,@RequestHeader(value = "Customer-Key") String customerKey

            ) {

        HeaderDataDTO data = new HeaderDataDTO();

data.setCustomerKey(customerKey);

data.setCustmerKey(custmerKey);

data.validate();

        return new ResponseEntity<>(data,

                HttpStatus.OK);

    }

您可以如下定义 DTO。


@Valid

@OneOf(value = {"customerKey", "custmerKey"})

public class HeaderDataDTO extends HeaderValidator {


    private String customerKey;

    private String custmerKey;

//getter and setter

HeaderValidator 应该如下所示。Validate 方法将验证对象。


import org.springframework.util.CollectionUtils;

import javax.validation.ConstraintViolation;

import javax.validation.Valid;

import javax.validation.Validation;

import javax.validation.Validator;


public abstract class HeaderValidator {


    public boolean validate() {

        Validator validator = Validation

                .buildDefaultValidatorFactory()

                .getValidator();

        Set<ConstraintViolation<HeaderValidator>> violations = validator.validate(this);

        if (!CollectionUtils.isEmpty(violations)) {

            throw <your exception>

        }

        return true;

    }


查看完整回答
反对 回复 2023-06-04
?
小唯快跑啊

TA贡献1863条经验 获得超2个赞

您可以像下面这样创建一个拦截器。


@Component

@Primary

public class HeadersInterceptor extends HandlerInterceptorAdapter {


    public boolean preHandle(HttpServletRequest request,

            HttpServletResponse response,

            Object handler) throws Exception {

            HttpInputMessage inputMessage=new ServletServerHttpRequest(request);          

HttpHeaders httpHeaders = inputMessage.getHeaders();

//validation code for header goes here.

//return true if validation is successful    

return true;


    }

 }

并将拦截器添加到您的配置中。


 @Configuration

public class InterceptorConfig implements WebMvcConfigurer {


    @Autowired

    HeadersInterceptor headersInterceptor;


    public void addInterceptors(InterceptorRegistry registry) {


        registry.addInterceptor(headersInterceptor);

    }

}`

现在您可以以任何方式自定义您的验证。


查看完整回答
反对 回复 2023-06-04
  • 3 回答
  • 0 关注
  • 138 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信