RestFul接口的安全验证事例

这次要写一些restful的接口,在访问安全这一块最开始我想到用redis存储模拟session,客户端访问会带token过来模拟jsessionid,然后比对,但是这样会让token暴露在网络中,很不安全而且没有意义。

  • 其实可以用签名的方法来解决这个问题:

首先:client开通服务的时候,server会给它创建authKey和一个token,authKey相当于是公钥可以暴露在网络中,token是私钥不会暴露在网络中

然后:client请求服务端的时候在header中添加请求的时间戳,并且对发送的信息以及时间戳和token拼接起来的字符串进行签名(可以采用MD5或者SHA-0、SHA-1等)

最后:把签名串以及信息和header的信息发送到服务端,然后服务端会取出header信息以及签名串和信息,先对header里面的时间和服务器接收到的时间校验然后求差值如果大于多少秒就返回时间错误(防止重复攻击),然后在服务器端也对数据拼接签名,最后对比签名是否相等,如果不等就返回错误信息,如果相等下面就可以开始处理业务信息。

  • 刚写了份demo代码 结构如下:

  • 工程中使用springmvc发布服务,其中controller中的代码如下:
package com.lijie.api;

import java.util.Calendar;

import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.lijie.utils.Signature;

/**
 *
 * @author Lijie
 *
 */
@Controller
public class ApiAuth {

    /**
     * 测试auth
     * @param request
     * @param authKey
     * @param sign
     * @param info
     * @return
     * @throws Exception
     */
    @RequestMapping(value = "/api/auth/test", method = RequestMethod.POST)
    @ResponseBody
    public String testAuth( HttpServletRequest request, String authKey, String sign,
                            String info) throws Exception {

        String reqTime = request.getHeader("ReqTime-Time");

        String token = "lijieauthtest01";

        String check = check(reqTime, info, token, sign);

        System.out.println("服务端:" + check);

        //do service

        return check;
    }

    /**
     * 校验参数和签名
     *
     * @param reqTime
     * @param info
     * @param token
     * @param sign
     * @return
     * @throws Exception
     */
    private String check(String reqTime, String info, String token, String sign) throws Exception {

        if (StringUtils.isEmpty(reqTime)) {
            return "头部时间为空";
        }
        if (StringUtils.isEmpty(info)) {
            return "信息为空";
        }
        if (StringUtils.isEmpty(token)) {
            return "没有授权";
        }
        if (StringUtils.isEmpty(sign)) {
            return "签名为空";
        }

        long serverTime = Calendar.getInstance().getTimeInMillis();

        long clientTime = Long.parseLong(reqTime);

        long flag = serverTime - clientTime;

        if (flag < 0 || flag > 5000) {
            return "时间错误";
        }

        String allStr = info + reqTime + token;

        String md5 = Signature.md5(allStr);

        if (sign.equals(md5)) {

            System.out.println("服务端未签名时为:" + allStr);

            System.out.println("服务端签名之后为:" + md5);

            return "签名成功";
        }

        return "签名错误";
    }

}
  • 测试httpclient请求的代码如下:
package com.lijie.test;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.junit.Test;

import com.lijie.utils.Signature;

public class MyTest {

    private static final String token = "lijieauthtest01";

    @Test
    public void authTest() throws Exception {
        //创建一个httpclient对象
        CloseableHttpClient client = HttpClients.createDefault();

        //创建一个post对象
        HttpPost post = new HttpPost("http://localhost:8080/api/auth/test");

        //创建一个Entity,模拟表单数据
        List<NameValuePair> formList = new ArrayList<>();

        //添加表单数据
        long clientTime = Calendar.getInstance().getTimeInMillis();
        String info = "这是一个测试 restful api的 info";
        String reqStr = info + clientTime + token;
        formList.add(new BasicNameValuePair("authKey", "1000001"));
        formList.add(new BasicNameValuePair("info", info));
        String md5 = Signature.md5(reqStr);
        formList.add(new BasicNameValuePair("sign", md5));
        //包装成一个Entity对象
        StringEntity entity = new UrlEncodedFormEntity(formList, "utf-8");

        //设置请求的内容
        post.setEntity(entity);

        //设置请求的报文头部的编码
        post.setHeader(
            new BasicHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-8"));

        //设置期望服务端返回的编码
        post.setHeader(new BasicHeader("Accept", "text/plain;charset=utf-8"));

        //设置时间
        post.setHeader(new BasicHeader("ReqTime-Time", clientTime + ""));

        System.out.println("客户端未签名时为:" + reqStr);

        System.out.println("客户端签名之后为:" + md5);

        //执行post请求
        CloseableHttpResponse response = client.execute(post);

        //获取响应码
        int statusCode = response.getStatusLine().getStatusCode();

        if (statusCode == 200) {

            //获取数据
            String resStr = EntityUtils.toString(response.getEntity());

            //输出
            System.out.println("请求成功,请求返回内容为: " + resStr);
        } else {

            //输出
            System.out.println("请求失败,错误码为: " + statusCode);
        }

        //关闭response和client
        response.close();
        client.close();
    }
}
  • Signature类其实就是MD5加密的一个工具类,代码如下:
package com.lijie.utils;

import java.security.MessageDigest;

/**
 *
 * @author Lijie
 *
 */
public class Signature {

    public static String md5(String data) throws Exception {
        // 将字符串转为字节数组
        byte[] strBytes = (data).getBytes();
        // 加密器
        MessageDigest md = MessageDigest.getInstance("MD5");
        // 执行加密
        md.update(strBytes);
        // 加密结果
        byte[] digest = md.digest();
        // 返回
        return byteArrayToHex(digest);
    }

    private static String byteArrayToHex(byte[] byteArray) {
        // 首先初始化一个字符数组,用来存放每个16进制字符

        char[] hexDigits = {    ‘0‘, ‘1‘, ‘2‘, ‘3‘, ‘4‘, ‘5‘, ‘6‘, ‘7‘, ‘8‘, ‘9‘, ‘A‘, ‘B‘, ‘C‘, ‘D‘,
                                ‘E‘, ‘F‘ };

        // new一个字符数组,这个就是用来组成结果字符串的(解释一下:一个byte是八位二进制,也就是2位十六进制字符(2的8次方等于16的2次方))

        char[] resultCharArray = new char[byteArray.length * 2];

        // 遍历字节数组,通过位运算(位运算效率高),转换成字符放到字符数组中去

        int index = 0;

        for (byte b : byteArray) {

            resultCharArray[index++] = hexDigits[b >>> 4 & 0xf];

            resultCharArray[index++] = hexDigits[b & 0xf];

        }

        // 字符数组组合成字符串返回

        return new String(resultCharArray);
    }

}
  • 开启服务,测试访问,其中服务端输出:

  • 客户端输出:

时间: 2024-10-24 12:01:15

RestFul接口的安全验证事例的相关文章

前端调用后端的方法(基于restful接口的mvc架构)

1.前端调用后台: 建议用你熟悉的一门服务端程序,例如ASP,PHP,JSP,C#这些都可以,然后把需要的数据从数据库中获得,回传给客户端浏览器(其实一般就是写到HTML中,或者生成XML文件)然后在用JS获得. 2.js只是前端的语言,它还没有访问数据库的能力.不过它可以向某个URL发送请求,并获得返回的数据.这个会用到Ajax技术. 用AJAX,页面不刷新,只提交字符串到后台导入数据库       通过纯AngularJS+REST API构建Web是否可行? 在构建Web系统的时候,可不可

RESTful接口签名认证实现机制

RESTful接口 互联网发展至今,催生出了很多丰富多彩的应用,极大地调动了人们对这些应用的使用热情.但同时也为互联网应用带来了严峻的考验.具体体现在以下几个方面: 1.     部署方式的改变:当用户量不多的情况下,可能只需部署一台服务器就可以解决问题,但是当很多用户的情况下,为抗住高并发访问,需要组成应用集群对外提供服务: 2.     应用构建的改变:很多应用采用了多种技术解决方案,不同编程语言(如C,Java,Python),所以很难采用传统应用构建模式将不同模块整合进来: 3.    

PHP restful 接口

首先我们来认识下RESTful Restful是一种设计风格而不是标准,比如一个接口原本是这样的: http://www.test.com/user/view/id/1 表示获取id为1的用户信息,如果使用Restful风格,可以变成这样: http://www.test.com/user/1 可以很明显的看出这样做的好处: 1.更简洁的URL,对程序员友好 2.不暴露内部代码结构,更安全 那么,如何实现这个接口呢?首先,我们需要接收到/user/1部分. $path = $_SERVER['P

Swagger文档化restful接口

1.注解 @Api:用在类上,说明该类的作用. @ApiOperation:注解来给API增加方法说明. @ApiImplicitParams : 用在方法上包含一组参数说明. @ApiImplicitParam:用来注解来给方法入参增加说明. @ApiResponses:用于表示一组响应 @ApiResponse:用在@ApiResponses中,一般用于表达一个错误的响应信息 l   code:数字,例如400 l   message:信息,例如"请求参数没填好" l   resp

Spring Boot提供RESTful接口时的错误处理实践

本文首发于个人网站:http://www.javaadu.online/,如需转载,请注明出处 使用Spring Boot开发微服务的过程中,我们会使用别人提供的接口,也会设计接口给别人使用,这时候微服务应用之间的协作就需要有一定的规范. 基于rpc协议,我们一般有两种思路:(1)提供服务的应用统一将异常包起来,然后用错误码交互:(2)提供服务的应用将运行时异常抛出,抛出自定义的业务异常,服务的调用者通过异常catch来处理异常情况. 基于HTTP协议,那么最流行的就是RESTful协议,服务提

RESTful 接口调试分享利器 restc

这个工具来自于https://elemefe.github.io/restc/  这里对Abp进行了一次封装 1.在项目中添加nuget包 Abp.Web.Api.Restc 2.在项目Abp模块的DependsOn添加AbpWebApiRestcModule Run It,启动项目,访问/api开头的restful接口 ,原先正常返回的干巴巴JSON数据变成了一个可以操作分享的UI界面了 项目源码https://github.com/yuzukwok/Abp.Web.Api.Restc大家可以

底层restful接口修改分析

记录接口调用次数,接口调用时间需求. 需要修改公共的类,就是restful接口,可以认为是底层的代码,具体的实现有哪些?插入数据库肯定不能影响性能. 底层restful接口修改分析,布布扣,bubuko.com

[转]简单识别 RESTful 接口

     本文描述了识别一个接口是否真的是 RESTful 接口的基本方法.符合 REST 架构风格的接口,称为 RESTful 接口.本文不打算从架构风格的推导方面描述,而是从 HTTP 标准的方面描述.识别的方法同时也是指导实践的原则.       一.是否使用了正确(合适)的方法 目前对于 HTTP 标准滥用较多的,就是方法.谈起 RESTful 接口的方法,很多资料告诉大家,说 GET.POST.PUT.DELETE,分别对应数据库操作的 SELECT.INSERT.UPDATE.DEL

Restful接口对操作系统进行操作

在产品开发过程中,有时候需要web端对服务器进行操作,如修改ip.重启设备.关机等.前后端交互有很多方法,常见的有web端直接调用系统命令.通过数据库交互和Restful接口交互.直接调用系统命令最简单,但是最不安全,基本上没人会使用:数据库交互鼻尖安全,但是比较消耗硬件资源.所以Restful交互是一种很好的方式. 下面代码是对Restful形式交互的实现: #-*- coding:utf-8 -*- #!/usr/bin/env python ''' Created on 2017.5.9