一、关键词
HTTP,HTTPS,AES,SHA-1,MD5,消息摘要,数字签名,数字加密,Java,Servlet,Bouncy Castle
二、名词解释
数字摘要:是将任意长度的消息变成固定长度的短消息,它类似于一个自变量是消息的函数,也就是Hash函数。数字摘要就是采用单项Hash函数将需要加密的明文“摘要”成一串固定长度(128位)的密文这一串密文又称为数字指纹,它有固定的长度,而且不同的明文摘要成密文,其结果总是不同的,而同样的明文其摘要必定一致。
AES:密码学中的高级加密标准(Advanced Encryption Standard,AES),又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。这个标准用来替代原先的DES,已经被多方分析且广为全世界所使用。是一种对称加密算法。
SHA-1:安全哈希算法(Secure Hash Algorithm)主要适用于数字签名标准 (Digital Signature Standard DSS)里面定义的数字签名算法(Digital Signature Algorithm DSA)。 SHA1有如下特性:不可以从消息摘要中复原信息;两个不同的消息不会产生同样的消息摘要。
MD5:Message Digest Algorithm MD5(中文名为消息摘要算法第五版)为计算机安全领域广泛使用的一种散列函数,用以提供消息的完整性保护。
三、项目背景
某合作公司需要通过互联网向我司传递一些用户数据,但是我所在项目组的外网服务器上并无部署https,只能基于http进行数据传输。为了保护双方共同的用户数据,必须对在互联网上传输的信息进行加密处理。
四、方案设计
这里涉及到两个问题,一是采用什么样的远程消息传递框架,二是如何对传输的数据进行加密。
本人平时开发所用的语言主要是Java,对于Jsp/Servlet还比较熟悉,结合去年参加过所在公司的微信公众号开发的经验,设计出了如下方案:
1.在客户端采用构造http post请求,把用户数据加密后放入request body中,并在http参数中放入调用方的签名;
2.服务端接收到请求,提取参数进行签名校验,通过后从request body中提取密文进行解密,然后进行后续处理,最终生成响应返回给客户端。
以下是具体处理的流程图:
在数据加密阶段,基于性能以及效率考虑,采用了Bouncy Castle提供的AES算法,而生成签名则采用了jdk提供的SHA-1,值得注意的是,基于安全考虑,消息密文的消息摘要也被列入到参与数字签名的参数之一。
五、代码实现
1.AES加密工具类:
import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.engines.AESFastEngine; import org.bouncycastle.crypto.modes.CBCBlockCipher; import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithIV; import org.bouncycastle.util.encoders.Hex; /** * AES encryption and decryption tool. * * @author ben * @creation 2014年3月20日 */ public class AESTool { protected static final Logger log = Logger.getLogger(AESTool.class); private byte[] initVector = { 0x32, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x38, 0x27, 0x36, 0x35, 0x33, 0x23, 0x32, 0x31 }; /** * FIXME For demo only, should rewrite this method in your product environment! * * @param appid * @return */ public String findKeyById(String appid) { // Fake key. String key = "123456789012345678901234567890~!"; return key; } /** * Encrypt the content with a given key using aes algorithm. * * @param content * @param key * must contain exactly 32 characters * @return * @throws Exception */ public String encrypt(String content, String key) throws Exception { if (key == null) { throw new IllegalArgumentException("Key cannot be null!"); } String encrypted = null; byte[] keyBytes = key.getBytes(); if (keyBytes.length != 32 && keyBytes.length != 24 && keyBytes.length != 16) { throw new IllegalArgumentException( "Key length must be 128/192/256 bits!"); } byte[] encryptedBytes = null; encryptedBytes = encrypt(content.getBytes(), keyBytes, initVector); encrypted = new String(Hex.encode(encryptedBytes)); return encrypted; } /** * Decrypt the content with a given key using aes algorithm. * * @param content * @param key * must contain exactly 32 characters * @return * @throws Exception */ public String decrypt(String content, String key) throws Exception { if (key == null) { throw new IllegalArgumentException("Key cannot be null!"); } String decrypted = null; byte[] encryptedContent = Hex.decode(content); byte[] keyBytes = key.getBytes(); byte[] decryptedBytes = null; if (keyBytes.length != 32 && keyBytes.length != 24 && keyBytes.length != 16) { throw new IllegalArgumentException( "Key length must be 128/192/256 bits!"); } decryptedBytes = decrypt(encryptedContent, keyBytes, initVector); decrypted = new String(decryptedBytes); return decrypted; } /** * Encrypt data. * * @param plain * @param key * @param iv * @return * @throws Exception */ public byte[] encrypt(byte[] plain, byte[] key, byte[] iv) throws Exception { PaddedBufferedBlockCipher aes = new PaddedBufferedBlockCipher( new CBCBlockCipher(new AESFastEngine())); CipherParameters ivAndKey = new ParametersWithIV(new KeyParameter(key), iv); aes.init(true, ivAndKey); return cipherData(aes, plain); } /** * Decrypt data. * * @param cipher * @param key * @param iv * @return * @throws Exception */ public byte[] decrypt(byte[] cipher, byte[] key, byte[] iv) throws Exception { PaddedBufferedBlockCipher aes = new PaddedBufferedBlockCipher( new CBCBlockCipher(new AESFastEngine())); CipherParameters ivAndKey = new ParametersWithIV(new KeyParameter(key), iv); aes.init(false, ivAndKey); return cipherData(aes, cipher); } /** * Encrypt or decrypt data. * * @param cipher * @param data * @return * @throws Exception */ private byte[] cipherData(PaddedBufferedBlockCipher cipher, byte[] data) throws Exception { int minSize = cipher.getOutputSize(data.length); byte[] outBuf = new byte[minSize]; int length1 = cipher.processBytes(data, 0, data.length, outBuf, 0); int length2 = cipher.doFinal(outBuf, length1); int actualLength = length1 + length2; byte[] result = new byte[actualLength]; System.arraycopy(outBuf, 0, result, 0, result.length); return result; } public static void main(String[] args) throws Exception { AESTool aesTool = new AESTool(); String appid = "canairport001"; String key = aesTool.findKeyById(appid); String xml = "<root><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name></root>"; String encrypted = aesTool.encrypt(xml, key); System.out.println("encrypted: \n" + encrypted); System.out.println("encrypted length: \n" + encrypted.length()); String decrypted = aesTool.decrypt(encrypted, key); System.out.println("decrypted: \n" + decrypted); System.out.println("decrypted length: \n" + decrypted.length()); boolean isSuccessful = StringUtils.equals(decrypted, xml); System.out.println(isSuccessful); } }
2.数字签名工具类:
import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; /** * @author lixuanbin * @creation 2013-1-30 */ public class SignatureUtil { protected static Logger log = Logger.getLogger(SignatureUtil.class); private static final char[] hexArray = "0123456789ABCDEF".toCharArray(); private String encryptionAlgorithm = "SHA-1"; public String bytesToHexString(byte[] bytes) { char[] hexChars = new char[bytes.length * 2]; for (int j = 0; j < bytes.length; j++) { int v = bytes[j] & 0xFF; hexChars[j * 2] = hexArray[v >>> 4]; hexChars[j * 2 + 1] = hexArray[v & 0x0F]; } return new String(hexChars); } public byte[] hexStringToBytes(String s) { int len = s.length(); byte[] data = new byte[len / 2]; for (int i = 0; i < len; i += 2) { data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character .digit(s.charAt(i + 1), 16)); } return data; } /** * 使用指定算法生成消息摘要,默认是md5 * * @param strSrc * , a string will be encrypted; <br/> * @param encName * , the algorithm name will be used, dafault to "MD5"; <br/> * @return */ public String digest(String strSrc, String encName) { MessageDigest md = null; String strDes = null; byte[] bt = strSrc.getBytes(); try { if (encName == null || encName.equals("")) { encName = "MD5"; } md = MessageDigest.getInstance(encName); md.update(bt); strDes = bytesToHexString(md.digest()); // to HexString } catch (NoSuchAlgorithmException e) { log.error("Invalid algorithm: " + encName); return null; } return strDes; } /** * 根据appid、token、lol以及时间戳来生成签名 * * @param appid * @param token * @param lol * @param millis * @return */ public String generateSignature(String appid, String token, String lol, long millis) { String timestamp = String.valueOf(millis); String signature = null; if (StringUtils.isNotBlank(token) && StringUtils.isNotBlank(timestamp) && StringUtils.isNotBlank(appid)) { List<String> srcList = new ArrayList<String>(); srcList.add(timestamp); srcList.add(appid); srcList.add(token); srcList.add(lol); // 按照字典序逆序拼接参数 Collections.sort(srcList); Collections.reverse(srcList); StringBuilder sb = new StringBuilder(); for (int i = 0; i < srcList.size(); i++) { sb.append(srcList.get(i)); } signature = digest(sb.toString(), encryptionAlgorithm); srcList.clear(); srcList = null; } return signature; } /** * 验证签名: <br/> * 1.根据appid获取该渠道的token;<br/> * 2.根据appid、token、lol以及时间戳计算一次签名;<br/> * 3.比较传过来的签名以及计算出的签名是否一致; * @param signature * @param appid * @param lol * @param millis * @return */ public boolean isValid(String signature, String appid, String lol, long millis) { String token = findTokenById(appid); String calculatedSignature = generateSignature(appid, token, lol, millis); log.info("calculated signature: \n" + calculatedSignature); if (StringUtils.equals(calculatedSignature, signature)) { return true; } else { return false; } } /** * FIXME For demo only, should be a different string in production. * @param appid * @return */ public String findTokenById(String appid) { String token = "#@!1234567890!@#"; return token; } public static void main(String[] args) { SignatureUtil generator = new SignatureUtil(); String xmlString = "<root><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name><name>test</name></root>"; System.out.println(xmlString.getBytes().length); String digest = generator.digest(xmlString, "MD5"); System.out.println(digest); System.out.println(digest.getBytes().length); String appid = "canairport001"; String token = generator.findTokenById(appid); long millis = System.currentTimeMillis(); String signature = generator.generateSignature(appid, token, digest, millis); System.out.println(signature); boolean isValid = generator.isValid(signature, appid, digest, millis); System.out.println(isValid); } }
3.发送方代码:
import java.io.IOException; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import org.apache.commons.lang.StringUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpHost; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.methods.HttpPost; import org.apache.http.conn.params.ConnRoutePNames; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicHeader; import org.apache.http.protocol.HTTP; import org.apache.http.util.EntityUtils; import org.apache.log4j.Logger; /** * @author ben * @creation 2014年6月9日 */ public class HttpclientUtil { protected static final Logger log = Logger.getLogger(HttpclientUtil.class); /** * 根据传入的uri和参数map拼接成实际uri * * @param uri * @param paraMap * @return */ public String buildUri(String uri, Map<String, String> paraMap) { StringBuilder sb = new StringBuilder(); uri = StringUtils.trim(uri); uri = StringUtils.removeEnd(uri, "/"); uri = StringUtils.removeEnd(uri, "?"); sb.append(uri); if (paraMap != null && !paraMap.isEmpty()) { sb.append("?"); Iterator<Entry<String, String>> iterator = paraMap.entrySet() .iterator(); while (iterator.hasNext()) { Map.Entry<String, String> pair = iterator.next(); try { String keyString = pair.getKey(); String valueString = pair.getValue(); sb.append(keyString); sb.append("="); sb.append(valueString); sb.append("&"); } catch (Exception e) { log.error(e, e); } } } return StringUtils.removeEnd(sb.toString(), "&"); } /** * Post an xml string to a specific host. * * @param targetHost * @param targetPort * @param protocol * @param proxyHost * @param proxyPort * @param proxyUser * @param proxyPassword * @param uri * @param paraMap * @param xml * @param charset * @return * @throws ClientProtocolException * @throws IOException */ public String postXmlString(String targetHost, int targetPort, String protocol, String proxyHost, int proxyPort, String proxyUser, String proxyPassword, String uri, Map<String, String> paraMap, String xml, String charset) throws ClientProtocolException, IOException { String result = null; DefaultHttpClient httpclient = new DefaultHttpClient(); if (StringUtils.isNotBlank(proxyHost) && proxyPort > 0) { // 设置上网代理 AuthScope authScope = new AuthScope(proxyHost, proxyPort); if (StringUtils.isNotBlank(proxyUser) && StringUtils.isNotBlank(proxyPassword)) { // 设置上网代理的用户名和密码 UsernamePasswordCredentials upc = new UsernamePasswordCredentials( proxyUser, proxyPassword); httpclient.getCredentialsProvider().setCredentials(authScope, upc); } HttpHost proxy = new HttpHost(proxyHost, proxyPort); httpclient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy); } HttpHost host = new HttpHost(targetHost, targetPort, protocol); uri = buildUri(uri, paraMap); log.info("post uri: " + uri); log.info("post content: " + xml); HttpPost post = new HttpPost(uri); StringEntity se = new StringEntity(xml, StringUtils.isNotBlank(charset) ? charset : "utf-8"); se.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE, "application/xml")); post.setEntity(se); HttpResponse response = httpclient.execute(host, post); if (HttpStatus.SC_OK == response.getStatusLine().getStatusCode()) { HttpEntity entity = response.getEntity(); if (entity != null) { result = EntityUtils.toString(entity); log.info("post result: " + result); } } else { log.error("post failed, status code: " + response.getStatusLine().getStatusCode()); } return result; } public static void main(String[] args) throws Exception { AESTool aes = new AESTool(); SignatureUtil signatureUtil = new SignatureUtil(); String appid = "canairport001"; String token = signatureUtil.findTokenById(appid); String key = aes.findKeyById(appid); long millis = System.currentTimeMillis(); String xml = "<dependency><groupId>commons-lang</groupId><artifactId>commons-lang</artifactId><version>2.5</version></dependency>"; xml = aes.encrypt(xml, key); String lol = signatureUtil.digest(xml, "MD5"); String signature = signatureUtil.generateSignature(appid, token, lol, millis); log.info("lol: \n" + lol); log.info("signature: \n" + signature); String uri = "http://127.0.0.1:8080/demo/psginfo.do"; Map<String, String> paraMap = new HashMap<String, String>(); paraMap.put("s", signature); paraMap.put("a", appid); paraMap.put("t", String.valueOf(millis)); paraMap.put("l", lol); paraMap.put("o", "test"); HttpclientUtil util = new HttpclientUtil(); try { String result = util.postXmlString("127.0.0.1", 8080, "http", null, 0, null, null, uri, paraMap, xml, "utf-8"); result = aes.decrypt(result, key); System.out.println(result); } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
4.服务端代码:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import co.speedar.wechat.util.AESTool; import co.speedar.wechat.util.SignatureUtil; /** * Servlet implementation class PsginfoServlet */ @WebServlet(urlPatterns = { "/psginfo.do" }, loadOnStartup = 1) public class PsginfoServlet extends HttpServlet { protected static final Logger log = Logger.getLogger(PsginfoServlet.class); private static final long serialVersionUID = 6536688299231165548L; private SignatureUtil signatureUtil = new SignatureUtil(); private AESTool aes = new AESTool(); /** * @see HttpServlet#HttpServlet() */ public PsginfoServlet() { super(); } /** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse * response) */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String echostr = request.getParameter("e"); log.info("echostr before echo: " + echostr); String signature = request.getParameter("s"); String appid = request.getParameter("a"); String timestamp = request.getParameter("t"); String lol = request.getParameter("l"); long millis = Long.valueOf(timestamp); // Need to check signature in product mode. if (signatureUtil.isValid(signature, appid, lol, millis)) { PrintWriter writer = response.getWriter(); log.info("echostr after echo: " + echostr); writer.print(echostr); writer.flush(); writer.close(); } } /** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse * response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Get request parameters. String signature = request.getParameter("s"); String appid = request.getParameter("a"); String timestamp = request.getParameter("t"); String lol = request.getParameter("l"); String operation = request.getParameter("o"); long millis = Long.valueOf(timestamp); // Get xml data. String encoding = StringUtils .isNotBlank(request.getCharacterEncoding()) ? request .getCharacterEncoding() : "utf-8"; String requestXmlString = getXmlStringFromHttpRequest(request); String digest = signatureUtil.digest(requestXmlString, "MD5"); // Check signature and digest. if (StringUtils.equals(digest, lol)) { if (signatureUtil.isValid(signature, appid, lol, millis)) { try { String key = aes.findKeyById(appid); requestXmlString = aes.decrypt(requestXmlString, key); log.info("received xml data:\n" + requestXmlString); // 校验xml合法性并执行相应动作 String responseXmlString = doSomeThing(requestXmlString, operation); responseXmlString = aes.encrypt(responseXmlString, key); log.info("responsed xml data:\n" + responseXmlString); response.setCharacterEncoding(encoding); PrintWriter writer = response.getWriter(); writer.print(responseXmlString); writer.flush(); writer.close(); } catch (Exception e) { log.error(e, e); } } else { log.error("invalid signature"); } } else { log.error("invalid digest."); } } /** * TODO Write your own business here. * * @param xml * @param operation * @return */ private String doSomeThing(String xml, String operation) { return "done"; } /** * Extract xml string form http request. * * @param request * @return * @throws IOException */ private String getXmlStringFromHttpRequest(HttpServletRequest request) { String requestXmlString = ""; try { InputStream inputStream = request.getInputStream(); String encoding = StringUtils.isNotBlank(request .getCharacterEncoding()) ? request.getCharacterEncoding() : "utf-8"; requestXmlString = getXmlStringFromInputStream(inputStream, encoding); encoding = null; inputStream.close(); inputStream = null; } catch (IOException e) { log.error(e, e); } return requestXmlString; } /** * Extract xml string from the inputStream. * * @param inputStream * @param charsetName * @return */ private String getXmlStringFromInputStream(InputStream inputStream, String charsetName) { String resultXmlString = ""; String tempString = null; BufferedReader bufferedReader; try { bufferedReader = new BufferedReader(new InputStreamReader( inputStream, charsetName)); tempString = bufferedReader.readLine(); while (tempString != null) { resultXmlString += tempString; tempString = bufferedReader.readLine(); } tempString = null; bufferedReader.close(); bufferedReader = null; } catch (UnsupportedEncodingException e) { log.error(e, e); } catch (IOException e) { log.error(e, e); } return StringUtils.trim(resultXmlString); } }
5.maven配置:
<dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk16</artifactId> <version>1.46</version> </dependency> <dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.5</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.2.5</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpmime</artifactId> <version>4.2.5</version> </dependency>
六、结语
在本方案设计实现过程中,消息传递的框架采用的是Java开发者所熟悉的Servlet技术,摘要、签名、加密所采用的算法,以及所依赖的第三方jar也是比较有口碑又大众化的货,对于有类似需要的开发者来说,本方案具有一定的参考意义。远程传递消息框架以及生成签名的环节,主要是模仿了微信公众平台的消息交互方式以及生成签名的思路,而有所创新的一小点是,把消息密文的MD5值也参与到了签名运算中,增加了被仿冒的难度,同时也便于服务方校验消息在传递过程中是否有被第三方所篡改。
基于简化工程配置的考虑,本示例项目中没有使用spring,您可以在您的生产项目中把本示例中的代码改造成春哥的单例业务bean。密钥、token建议别直接写到春哥的context配置文件中,而是写在您的生产容器的环境变量中,防止被窃取。
另外,在本方案中生成签名的参数您可以酌情增减并调换顺序,替换签名所采用的算法,或者根据您的实际需要“个性化”一下您的加密算法,以期达到更好的安全效果。
Last but not the least,在密钥以及token交换的阶段,请采取您所认可的安全有效的方式进行,譬如面对面,微信,qq,微薄私信,电话,短信,邮件(可以参考本人之前写过的一篇文章:http://lixuanbin.iteye.com/blog/1544344)
七、参考资料
【Java加密与解密的艺术】——作者:梁栋,出版日期:2010年12月,ISBN:978-7-111-29762-8
http://stackoverflow.com/questions/6729834/need-solution-for-wrong-iv-length-in-aes
http://baike.baidu.com/view/941329.htm?fr=aladdin
http://baike.baidu.com/subview/133041/5358738.htm?fr=aladdin
http://baike.baidu.com/view/1228622.htm?fr=aladdin
http://baike.baidu.com/view/7636.htm?fr=aladdin
相关推荐
免费JAVA毕业设计 2024成品源码+论文+数据库+启动教程 启动教程:https://www.bilibili.com/video/BV1SzbFe7EGZ 项目讲解视频:https://www.bilibili.com/video/BV1Tb421n72S 二次开发教程:https://www.bilibili.com/video/BV18i421i7Dx
,IGBT结温估算 模型见另一个发布
"S7-200 PLC驱动的智能粮仓系统:带解释的接线图与组态画面原理详解",S7-200 mcgs基于plc的自动智能粮仓系统 带解释的梯形图接线图原理图图纸,io分配,组态画面 ,S7-200; PLC; 自动智能粮仓系统; 梯形图接线图; 原理图图纸; IO分配; 组态画面,基于S7-200 PLC的智能粮仓系统设计与实现
手机编程-1738391379497.jpg
,rk3399pro,rk3568,车载方案设计,4路AHD-1080P摄像头输入,防撞识别,助力车泥头车安全运输
,CAD、DXF导图,自动进行位置路径规划,源码可进行简单功能添加实现设备所需功能,已经在冲孔机,点胶机上应用,性价比超高。 打孔机实测一分钟1400个孔
,电机控制资料-- 注:本驱动器适合于直流有感无刷电机 功能特点 支持电压9V~36V,额定输出电流5A 支持电位器、开关、0~3.3V模拟信号范围、0 3.3 5 24V逻辑电平、PWM 频率 脉冲信号、RS485多种输入信号 支持占空比调速(调压)、速度闭环控制(稳速)、电流控制(稳流)多种调速方式 支持按键控制正反转速度,启停 特色功能 1. 霍尔自学习 电机的三相线和三霍尔信号线可不按顺序连接,驱动器可自动对电机霍尔顺序进行学习。 2. 稳速控制响应时间短 稳速控制时电机由正转2000RPM切为反转2000RPM,用时约1.0s,电机切过程平稳 3. 极低速稳速控制 电机进行极低速稳速控制,电机稳速控制均匀,无忽快忽慢现象。
《HFSS同轴馈电矩形微带天线的模型制作与参数优化:从结果中学习,使用HFSS软件包进行实践的详细教程》,HFSS同轴馈电矩形微带天线 天线模型,附带结果,可改参数,HFSS软件包 (有教程,具体到每一步,可以自己做出来) ,HFSS; 同轴馈电; 矩形微带天线; 可改参数; HFSS软件包; 附带结果; 教程,HFSS软件包:可改参微带天线模型附带结果教程
"基于第二篇文章求解方法,改进粒子群算法在微电网综合能源优化调度的应用与复现代码展示——第一篇模型的参考与实践",基于改进粒子群算法微电网综合能源优化调度 求解方法主要参考第二篇文章 模型参照第一篇 复现代码 ,核心关键词: 基于改进粒子群算法; 微电网综合能源优化调度; 求解方法; 第二篇文章; 模型; 第一篇文章; 复现代码;,基于第二篇求解方法的改进粒子群算法在微电网综合能源优化调度中的应用研究
基于Comsol模拟的三层顶板随机裂隙浆液扩散模型:考虑重力影响的瞬态扩散规律分析,Comsol模拟,考虑三层顶板包含随机裂隙的浆液扩散模型,考虑浆液重力的影响,模型采用的DFN插件建立随机裂隙,采用达西定律模块中的储水模型为控制方程,分析不同注浆压力条件下的浆液扩散规律,建立瞬态模型 ,Comsol模拟; 随机裂隙浆液扩散模型; 浆液重力影响; DFN插件; 达西定律模块储水模型; 注浆压力条件; 浆液扩散规律; 瞬态模型,Comsol浆液扩散模型:随机裂隙下考虑重力的瞬态扩散分析
"基于S7-200 PLC与MCGS组态的五层电梯控制系统设计与实现:带详细接线图、IO分配及组态画面解析",S7-200 PLC和MCGS组态5层电梯五层电梯PLC控制系统 带解释的梯形图接线图原理图图纸,io分配,组态画面 ,核心关键词:S7-200 PLC; MCGS组态; 五层电梯; PLC控制系统; 梯形图接线图; IO分配; 组态画面。,S7-200 PLC与MCGS组态五层电梯控制系统原理图及梯形图解析
一、项目简介 本项目是一套基于springBoot+mybatis+maven+vue夕阳红公寓管理系统 包含:项目源码、数据库脚本等,该项目附带全部源码可作为毕设使用。 项目都经过严格调试,eclipse或者idea 确保可以运行! 该系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值 二、技术实现 jdk版本:1.8 及以上 ide工具:IDEA或者eclipse 数据库: mysql5.5及以上 后端:spring+springboot+mybatis+maven+mysql 前端: vue , css,js , elementui 三、系统功能 1、系统角色主要包括:管理员、用户 2、系统功能 主要功能包括: 用户登录注册 首页 个人中心 修改密码 个人信息 访客管理 公告信息管理 缴费管理 维修管理 行程轨迹管理 单页号类型管理 公告类型管理 维修类型管理 租客管理 轮播图管理 余额充值等功能 详见 https://flypeppa.blog.csdn.net/article/details/143117373
基于时空Transformer的端到端的视频注视目标检测.pdf
Online Retail.xlsx
,C#地磅称重无人值守管理软件。 软件实现功能: 1、身份证信息读取。 2、人证识别。 3、车牌识别(臻识摄像头、海康摄像头)。 4、LED显示屏文字输出。 5、称重仪数据。 6、二维码扫码。 7、语音播报。 8、红外对射功能。 9、道闸控制。
com.deepseek.chat.apk
基于pyqt5+OpenPose的太极拳姿态识别系统可视化界面python源码+数据集.zip,个人大三大作业设计项目、经导师指导并认可通过的高分设计项目,评审分99分,代码完整确保可以运行,小白也可以亲自搞定,主要针对计算机相关专业的正在做毕设的学生和需要项目实战练习的学习者,也可作为课程设计、期末大作业。 该压缩包是一个基于PyQt5和OpenPose技术的太极拳姿态识别系统的源代码和相关资源集合。系统能够实现对太极拳动作的实时姿态识别,并通过可视化界面展示出来,为学习和教学太极拳提供便利。 二、技术栈与组件 PyQt5:一个Python绑定的Qt库,用于创建图形用户界面(GUI)应用程序。它提供了丰富的组件和工具,可以方便地构建各种复杂界面,如按钮、文本框、图像视图等,同时也支持事件驱动编程,使得用户交互更加灵活。 OpenPose:一个来自卡内基梅隆大学(CMU)的开源库,主要用于人体、面部、手部以及脚部的关键点检测。它采用了深度学习的方法,能够在单张图片上实时估计多人的关节位置,对于运动分析、姿态识别等领域非常有用。
1、文件内容:pygtk2-devel-2.24.0-9.el7.rpm以及相关依赖 2、文件形式:tar.gz压缩包 3、安装指令: #Step1、解压 tar -zxvf /mnt/data/output/pygtk2-devel-2.24.0-9.el7.tar.gz #Step2、进入解压后的目录,执行安装 sudo rpm -ivh *.rpm 4、安装指导:私信博主,全程指导安装
"金纳米超表面模型:几何相位控制下的涡旋光生成与FDTD仿真研究",几何相位 金属超表面模型 涡旋光生成 FDTD仿真 复现lunwen:2012年Nano Letters:Dispersionless Phase Discontinuities for Controlling Light Propagation lunwen介绍:金纳米结构超表面模型,金属材料矩形结构,通过旋转角度执行几何相位,构建异常折反射超表面模型,通过涡旋相位匹配几何相位,构建生产轨道角动量的涡旋光场超表面; 案例内容:主要包括金纳米柱的单元结构仿真、几何相位计算,涡旋光的螺旋相位计算代码,以及异常折反射的超表面模型和轨道角动量光束生成的超表面模型; 案例包括fdtd模型、fdtd建模脚本、Matlab相位计算代码和电场复现结果,以及一份word教程,异常折反射和涡旋光相位的构建代码可用于任意波段,具备可拓展性。 ,核心关键词: 1. 几何相位 2. 金属超表面模型 3. 涡旋光生成 4. FDTD仿真 5. 复现论文 6. 金纳米结构 7. 异常折反射超表面模型 8. 轨道角动量光束 9. 单元结构仿
comso三维声表面波诱导液滴行为研究:液滴拉伸断裂过程的可视化及分析,包含液滴最高坐标、底面接触面积、空气接触面积与能量项研究。,comso三维声表面波作用液滴,液滴拉伸断裂形成液滴,结果图包含液滴最高坐标,液滴与底面接触面积,与空气接触面积,以及能量项 ,关键词:comso三维声表面波;液滴拉伸断裂;最高坐标;接触面积(底面/空气);能量项;结果图。,声波作用下液滴断裂,图示液滴信息及能量项分析