您当前的位置:首页 > 电脑百科 > 程序开发 > 编程百科

动手实现对外安全的接口(改进篇)

时间:2022-05-30 17:39:03  来源:  作者:大鸡腿同学

前言

感谢我一位阿里的安全大佬朱哥,在我发第一篇博客的时候进行指点。 我对上一篇的不足之处进行总结一下:

  1. 加密过程太过简单,我上一篇博客也谈到需要改进,其中包括AppSecret等等也加入到签名里面。
  2. 时间戳的问题也要加入进去,防止数据篡改。时间戳的问题是防止数据重放
  3. 加密算法需要再改进,MD5需要加点salt等等
  4. 参数需要排序,这个有点出入。因为一般来说内部接口使用类进行传输参数,可以重写类的toString()方法来规范参数的排序。但是有个缺陷是:无法通配到不是按对象来接收的参数,这时就重写不了toString了,只能我们手动去排序。

show you the code

get处理方法

new 拦截器

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson.JSON;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import JAVAx.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.Map;

public class MyBetterInterceptor implements HandlerInterceptor {

    public static final ThreadLocal<String> local = new ThreadLocal<>();

    public static final ThreadLocal<String> localSign = new ThreadLocal<>();

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String sign = request.getHeader("sign");
        //这里对应appId,外面可以去数据库查Secret
        String appId = request.getHeader("appId");
        if (valid(sign) || valid(appId)) {
            throw new IllegalAccessException("验签非法");
        }
        String time = request.getHeader("createTime");
        if (valid(time)) {
            throw new IllegalAccessException("时间戳为空");
        }
        long second = DateUtil.between(new Date(), DateUtil.parse(time), DateUnit.MINUTE);
        if (!(second <= 30 && DateUtil.compare(new Date(), DateUtil.parse(time)) > 0)) {
            throw new IllegalAccessException("非法时间戳");
        }
        String sb;
        if ("POST".equals(request.getMethod())) {
            //System.out.println("post参数:" + new String(IoUtil.readBytes(request.getInputStream(), false)));
            /*String parameter = new String(IoUtil.readBytes(request.getInputStream(), false));
            sb = ParameterUtil.generateSign(ParameterUtil.postParameter(parameter, time));*/
            local.set(time);
            localSign.set(sign);
            return true;
        } else {
            Map<String, String[]> map = request.getParameterMap();
            System.out.println(JSON.toJSONString(map));
            if (map == null || map.size() == 0) {
                return false;
            }
            sb = ParameterUtil.generateSign(ParameterUtil.getParameter(map, time));
        }

        System.out.println("加密混淆之后的字符串:" + sb);
        //Todo 去数据库查询appId对应的Sercert,比如这里固定查出来是dajitui
        sb = sb + "dajitui";
        System.out.println("sign:" + SecureUtil.md5(sb));
        if (SecureUtil.md5(sb).equals(sign)) {
            return true;
        }

        return true;
    }

    private boolean valid(String value) {
        return StrUtil.isBlank(value);
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}

注意点

1.拦截器:我们只能处理get请求的参数,post的话request只能读取一次,后面无法再读取一遍,我们使用aop去进行验签。 2.加密算法有所改变,添加了Sercert特有的参数,即使appid被别人拿到了,也没有多大影响。

加密处理过程

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import com.alibaba.fastjson.JSON;
import org.Apache.commons.codec.digest.DigestUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author M
 */
public class ParameterUtil {
    ParameterUtil() {
    }

    public static Map<String, Object> getParameter(Map<String, String[]> map, String time) {
        if (map == null || map.size() == 0) {
            return new HashMap<>(6);
        }
        Map<String, Object> resultMap = new HashMap<>(map.size());
        map.forEach((k, v) -> {
            if (v.length == 1) {
                resultMap.put(k, v[0]);
            } else {
                resultMap.put(k, new ArrayList<>(Arrays.asList(v)));
            }
        });
        resultMap.put("time",time);
        System.out.println("map:"+ JSON.toJSONString(resultMap));
        return resultMap;
    }

    public static Map<String, Object> postParameter(String msg, String time) {
        if (StrUtil.isBlank(msg)) {
            return new HashMap<>(6);
        }
        JSONArray array=new JSONArray(msg);
        Map<String, Object> resultMap = new HashMap<>(25);
        for(JSONObject object:array.jsonIter()){
            for (String s : object.keySet()) {
                resultMap.put(s, object.get(s));
            }
            resultMap.put("time",time);
        }
        System.out.println("map:"+ JSON.toJSONString(resultMap));
        return resultMap;
    }

    public static final String SEPERATE_CHAR = "#%$";
    public static String generateSign(Map<String, Object> param) {
        Set<String> keys = param.keySet();
        //过滤掉sign,对key进行排序
        List<String> sortedKeys = keys.stream().filter(key -> !Objects.equals(key, "sign")).sorted().collect(Collectors.toList());
        //加上自定义字符串混淆算法
        String calcSign = sortedKeys.stream().map(param::get).map(String::valueOf).collect(Collectors.joining(SEPERATE_CHAR));
        //对字符串进行hash
        calcSign = DigestUtils.sha1Hex(calcSign);
        return calcSign;
    }

    public static void mAIn(String[] args) {
        System.out.println(ParameterUtil.postParameter("{n" +
                "    "status": "0000",n" +
                "    "message": "success",n" +
                "    "data": {n" +
                "        "title": {n" +
                "            "id": "001",n" +
                "            "name" : "白菜"n" +
                "        },n" +
                "        "content": [n" +
                "            {n" +
                "                "id": "001",n" +
                "                "value":"你好 白菜"n" +
                "            },n" +
                "            {n" +
                "                "id": "002",n" +
                "                 "value":"你好 萝卜" n" +
                "            }n" +
                "        ]n" +
                "    }n" +
                "}",""));
    }

}

其中 postParameter是处理post参数,getParameter处理get参数转成Map<String,Object>形式。

generateSign才是真正的大头

public static final String SEPERATE_CHAR = "#%$";
    public static String generateSign(Map<String, Object> param) {
        Set<String> keys = param.keySet();
        //过滤掉sign,对key进行排序
        List<String> sortedKeys = keys.stream().filter(key -> !Objects.equals(key, "sign")).sorted().collect(Collectors.toList());
        //加上自定义字符串混淆算法
        String calcSign = sortedKeys.stream().map(param::get).map(String::valueOf).collect(Collectors.joining(SEPERATE_CHAR));
        //对字符串进行hash
        calcSign = DigestUtils.sha1Hex(calcSign);
        return calcSign;
    }

将key进行排序,排序完获取value,然后它们之间使用特定分隔符分开来,让别人猜不到我们的加密算法。

最后的sign

		sb = ParameterUtil.generateSign(ParameterUtil.getParameter(map, time));
        System.out.println("加密混淆之后的字符串:" + sb);
        //Todo 去数据库查询appId对应的Sercert,比如这里固定查出来是dajitui
        sb = sb + "dajitui";
        System.out.println("sign:" + SecureUtil.md5(sb));

特有的根据appid获取sercert过程,然后加入再进行md5加密。

post处理方法

使用aop+自定义注解

定义一个注解

@Target({METHOD, FIELD, TYPE})
@Retention(RUNTIME)
public @interface NoParameter {

}

一把梭哈

	@NoParameter
    @PostMapping("/rpc/test1")
    public String a1(@RequestBody Student1 student1){
        System.out.println("list:"+student1);
        return "123";
    }

切面逻辑

import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson.JSON;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class TestAspect {

    @Pointcut("@annotation(com.example.demo.NoParameter)")
    public void annotationPoinCut() {

    }

    @Before(value = "annotationPoinCut()")
    public void beforeTest(JoinPoint point) throws IllegalAccessException {
        String args = JSON.toJSONString(point.getArgs());
        String sb = ParameterUtil.generateSign(ParameterUtil.postParameter(args, MyBetterInterceptor.local.get()));
        sb = sb + "dajitui";
        System.out.println("sign:" + SecureUtil.md5(sb));
        if (!SecureUtil.md5(sb).equals(MyBetterInterceptor.localSign.get())) {
            throw new IllegalAccessException("验签失败");
        }
        MyBetterInterceptor.local.remove();
        MyBetterInterceptor.localSign.remove();
    }

}


Tags:接口   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
一篇文章带你了解Python的分布式进程接口
在Thread和Process中,应当优选Process,因为Process更稳定,而且,Process可以分布到多台机器上,而Thread最多只能分布到同一台机器的多个CPU上。一、前言在Thread和Process中,应当优...【详细内容】
2024-04-11  Search: 接口  点击:(10)  评论:(0)  加入收藏
走进光纤世界:揭秘光纤接口的四大类型
光纤技术作为现代通信的基石,已经广泛应用于各种数据传输场景中。它以高速率、长传输距离、抗干扰性强、低衰减以及带宽占用小等优点,成为了数据通信领域中的重要物理层连接方...【详细内容】
2024-04-10  Search: 接口  点击:(8)  评论:(0)  加入收藏
马斯克脑机接口再造奇迹:瘫痪小伙意念玩赛车击败正常人
马斯克脑机接口公司的首位志愿者,又给我们展现了一段“绝活”:  肩部以下全无知觉的他,只靠脑中的一块芯片,就打起了马里奥赛车游戏。  只见赛车前进、转弯,控制得相当灵活。...【详细内容】
2024-03-26  Search: 接口  点击:(25)  评论:(0)  加入收藏
中国移动董事长杨杰谈脑机接口:假若真能实现,将真正实现人的永生
每经记者:杨卉 每经编辑:梁枭今日(3月21日)晚间,中国移动(SH600941,股价102.68元,市值2.2万亿元)披露了2023年业绩数据。公司营业收入突破万亿元大关,其中通信服务收入8635亿元,同比增...【详细内容】
2024-03-22  Search: 接口  点击:(14)  评论:(0)  加入收藏
瘫痪8年小哥植入马斯克脑机接口,狂打8小时「文明6」!Neuralink首个人类植入者直播来了
一名因潜水事故导致肩部以下瘫痪八年的 29 岁男子,正在借助脑机接口设备重温在线国际象棋和杀时间大作游戏《文明 6》。这是脑机接口公司 Neuralink 最新一场直播的内容,迅速...【详细内容】
2024-03-21  Search: 接口  点击:(27)  评论:(0)  加入收藏
脑机接口将为人类带来什么
◎实习记者 蒋 捷 实习生 杨茜茹怎样才能突破身体的限制,用意识与万物相连?在《西游记》中,孙悟空拥有凭借意念隔空取物的超能力。如今,脑机接口技术可以建立大脑与外部设备的直...【详细内容】
2024-02-29  Search: 接口  点击:(31)  评论:(0)  加入收藏
马斯克:首位脑机接口受试者可通过意念控制鼠标
当地时间周一(19日)晚上的一次活动中,马斯克透露了旗下的脑机接口公司Neuralink首位人类受试者的最新进展。他表示,该受试者已经完全康复,并可以通过大脑控制电脑鼠标。在社交媒...【详细内容】
2024-02-21  Search: 接口  点击:(38)  评论:(0)  加入收藏
如何防止被恶意刷接口?
前言在面试时,经常会被问一个问题:如何防止别人恶意刷接口?这是一个非常有意思的问题,防范措施挺多的。今天这篇文章专门跟大家一起聊聊,希望对你会有所帮助。图片1 防火墙防火墙...【详细内容】
2024-02-19  Search: 接口  点击:(47)  评论:(0)  加入收藏
芯片植入人脑待长期评估,马斯克脑机接口终极目标:人与AI共生
&middot;伦敦国王学院植入式医疗设备教授认为,Neuralink在与参与者一起训练系统前,要给参与者时间恢复。真正的成功需要长期评估脑机接口的稳定性和对参与者的好处。&middot;...【详细内容】
2024-02-01  Search: 接口  点击:(90)  评论:(0)  加入收藏
双头Type-C接口:解析充电方向的奥秘
随着科技的飞速发展,电子设备之间的连接与充电方式也在不断地革新。其中,Type-C接口以其高效、便捷的特性,逐渐成为了主流。特别是双头Type-C线,更是为用户带来了前所未有的便利...【详细内容】
2024-01-02  Search: 接口  点击:(161)  评论:(0)  加入收藏
▌简易百科推荐
Meta如何将缓存一致性提高到99.99999999%
介绍缓存是一种强大的技术,广泛应用于计算机系统的各个方面,从硬件缓存到操作系统、网络浏览器,尤其是后端开发。对于Meta这样的公司来说,缓存尤为重要,因为它有助于减少延迟、扩...【详细内容】
2024-04-15    dbaplus社群  Tags:Meta   点击:(3)  评论:(0)  加入收藏
SELECT COUNT(*) 会造成全表扫描?回去等通知吧
前言SELECT COUNT(*)会不会导致全表扫描引起慢查询呢?SELECT COUNT(*) FROM SomeTable网上有一种说法,针对无 where_clause 的 COUNT(*),MySQL 是有优化的,优化器会选择成本最小...【详细内容】
2024-04-11  dbaplus社群    Tags:SELECT   点击:(3)  评论:(0)  加入收藏
10年架构师感悟:从问题出发,而非技术
这些感悟并非来自于具体的技术实现,而是关于我在架构设计和实施过程中所体会到的一些软性经验和领悟。我希望通过这些分享,能够激发大家对于架构设计和技术实践的思考,帮助大家...【详细内容】
2024-04-11  dbaplus社群    Tags:架构师   点击:(2)  评论:(0)  加入收藏
Netflix 是如何管理 2.38 亿会员的
作者 | Surabhi Diwan译者 | 明知山策划 | TinaNetflix 高级软件工程师 Surabhi Diwan 在 2023 年旧金山 QCon 大会上发表了题为管理 Netflix 的 2.38 亿会员 的演讲。她在...【详细内容】
2024-04-08    InfoQ  Tags:Netflix   点击:(5)  评论:(0)  加入收藏
即将过时的 5 种软件开发技能!
作者 | Eran Yahav编译 | 言征出品 | 51CTO技术栈(微信号:blog51cto) 时至今日,AI编码工具已经进化到足够强大了吗?这未必好回答,但从2023 年 Stack Overflow 上的调查数据来看,44%...【详细内容】
2024-04-03    51CTO  Tags:软件开发   点击:(9)  评论:(0)  加入收藏
跳转链接代码怎么写?
在网页开发中,跳转链接是一项常见的功能。然而,对于非技术人员来说,编写跳转链接代码可能会显得有些困难。不用担心!我们可以借助外链平台来简化操作,即使没有编程经验,也能轻松实...【详细内容】
2024-03-27  蓝色天纪    Tags:跳转链接   点击:(16)  评论:(0)  加入收藏
中台亡了,问题到底出在哪里?
曾几何时,中台一度被当做“变革灵药”,嫁接在“前台作战单元”和“后台资源部门”之间,实现企业各业务线的“打通”和全域业务能力集成,提高开发和服务效率。但在中台如火如荼之...【详细内容】
2024-03-27  dbaplus社群    Tags:中台   点击:(14)  评论:(0)  加入收藏
员工写了个比删库更可怕的Bug!
想必大家都听说过删库跑路吧,我之前一直把它当一个段子来看。可万万没想到,就在昨天,我们公司的某位员工,竟然写了一个比删库更可怕的 Bug!给大家分享一下(不是公开处刑),希望朋友们...【详细内容】
2024-03-26  dbaplus社群    Tags:Bug   点击:(9)  评论:(0)  加入收藏
我们一起聊聊什么是正向代理和反向代理
从字面意思上看,代理就是代替处理的意思,一个对象有能力代替另一个对象处理某一件事。代理,这个词在我们的日常生活中也不陌生,比如在购物、旅游等场景中,我们经常会委托别人代替...【详细内容】
2024-03-26  萤火架构  微信公众号  Tags:正向代理   点击:(15)  评论:(0)  加入收藏
看一遍就理解:IO模型详解
前言大家好,我是程序员田螺。今天我们一起来学习IO模型。在本文开始前呢,先问问大家几个问题哈~什么是IO呢?什么是阻塞非阻塞IO?什么是同步异步IO?什么是IO多路复用?select/epoll...【详细内容】
2024-03-26  捡田螺的小男孩  微信公众号  Tags:IO模型   点击:(10)  评论:(0)  加入收藏
站内最新
站内热门
站内头条