• 售前

  • 售后

热门帖子
入门百科

Vue+Springboot实现接口签名的示例代码

[复制链接]
axly530 显示全部楼层 发表于 2021-10-26 13:42:11 |阅读模式 打印 上一主题 下一主题
1、实现思路


接口署名目标是为了,确保请求参数不会被窜改,请求的数据是否已超时,数据是否重复提交等。

接口署名表示图

客户端提交请求时,将以下参数按照约定署名方式举行署名,随后将参数和署名一同提交服务端:

1.请求头部门(header)
appid:针对差别的调用方分配差别的appid。
noce:请求的流水号,防止重复提交。
timestamp:请求时间戳,验证请求是否已超时失效。
2.数据部门
Path:按照path中的参数将所有key=value举行拼接。
Query:按照所有key=value举行拼接。
Form:按照所有key=value举行拼接
Body:Json,按照所有key=value举行拼接。String,整个字符串作为一个拼接。


署名

服务端提吸收交请求后,同样通过吸收的“请求头部门”、“数据部门”的参数举行拼接。随后验证客户端提交的署名是否精确。

2、代码实现


客户端(Vue)起首必要安装“jsrsasign”库,以便实现 RSA 加密、解密、署名、验签等功能。
官方地址:http://kjur.github.io/jsrsasign/
执行以下下令:
  1. npm install jsrsasign -save
复制代码
安装完成后,封装sign.js
  1. import {KJUR, KEYUTIL, hex2b64, b64tohex} from 'jsrsasign'
  2. // 签名算法
  3. const ALGORITHM = 'SHA256withRSA'
  4. // 私钥签名
  5. const RSA_SIGN = (privateKey, src) => {
  6.     const signature = new KJUR.crypto.Signature({'alg': ALGORITHM})
  7.     // 来解析密钥
  8.     const priKey = KEYUTIL.getKey(privateKey)
  9.     signature.init(priKey)
  10.     // 传入待签明文
  11.     signature.updateString(src)
  12.     const a = signature.sign()
  13.     // 转换成base64,返回
  14.     return hex2b64(a)
  15. }
  16. // 公钥验签
  17. const RSA_VERIFY_SIGN = (publicKey, src, data) => {
  18.     const signature = new KJUR.crypto.Signature({'alg': ALGORITHM, 'prvkeypem': publicKey})
  19.     signature.updateString(src)
  20.     return signature.verify(b64tohex(data))
  21. }
  22. export {
  23.     RSA_SIGN,
  24.     RSA_VERIFY_SIGN
  25. }
复制代码
客户端(Vue)通过sign.js举行加签、验签。
  1. const src = '我是一段测试字符串2'
  2. const publicKey = '-----BEGIN PUBLIC KEY-----\n' +
  3.             'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC35wxzdTzseajkYL06hEKBCEJu\n' +
  4.             'JQ/nySId2oTnsxbLiSTEjpAESSbML1lqkKaIwjrSFZzyLMH6DirsoEQcATqqoCDU\n' +
  5.             '/H9QNVb5jMSAxxdQusQkTWz6k07bEuy1ppVjpGxNi8o2OGNd+lwPC/hOSDR7lpfm\n' +
  6.             'aXLIjEwKSXzil7YAHQIDAQAB\n' +
  7.             '-----END PUBLIC KEY-----'
  8. const privateKey = '-----BEGIN PRIVATE KEY-----\n' +
  9.             'MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBALfnDHN1POx5qORg\n' +
  10.             'vTqEQoEIQm4lD+fJIh3ahOezFsuJJMSOkARJJswvWWqQpojCOtIVnPIswfoOKuyg\n' +
  11.             'RBwBOqqgINT8f1A1VvmMxIDHF1C6xCRNbPqTTtsS7LWmlWOkbE2LyjY4Y136XA8L\n' +
  12.             '+E5INHuWl+ZpcsiMTApJfOKXtgAdAgMBAAECgYB2PAcGSC7mPoW2ZvfiIlx7hurm\n' +
  13.             '0885D1hu5yohqUOTklXgRWQUTU+zYRHU8LERJgcZQKoKDXqdIPS584Q2mRe0uZMr\n' +
  14.             'vaiaBVEnHQreUJUQ8UN12pPUdBHDZvOk3L7/fZHk6A8uy5e09p2rsn+Vfki3zijp\n' +
  15.             '7Pd758HMtjuiHBb2QQJBAOuN6jdWBr/zb7KwM9N/cD1jJd6snOTNsLazH/Z3Yt0T\n' +
  16.             'jlsFmRJ6rIt/+jaLKG6YTR8SFyW5LIQTbreeQHPw4FECQQDH3Wpd/mBMMcgpxLZ0\n' +
  17.             'F5p1ieza+VA5fbxkQ0hdubEP26B6YwhkTB/xMSOwEjmUI57kfgOTvub36/peb8rI\n' +
  18.             'JdwNAkB3fzwlrGeqMzYkIU15avomuki46TqCvHJ8jOyXHUOzQbuDI5jfDgrAjkEC\n' +
  19.             'MKBnUq41J/lEMueJbU5KqmaqKrWxAkAyexlHnl1iQVymOBpBXkjUET8y26/IpZp0\n' +
  20.             '1I2tpp4zPCzfXK4c7yFOQTQbX68NXKXgXne21Ivv6Ll3KtNUFEPtAkBcx5iWU430\n' +
  21.             '0/s6218/enaa8jgdqw8Iyirnt07uKabQXqNnvbPYCgpeswEcSvQqMVZVKOaMrjKO\n' +
  22.             'G319Es83iq/m\n' +
  23.             '-----END PRIVATE KEY-----\n'
  24. console.log('明文:', src)
  25. const data = RSA_SIGN(privateKey, src)
  26. console.log('签名后的结果:', data)
  27. const res = RSA_VERIFY_SIGN(publicKey, src, data)
  28. console.log('验签结果:', res)
复制代码
服务端(Spring boot)吸收请求后,必要对数据和署名,举行验证。

起首引入依赖——hutool工具包,Hutool是一个Java工具包,也只是一个工具包,它资助我们简化每一行代码,镌汰每一个方法,让Java语言也可以“甜甜的”。

官网地址:https://www.hutool.cn/

在pom.xml下增长如下设置:
  1. <dependency>
  2.     <groupId>cn.hutool</groupId>
  3.     <artifactId>hutool-all</artifactId>
  4.     <version>5.3.5</version>
  5. </dependency>
复制代码
服务端(Spring boot)起首要获取客户端(Vue)请求的数据,上文已经描述了请求的数据有两部门,分别是“请求头部门”、“数据部门”。所以必要设置拦截器,对以上两部门举行获取。

设置拦截器(MyInterceptor.java),代码如下:
  1. import lombok.extern.slf4j.Slf4j;
  2. import org.springframework.beans.factory.annotation.Value;
  3. import org.springframework.stereotype.Component;
  4. import org.springframework.util.StreamUtils;
  5. import org.springframework.web.servlet.HandlerInterceptor;
  6. import javax.servlet.http.HttpServletRequest;
  7. import javax.servlet.http.HttpServletResponse;
  8. @Slf4j
  9. @Component
  10. public class MyInterceptor implements HandlerInterceptor {
  11.     @Override
  12.     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  13.         //获取请求参数
  14.         String queryString = request.getQueryString();
  15.         log.info("请求参数:{}", queryString);
  16.         // 获取header
  17.         log.info("key:{}",request.getHeader("timestamp"));
  18.         MyHttpServletRequestWrapper myRequestWrapper = new MyHttpServletRequestWrapper(request);
  19.         //获取请求body
  20.         byte[] bodyBytes = StreamUtils.copyToByteArray(myRequestWrapper.getInputStream());
  21.         String body = new String(bodyBytes, request.getCharacterEncoding());
  22.         log.info("请求体:{}", body);
  23.         return true;
  24.     }
  25. }
复制代码
在获取“请求体body”时,由于“HttpServletRequest”只能读取一次,拦截器读取后,后续Controller在读取时为空,所以必要重写HttpServletRequestWrapper:
  1. import org.springframework.util.StreamUtils;
  2. import javax.servlet.ReadListener;
  3. import javax.servlet.ServletInputStream;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpServletRequestWrapper;
  6. import java.io.*;
  7. public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper {
  8.     /**
  9.      * 缓存下来的HTTP body
  10.      */
  11.     private byte[] body;
  12.     public MyHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
  13.         super(request);
  14.         body = StreamUtils.copyToByteArray(request.getInputStream());
  15.     }
  16.     @Override
  17.     public ServletInputStream getInputStream() throws IOException {
  18.         InputStream bodyStream = new ByteArrayInputStream(body);
  19.         return new ServletInputStream(){
  20.             @Override
  21.             public int read() throws IOException {
  22.                 return bodyStream.read();
  23.             }
  24.             @Override
  25.             public boolean isFinished() {
  26.                 return false;
  27.             }
  28.             @Override
  29.             public boolean isReady() {
  30.                 return true;
  31.             }
  32.             @Override
  33.             public void setReadListener(ReadListener readListener) {
  34.             }
  35.         };
  36.     }
  37.     @Override
  38.     public BufferedReader getReader() throws IOException {
  39.         return new BufferedReader(new InputStreamReader(getInputStream()));
  40.     }
  41. }
复制代码
之后,必要创建过滤器,将“MyHttpServletRequestWrapper” 替换“ServletRequest”,代码如下:
  1. import lombok.extern.slf4j.Slf4j;
  2. import javax.servlet.*;
  3. import javax.servlet.http.HttpServletRequest;
  4. import java.io.IOException;
  5. @Slf4j
  6. public class RepeatedlyReadFilter implements Filter {
  7.     @Override
  8.     public void init(FilterConfig filterConfig) throws ServletException {
  9.     }
  10.     @Override
  11.     public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
  12.         ServletRequest requestWrapper = null;
  13.         if(servletRequest instanceof HttpServletRequest) {
  14.             requestWrapper = new MyHttpServletRequestWrapper((HttpServletRequest) servletRequest);
  15.         }
  16.         if(requestWrapper == null) {
  17.             filterChain.doFilter(servletRequest, servletResponse);
  18.         } else {
  19.             filterChain.doFilter(requestWrapper, servletResponse);
  20.         }
  21.     }
  22.     @Override
  23.     public void destroy() {
  24.     }
  25. }
复制代码
之后创建自界说设置,CorsConfig.java,将过滤器、拦截器加入设置:
  1. import com.xyf.interceptor.MyInterceptor;
  2. import com.xyf.interceptor.RepeatedlyReadFilter;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.boot.web.servlet.FilterRegistrationBean;
  5. import org.springframework.context.annotation.Bean;
  6. import org.springframework.context.annotation.Configuration;
  7. import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
  8. import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
  9. @Configuration
  10. public class CorsConfig extends WebMvcConfigurationSupport {
  11.     private MyInterceptor myInterceptor;
  12.     @Autowired
  13.     public CorsConfig (MyInterceptor myInterceptor){
  14.         this.myInterceptor = myInterceptor;
  15.     }
  16.     // 注册过滤器
  17.     @Bean
  18.     public FilterRegistrationBean<RepeatedlyReadFilter> repeatedlyReadFilter() {
  19.         FilterRegistrationBean registration = new FilterRegistrationBean();
  20.         RepeatedlyReadFilter repeatedlyReadFilter = new RepeatedlyReadFilter();
  21.         registration.setFilter(repeatedlyReadFilter);
  22.         registration.addUrlPatterns("/*");
  23.         return registration;
  24.     }
  25.     @Override
  26.     protected void addInterceptors(InterceptorRegistry registry) {
  27.         // addPathPatterns添加需要拦截的命名空间;
  28.         // excludePathPatterns添加排除拦截命名空间
  29.         registry.addInterceptor(myInterceptor).addPathPatterns("/**");
  30.         //.excludePathPatterns("/api/sys/login")
  31.     }
  32. }
复制代码
末了,完成验签,代码如下:
  1. import cn.hutool.core.codec.Base64;
  2. import cn.hutool.crypto.SecureUtil;
  3. import cn.hutool.crypto.asymmetric.Sign;
  4. import cn.hutool.crypto.asymmetric.SignAlgorithm;
  5. byte[] data = "我是一段测试字符串2".getBytes();
  6.         String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC35wxzdTzseajkYL06hEKBCEJu\n" +
  7.                 "JQ/nySId2oTnsxbLiSTEjpAESSbML1lqkKaIwjrSFZzyLMH6DirsoEQcATqqoCDU\n" +
  8.                 "/H9QNVb5jMSAxxdQusQkTWz6k07bEuy1ppVjpGxNi8o2OGNd+lwPC/hOSDR7lpfm\n" +
  9.                 "aXLIjEwKSXzil7YAHQIDAQAB";
  10. Sign sign = SecureUtil.sign(SignAlgorithm.SHA256withRSA,null,publicKey);
  11. //客户端传来的签名
  12. String qm = "IhY3LNuFn0isud1Pk6BL2eJV3Jl/UzDCYsdG9CYyJwOGqwnzStsv/RiYLnVP4bnQh1NRPMazY6ux/5Zz5Ypcx6RI5W1p5BDbO2afuIZX7x/eIu5utwsanhbxEfvm3XOsyuTbnMDh6BQUrXb4gUz9qgt9IXWjQdqnQRRv3ywzWcA=";
  13. byte[] signed = Base64.decode(qm);
  14. //验证签名
  15. boolean verify = sign.verify(data, signed);
复制代码
3、公钥、私钥天生


可通过一些网站在线天生公钥、私钥
网址:https://www.bejson.com/enc/rsa/

bejson在线天生公钥、私钥

4、其他题目


由于客户端加签、服务端验签。所以加签、验签的方式务必划一,否则将无法验证署名。Vue、Java有差别的署名工具库,使用前要做好测试。
到此这篇关于Vue+Springboot实现接口署名的示例代码的文章就介绍到这了,更多相关Springboot 接口署名内容请搜刮草根技术分享以前的文章或继续欣赏下面的相关文章希望大家以后多多支持草根技术分享!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x

帖子地址: 

回复

使用道具 举报

分享
推广
火星云矿 | 预约S19Pro,享500抵1000!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

草根技术分享(草根吧)是全球知名中文IT技术交流平台,创建于2021年,包含原创博客、精品问答、职业培训、技术社区、资源下载等产品服务,提供原创、优质、完整内容的专业IT技术开发社区。
  • 官方手机版

  • 微信公众号

  • 商务合作