接口安全防线加解密:springboot 全局/指定接口解密(同时支持参数在body和param)

这篇具有很好参考价值的文章主要介绍了接口安全防线加解密:springboot 全局/指定接口解密(同时支持参数在body和param)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

接口安全防线加解密:springboot 全局/指定接口解密(同时支持参数在body和param)


优势:通过注解形式,不需要改变原接口请求参数,在拦截器里面把加密数据解密为原接口请求参数。同时支持application/x-www-form-urlencoded和application/json 的解密

1.原理

1.1.过滤器,过滤所有请求,利用HttpServletRequestWrapper解决request中流读取一次的处理,方便后续修改请求内容

1.2.自定义注解,通过自定义注解可以标识,指定哪些接口在拦截器中处理数据

1.3.拦截器,拦截带有指定注解的请求,把数据进行加密解密后返回处理


2.支持范围

2.1.实际可以自己改造适合多种情况处理,已支持以下

1.application/json 加密参数在body
2.application/x-www-form-urlencoded 支持参数在body或者在param

2.2.为什么不用RequestBodyAdvice

1.因为RequestBodyAdvice只支持body内容的数据加解密处理,具有局限性。


3.具体实现代码

3.1. 过滤器(目的读取request,为后续自定义数据做铺垫)

/**
 * @Author: bright chen
 */
@WebFilter(value = "/*", filterName = "uriFormatFilter")
public class UriFormatFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {

        String uri = httpServletRequest.getRequestURI();
        String newUri = uri.replace("//","/");
        httpServletRequest = new HttpServletRequestWrapper(httpServletRequest){
            @Override
            public String getRequestURI() {
                return newUri;
            }
        };
        ServletRequest requestWrapper=new RequestWrapper(httpServletRequest);
        if(requestWrapper!=null){
            filterChain.doFilter(requestWrapper,httpServletResponse);
        }else{
            filterChain.doFilter(httpServletRequest, httpServletResponse);
        }
    }
}

3.2.自定义HttpServletRequestWrapper(重点1,param赋值,body赋值,param pojo读取时赋值)

/**
 * @Author: bright chen
 */
public class RequestWrapper extends HttpServletRequestWrapper {
    private String body;
    private Map<String, String[]> params = new HashMap<String, String[]>();

    public RequestWrapper(HttpServletRequest request) {
        super(request);
        StringBuilder stringBuilder = new StringBuilder();
        BufferedReader bufferedReader = null;
        InputStream inputStream = null;
        try {
            inputStream = request.getInputStream();
            if (inputStream != null) {
                bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                char[] charBuffer = new char[128];
                int bytesRead = -1;
                while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
                    stringBuilder.append(charBuffer, 0, bytesRead);
                }
            } else {
                stringBuilder.append("");
            }
        } catch (IOException ex) {

        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        body = stringBuilder.toString();
        this.params.putAll(request.getParameterMap());
    }


    @Override
    public Map<String, String[]> getParameterMap() {
        return params;
    }


    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
        ServletInputStream servletInputStream = new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {
            }

            @Override
            public int read() throws IOException {
                return byteArrayInputStream.read();
            }
        };
        return servletInputStream;
    }


    // 重载一个构造方法
    public RequestWrapper(HttpServletRequest request, Map<String, Object> extendParams, String body) {
        this(request);
        if (body != null && body.length() > 0) {
            setBody(body);
        }
        if (extendParams.size() > 0) {
            addAllParameters(extendParams);// 这里将扩展参数写入参数表
        }
    }


    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }

    public String getBody() {
        return this.body;
    }

    // 赋值给body字段
    public void setBody(String body) {
        this.body = body;
    }


    @Override
    public String getParameter(String name) {// 重写getParameter,代表参数从当前类中的map获取
        String[] values = params.get(name);
        if (values == null || values.length == 0) {
            return null;
        }
        return values[0];
    }

    public String[] getParameterValues(String name) {// 同上
        return params.get(name);
    }


    /**
     * 参数为pojo类型时,会通过此方法获取所有的请求参数并进行遍历,对pojo属性赋值
     *
     * @return
     */
    @Override
    public Enumeration<String> getParameterNames() {// 同上
        ArrayList<String> list = list = new ArrayList<>();
        for (Map.Entry<String, String[]> entry : params.entrySet()) {
            list.add(entry.getKey());
        }
        return Collections.enumeration(list);
    }

    public void addAllParameters(Map<String, Object> otherParams) {// 增加多个参数
        for (Map.Entry<String, Object> entry : otherParams.entrySet()) {
            addParameter(entry.getKey(), entry.getValue());
        }
    }


    public void addParameter(String name, Object value) {// 增加参数
        if (value != null) {
            if (value instanceof String[]) {
                params.put(name, (String[]) value);
            } else if (value instanceof String) {
                params.put(name, new String[]{(String) value});
            } else {
                params.put(name, new String[]{String.valueOf(value)});
            }
        }
    }
}

3.3.自定义注解 @DecryptApi,后期在controller层带上这个注解则该controller层接口会被过滤器及拦截器进行解密处理。

/**
 * 支持params和body的解密
 * 支持
 * @Author: bright chen
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.PARAMETER})
public @interface DecryptApi {
}

3.4. (核心代码) 拦截器 DecryptInterceptor,对带有@DecryptApi注解的接口数据进行解密处理后重新赋值,注意配置 拦截所有请求

/**
 * @Author: bright chen
 * api 解密拦截器
 */
public class DecryptInterceptor extends HandlerInterceptorAdapter {
    private static Logger logger = LogManager.getLogger(DecryptInterceptor.class);
    @Autowired
    private EncryptProperties encryptProperties;

    /**
     * Controller之前执行
     * preHandle:拦截于请求刚进入时,进行判断,需要boolean返回值,如果返回true将继续执行,如果返回false,将不进行执行。一般用于登录校验
     * 1.当preHandle方法返回false时,从当前拦截器往回执行所有拦截器的afterCompletion方法,再退出拦截器链。也就是说,请求不继续往下传了,直接沿着来的链往回跑。
     * 2.当preHandle方法全为true时,执行下一个拦截器,直到所有拦截器执行完。再运行被拦截的Controller。然后进入拦截器链,运行所有拦截器的postHandle方法,
     * 完后从最后一个拦截器往回执行所有拦截器的afterCompletion方法.
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        try {


            if (handler instanceof HandlerMethod) {
                HandlerMethod method = (HandlerMethod) handler;
                // RequireLogin annotation = method.getMethodAnnotation(RequireLogin.class);
                if (method.hasMethodAnnotation(DecryptApi.class)) {
                    // 需要对数据进行加密解密
                    // 1.对application/json类型
                    String contentType = request.getContentType();
                    if (contentType == null && !"GET".equals(request.getMethod())) {
                        // 请求不通过,返回错误信息给客户端
                        responseResult(response, response.getWriter(), TResponse.FAIL("Decrypt failed"));
                        return false;
                    }
                    String requestBody = null;
                    boolean shouldEncrypt = false;

                    if ((contentType != null && StringUtils.substringMatch(contentType, 0,
                            MediaType.APPLICATION_FORM_URLENCODED_VALUE)) || "GET".equals(request.getMethod())) {
                        // 1.application/x-www-form-urlencoded 支持参数在body或者在param
                        shouldEncrypt = true;
                        requestBody = convertFormToString(request);
                        if (requestBody == null || "{}".equals(requestBody)) {
                            requestBody = URLDecoder.decode(convertInputStreamToString(request.getInputStream()),
                                    "UTF-8");
                            List<String> uriToList =
                                    Stream.of(requestBody.split("&")).map(elem -> new String(elem)).collect(Collectors.toList());

                            Map<String, String> uriToListToMap = new HashMap<>();


                            for (String individualElement : uriToList) {
                                if (individualElement.split("=")[0] != null && !"".equals(individualElement.split("=")[0])) {
                                    uriToListToMap.put(individualElement.split("=")[0],
                                            individualElement.substring(individualElement.split("=")[0].length() + 1));
                                }

                            }
                            requestBody = JSONObject.toJSONString(uriToListToMap);
                        }

                    } else if (StringUtils.substringMatch(contentType, 0, MediaType.APPLICATION_JSON_VALUE)) {
                        // application/json 支持加密参数在body
                        shouldEncrypt = true;
                        requestBody = convertInputStreamToString(request.getInputStream());

                    }

                    if (requestBody == null || "{}".equals(requestBody)||!shouldEncrypt) {
                        return true;
                    } else {

                        String result = decodeApi(JSON.parseObject(requestBody, StdRequestApi.class),
                                encryptProperties.getPrivateKey());
                        if (result == null) {
                            // 请求不通过,返回错误信息给客户端
                            responseResult(response, response.getWriter(), TResponse.FAIL("Decrypt failed"));
                            return false;
                        }
                        JSONObject jasonObject = JSONObject.parseObject(result);
                        Map map = (Map) jasonObject;
                        if (request instanceof RequestWrapper) {
                            RequestWrapper requestWrapper = (RequestWrapper) request;
                            requestWrapper.setBody(result);
                            requestWrapper.addAllParameters(map);
                            // requestWrapper = new RequestWrapper(request, map, result);
                            return true;
                        }
                    }
                } else {
                    String contentType = request.getContentType();
                    if (contentType != null && contentType.length() > 0 && StringUtils.substringMatch(contentType, 0,
                            MediaType.APPLICATION_FORM_URLENCODED_VALUE)) {
                        // 1.application/x-www-form-urlencoded 支持参数在body或者在param
                        String requestBody = convertFormToString(request);
                        if (requestBody == null || "{}".equals(requestBody)) {
                            // 把流数据放进param中,不解密
                            requestBody = URLDecoder.decode(convertInputStreamToString(request.getInputStream()),
                                    "UTF-8");
                            List<String> uriToList =
                                    Stream.of(requestBody.split("&")).map(elem -> new String(elem)).collect(Collectors.toList());

                            Map<String, Object> uriToListToMap = new HashMap<>();

                            for (String individualElement : uriToList) {
                                if (individualElement.split("=")[0] != null && !"".equals(individualElement.split("=")[0])) {
                                    uriToListToMap.put(individualElement.split("=")[0],
                                            individualElement.substring(individualElement.split("=")[0].length() + 1));
                                }
                            }
                            if (request instanceof RequestWrapper) {
                                RequestWrapper requestWrapper = (RequestWrapper) request;
                                requestWrapper.setBody(requestBody);
                                requestWrapper.addAllParameters(uriToListToMap);
                                return true;
                            }
                        }
                    }
                }

                return true;
            }
            return true;

        } catch (Exception e) {
            e.printStackTrace();
            logger.error(e.getMessage() + "异常地址:" + request.getServletPath());
            responseResult(response, response.getWriter(), TResponse.FAIL("Decrypt failed"));
            return false;
        }
    }


    /**
     * 返回信息给客户端
     *
     * @param response
     * @param tResponse
     */
    private void responseResult(HttpServletResponse response, TResponse tResponse) throws IOException {
        response.setContentType(HttpConstant.CONTENT_TYPE_JSON);
        String json = JSONObject.toJSONString(tResponse);
        PrintWriter out = response.getWriter();
        out.print(json);
        out.flush();
        out.close();
    }

    private String convertFormToString(HttpServletRequest request) {
        Map<String, String> result = new HashMap<>(8);
        Enumeration<String> parameterNames = request.getParameterNames();
        while (parameterNames.hasMoreElements()) {
            String name = parameterNames.nextElement();
            result.put(name, request.getParameter(name));
        }
        try {
            return JSON.toJSONString(result);
        } catch (Exception e) {
            throw new IllegalArgumentException(e);
        }
    }

    private String convertInputStreamToString(InputStream inputStream) throws IOException {
        return StreamUtils.copyToString(inputStream, Charset.forName("UTF-8"));
    }


    public String decodeApi(StdRequestApi stdRequestApi, String apiPrivateKey) {
        try {
            // 1.rsa解密
            // 2.AES验签
            // 3.AES解密
            return deData;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 返回信息给客户端
     *
     * @param response
     * @param out
     * @param tResponse
     */
    private void responseResult(HttpServletResponse response, PrintWriter out, TResponse tResponse) {
        response.setContentType(HttpConstant.CONTENT_TYPE_JSON);
        String json = JSONObject.toJSONString(tResponse);
        out.print(json);
        out.flush();
        out.close();
    }


}

3.5.公钥私钥配置读取

/**
 * @Author: bright chen
 */
@Component
@ConfigurationProperties(prefix = "api")
public class EncryptProperties {
    private String privateKey;
    private String publicKey;

    public String getPrivateKey() {
        return privateKey;
    }

    public void setPrivateKey(String privateKey) {
        this.privateKey = privateKey;
    }

    public String getPublicKey() {
        return publicKey;
    }

    public void setPublicKey(String publicKey) {
        this.publicKey = publicKey;
    }
}

PS:本文编写于公司产品被黑客入侵后的处理方案之一,具体加密解密逻辑不在本文中讲述文章来源地址https://www.toymoban.com/news/detail-608001.html

到了这里,关于接口安全防线加解密:springboot 全局/指定接口解密(同时支持参数在body和param)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Springboot接口返回参数以及入参RSA加密解密

    网上有好多通过aop切面以及自定义的RSA工具类进行加密解密的方法,期中的过程繁琐也不好用,博主研究了一天从网上到了超好用的基于Springboot框架实现的接口RSA加密解密方式,通过 rsa-encrypt-body-spring-boot 实现了对Spring Boot接口返回值、参数值通过注解的方式自动加解密。

    2024年02月13日
    浏览(48)
  • 安全实现SpringBoot配置文件自动加解密

    应用程序开发的时候,往往会存在一些敏感的配置属性 数据库账号、密码 第三方服务账号密码 内置加密密码 其他的敏感配置 对于安全性要求比较高的公司,往往不允许敏感配置以明文的方式出现。 通常做法是对这些敏感配置进行加密,然后在使用的地方进行解密。但是有

    2024年02月06日
    浏览(40)
  • 强化CentOS安全防线:如何有效应对常见安全威胁

    探索CentOS系统安全的世界,了解如何从容应对常见安全威胁。本文深入解析了强化CentOS安全防线的实用技巧和关键策略,帮助你建立稳固的网络防护体系。无论你是初入安全领域的新手还是经验丰富的专业人士,相信都能从中获取宝贵经验。如果你也比较关心网络安全,不妨

    2024年03月18日
    浏览(46)
  • 夯实网络安全基石,筑牢网络安全防线

            没有网络安全就没有国家安全,这句话我们常常能在各种新闻里看见。安全是发展的前提,发展是安全的保障,共同推进安全和发展。Z强调:“要坚持依法治网、依法办网、依法上网。”今年的国家网络安全宣传周在9月11日至17日全国范围内开展。        今年的网

    2024年02月09日
    浏览(45)
  • IP定位助力网络安全防线

    随着互联网技术的飞速发展,网络安全问题日益凸显。在网络安全领域,IP地址定位技术正发挥着越来越重要的作用,成为维护网络安全的一道有力防线。 一、追踪黑客攻击者,维护公共安全 在网络安全领域,黑客攻击是一个严重的问题。黑客常常利用匿名的IP地址进行攻击

    2024年01月19日
    浏览(39)
  • 【机房安全之道】构筑坚固的网络防线

    引言: 在数字化时代,机房成为了许多组织和企业的核心基础设施,承载着重要的数据和应用。然而,随着网络攻击日益猖獗,机房的安全性显得尤为重要。本文将深入探讨如何构建坚固的网络防线,保护机房免受攻击的方法和措施。 第一部分:了解威胁与漏洞 在构建机房

    2024年01月21日
    浏览(94)
  • 机房安全之道:构筑坚固的网络防线

    引言: 在数字化时代,机房成为了许多组织和企业的核心基础设施,承载着重要的数据和应用。然而,随着网络攻击日益猖獗,机房的安全性显得尤为重要。本文将深入探讨如何构建坚固的网络防线,保护机房免受攻击的方法和措施。 第一部分:了解威胁与漏洞 在构建机房

    2024年02月10日
    浏览(39)
  • 网络安全的新防线:主动进攻,预防为先

    进攻性安全(Offensive security)是指一系列主动安全策略,这些策略与恶意行为者在现实世界的攻击中使用的策略相同,区别在于其目的是加强而非损害网络安全。常见的进攻性安全方法包括红队、渗透测试和漏洞评估。 进攻性安全行动通常由道德黑客实施,这些网络安全专业

    2024年02月19日
    浏览(45)
  • 网络通信安全的坚固防线双向认证技术详解

    目录 什么是双向认证 双向认证的工作原理 双向认证的实现方式 双向认证的重要性 双向认证的挑战 安全最佳实践 小结 双向认证,又称为双向身份验证或双向鉴别,是一种在通信双方之间建立信任关系的安全机制。在通信过程中,两个实体需要进行双向的身份认证,具体来

    2024年02月04日
    浏览(56)
  • IP地址定位技术筑牢网络安全防线

    随着互联网技术的飞速发展,网络安全问题日益凸显,成为人们关注的焦点。如何有效防范和打击网络犯罪,维护国家安全和社会稳定,是摆在我们面前的一项紧迫任务。IP地址定位技术作为网络安全领域的一项重要技术,在防范和打击网络犯罪中发挥着越来越重要的作用。

    2024年02月02日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包