首页 > 编程语言 >对外提供接口签名验签例子程序

对外提供接口签名验签例子程序

时间:2022-11-04 13:34:20浏览次数:32  
标签:return String 接口 response 签名 验签 new SignConstant public

对外界提供的接口,需要加签名保证安全,签名可以用md5,sha256都行,本文是md5

签名文档

 

 

比如:

appId:testffhz 

appSecret:rv30GsgvPxxSH23L

 

代码

拦截器:

/**
 * 对外接口签名验签
 *
 * @author lihaoyang
 * @date 2022/2/22
 */
@Slf4j
@Component
@Order(-9)
public class SignVerifyInterceptor extends HandlerInterceptorAdapter {


    @Autowired
    private OpenApiUsersProperties openApiUsersProperties;

    @Autowired
    private RedisUtil redisUtil;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {


        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        MyRequestWrapper requestWrapper = new MyRequestWrapper(request);
        String bodyJson = new String(requestWrapper.getBody(),Charset.forName("UTF-8"));

        log.info("signVerifyInterceptor bodyJson:{}",bodyJson);
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        SkSign openApiSignAnnotation = handlerMethod.getMethodAnnotation(SkSign.class);
        if (Objects.isNull(openApiSignAnnotation)) {
            return true;
        }
        String appId = requestWrapper.getHeader(SignConstant.APP_ID);
        String sign = requestWrapper.getHeader(SignConstant.SIGNATURE_STR);
        String timestamp = requestWrapper.getHeader(SignConstant.TIME_STAMP);
        String nonce = requestWrapper.getHeader(SignConstant.NONCE_STR);
        log.info("signverify request param,appId:{},sign:{},timestamp:{},nonce:{}", appId, sign, timestamp, nonce);
        if (StringUtils.isBlank(appId)) {
            log.info("signverify:appId is blank ");
            response(response, Result.error(ResultEnum.IllegalArgument.getCode(), SignConstant.LOST_PARAM_APPID));
            return false;
        }
        if (StringUtils.isBlank(sign)) {
            log.info("signverify:sign is blank ");
            response(response, Result.error(ResultEnum.IllegalArgument.getCode(), SignConstant.LOST_PARAM_SIGN));
            return false;
        }
        if (StringUtils.isBlank(timestamp)) {
            log.info("signverify:timestamp is blank ");
            response(response, Result.error(ResultEnum.IllegalArgument.getCode(), SignConstant.LOST_PARAM_TIME_STAMP));
            return false;
        }
        if (StringUtils.isBlank(nonce)) {
            log.info("signverify:nonce is blank ");
            response(response, Result.error(ResultEnum.IllegalArgument.getCode(), SignConstant.LOST_PARAM_NONCE));
            return false;
        }

        DateTime reqDateTime = null;
        try {
            reqDateTime = DateUtil.parse(timestamp, DatePattern.NORM_DATETIME_PATTERN);
        } catch (Exception e) {
            log.warn("signverify ,解析timestamp失败,入参 timestamp:{}",timestamp);
            response(response, Result.error(ResultEnum.IllegalArgument.getCode(), SignConstant.TIMESTAMP_FMT_ERROR));
            return false;
        }

        if (DateUtil.between(reqDateTime.toJdkDate(), new Date(), DateUnit.SECOND) > SignConstant.expireTimeSeconds) {
            //超时
            log.warn("signverify ,sign过期,超时时间(秒数):{}", SignConstant.expireTimeSeconds);
            response(response,Result.error(ResultEnum.OPEN_API_TIMEOUT.getCode(), SignConstant.OUT_OF_DATE_REQUEST));
            return false;
        }

        Long nonceCounter = redisUtil.incr(RedisKeyUtil.getNonceKey(nonce),1);
        if (nonceCounter > 1) {
            log.info("signverify ,重复调用,nonceCounter:{}",nonceCounter);
            //重复调用
            response(response,Result.error(ResultEnum.OPEN_API_REPEAT_CALL));
            return false;
        } else {
            log.info("signverify ,nonce 首次调用设置过期时间,nonce:{}",nonce);
            redisUtil.expire(RedisKeyUtil.getNonceKey(nonce), SignConstant.expireTimeSeconds + 10);
        }

        String signStr = appId + getAppSecret(appId) + bodyJson + timestamp + nonce;
        String sign2 = DigestUtils.md5Hex(signStr).toUpperCase();
        log.info("signverify get sign >>>>>>:{}" + signStr);
        if (!StringUtils.equals(sign2, sign)) {
            response(response, Result.error(ResultEnum.SIGN_AUTH_FAIL));
            return false;
        }
        return true;
    }

    private String getAppSecret(String appId) {
        return openApiUsersProperties.getAppMap().get(appId);
    }


    public static void response(HttpServletResponse response, Result<?> result) throws IOException {
        response.setContentType("application/json");
        response.setCharacterEncoding("utf-8");
        response.getWriter().write(JSONUtil.toJsonStr(result));
        response.getWriter().flush();
    }

}

WebMVC配置

/**
 * @author lihaoyang
 * @date 2022/5/27
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private SignVerifyInterceptor signVerifyInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        /*****************/
        registry.addInterceptor(signVerifyInterceptor);
    }
}

配置类,这里配置到了Apollo,可以配置在表,都行

/**
 *  对外对接
 * @author lihaoyang
 * @date 2022/2/25
 */
@Slf4j
@Configuration
@ConfigurationProperties(prefix = "openapi")
public class OpenApiUsersProperties {



    private Map<String, String> appMap = new HashMap();

    public Map<String, String> getAppMap() {
        return appMap;
    }

    public void setAppMap(Map<String, String> appMap) {
        this.appMap = appMap;
    }


}

 

 

签名注解:

/**
 * 对外接口签名
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface SkSign {
}

Request包装类和过滤器,不加这个json的参数在拦截器读一次controller就读不到了

/**
 * @author lihaoyang
 * @date 2022/5/30
 */
@Slf4j
@Component
@WebFilter(urlPatterns = "/*", filterName = "requestWrapperFilter")
public class RequestWrapperFilter implements Filter {



    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        log.info("enter requestWrapperFilter doFilter");
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        MyRequestWrapper requestWrapper = new MyRequestWrapper(httpServletRequest);
        chain.doFilter(requestWrapper,response);
    }
}
public class MyRequestWrapper extends HttpServletRequestWrapper {

    private final byte[] body;

    public MyRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        body = getBodyString(request).getBytes(Charset.forName("UTF-8"));
    }

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

    @Override
    public ServletInputStream getInputStream() throws IOException {

        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body);

        return new ServletInputStream() {

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

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

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

            @Override
            public void setReadListener(ReadListener readListener) {

            }
        };
    }

    /**
     * 获取请求Body
     *
     * @param request
     * @return
     */
    private String getBodyString(ServletRequest request) {
        StringBuilder sb = new StringBuilder();
        InputStream inputStream = null;
        BufferedReader reader = null;
        try {
            inputStream = request.getInputStream();
            reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
            String line = "";
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return sb.toString();
    }

    public byte[] getBody() {
        return body;
    }
}

Controller

 @SkSign
    @PostMapping("pageList")
    public Result pageList(@RequestBody TeamListReq req){
        req.initParam();
        Page<TeamListResp> page = teamService.findPageList(req);
        return Result.OK(page);
    }

 

请求:

 

 

标签:return,String,接口,response,签名,验签,new,SignConstant,public
From: https://www.cnblogs.com/lihaoyang/p/16857451.html

相关文章