Vue框架怎样实现接口签名,要点有哪些
Admin 2022-10-20 群英技术资讯 349 次浏览
接口签名目的是为了,确保请求参数不会被篡改,请求的数据是否已超时,数据是否重复提交等。
接口签名示意图
客户端提交请求时,将以下参数按照约定签名方式进行签名,随后将参数和签名一同提交服务端:
1.请求头部分(header)
appid:针对不同的调用方分配不同的appid。
noce:请求的流水号,防止重复提交。
timestamp:请求时间戳,验证请求是否已超时失效。
2.数据部分
Path:按照path中的参数将所有key=value进行拼接。
Query:按照所有key=value进行拼接。
Form:按照所有key=value进行拼接
Body:Json,按照所有key=value进行拼接。String,整个字符串作为一个拼接。
签名
服务端提接收交请求后,同样通过接收的“请求头部分”、“数据部分”的参数进行拼接。随后验证客户端提交的签名是否正确。
客户端(Vue)首先需要安装“jsrsasign”库,以便实现 RSA 加密、解密、签名、验签等功能。
官方地址:http://kjur.github.io/jsrsasign/
执行以下命令:
1 | npm install jsrsasign -save |
安装完成后,封装sign.js
?import {KJUR, KEYUTIL, hex2b64, b64tohex} from 'jsrsasign' // 签名算法 const ALGORITHM = 'SHA256withRSA' // 私钥签名 const RSA_SIGN = (privateKey, src) => { const signature = new KJUR.crypto.Signature({ 'alg' : ALGORITHM}) // 来解析密钥 const priKey = KEYUTIL.getKey(privateKey) signature.init(priKey) // 传入待签明文 signature.updateString(src) const a = signature.sign() // 转换成base64,返回 return hex2b64(a) } // 公钥验签 const RSA_VERIFY_SIGN = (publicKey, src, data) => { const signature = new KJUR.crypto.Signature({ 'alg' : ALGORITHM, 'prvkeypem' : publicKey}) signature.updateString(src) return signature.verify(b64tohex(data)) } export { RSA_SIGN, RSA_VERIFY_SIGN } |
客户端(Vue)通过sign.js进行加签、验签。
?const src = '我是一段测试字符串2' const publicKey = '-----BEGIN PUBLIC KEY-----\n' + 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC35wxzdTzseajkYL06hEKBCEJu\n' + 'JQ/nySId2oTnsxbLiSTEjpAESSbML1lqkKaIwjrSFZzyLMH6DirsoEQcATqqoCDU\n' + '/H9QNVb5jMSAxxdQusQkTWz6k07bEuy1ppVjpGxNi8o2OGNd+lwPC/hOSDR7lpfm\n' + 'aXLIjEwKSXzil7YAHQIDAQAB\n' + '-----END PUBLIC KEY-----' const privateKey = '-----BEGIN PRIVATE KEY-----\n' + 'MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBALfnDHN1POx5qORg\n' + 'vTqEQoEIQm4lD+fJIh3ahOezFsuJJMSOkARJJswvWWqQpojCOtIVnPIswfoOKuyg\n' + 'RBwBOqqgINT8f1A1VvmMxIDHF1C6xCRNbPqTTtsS7LWmlWOkbE2LyjY4Y136XA8L\n' + '+E5INHuWl+ZpcsiMTApJfOKXtgAdAgMBAAECgYB2PAcGSC7mPoW2ZvfiIlx7hurm\n' + '0885D1hu5yohqUOTklXgRWQUTU+zYRHU8LERJgcZQKoKDXqdIPS584Q2mRe0uZMr\n' + 'vaiaBVEnHQreUJUQ8UN12pPUdBHDZvOk3L7/fZHk6A8uy5e09p2rsn+Vfki3zijp\n' + '7Pd758HMtjuiHBb2QQJBAOuN6jdWBr/zb7KwM9N/cD1jJd6snOTNsLazH/Z3Yt0T\n' + 'jlsFmRJ6rIt/+jaLKG6YTR8SFyW5LIQTbreeQHPw4FECQQDH3Wpd/mBMMcgpxLZ0\n' + 'F5p1ieza+VA5fbxkQ0hdubEP26B6YwhkTB/xMSOwEjmUI57kfgOTvub36/peb8rI\n' + 'JdwNAkB3fzwlrGeqMzYkIU15avomuki46TqCvHJ8jOyXHUOzQbuDI5jfDgrAjkEC\n' + 'MKBnUq41J/lEMueJbU5KqmaqKrWxAkAyexlHnl1iQVymOBpBXkjUET8y26/IpZp0\n' + '1I2tpp4zPCzfXK4c7yFOQTQbX68NXKXgXne21Ivv6Ll3KtNUFEPtAkBcx5iWU430\n' + '0/s6218/enaa8jgdqw8Iyirnt07uKabQXqNnvbPYCgpeswEcSvQqMVZVKOaMrjKO\n' + 'G319Es83iq/m\n' + '-----END PRIVATE KEY-----\n' console.log( '明文:' , src) const data = RSA_SIGN(privateKey, src) console.log( '签名后的结果:' , data) const res = RSA_VERIFY_SIGN(publicKey, src, data) console.log( '验签结果:' , res) |
服务端(Spring boot)接收请求后,需要对数据和签名,进行验证。
首先引入依赖——hutool工具包,Hutool是一个Java工具包,也只是一个工具包,它帮助我们简化每一行代码,减少每一个方法,让Java语言也可以“甜甜的”。
官网地址:https://www.hutool.cn/
在pom.xml下增加如下配置:
?< dependency > < groupId >cn.hutool</ groupId > < artifactId >hutool-all</ artifactId > < version >5.3.5</ version > </ dependency > |
服务端(Spring boot)首先要获取客户端(Vue)请求的数据,上文已经描述了请求的数据有两部分,分别是“请求头部分”、“数据部分”。所以需要配置拦截器,对以上两部分进行获取。
配置拦截器(MyInterceptor.java),代码如下:
?import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.util.StreamUtils; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Slf4j @Component public class MyInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //获取请求参数 String queryString = request.getQueryString(); log.info( "请求参数:{}" , queryString); // 获取header log.info( "key:{}" ,request.getHeader( "timestamp" )); MyHttpServletRequestWrapper myRequestWrapper = new MyHttpServletRequestWrapper(request); //获取请求body byte [] bodyBytes = StreamUtils.copyToByteArray(myRequestWrapper.getInputStream()); String body = new String(bodyBytes, request.getCharacterEncoding()); log.info( "请求体:{}" , body); return true ; } } |
在获取“请求体body”时,由于“HttpServletRequest”只能读取一次,拦截器读取后,后续Controller在读取时为空,所以需要重写HttpServletRequestWrapper:
?import org.springframework.util.StreamUtils; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.io.*; public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper { /** * 缓存下来的HTTP body */ private byte [] body; public MyHttpServletRequestWrapper(HttpServletRequest request) throws IOException { super (request); body = StreamUtils.copyToByteArray(request.getInputStream()); } @Override public ServletInputStream getInputStream() throws IOException { InputStream bodyStream = new ByteArrayInputStream(body); return new ServletInputStream(){ @Override public int read() throws IOException { return bodyStream.read(); } @Override public boolean isFinished() { return false ; } @Override public boolean isReady() { return true ; } @Override public void setReadListener(ReadListener readListener) { } }; } @Override public BufferedReader getReader() throws IOException { return new BufferedReader( new InputStreamReader(getInputStream())); } } |
之后,需要创建过滤器,将“MyHttpServletRequestWrapper” 替换“ServletRequest”,代码如下:
?import lombok.extern.slf4j.Slf4j; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import java.io.IOException; @Slf4j public class RepeatedlyReadFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { ServletRequest requestWrapper = null ; if (servletRequest instanceof HttpServletRequest) { requestWrapper = new MyHttpServletRequestWrapper((HttpServletRequest) servletRequest); } if (requestWrapper == null ) { filterChain.doFilter(servletRequest, servletResponse); } else { filterChain.doFilter(requestWrapper, servletResponse); } } @Override public void destroy() { } } |
之后创建自定义配置,CorsConfig.java,将过滤器、拦截器加入配置:
?import com.xyf.interceptor.MyInterceptor; import com.xyf.interceptor.RepeatedlyReadFilter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; @Configuration public class CorsConfig extends WebMvcConfigurationSupport { private MyInterceptor myInterceptor; @Autowired public CorsConfig (MyInterceptor myInterceptor){ this .myInterceptor = myInterceptor; } // 注册过滤器 @Bean public FilterRegistrationBean<RepeatedlyReadFilter> repeatedlyReadFilter() { FilterRegistrationBean registration = new FilterRegistrationBean(); RepeatedlyReadFilter repeatedlyReadFilter = new RepeatedlyReadFilter(); registration.setFilter(repeatedlyReadFilter); registration.addUrlPatterns( "/*" ); return registration; } @Override protected void addInterceptors(InterceptorRegistry registry) { // addPathPatterns添加需要拦截的命名空间; // excludePathPatterns添加排除拦截命名空间 registry.addInterceptor(myInterceptor).addPathPatterns( "/**" ); //.excludePathPatterns("/api/sys/login") } } |
最后,完成验签,代码如下:
?import cn.hutool.core.codec.Base64; import cn.hutool.crypto.SecureUtil; import cn.hutool.crypto.asymmetric.Sign; import cn.hutool.crypto.asymmetric.SignAlgorithm; byte [] data = "我是一段测试字符串2" .getBytes(); String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC35wxzdTzseajkYL06hEKBCEJu\n" + "JQ/nySId2oTnsxbLiSTEjpAESSbML1lqkKaIwjrSFZzyLMH6DirsoEQcATqqoCDU\n" + "/H9QNVb5jMSAxxdQusQkTWz6k07bEuy1ppVjpGxNi8o2OGNd+lwPC/hOSDR7lpfm\n" + "aXLIjEwKSXzil7YAHQIDAQAB" ; Sign sign = SecureUtil.sign(SignAlgorithm.SHA256withRSA, null ,publicKey); //客户端传来的签名 String qm = "IhY3LNuFn0isud1Pk6BL2eJV3Jl/UzDCYsdG9CYyJwOGqwnzStsv/RiYLnVP4bnQh1NRPMazY6ux/5Zz5Ypcx6RI5W1p5BDbO2afuIZX7x/eIu5utwsanhbxEfvm3XOsyuTbnMDh6BQUrXb4gUz9qgt9IXWjQdqnQRRv3ywzWcA=" ; byte [] signed = Base64.decode(qm); //验证签名 boolean verify = sign.verify(data, signed); |
可通过一些网站在线生成公钥、私钥
网址:https://www.bejson.com/enc/rsa/
bejson在线生成公钥、私钥
由于客户端加签、服务端验签。所以加签、验签的方式务必一致,否则将无法验证签名。Vue、Java有不同的签名工具库,使用前要做好测试。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。
猜你喜欢
判断方法:1、用“输入控件对象.value”语句获取用户输入的数据;2、用“var re = /^[0-9]+.?[0-9]*/;”语句定义正则表达式对象;3、用“re.test(输入数据)”语句通过正则表达式判断输入数据是否为数值类型。
JS实现懒加载图片方法有哪些?实现懒加载图片方法有很多,这篇文章就给大家分享三种利用原生JS实现懒加载lazyLoad的方法以及对比,下文实例对大家理解JS实现懒加载图片有一定的帮助,下面我们一起来看看吧。
这篇文章主要为大家详细介绍了微信小程序实现播放音频功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
这篇文章主要给大家分享javascript异步的相关内容,对大家学习和理解javascript异步有一定的帮助,下文有详细的示例和解释,感兴趣的朋友可以参考,接下来跟随小编一起来学习一下吧。
在jquery中,可以利用“$("元素:not(:last-child)")”语句选择除了最后一列的其它元素;“:last-child”选择器可以选取最后一列元素,“:not()”选择器用于选取除指定元素以外的其它元素。
成为群英会员,开启智能安全云计算之旅
立即注册Copyright © QY Network Company Ltd. All Rights Reserved. 2003-2020 群英 版权所有
增值电信经营许可证 : B1.B2-20140078 粤ICP备09006778号 域名注册商资质 粤 D3.1-20240008