接口安全 时效性+签名+数据加密

导入maven依赖

<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.11</version>
</dependency>

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15</artifactId>
    <version>1.46</version>
</dependency>

实现思路

1、所有接口通过注解+aop实现接口时效性、验证签名
2、具体业务接口再解密数据,然后进行业务代码处理逻辑
接口安全校验流程

这里贴出简单的代码(在注解中实现接口时效性校验),以及验证签名的代码片段,数据解密部分的代码
可以自己实现,就不贴代码啦

show you the code

定义注解类

package com.pica.cloud.commercialization.crrs.interceptor;

import java.lang.annotation.*;

/**
 * @ClassName SecurityAuth
 * @Description
 * @Author Chongwen.jiang
 * @Date 2019/8/26 10:46
 * @ModifyDate 2019/8/26 10:46
 * @Version 1.0
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SecurityAuth {

}

注解类业务代码实现

package com.pica.cloud.commercialization.crrs.interceptor;

import com.pica.cloud.commercialization.crrs.util.Signutil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.lang.annotation.Annotation;

/**
 * @ClassName SecurityAuthInterceptor
 * @Description
 * @Author Chongwen.jiang
 * @Date 2019/8/26 10:48
 * @ModifyDate 2019/8/26 10:48
 * @Version 1.0
 */
@Component
@Slf4j
public class SecurityAuthInterceptor extends HandlerInterceptorAdapter{

    private static final String PARAMETERS_ILLEGAL = "{\"code\":\"20000000\",\"data\":{},\"message\":\"接口调用参数非法\"}";

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Annotation securityAuth = handlerMethod.getMethod().getAnnotation(SecurityAuth.class);
            if (securityAuth == null) {
                securityAuth = handlerMethod.getMethod().getDeclaringClass().getAnnotation(SecurityAuth.class);
            }
            if (securityAuth != null) {
                String invokeTime = request.getHeader("invokeTime");
                String sign = request.getHeader("sign");
                log.info("getHeaderParamenters===>time:{}, sign:{}", invokeTime, sign);
                if(StringUtils.isNotEmpty(invokeTime) && StringUtils.isNotEmpty(sign)) {
                    if(!Signutil.interfaceSecurityAuth(Long.valueOf(invokeTime), sign)) {
                        sendJsonMessage(response, PARAMETERS_ILLEGAL);
                        return false;
                    }
                } else {
                    sendJsonMessage(response, PARAMETERS_ILLEGAL);
                    return false;
                }
            }
        }
        return true;
    }

    private void sendJsonMessage(HttpServletResponse response, String body) throws Exception {
        response.setContentType("application/json; charset=utf-8");
        PrintWriter writer = response.getWriter();
        writer.print(body);
        writer.close();
        response.flushBuffer();
    }


}


需要安全校验的接口添加注解使用

package com.pica.cloud.commercialization.crrs.controller;


import com.pica.cloud.commercialization.crrs.base.ApiResponse;
import com.pica.cloud.commercialization.crrs.interceptor.SecurityAuth;
import com.pica.cloud.commercialization.crrs.model.req.DocPatientReq;
import com.pica.cloud.commercialization.crrs.service.CaseHistoryService;
import com.pica.cloud.commercialization.crrs.service.ProjectService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;

/**
 * <p>
 * 前端控制器
 * </p>
 *
 * @author ChenChang
 * @since 2018-11-22
 */
@RestController
@RequestMapping("/caseHistory")
@SecurityAuth
@Api(tags = "Tims活动相关接口")
public class CaseHistoryController {

    @Autowired
    private ProjectService projectService;

    @Autowired
    private CaseHistoryService caseHistoryService;

    /**
     * @Description 查询项目列表
     * @Author Chongwen.jiang
     * @Date 2019/8/21 15:58
     * @ModifyDate 2019/8/21 15:58
     * @Params []
     * @Return com.pica.cloud.commercialization.crrs.base.ApiResponse<?>
     */
    @GetMapping("/activity")
    @ApiOperation(value = "查询项目列表信息")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "invokeTime", value = "接口调用时间", paramType = "header", dataType = "String", required = true),
            @ApiImplicitParam(name = "sign", value = "接口调用签名", paramType = "header", dataType = "String", required = true)
    })
    public ApiResponse<?> getProjectListInfo () {
        return new ApiResponse<>(projectService.getProjectListInfo());
    }


    /**
     * @Description 查询已同意人数 / 初审通过人数
     * @Author Chongwen.jiang
     * @Date 2019/8/21 16:05
     * @ModifyDate 2019/8/21 16:05
     * @Params [inviteCountReq]
     * @Return com.pica.cloud.commercialization.crrs.base.ApiResponse<?>
     */
    @PostMapping("/doctorPatient")
    @ApiOperation(value = "查询已同意人数 / 初审通过人数")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "invokeTime", value = "接口调用时间", paramType = "header", dataType = "String", required = true),
            @ApiImplicitParam(name = "sign", value = "接口调用签名", paramType = "header", dataType = "String", required = true)
    })
    public ApiResponse<?> getDocPatientCount(@Validated @RequestBody DocPatientReq docPatientReq) {
        return new ApiResponse<>(caseHistoryService.getDocPatientCount(docPatientReq));
    }

SignUtil代码(时效性校验,生成签名,验证签名)

package com.pica.cloud.commercialization.crrs.util;

import org.apache.commons.codec.digest.DigestUtils;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.*;

/**
 * @program: crrs-backend
 * @description:
 * @author: sui.liu
 * @create: 2019-08-20 10:16
 **/
public class Signutil {
    static final String salt1 = "6dcbc521";
    static final String salt2 = "c0744844";

    static final String salt3 = "2!ih@3h#42^k";
    static final String salt4 = "n!6n$7b%3l&k";

    public static String getPicaOpenId(String appId, Integer doctorId) {
        return DigestUtils.md5Hex(salt1 + appId + salt2 + doctorId).toUpperCase();
    }

    /**
     * @Description 接口安全性校验
     * @Author Chongwen.jiang
     * @Date 2019/8/26 11:01
     * @ModifyDate 2019/8/26 11:01
     * @Params [executeTime, encrypt]
     * @Return boolean
     */
    public static boolean interfaceSecurityAuth(Long executeTime, String encrypt) {
        //获取10分钟之前的时间
        LocalDateTime min = LocalDateTime.now().minusMinutes(10);

        LocalDateTime time = LocalDateTime.ofEpochSecond(executeTime/1000,0, ZoneOffset.ofHours(8));
        if (time.compareTo(min) >= 0 && time.compareTo(LocalDateTime.now()) < 0) {
            String md5Str = DigestUtils.md5Hex(salt3 + executeTime + salt4).toUpperCase();
            if (md5Str.equals(encrypt)) {
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    }

    public static void main(String[] args) {
        /*Long executeTime = System.currentTimeMillis();
        String md5Str = DigestUtils.md5Hex(salt3 + executeTime + salt4).toUpperCase();
        System.out.println("executeTime:" + executeTime + "\nsign:" + md5Str);

        interfaceSecurityAuth(executeTime,md5Str);*/

        //生成签名
        SortedMap<String, Object> params = Collections.synchronizedSortedMap(new TreeMap<>());
        params.put("id","adsfasdfasdfdd");
        params.put("name","张三");
        params.put("phone","13517477655");
        String key = "selfPrivateKey";
        String reqSign = createSign(params, key);
        //验证签名
        Boolean checkSign = checkSign(reqSign, params, key);
        System.out.println("checkSign: " + checkSign);

    }

    /**
     * @Description 使用md5生成签名
     * @Author Chongwen.jiang
     * @Date 2019/9/11 10:23
     * @ModifyDate 2019/9/11 10:23
     * @Params [parameters(生成签名的参数), key(自己的私钥)]
     * @Return java.lang.String
     */
    public static String createSign(SortedMap<String, Object> parameters, String key){
        StringBuffer sb = new StringBuffer();
        //所有参与传参的参数按照accsii排序(升序)
        Set es = parameters.entrySet();
        Iterator it = es.iterator();
        while(it.hasNext()) {
            Map.Entry entry = (Map.Entry)it.next();
            String k = (String)entry.getKey();
            Object v = entry.getValue();
            if(null != v && !"".equals(v)
                    && !"sign".equals(k) && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }

        //最后加上自己的私钥
        sb.append("key=" + key);
        System.out.println("签名字符串:"+sb.toString());
        String sign = "";
        try {
            sign = DigestUtils.md5Hex(sb.toString().getBytes("gbk")).toUpperCase();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return sign;
    }

    /**
     * @Description 验证签名是否有效及正确性
     * @Author Chongwen.jiang
     * @Date 2019/9/11 10:23
     * @ModifyDate 2019/9/11 10:23
     * @Params [reqSign(接口入参签名), parameters(接口入参), key(对方公钥)]
     * @Return java.lang.Boolean
     */
    public static Boolean checkSign(String reqSign, SortedMap<String, Object> parameters, String key){
        String selfSign = createSign(parameters, key);
        if(!reqSign.equals(selfSign)){
            System.out.println("签名错误");
            return false;
        }
        return true;
    }

    /**
     * @Description 使用对方公钥数据加密
     * @Author Chongwen.jiang
     * @Date 2019/9/11 11:38
     * @ModifyDate 2019/9/11 11:38
     * @Params [parameters]
     * @Return java.lang.String
     */
    public static String encryptData(SortedMap<String, Object> parameters){
        //请求数据加密好之后,调用接口
        return null;
    }

    /**
     * @Description 使用自己的私钥解密数据
     * @Author Chongwen.jiang
     * @Date 2019/9/11 11:42
     * @ModifyDate 2019/9/11 11:42
     * @Params [parameters]
     * @Return java.lang.String
     */
    public static String decryptData(SortedMap<String, Object> parameters){
        //接收到接口入参后解密数据
        //然后进行业务代码处理

        return null;
    }

}

发布了36 篇原创文章 · 获赞 4 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/weixin_41205148/article/details/100733420