fix(接口测试): 修复大批量场景执行偶发证书认证失败问题

Signed-off-by: fit2-zhao <yong.zhao@fit2cloud.com>
This commit is contained in:
fit2-zhao 2023-11-17 15:40:02 +08:00 committed by Craftsman
parent 488ed0fa90
commit 4712d98627
5 changed files with 110 additions and 41 deletions

View File

@ -620,9 +620,11 @@ public class ElementUtil {
return processor;
}
public static void setBaseParams(TestElement sampler, MsTestElement parent, ParameterConfig config, String id, String indexPath) {
public static String setBaseParams(TestElement sampler, MsTestElement parent, ParameterConfig config, String id, String indexPath) {
sampler.setProperty("MS-ID", id);
sampler.setProperty("MS-RESOURCE-ID", ElementUtil.getResourceId(id, config, parent, indexPath));
String resourceId = ElementUtil.getResourceId(id, config, parent, indexPath);
sampler.setProperty("MS-RESOURCE-ID", resourceId);
return resourceId;
}
private static final List<String> preOperates = new ArrayList<String>() {{

View File

@ -129,7 +129,8 @@ public class MsHTTPSamplerProxy extends MsTestElement {
}
sampler.setProperty(TestElement.TEST_CLASS, HTTPSamplerProxy.class.getName());
sampler.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("HttpTestSampleGui"));
ElementUtil.setBaseParams(sampler, this.getParent(), config, this.getId(), this.getIndex());
String resourceId = ElementUtil.setBaseParams(sampler, this.getParent(), config, this.getId(), this.getIndex());
sampler.setMethod(this.getMethod());
sampler.setContentEncoding(StandardCharsets.UTF_8.name());
sampler.setFollowRedirects(this.isFollowRedirects());
@ -227,7 +228,7 @@ public class MsHTTPSamplerProxy extends MsTestElement {
if (this.authManager != null && MsAuthManager.mechanismMap.containsKey(this.authManager.getVerification())) {
this.authManager.setAuth(httpSamplerTree, this.authManager, sampler);
}
addCertificate(config, httpSamplerTree);
addCertificate(config, httpSamplerTree, resourceId);
if (httpConfig != null) {
//根据配置增加全局前后至脚本
JMeterScriptUtil.setScriptByHttpConfig(httpConfig, httpSamplerTree, config, useEnvironment, this.getEnvironmentId(), false);
@ -500,7 +501,7 @@ public class MsHTTPSamplerProxy extends MsTestElement {
/**
* 加载SSL认证
*/
private void addCertificate(ParameterConfig config, HashTree httpSamplerTree) {
private void addCertificate(ParameterConfig config, HashTree httpSamplerTree, String resourceId) {
if (config != null && config.isEffective(this.getProjectId()) && getEnvironmentConfig(config).getSslConfig() != null) {
KeyStoreConfig sslConfig = getEnvironmentConfig(config).getSslConfig();
List<KeyStoreFile> files = sslConfig.getFiles();
@ -542,6 +543,7 @@ public class MsHTTPSamplerProxy extends MsTestElement {
keystoreConfig.setProperty("startIndex", 0);
keystoreConfig.setProperty(ElementConstants.MS_KEYSTORE_FILE_PATH, msKeyStore.getPath());
keystoreConfig.setProperty(ElementConstants.MS_KEYSTORE_FILE_PASSWORD, msKeyStore.getPassword());
keystoreConfig.setProperty("MS-RESOURCE-ID", resourceId);
httpSamplerTree.add(keystoreConfig);
config.getKeyStoreMap().put(this.getProjectId(), new MsKeyStore(msKeyStore.getPath(), msKeyStore.getPassword()));
}

View File

@ -61,7 +61,8 @@ public class KeystoreConfig extends ConfigTestElement implements TestBean, TestS
@Override
public void testEnded(String host) {
log.info("Destroying Keystore");
SSLManager.getInstance().destroyKeystore();
String resourceId = this.getPropertyAsString("MS-RESOURCE-ID");
SSLManager.getInstance().destroyKeystore(resourceId);
}
@Override
@ -110,10 +111,23 @@ public class KeystoreConfig extends ConfigTestElement implements TestBean, TestS
} catch (IOException e) {
log.error(e.getMessage());
}
// 获取请求上的资源ID
String resourceId = this.getPropertyAsString("MS-RESOURCE-ID");
if (StringUtils.isNotBlank(resourceId)) {
KeystoreDTO dto = new KeystoreDTO();
dto.setStartIndex(startIndexAsInt);
dto.setEndIndex(endIndexAsInt);
dto.setPreload(this.preload);
dto.setClientCertAliasVarName(this.clientCertAliasVarName);
dto.setPwd(password);
dto.setPath(path);
SSLManager.keyMap.put(resourceId, dto);
}
SSLManager.getInstance().configureKeystore(Boolean.parseBoolean(preload),
startIndexAsInt,
endIndexAsInt,
clientCertAliasVarName, in, password);
}
/**

View File

@ -0,0 +1,13 @@
package org.apache.jmeter.config;
import lombok.Data;
@Data
public class KeystoreDTO {
private int startIndex;
private int endIndex;
private String preload;
private String clientCertAliasVarName;
private String pwd;
private String path;
}

View File

@ -17,8 +17,12 @@
package org.apache.jmeter.util;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.jmeter.config.KeystoreDTO;
import org.apache.jmeter.gui.GuiPackage;
import org.apache.jmeter.threads.JMeterContext;
import org.apache.jmeter.threads.JMeterContextService;
import org.apache.jmeter.util.keystore.JmeterKeyStore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -30,7 +34,9 @@ import java.io.InputStream;
import java.net.HttpURLConnection;
import java.security.*;
import java.security.cert.CertificateException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
/**
@ -42,7 +48,6 @@ import java.util.Locale;
* more information.
* <p>
* TODO? - N.B. does not currently allow the selection of a client certificate.
*
*/
public abstract class SSLManager {
private static final Logger log = LoggerFactory.getLogger(SSLManager.class);
@ -57,20 +62,30 @@ public abstract class SSLManager {
private static final String PKCS12 = "pkcs12"; // $NON-NLS-1$
/** Singleton instance of the manager */
public static final Map<String, KeystoreDTO> keyMap = new HashMap<>();
/**
* Singleton instance of the manager
*/
private static SSLManager manager;
private static final boolean IS_SSL_SUPPORTED = true;
/** Cache the KeyStore instance */
/**
* Cache the KeyStore instance
*/
private JmeterKeyStore keyStore;
/** Cache the TrustStore instance - null if no truststore name was provided */
/**
* Cache the TrustStore instance - null if no truststore name was provided
*/
private KeyStore trustStore = null;
// Have we yet tried to load the truststore?
private volatile boolean truststoreLoaded=false;
private volatile boolean truststoreLoaded = false;
/** Have the password available */
/**
* Have the password available
*/
protected volatile String defaultpw = System.getProperty(KEY_STORE_PASSWORD);
private int keystoreAliasStartIndex;
@ -91,8 +106,7 @@ public abstract class SSLManager {
/**
* Default implementation of setting the Provider
*
* @param provider
* the provider to use
* @param provider the provider to use
*/
protected void setProvider(Provider provider) {
if (null != provider) {
@ -102,7 +116,7 @@ public abstract class SSLManager {
protected synchronized JmeterKeyStore getKeyStore() {
if (null == this.keyStore) {
String fileName = System.getProperty(JAVAX_NET_SSL_KEY_STORE,""); // empty if not provided
String fileName = System.getProperty(JAVAX_NET_SSL_KEY_STORE, ""); // empty if not provided
String fileType = System.getProperty(JAVAX_NET_SSL_KEY_STORE_TYPE, // use the system property to determine the type
fileName.toLowerCase(Locale.ENGLISH).endsWith(".p12") ? PKCS12 : "JKS"); // otherwise use the name
log.info("JmeterKeyStore Location: {} type {}", fileName, fileType);
@ -111,7 +125,7 @@ public abstract class SSLManager {
log.info("KeyStore created OK");
} catch (Exception e) {
this.keyStore = null;
throw new IllegalArgumentException("Could not create keystore: "+e.getMessage(), e);
throw new IllegalArgumentException("Could not create keystore: " + e.getMessage(), e);
}
try {
@ -154,9 +168,9 @@ public abstract class SSLManager {
*
* @return the configured {@link JmeterKeyStore}
*/
protected synchronized JmeterKeyStore getKeyStore(InputStream is,String password) {
protected synchronized JmeterKeyStore getKeyStore(InputStream is, String password) {
if (null == this.keyStore) {
String fileName = System.getProperty(JAVAX_NET_SSL_KEY_STORE,""); // empty if not provided
String fileName = System.getProperty(JAVAX_NET_SSL_KEY_STORE, ""); // empty if not provided
String fileType = System.getProperty(JAVAX_NET_SSL_KEY_STORE_TYPE, // use the system property to determine the type
fileName.toLowerCase(Locale.ENGLISH).endsWith(".p12") ? PKCS12 : "JKS"); // otherwise use the name
log.info("JmeterKeyStore Location: {} type {}", fileName, fileType);
@ -165,7 +179,7 @@ public abstract class SSLManager {
log.info("KeyStore created OK");
} catch (Exception e) {
this.keyStore = null;
throw new IllegalArgumentException("Could not create keystore: "+e.getMessage(), e);
throw new IllegalArgumentException("Could not create keystore: " + e.getMessage(), e);
}
try {
@ -267,7 +281,7 @@ public abstract class SSLManager {
/**
* Opens and initializes the TrustStore.
*
* <p>
* There are 3 possibilities:
* <ul>
* <li>no truststore name provided, in which case the default Java truststore
@ -280,16 +294,14 @@ public abstract class SSLManager {
* If the KeyStore object cannot be created, then this is currently treated the
* same as if no truststore name was provided.
*
* @return
* {@code null} when Java truststore should be used.
* Otherwise the truststore, which may be empty if the file could not be
* loaded.
*
* @return {@code null} when Java truststore should be used.
* Otherwise the truststore, which may be empty if the file could not be
* loaded.
*/
protected KeyStore getTrustStore() {
if (!truststoreLoaded) {
truststoreLoaded=true;// we've tried ...
truststoreLoaded = true;// we've tried ...
String fileName = System.getProperty(SSL_TRUST_STORE);
if (fileName == null) {
@ -302,7 +314,7 @@ public abstract class SSLManager {
log.info("TrustStore created OK, Type: JKS");
} catch (Exception e) {
this.trustStore = null;
throw new RuntimeException("Problem creating truststore: "+e.getMessage(), e);
throw new RuntimeException("Problem creating truststore: " + e.getMessage(), e);
}
try {
@ -342,6 +354,26 @@ public abstract class SSLManager {
if (null == SSLManager.manager) {
SSLManager.manager = new JsseSSLManager(null);
}
try {
// 重新加载认证文件
JMeterContext threadContext = JMeterContextService.getContext();
if (SSLManager.manager.keyStore == null && threadContext != null && threadContext.getCurrentSampler() != null) {
String resourceId = threadContext.getCurrentSampler().getPropertyAsString("MS-RESOURCE-ID");
log.info("重新加载认证文件{}", resourceId);
if (StringUtils.isNotBlank(resourceId) && keyMap.containsKey(resourceId)) {
KeystoreDTO dto = keyMap.get(resourceId);
// 加载认证文件
InputStream in = new FileInputStream(new File(dto.getPath()));
SSLManager.manager.configureKeystore(Boolean.parseBoolean(dto.getPreload()), dto.getStartIndex(),
dto.getEndIndex(), dto.getClientCertAliasVarName(), in, dto.getPwd());
keyMap.remove(resourceId);
log.info("移除认证文件{}", resourceId);
}
}
} catch (Exception e) {
log.error("证书处理失败{}", e.getMessage());
}
return SSLManager.manager;
}
@ -358,23 +390,19 @@ public abstract class SSLManager {
/**
* Configure Keystore
*
* @param preload
* flag whether the keystore should be opened within this method,
* or the opening should be delayed
* @param startIndex
* first index to consider for a key
* @param endIndex
* last index to consider for a key
* @param clientCertAliasVarName
* name of the default key, if empty the first key will be used
* as default key
* @param preload flag whether the keystore should be opened within this method,
* or the opening should be delayed
* @param startIndex first index to consider for a key
* @param endIndex last index to consider for a key
* @param clientCertAliasVarName name of the default key, if empty the first key will be used
* as default key
*/
public synchronized void configureKeystore(boolean preload, int startIndex, int endIndex, String clientCertAliasVarName,InputStream is,String password) {
public synchronized void configureKeystore(boolean preload, int startIndex, int endIndex, String clientCertAliasVarName, InputStream is, String password) {
this.keystoreAliasStartIndex = startIndex;
this.keystoreAliasEndIndex = endIndex;
this.clientCertAliasVarName = clientCertAliasVarName;
if(preload) {
keyStore = getKeyStore(is,password);
if (preload) {
keyStore = getKeyStore(is, password);
}
}
@ -382,6 +410,16 @@ public abstract class SSLManager {
* Destroy Keystore
*/
public synchronized void destroyKeystore() {
keyStore=null;
keyStore = null;
}
/**
* Destroy Keystore
*/
public synchronized void destroyKeystore(String resourceId) {
if (StringUtils.isNotBlank(resourceId)) {
keyMap.remove(resourceId);
}
keyStore = null;
}
}