精华内容
下载资源
问答
  • 与前端对接API接口,如果被第三方抓包并进行恶意篡改参数,可能会导致数据泄露,甚至会被篡改数据,我主要围绕时间戳,token,签名三个部分来保证API接口的安全性 1.用户成功登陆站点后,服务器会返回一个token...

    与前端对接的API接口,如果被第三方抓包并进行恶意篡改参数,可能会导致数据泄露,甚至会被篡改数据,我主要围绕时间戳,token,签名三个部分来保证API接口的安全性

    1.用户成功登陆站点后,服务器会返回一个token,用户的任何操作都必须带了这个参数,可以将这个参数直接放到header里。

    2.客户端用需要发送的参数和token生成一个签名sign,作为参数一起发送给服务端,服务端在用同样的方法生成sign进行检查是否被篡改。

    3.但这依然存在问题,可能会被进行恶意无限制访问,这时我们需要引入一个时间戳参数,如果超时即是无效的。

    4.服务端需要对token,签名,时间戳进行验证,只有token有效,时间戳未超时,签名有效才能被放行。

    开放接口

    没有进行任何限制,简单粗暴的访问方式,这样的接口方式一般在开放的应用平台,查天气,查快递,只要你输入正确对应的参数调用,即可获取到自己需要的信息,我们可以任意修改参数值。

    复制代码

     *
     * Description: 开放的接口
     * @author huangweicheng
     * @date 2020/12/21
    */
    @RestController
    @RequestMapping("/token")
    public class TokenSignController {
    
        @Autowired
        private TokenSignService tokenSignService;
    
        @RequestMapping(value = "openDemo",method = RequestMethod.GET)
        public List<PersonEntity> openDemo(int personId){
            return tokenSignService.getPersonList(personId);
        }
    }

    复制代码

    Token认证获取

    用户登录成功后,会获取一个ticket值,接下去任何接口的访问都需要这个参数。我们把它放置在redis内,有效期为10分钟,在ticket即将超时,无感知续命。延长使用时间,如果用户在一段时间内没进行任何操作,就需要重新登录系统。

    @RequestMapping(value = "login",method = RequestMethod.POST)
        public JSONObject login(@NotNull String username, @NotNull String password){
            return tokenSignService.login(username,password);
        }

    登录操作,查看是否有这个用户,用户名和密码匹配即可成功登录。

    复制代码

        /** 
         * 
         * Description:验证登录,ticket成功后放置缓存中,
         * @param
         * @author huangweicheng
         * @date 2020/12/31   
        */ 
        public JSONObject login(String username,String password){
            JSONObject result = new JSONObject();
            PersonEntity personEntity = personDao.findByLoginName(username);
            if (personEntity == null || (personEntity != null && !personEntity.getPassword().equals(password))){
                result.put("success",false);
                result.put("ticket","");
                result.put("code","999");
                result.put("message","用户名和密码不匹配");
                return result;
            }
            if (personEntity.getLoginName().equals(username) && personEntity.getPassword().equals(password)){
                String ticket = UUID.randomUUID().toString();
                ticket = ticket.replace("-","");
                redisTemplate.opsForValue().set(ticket,personEntity.getLoginName(),10L, TimeUnit.MINUTES);
                result.put("success",true);
                result.put("ticket",ticket);
                result.put("code",200);
                result.put("message","登录成功");
                return result;
            }
            result.put("success",false);
            result.put("ticket","");
            result.put("code","1000");
            result.put("message","未知异常,请重试");
            return result;
        }

    复制代码

    Sign签名

    把所有的参数拼接一起,在加入系统秘钥,进行MD5计算生成一个sign签名,防止参数被人恶意篡改,后台按同样的方法生成秘钥,进行签名对比。

    复制代码

        /**
         * @param request
         * @return
         */
        public static Boolean checkSign(HttpServletRequest request,String sign){
            Boolean flag= false;
            //检查sigin是否过期
            Enumeration<?> pNames =  request.getParameterNames();
            Map<String, String> params = new HashMap<String, String>();
            while (pNames.hasMoreElements()) {
                String pName = (String) pNames.nextElement();
                if("sign".equals(pName)) continue;
                String pValue = (String)request.getParameter(pName);
                params.put(pName, pValue);
            }
            System.out.println("现在的sign-->>" + sign);
            System.out.println("验证的sign-->>" + getSign(params,secretKeyOfWxh));
            if(sign.equals(getSign(params, secretKeyOfWxh))){
                flag = true;
            }
            return flag;
        }

    复制代码

    重复访问

    引入一个时间戳参数,保证接口仅在一分钟内有效,需要和客户端时间保持一致。

    复制代码

      public static long getTimestamp(){
            long timestampLong = System.currentTimeMillis();
    
            long timestampsStr = timestampLong / 1000;
    
            return timestampsStr;
        }

    复制代码

    需要跟当前服务器时间进行对比,如果超过一分钟,就拒绝本次请求,节省服务器查询数据的消耗

    拦截器

    每次请求都带有这三个参数,我们都需要进行验证,只有在三个参数都满足我们的要求,才允许数据返回或被操作。

    复制代码

    public class LoginInterceptor implements HandlerInterceptor {
    
        @Autowired
        private RedisTemplate redisTemplate;
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler) throws IOException {
            JSONObject jsonObject = new JSONObject();
            String ticket = request.getParameter("ticket");
            String sign = request.getParameter("sign");
            String ts = request.getParameter("ts");
            if (StringUtils.isEmpty(ticket) || StringUtils.isEmpty(sign) || StringUtils.isEmpty(ts)){
                jsonObject.put("success",false);
                jsonObject.put("message","args is isEmpty");
                jsonObject.put("code","1001");
                PrintWriter printWriter = response.getWriter();
                printWriter.write(jsonObject.toJSONString());
                return false;
            }
            //如果redis存在ticket就认为是合法的请求
            if (redisTemplate.hasKey(ticket)){
                System.out.println(redisTemplate.opsForValue().getOperations().getExpire(ticket));
                String values = (String) redisTemplate.opsForValue().get(ticket);
                //判断ticket是否即将过期,进行续命操作
                if (redisTemplate.opsForValue().getOperations().getExpire(ticket) != -2 && redisTemplate.opsForValue().getOperations().getExpire(ticket) < 20){
                    redisTemplate.opsForValue().set(ticket,values,10L, TimeUnit.MINUTES);
                }
                System.out.println(SignUtils.getTimestamp());
                //判断是否重复访问,存在重放攻击的时间窗口期
                if (SignUtils.getTimestamp() - Long.valueOf(ts) > 600){
                    jsonObject.put("success",false);
                    jsonObject.put("message","Overtime to connect to server");
                    jsonObject.put("code","1002");
                    PrintWriter printWriter = response.getWriter();
                    printWriter.write(jsonObject.toJSONString());
                    return false;
                }
                //验证签名
                if (!SignUtils.checkSign(request,sign)){
                    jsonObject.put("success",false);
                    jsonObject.put("message","sign is invalid");
                    jsonObject.put("code","1003");
                    PrintWriter printWriter = response.getWriter();
                    printWriter.write(jsonObject.toJSONString());
                    return false;
                }
                return true;
            }else {
                jsonObject.put("success",false);
                jsonObject.put("message","ticket is invalid,Relogin.");
                jsonObject.put("code","1004");
                PrintWriter printWriter = response.getWriter();
                printWriter.write(jsonObject.toJSONString());
            }
            return false;
        }
    }

    复制代码

    访问

    先登录系统,获取合法的ticket

    生成一个合法的sign验证,获取测试ts,访问openDemo,即可正常访问。还可以将参数加密,将http换成https,就不一 一展开了。

    demo代码 https://github.com/hwc4110/spring-demo1221

     

     

     

    平时怎么保养机械表才不易故障

    多功能石英表秒针不走怎么办

    手表小表盘功能介绍

    展开全文
  • App开放接口api安全性—Token签名sign的设计与实现 使用了https后,还有必要对数据进行签名来确保数据没有被篡改吗? 加密和签名的区别?
    展开全文
  • 在app开放接口api的设计中,避免不了的就是安全性问题,因为大多数接口涉及到用户的个人信息以及一些敏感的数据,所以对这些 接口需要进行身份的认证,那么这就需要用户提供一些信息,比如用户名密码等,但是为了...

    前言

    在app开放接口api的设计中,避免不了的就是安全性问题,因为大多数接口涉及到用户的个人信息以及一些敏感的数据,所以对这些 接口需要进行身份的认证,那么这就需要用户提供一些信息,比如用户名密码等,但是为了安全起见让用户暴露的明文密码次数越少越好,我们一般在web项目 中,大多数采用保存的session中,然后在存一份到cookie中,来保持用户的回话有效性。但是在app提供的开放接口中,后端服务器在用户登录后 如何去验证和维护用户的登陆有效性呢,以下是参考项目中设计的解决方案,其原理和大多数开放接口安全验证一样,如淘宝的开放接口token验证,微信开发 平台token验证都是同理。
     

    签名设计

    对于敏感的api接口,需使用https协议

    https是在http超文本传输协议加入SSL层,它在网络间通信是加密的,所以需要加密证书。

    https协议需要ca证书,一般需要交费。
     

    签名的设计

    原理:用户登录后向服务器提供用户认证信息(如账户和密码),服务器认证完后给客户端返回一个Token令牌,用户再次获取信息时,带上此令牌,如果令牌正取,则返回数据。对于获取Token信息后,访问用户相关接口,客户端请求的url需要带上如下参数:

    时间戳:timestamp

    Token令牌:token

    然后将所有用户请求的参数按照字母排序(包括timestamp,token),然后更具MD5加密(可以加点盐),全部大写,生成sign签名,这就是 所说的url签名算法。然后登陆后每次调用用户信息时,带上sign,timestamp,token参数。

    例如:原请求https://www.andy.cn/api/user/update/info.shtml?city=北京 (post和get都一样,对所有参数排序加密)

    加上时间戳和token

    https://www.andy.cn/api/user/update/info.shtml?city=北京×tamp=12445323134&token=wefkfjdskfjewfjkjfdfnc 
    然后更具url参数生成sign

    最终的请求如

    https://www.andy.cn /api/user/update/info.shtml?city=北京×tamp=12445323134& token=wefkfjdskfjewfjkjfdfnc&sign=FDK2434JKJFD334FDF2 
    其最终的原理是减小明文的暴露次数;保证数据安全的访问。

    具体实现如下:

    1. api请求客户端想服务器端一次发送用用户认证信息(用户名和密码),服务器端请求到改请求后,验证用户信息是否正确。

    如果正确:则返回一个唯一不重复的字符串(一般为UUID),然后在Redis(任意缓存服务器)中维护Token----Uid的用户信息关系,以便其他api对token的校验。

    如果错误:则返回错误码。



    2.服务器设计一个url请求拦截规则

    (1)判断是否包含timestamp,token,sign参数,如果不含有返回错误码。

    (2)判断服务器接到请求的时间和参数中的时间戳是否相差很长一段时间(时间自定义如半个小时),如果超过则说明该 url已经过期(如果url被盗,他改变了时间戳,但是会导致sign签名不相等)。

    (3)判断token是否有效,根据请求过来的token,查询redis缓存中的uid,如果获取不到这说明该token已过期。

    (4)根据用户请求的url参数,服务器端按照同样的规则生成sign签名,对比签名看是否相等,相等则放行。(自然url签名 也无法100%保证其安全,也可以通过公钥AES对数据和url加密,但这样如果无法确保公钥丢失,所以签名只是很大程 度上保证安全)。

    (5)此url拦截只需对获取身份认证的url放行(如登陆url),剩余所有的url都需拦截。

    3.Token和Uid关系维护

    对于用户登录我们需要创建token--uid的关系,用户退出时需要需删除token--uid的关系

     

     

     

     

     

    转载:http://www.cnblogs.com/whcghost/p/5657594.html

    展开全文

空空如也

空空如也

1 2 3 4 5 ... 14
收藏数 262
精华内容 104
关键字:

安全设计—接口数据的设计