Merge branch 'master' into eureka

# Conflicts:
#	sop-admin/sop-admin-server/pom.xml
#	sop-gateway/pom.xml
#	sop-website/pom.xml
eureka
tanghc 4 years ago
commit 3bf7cc4633
  1. 2
      README.md
  2. 4
      changelog.md
  3. 2
      sop-auth/pom.xml
  4. 4
      sop-common/sop-bridge-eureka/pom.xml
  5. 4
      sop-common/sop-bridge-nacos/pom.xml
  6. 2
      sop-common/sop-gateway-common/pom.xml
  7. 7
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ApiConfig.java
  8. 11
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/ApiContext.java
  9. 11
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/config/AbstractConfiguration.java
  10. 8
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/ServerWebExchangeUtil.java
  11. 46
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/codec/MessageReaderFactory.java
  12. 6
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/EnvironmentKeys.java
  13. 244
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/session/ApiHttpSession.java
  14. 30
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/session/ApiRedisTemplate.java
  15. 93
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/session/ApiSessionManager.java
  16. 269
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/session/RedisHttpSession.java
  17. 105
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/session/RedisSessionManager.java
  18. 20
      sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/session/SessionManager.java
  19. 39
      sop-common/sop-gateway-common/src/main/resources/sop-bridge.properties
  20. 2
      sop-common/sop-service-common/pom.xml
  21. 2
      sop-example/sop-springmvc/pom.xml
  22. 2
      sop-example/sop-story/pom.xml
  23. 11
      sop-example/sop-story/src/main/java/com/gitee/sop/storyweb/controller/Example1001_BaseController.java
  24. 11
      sop-example/sop-story/src/main/java/com/gitee/sop/storyweb/controller/param/LargeTextParam.java
  25. 2
      sop-gateway/src/main/java/com/gitee/sop/gateway/interceptor/MyRouteInterceptor.java
  26. 4
      sop-gateway/src/main/resources/META-INF/gateway.properties
  27. 2
      sop-mysql5.6以下版本.sql
  28. 2
      sop-test/src/main/java/com/gitee/sop/test/HttpTool.java
  29. 3
      sop-test/src/main/resources/large_data.txt
  30. 7
      sop-test/src/test/java/com/gitee/sop/test/AlipayClientPostTest.java
  31. 20
      sop-test/src/test/java/com/gitee/sop/test/AllInOneTest.java
  32. 10
      sop-test/src/test/java/com/gitee/sop/test/DownloadTest.java
  33. 65
      sop-test/src/test/java/com/gitee/sop/test/LargeBodyPostTest.java
  34. 8
      sop-test/src/test/java/com/gitee/sop/test/SpringmvcDemoPostTest.java
  35. 8
      sop-test/src/test/java/com/gitee/sop/test/ThrowExceptionDemoTest.java
  36. 2
      sop.sql

@ -22,6 +22,8 @@ SOP封装了开放平台大部分功能包括:签名验证、统一异常处
以上情况都可以考虑使用SOP
## 例子
```java
// 加一个注解即可
@Open("story.get")

@ -1,5 +1,9 @@
# changelog
## 4.0.1
- 修复超大文本无法请求BUG(设置`spring.codec.max-in-memory-size`无效)
## 4.0.0(不兼容3.x)
- 新增@Open注解,代替ApiMapping和ApiAbility

@ -25,7 +25,7 @@
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-service-common</artifactId>
<version>4.0.0-SNAPSHOT</version>
<version>4.0.1-SNAPSHOT</version>
</dependency>
<!-- sop相关配置 end-->

@ -10,7 +10,7 @@
</parent>
<modelVersion>4.0.0</modelVersion>
<version>4.0.0-SNAPSHOT</version>
<version>4.0.1-SNAPSHOT</version>
<artifactId>sop-bridge-eureka</artifactId>
@ -18,7 +18,7 @@
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-gateway-common</artifactId>
<version>4.0.0-SNAPSHOT</version>
<version>4.0.1-SNAPSHOT</version>
</dependency>
<dependency>

@ -10,7 +10,7 @@
</parent>
<modelVersion>4.0.0</modelVersion>
<version>4.0.0-SNAPSHOT</version>
<version>4.0.1-SNAPSHOT</version>
<artifactId>sop-bridge-nacos</artifactId>
@ -18,7 +18,7 @@
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-gateway-common</artifactId>
<version>4.0.0-SNAPSHOT</version>
<version>4.0.1-SNAPSHOT</version>
</dependency>
<dependency>

@ -11,7 +11,7 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>sop-gateway-common</artifactId>
<version>4.0.0-SNAPSHOT</version>
<version>4.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>

@ -27,8 +27,6 @@ import com.gitee.sop.gatewaycommon.result.ResultAppender;
import com.gitee.sop.gatewaycommon.result.ResultExecutorForGateway;
import com.gitee.sop.gatewaycommon.secret.CacheIsvManager;
import com.gitee.sop.gatewaycommon.secret.IsvManager;
import com.gitee.sop.gatewaycommon.session.ApiSessionManager;
import com.gitee.sop.gatewaycommon.session.SessionManager;
import com.gitee.sop.gatewaycommon.validate.ApiEncrypter;
import com.gitee.sop.gatewaycommon.validate.ApiSigner;
import com.gitee.sop.gatewaycommon.validate.ApiValidator;
@ -83,11 +81,6 @@ public class ApiConfig {
*/
private Validator validator = new ApiValidator();
/**
* session管理
*/
private SessionManager sessionManager = new ApiSessionManager();
/**
* isv路由权限
*/

@ -1,7 +1,5 @@
package com.gitee.sop.gatewaycommon.bean;
import com.gitee.sop.gatewaycommon.session.SessionManager;
/**
* 应用上下文,方便获取信息
*
@ -9,15 +7,6 @@ import com.gitee.sop.gatewaycommon.session.SessionManager;
*/
public class ApiContext {
/**
* 获取session管理器
*
* @return 返回SessionManager
*/
public static SessionManager getSessionManager() {
return getApiConfig().getSessionManager();
}
public static ApiConfig getApiConfig() {
return ApiConfig.getInstance();

@ -22,7 +22,6 @@ import com.gitee.sop.gatewaycommon.route.RegistryListener;
import com.gitee.sop.gatewaycommon.route.ServiceListener;
import com.gitee.sop.gatewaycommon.route.ServiceRouteListener;
import com.gitee.sop.gatewaycommon.secret.IsvManager;
import com.gitee.sop.gatewaycommon.session.SessionManager;
import com.gitee.sop.gatewaycommon.util.RouteInterceptorUtil;
import com.gitee.sop.gatewaycommon.validate.SignConfig;
import com.gitee.sop.gatewaycommon.validate.Validator;
@ -58,8 +57,8 @@ import java.util.concurrent.locks.ReentrantLock;
@Slf4j
public class AbstractConfiguration implements ApplicationContextAware, ApplicationRunner {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
private volatile boolean isStartupCompleted;
@ -151,12 +150,6 @@ public class AbstractConfiguration implements ApplicationContextAware, Applicati
return ApiConfig.getInstance().getUserKeyManager();
}
@Bean
@ConditionalOnMissingBean
SessionManager sessionManager() {
return ApiConfig.getInstance().getSessionManager();
}
@Bean
@ConditionalOnMissingBean
ParameterFormatter parameterFormatter() {

@ -3,6 +3,7 @@ package com.gitee.sop.gatewaycommon.gateway;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.gitee.sop.gatewaycommon.bean.SopConstants;
import com.gitee.sop.gatewaycommon.gateway.codec.MessageReaderFactory;
import com.gitee.sop.gatewaycommon.gateway.common.FileUploadHttpServletRequest;
import com.gitee.sop.gatewaycommon.gateway.common.RequestContentDataExtractor;
import com.gitee.sop.gatewaycommon.gateway.common.SopServerHttpRequestDecorator;
@ -22,7 +23,6 @@ import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.MultiValueMap;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.reactive.function.server.HandlerStrategies;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilterChain;
@ -48,11 +48,7 @@ public class ServerWebExchangeUtil {
private static final FormHttpMessageConverter formHttpMessageConverter = new FormHttpMessageConverter();
private static final List<HttpMessageReader<?>> messageReaders;
static {
messageReaders = HandlerStrategies.withDefaults().messageReaders();
}
private static final List<HttpMessageReader<?>> messageReaders = MessageReaderFactory.build();
/**
* 重定向

@ -0,0 +1,46 @@
package com.gitee.sop.gatewaycommon.gateway.codec;
import com.gitee.sop.gatewaycommon.manager.EnvironmentKeys;
import org.springframework.core.codec.AbstractDataBufferDecoder;
import org.springframework.http.codec.HttpMessageReader;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.reactive.function.server.HandlerStrategies;
import java.lang.reflect.Method;
import java.util.List;
/**
* @author tanghc
*/
public class MessageReaderFactory {
public static final String METHOD_SET_MAX_IN_MEMORY_SIZE = "setMaxInMemorySize";
public static final String METHOD_GET_DECODER = "getDecoder";
public static final int DEFAULT_SIZE = 256 * 1024;
public static List<HttpMessageReader<?>> build() {
String maxInMemorySizeValueStr = EnvironmentKeys.MAX_IN_MEMORY_SIZE.getValue();
int maxInMemorySizeValue = Integer.parseInt(maxInMemorySizeValueStr);
List<HttpMessageReader<?>> messageReaders = HandlerStrategies.withDefaults().messageReaders();
if (DEFAULT_SIZE == maxInMemorySizeValue) {
return messageReaders;
}
// 设置POST缓存大小
for (HttpMessageReader<?> httpMessageReader : messageReaders) {
Method[] methods = ReflectionUtils.getDeclaredMethods(httpMessageReader.getClass());
for (Method method : methods) {
String methodName = method.getName();
if (METHOD_SET_MAX_IN_MEMORY_SIZE.equals(methodName)) {
ReflectionUtils.invokeMethod(method, httpMessageReader, maxInMemorySizeValue);
} else if (METHOD_GET_DECODER.equals(methodName)) {
Object decoder = ReflectionUtils.invokeMethod(method, httpMessageReader);
if (decoder instanceof AbstractDataBufferDecoder) {
AbstractDataBufferDecoder<?> bufferDecoder = (AbstractDataBufferDecoder<?>) decoder;
bufferDecoder.setMaxInMemorySize(maxInMemorySizeValue);
}
}
}
}
return messageReaders;
}
}

@ -30,6 +30,12 @@ public enum EnvironmentKeys {
*/
PRE_DOMAIN("pre.domain"),
/**
* post请求body缓存大小
*/
MAX_IN_MEMORY_SIZE("spring.codec.max-in-memory-size", "262144")
;
private final String key;

@ -1,244 +0,0 @@
package com.gitee.sop.gatewaycommon.session;
import org.springframework.util.Assert;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
import javax.servlet.http.HttpSessionContext;
import java.io.Serializable;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;
import java.util.Vector;
/**
* @author tanghc
*/
@SuppressWarnings("deprecation")
public class ApiHttpSession implements HttpSession, Serializable {
private static final long serialVersionUID = 946272038219216222L;
private static final String ATTRIBUTE_NAME_MUST_NOT_BE_NULL = "Attribute name must not be null";
private final String id;
private final long creationTime = System.currentTimeMillis();
private int maxInactiveInterval;
private long lastAccessedTime = System.currentTimeMillis();
private final transient ServletContext servletContext;
private final transient Map<String, Object> attributes = new LinkedHashMap<>();
private boolean invalid = false;
private boolean isNew = true;
/**
* Create a new ApiHttpSession
*/
public ApiHttpSession() {
this(null);
}
/**
* Create a new ApiHttpSession.
*
* @param servletContext the ServletContext that the session runs in
*/
public ApiHttpSession(ServletContext servletContext) {
this(servletContext, null);
}
/**
* Create a new ApiHttpSession.
*
* @param servletContext the ServletContext that the session runs in
* @param id a unique identifier for this session
*/
public ApiHttpSession(ServletContext servletContext, String id) {
this.servletContext = servletContext;
this.id = this.buildId(id);
}
protected String buildId(String id) {
return (id != null ? id : UUID.randomUUID().toString().replace("-", "").toUpperCase());
}
@Override
public long getCreationTime() {
return this.creationTime;
}
@Override
public String getId() {
return this.id;
}
public void access() {
this.lastAccessedTime = System.currentTimeMillis();
this.isNew = false;
}
@Override
public long getLastAccessedTime() {
return this.lastAccessedTime;
}
@Override
public ServletContext getServletContext() {
return this.servletContext;
}
@Override
public void setMaxInactiveInterval(int interval) {
this.maxInactiveInterval = interval;
}
@Override
public int getMaxInactiveInterval() {
return this.maxInactiveInterval;
}
/**
* @return 返回HttpSessionContext已废弃不能使用
* @deprecated 已废弃不能使用
*/
@Override
@Deprecated
public HttpSessionContext getSessionContext() {
throw new UnsupportedOperationException("getSessionContext");
}
@Override
public Object getAttribute(String name) {
Assert.notNull(name, ATTRIBUTE_NAME_MUST_NOT_BE_NULL);
return this.attributes.get(name);
}
@Override
public Object getValue(String name) {
return getAttribute(name);
}
@Override
public Enumeration<String> getAttributeNames() {
return new Vector<String>(this.attributes.keySet()).elements();
}
@Override
public String[] getValueNames() {
return this.attributes.keySet().toArray(new String[this.attributes.size()]);
}
@Override
public void setAttribute(String name, Object value) {
Assert.notNull(name, ATTRIBUTE_NAME_MUST_NOT_BE_NULL);
if (value != null) {
this.attributes.put(name, value);
if (value instanceof HttpSessionBindingListener) {
((HttpSessionBindingListener) value).valueBound(new HttpSessionBindingEvent(this, name, value));
}
} else {
removeAttribute(name);
}
}
@Override
public void putValue(String name, Object value) {
setAttribute(name, value);
}
@Override
public void removeAttribute(String name) {
Assert.notNull(name, ATTRIBUTE_NAME_MUST_NOT_BE_NULL);
Object value = this.attributes.remove(name);
if (value instanceof HttpSessionBindingListener) {
((HttpSessionBindingListener) value).valueUnbound(new HttpSessionBindingEvent(this, name, value));
}
}
@Override
public void removeValue(String name) {
removeAttribute(name);
}
/**
* Clear all of this session's attributes.
*/
public void clearAttributes() {
for (Iterator<Map.Entry<String, Object>> it = this.attributes.entrySet().iterator(); it.hasNext(); ) {
Map.Entry<String, Object> entry = it.next();
String name = entry.getKey();
Object value = entry.getValue();
it.remove();
if (value instanceof HttpSessionBindingListener) {
((HttpSessionBindingListener) value).valueUnbound(new HttpSessionBindingEvent(this, name, value));
}
}
}
@Override
public void invalidate() {
this.invalid = true;
clearAttributes();
}
public boolean isInvalid() {
return this.invalid;
}
public void setNew(boolean value) {
this.isNew = value;
}
@Override
public boolean isNew() {
return this.isNew;
}
/**
* Serialize the attributes of this session into an object that can be
* turned into a byte array with standard Java serialization.
*
* @return a representation of this session's serialized state
*/
public Serializable serializeState() {
HashMap<String, Serializable> state = new HashMap<>(16);
for (Iterator<Map.Entry<String, Object>> it = this.attributes.entrySet().iterator(); it.hasNext(); ) {
Map.Entry<String, Object> entry = it.next();
String name = entry.getKey();
Object value = entry.getValue();
it.remove();
if (value instanceof Serializable) {
state.put(name, (Serializable) value);
} else {
// Not serializable... Servlet containers usually automatically
// unbind the attribute in this case.
if (value instanceof HttpSessionBindingListener) {
((HttpSessionBindingListener) value).valueUnbound(new HttpSessionBindingEvent(this, name, value));
}
}
}
return state;
}
/**
* Deserialize the attributes of this session from a state object created by
* {@link #serializeState()}.
*
* @param state a representation of this session's serialized state
*/
@SuppressWarnings("unchecked")
public void deserializeState(Serializable state) {
Assert.isTrue(state instanceof Map, "Serialized state needs to be of type [java.util.Map]");
this.attributes.putAll((Map<String, Object>) state);
}
}

@ -1,30 +0,0 @@
package com.gitee.sop.gatewaycommon.session;
import org.springframework.data.redis.connection.DefaultStringRedisConnection;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* @author tanghc
*/
public class ApiRedisTemplate extends RedisTemplate<String, Object> {
public ApiRedisTemplate() {
RedisSerializer<String> stringSerializer = new StringRedisSerializer();
setKeySerializer(stringSerializer);
setHashKeySerializer(stringSerializer);
}
public ApiRedisTemplate(RedisConnectionFactory connectionFactory) {
this();
setConnectionFactory(connectionFactory);
afterPropertiesSet();
}
@Override
protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) {
return new DefaultStringRedisConnection(connection);
}
}

@ -1,93 +0,0 @@
package com.gitee.sop.gatewaycommon.session;
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import java.util.concurrent.TimeUnit;
/**
* session管理,默认存放的是{@link ApiHttpSession}采用谷歌guava缓存实现
*
* @author tanghc
*/
public class ApiSessionManager implements SessionManager {
private static Logger logger = LoggerFactory.getLogger(ApiSessionManager.class);
private int sessionTimeout = 20;
private LoadingCache<String, HttpSession> cache;
public ApiSessionManager() {
cache = this.buildCache();
}
@Override
public HttpSession getSession(String sessionId) {
if (sessionId == null) {
return this.createSession(sessionId);
}
try {
return cache.get(sessionId);
} catch (Exception e) {
logger.error(e.getMessage(), e);
throw ErrorEnum.ISP_UNKNOWN_ERROR.getErrorMeta().getException();
}
}
/**
* 创建一个session
*
* @param sessionId 传null将返回一个新session
* @return 返回session
*/
protected HttpSession createSession(String sessionId) {
ServletContext servletContext = null;
HttpSession session = this.newSession(sessionId, servletContext);
session.setMaxInactiveInterval(getSessionTimeout());
this.cache.put(session.getId(), session);
return session;
}
/**
* 返回新的session实例
*
* @param sessionId
* @param servletContext
* @return 返回session
*/
protected HttpSession newSession(String sessionId, ServletContext servletContext) {
return new ApiHttpSession(servletContext, sessionId);
}
protected LoadingCache<String, HttpSession> buildCache() {
return CacheBuilder.newBuilder().expireAfterAccess(getSessionTimeout(), TimeUnit.MINUTES)
.build(new CacheLoader<String, HttpSession>() {
// 找不到sessionId对应的HttpSession时,进入这个方法
// 找不到就新建一个
@Override
public HttpSession load(String sessionId) throws Exception {
return createSession(sessionId);
}
});
}
public void setSessionTimeout(int sessionTimeout) {
this.sessionTimeout = sessionTimeout;
}
/**
* 过期时间,分钟,默认20分钟
*
* @return 返回过期时间
*/
public int getSessionTimeout() {
return sessionTimeout;
}
}

@ -1,269 +0,0 @@
package com.gitee.sop.gatewaycommon.session;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.Assert;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionContext;
import java.io.Serializable;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
* RedisHttpSession
*
* @author tanghc
*/
@SuppressWarnings("deprecation")
public class RedisHttpSession implements HttpSession, Serializable {
private static final long serialVersionUID = -8081963657251144855L;
private static final int SEC60 = 60;
private static final String SESSION_ATTR = "session_attr:";
private static final String CREATION_TIME = "creationTime";
private static final String LAST_ACCESSED_TIME = "lastAccessedTime";
private static final String MAX_INACTIVE_INTERVAL = "maxInactiveInterval";
/**
* 存入redis的key
*/
private String key;
/**
* sessionId
*/
private String id;
private transient ServletContext servletContext;
private transient RedisTemplate redisTemplate;
private RedisHttpSession() {
}
protected static String buildId(String id) {
return (id != null ? id : UUID.randomUUID().toString().replace("-", "").toUpperCase());
}
public static String buildKey(String keyPrefix, String sessionId) {
Assert.notNull(keyPrefix, "sessionPrefix不能为null");
return keyPrefix + sessionId;
}
/**
* 创建新的session
*
* @param servletContext servletContext
* @param sessionId sessionId
* @param sessionTimeout 过期时间单位秒
* @param redisTemplate redis客户端
* @param keyPrefix 存入的key前缀
* @return 返回session
*/
public static RedisHttpSession createNewSession(ServletContext servletContext, String sessionId, int sessionTimeout, RedisTemplate redisTemplate, String keyPrefix) {
Assert.notNull(redisTemplate, "redisTemplate can not null.");
Assert.notNull(sessionId, "sessionId can not null.");
Assert.notNull(keyPrefix, "keyPrefix can not null.");
RedisHttpSession redisHttpSession = new RedisHttpSession();
redisHttpSession.setId(sessionId);
redisHttpSession.setKey(buildKey(keyPrefix, sessionId));
redisHttpSession.setRedisTemplate(redisTemplate);
redisHttpSession.setServletContext(servletContext);
long creationTime = System.currentTimeMillis();
// 过期时间,分转换成秒
int maxInactiveInterval = sessionTimeout * SEC60;
redisHttpSession.setCreationTime(creationTime);
redisHttpSession.setLastAccessedTime(creationTime);
redisHttpSession.setMaxInactiveInterval(maxInactiveInterval);
redisHttpSession.refresh();
return redisHttpSession;
}
/**
* 创建已经存在的session,数据在redis里面
*
* @param sessionId sessionId
* @param servletContext servletContext
* @param redisTemplate redis客户端
* @param keyPrefix 存入的key前缀
* @return 返回session
*/
public static RedisHttpSession createExistSession(String sessionId, ServletContext servletContext, RedisTemplate redisTemplate, String keyPrefix) {
Assert.notNull(redisTemplate, "redisTemplate can not null.");
Assert.notNull(sessionId, "sessionId can not null.");
Assert.notNull(keyPrefix, "keyPrefix can not null.");
RedisHttpSession redisHttpSession = new RedisHttpSession();
redisHttpSession.setId(sessionId);
redisHttpSession.setKey(buildKey(keyPrefix, sessionId));
redisHttpSession.setRedisTemplate(redisTemplate);
redisHttpSession.setServletContext(servletContext);
redisHttpSession.refresh();
return redisHttpSession;
}
public void setId(String id) {
this.id = id;
}
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
public void setCreationTime(long creationTime) {
this.redisTemplate.opsForHash().put(key, CREATION_TIME, String.valueOf(creationTime));
}
@Override
public long getCreationTime() {
Object createTime = this.redisTemplate.opsForHash().get(key, CREATION_TIME);
return Long.valueOf(String.valueOf(createTime));
}
@Override
public String getId() {
return id;
}
@Override
public long getLastAccessedTime() {
Object lastAccessedTime = this.redisTemplate.opsForHash().get(key, LAST_ACCESSED_TIME);
return Long.valueOf(String.valueOf(lastAccessedTime));
}
@Override
public ServletContext getServletContext() {
return servletContext;
}
@Override
public void setMaxInactiveInterval(int interval) {
this.redisTemplate.opsForHash().put(key, MAX_INACTIVE_INTERVAL, String.valueOf(interval));
}
@Override
public int getMaxInactiveInterval() {
Object maxInactiveInterval = this.redisTemplate.opsForHash().get(key, MAX_INACTIVE_INTERVAL);
return Integer.valueOf(String.valueOf(maxInactiveInterval));
}
/**
* @return 返回HttpSessionContext已废弃不能使用
* @deprecated 已废弃始终返回null
*/
@Override
@Deprecated
public HttpSessionContext getSessionContext() {
return null;
}
@Override
public Object getAttribute(String name) {
return this.redisTemplate.opsForHash().get(key, SESSION_ATTR + name);
}
@Override
public Object getValue(String name) {
return getAttribute(name);
}
@Override
public Enumeration<String> getAttributeNames() {
return Collections.enumeration(getAttributeKeys());
}
private Set<String> getAttributeKeys() {
Set keys = this.redisTemplate.opsForHash().keys(key);
Set<String> attrNames = new HashSet<>();
for (Object key : keys) {
String k = String.valueOf(key);
if (k.startsWith(SESSION_ATTR)) {
attrNames.add(k.substring(SESSION_ATTR.length()));
}
}
return attrNames;
}
@Override
public String[] getValueNames() {
return getAttributeKeys().toArray(new String[0]);
}
@Override
public void setAttribute(String name, Object value) {
this.redisTemplate.opsForHash().put(key, SESSION_ATTR + name, value);
}
@Override
public void putValue(String name, Object value) {
setAttribute(name, value);
}
@Override
public void removeAttribute(String name) {
this.redisTemplate.opsForHash().delete(key, name);
}
@Override
public void removeValue(String name) {
removeAttribute(name);
}
@Override
public void invalidate() {
this.redisTemplate.delete(key);
}
@Override
public boolean isNew() {
return false;
}
/**
* update expireTime,accessTime
*/
public void refresh() {
// token更新过期时间
this.redisTemplate.expire(key, getMaxInactiveInterval(), TimeUnit.SECONDS);
// 设置访问时间
this.setLastAccessedTime(System.currentTimeMillis());
}
public void setLastAccessedTime(long lastAccessedTime) {
this.redisTemplate.opsForHash().put(key, LAST_ACCESSED_TIME, String.valueOf(lastAccessedTime));
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public boolean isInvalidated() {
return !this.redisTemplate.hasKey(key);
}
public void setRedisTemplate(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
}

@ -1,105 +0,0 @@
package com.gitee.sop.gatewaycommon.session;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.Assert;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import java.util.UUID;
/**
* SessionManager的redis实现使用redis管理session
*
* @author tanghc
*/
public class RedisSessionManager implements SessionManager {
private ApiRedisTemplate redisTemplate;
/**
* 过期时间30分钟
*/
private int sessionTimeout = 30;
/**
* 存入redis中key的前缀
*/
private String keyPrefix = "session:";
public RedisSessionManager(@SuppressWarnings("rawtypes") RedisTemplate redisTemplate) {
Assert.notNull(redisTemplate, "RedisSessionManager中的redisTemplate不能为null");
this.redisTemplate = new ApiRedisTemplate(redisTemplate.getConnectionFactory());
}
@Override
public HttpSession getSession(String sessionId) {
return this.getSession(sessionId, this.keyPrefix);
}
public HttpSession getSession(String sessionId, String keyPrefix) {
if (this.hasKey(sessionId)) {
return RedisHttpSession.createExistSession(sessionId, getServletContext(), redisTemplate, keyPrefix);
} else {
sessionId = this.buildSessionId(sessionId);
return RedisHttpSession.createNewSession(getServletContext(), sessionId, this.getSessionTimeout(),
redisTemplate, keyPrefix);
}
}
/**
* 构建sessionId
*
* @param id
* @return 返回sessionid
*/
public String buildSessionId(String id) {
return (id != null ? id : UUID.randomUUID().toString().replace("-", "").toUpperCase());
}
public boolean hasKey(String sessionId) {
if (sessionId == null) {
return false;
} else {
String key = RedisHttpSession.buildKey(this.keyPrefix, sessionId);
return redisTemplate.hasKey(key);
}
}
public ServletContext getServletContext() {
return null;
}
public int getSessionTimeout() {
return sessionTimeout;
}
/**
* 设置session过期时间单位分钟
*
* @param sessionTimeout
*/
public void setSessionTimeout(int sessionTimeout) {
this.sessionTimeout = sessionTimeout;
}
public ApiRedisTemplate getRedisTemplate() {
return redisTemplate;
}
public void setRedisTemplate(ApiRedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
public String getKeyPrefix() {
return keyPrefix;
}
/**
* 设置存入redis中key的前缀默认为"session:"
*
* @param keyPrefix
*/
public void setKeyPrefix(String keyPrefix) {
this.keyPrefix = keyPrefix;
}
}

@ -1,20 +0,0 @@
package com.gitee.sop.gatewaycommon.session;
import javax.servlet.http.HttpSession;
/**
* session管理
*
* @author tanghc
*
*/
public interface SessionManager {
/**
* 根据sessionId获取session
*
* @param sessionId 客户端传过来的sessionId,为null时创建一个新session
* @return 返回session
*/
HttpSession getSession(String sessionId);
}

@ -1,39 +0,0 @@
# 固定不变,不能改
spring.application.name=sop-gateway
# 不用改,如果要改,请全局替换修改
sop.secret=MZZOUSTua6LzApIWXCwEgbBmxSzpzC
# 网关入口
sop.gateway-index-path=/
# nacos cloud配置
spring.cloud.nacos.discovery.server-addr=${register.url}
# 数据库配置
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://${mysql.host}/sop?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai
spring.datasource.username=${mysql.username}
spring.datasource.password=${mysql.password}
# https://blog.csdn.net/qq_36872046/article/details/81058045
# 路由转发超时时间,毫秒,默认值1000,详见:RibbonClientConfiguration.DEFAULT_READ_TIMEOUT。
# 如果微服务端 处理时间过长,会导致ribbon read超时,解决办法将这个值调大一点
ribbon.ReadTimeout=2000
# 设置为true(默认false),则所有请求都重试,默认只支持get请求重试
# 请谨慎设置,因为post请求大多都是写入请求,如果要支持重试,确保服务的幂等性
ribbon.OkToRetryOnAllOperations=false
spring.cloud.gateway.discovery.locator.lower-case-service-id=true
spring.cloud.gateway.discovery.locator.enabled=true
# 不用改
mybatis.fill.com.gitee.fastmybatis.core.support.DateFillInsert=gmt_create
mybatis.fill.com.gitee.fastmybatis.core.support.DateFillUpdate=gmt_modified
# 文件上传配置
spring.servlet.multipart.enabled=true
# 这里设置大一点没关系,真实大小由upload.max-file-size控制
spring.servlet.multipart.max-file-size=50MB
# 允许上传文件大小,不能超过这个值,单位:B,KB,MB
upload.max-file-size=2MB

@ -12,7 +12,7 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>sop-service-common</artifactId>
<version>4.0.0-SNAPSHOT</version>
<version>4.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>

@ -26,7 +26,7 @@
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-service-common</artifactId>
<version>4.0.0-SNAPSHOT</version>
<version>4.0.1-SNAPSHOT</version>
</dependency>
<!-- nacos -->
<dependency>

@ -19,7 +19,7 @@
<dependency>
<groupId>com.gitee.sop</groupId>
<artifactId>sop-service-common</artifactId>
<version>4.0.0-SNAPSHOT</version>
<version>4.0.1-SNAPSHOT</version>
</dependency>
<dependency>

@ -2,6 +2,7 @@ package com.gitee.sop.storyweb.controller;
import com.gitee.sop.servercommon.annotation.Open;
import com.gitee.sop.storyweb.controller.param.CategoryParam;
import com.gitee.sop.storyweb.controller.param.LargeTextParam;
import com.gitee.sop.storyweb.controller.param.StoryParam;
import com.gitee.sop.storyweb.controller.result.CategoryResult;
import com.gitee.sop.storyweb.controller.result.StoryResult;
@ -13,6 +14,7 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
@ -69,6 +71,15 @@ public class Example1001_BaseController {
return result;
}
@Open(value = "story.get.large")
@RequestMapping("/get/large/v1")
public StoryResult getStoryLarge(LargeTextParam param) {
StoryResult result = new StoryResult();
int length = param.getContent().getBytes(StandardCharsets.UTF_8).length;
result.setName("length:" + length);
return result;
}
// 返回数组结果
@ApiOperation(value = "返回数组结果", notes = "返回数组结果")
@Open("story.list")

@ -0,0 +1,11 @@
package com.gitee.sop.storyweb.controller.param;
import lombok.Data;
/**
* @author tanghc
*/
@Data
public class LargeTextParam {
private String content;
}

@ -17,7 +17,7 @@ public class MyRouteInterceptor implements RouteInterceptor {
@Override
public void preRoute(RouteInterceptorContext context) {
ApiParam apiParam = context.getApiParam();
System.out.println("请求接口:" + apiParam.fetchNameVersion());
System.out.println(String.format("请求接口:%s, ip:%s", apiParam.fetchNameVersion(), apiParam.fetchIp()));
}
@Override

@ -26,6 +26,10 @@ spring.cloud.gateway.discovery.locator.enabled=true
mybatis.fill.com.gitee.fastmybatis.core.support.DateFillInsert=gmt_create
mybatis.fill.com.gitee.fastmybatis.core.support.DateFillUpdate=gmt_modified
# POST请求最大缓存,默认是262144(256KB),即:256 * 1024
# 如果需要传输大文本可以把值设置高一点
spring.codec.max-in-memory-size=262144
# 文件上传配置
spring.servlet.multipart.enabled=true
# 这里设置大一点没关系,真实大小由upload.max-file-size控制

@ -245,7 +245,7 @@ SET FOREIGN_KEY_CHECKS = 0;
LOCK TABLES `admin_user_info` WRITE;
ALTER TABLE `admin_user_info` DISABLE KEYS;
INSERT INTO `admin_user_info` (`id`, `username`, `password`, `status`, `gmt_create`, `gmt_modified`) VALUES
(1,'admin','14e1b600b1fd579f47433b88e8d85291',1,'2019-04-02 19:55:26','2019-04-02 19:55:26');
(1,'admin','a62cd510fb9a8a557a27ef279569091f',1,'2019-04-02 19:55:26','2019-04-02 19:55:26');
ALTER TABLE `admin_user_info` ENABLE KEYS;
UNLOCK TABLES;

@ -501,7 +501,7 @@ public class HttpTool {
if (file.isDirectory()) {
throw new IOException("File '" + file + "' exists but is a directory");
}
if (file.canRead() == false) {
if (!file.canRead()) {
throw new IOException("File '" + file + "' cannot be read");
}
} else {

File diff suppressed because one or more lines are too long

@ -52,16 +52,15 @@ public class AlipayClientPostTest extends TestBase {
bizContent.put("name", "葫芦娃");
params.put("biz_content", JSON.toJSONString(bizContent));
String content = AlipaySignature.getSignContent(params);
String sign = AlipaySignature.rsa256Sign(content, privateKey, "utf-8");
params.put("sign", sign);
System.out.println("----------- 请求信息 -----------");
System.out.println("请求参数:" + buildParamQuery(params));
System.out.println("商户秘钥:" + privateKey);
String content = AlipaySignature.getSignContent(params);
System.out.println("待签名内容:" + content);
String sign = AlipaySignature.rsa256Sign(content, privateKey, "utf-8");
System.out.println("签名(sign):" + sign);
params.put("sign", sign);
System.out.println("URL参数:" + buildUrlQuery(params));
System.out.println("----------- 返回结果 -----------");

@ -2,8 +2,10 @@ package com.gitee.sop.test;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.junit.Assert;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
@ -204,6 +206,24 @@ public class AllInOneTest extends TestBase {
client.execute(requestBuilder);
}
/**
* 测试post提交大文本内容
* @throws IOException
*/
public void testLargeBody() throws IOException {
String root = System.getProperty("user.dir");
// 这个文件有600KB的数据
File file = new File(root + "/src/main/resources/large_data.txt");
String fileContent = FileUtils.readFileToString(file, StandardCharsets.UTF_8);
Client.RequestBuilder requestBuilder = new Client.RequestBuilder()
.method("story.get.large")
.version("1.0")
.bizContent(new BizContent().add("content", fileContent))
.httpMethod(HttpTool.HTTPMethod.POST);
client.execute(requestBuilder);
}
/**
* 下载文件
*/

@ -29,7 +29,7 @@ public class DownloadTest extends TestBase {
// 公共请求参数
Map<String, String> params = new HashMap<String, String>();
params.put("app_id", appId);
params.put("method", "story.download");
params.put("method", "file.download");
params.put("format", "json");
params.put("charset", "utf-8");
params.put("sign_type", "RSA2");
@ -42,16 +42,16 @@ public class DownloadTest extends TestBase {
bizContent.put("name", "葫芦娃");
params.put("biz_content", JSON.toJSONString(bizContent));
String content = AlipaySignature.getSignContent(params);
String sign = AlipaySignature.rsa256Sign(content, privateKey, "utf-8");
params.put("sign", sign);
System.out.println("----------- 请求信息 -----------");
System.out.println("请求参数:" + buildParamQuery(params));
System.out.println("商户秘钥:" + privateKey);
String content = AlipaySignature.getSignContent(params);
System.out.println("待签名内容:" + content);
String sign = AlipaySignature.rsa256Sign(content, privateKey, "utf-8");
System.out.println("签名(sign):" + sign);
params.put("sign", sign);
System.out.println("URL参数:" + buildUrlQuery(params));
System.out.println("----------- 返回结果 -----------");
InputStream fileInputStream = download(url, params);

@ -0,0 +1,65 @@
package com.gitee.sop.test;
import com.alibaba.fastjson.JSON;
import com.gitee.sop.test.alipay.AlipaySignature;
import org.apache.commons.io.FileUtils;
import org.junit.Test;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* 模仿支付宝客户端请求接口
*/
public class LargeBodyPostTest extends TestBase {
String url = "http://localhost:8081";
String appId = "2019032617262200001";
// 平台提供的私钥
String privateKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCXJv1pQFqWNA/++OYEV7WYXwexZK/J8LY1OWlP9X0T6wHFOvxNKRvMkJ5544SbgsJpVcvRDPrcxmhPbi/sAhdO4x2PiPKIz9Yni2OtYCCeaiE056B+e1O2jXoLeXbfi9fPivJZkxH/tb4xfLkH3bA8ZAQnQsoXA0SguykMRZntF0TndUfvDrLqwhlR8r5iRdZLB6F8o8qXH6UPDfNEnf/K8wX5T4EB1b8x8QJ7Ua4GcIUqeUxGHdQpzNbJdaQvoi06lgccmL+PHzminkFYON7alj1CjDN833j7QMHdPtS9l7B67fOU/p2LAAkPMtoVBfxQt9aFj7B8rEhGCz02iJIBAgMBAAECggEARqOuIpY0v6WtJBfmR3lGIOOokLrhfJrGTLF8CiZMQha+SRJ7/wOLPlsH9SbjPlopyViTXCuYwbzn2tdABigkBHYXxpDV6CJZjzmRZ+FY3S/0POlTFElGojYUJ3CooWiVfyUMhdg5vSuOq0oCny53woFrf32zPHYGiKdvU5Djku1onbDU0Lw8w+5tguuEZ76kZ/lUcccGy5978FFmYpzY/65RHCpvLiLqYyWTtaNT1aQ/9pw4jX9HO9NfdJ9gYFK8r/2f36ZE4hxluAfeOXQfRC/WhPmiw/ReUhxPznG/WgKaa/OaRtAx3inbQ+JuCND7uuKeRe4osP2jLPHPP6AUwQKBgQDUNu3BkLoKaimjGOjCTAwtp71g1oo+k5/uEInAo7lyEwpV0EuUMwLA/HCqUgR4K9pyYV+Oyb8d6f0+Hz0BMD92I2pqlXrD7xV2WzDvyXM3s63NvorRooKcyfd9i6ccMjAyTR2qfLkxv0hlbBbsPHz4BbU63xhTJp3Ghi0/ey/1HQKBgQC2VsgqC6ykfSidZUNLmQZe3J0p/Qf9VLkfrQ+xaHapOs6AzDU2H2osuysqXTLJHsGfrwVaTs00ER2z8ljTJPBUtNtOLrwNRlvgdnzyVAKHfOgDBGwJgiwpeE9voB1oAV/mXqSaUWNnuwlOIhvQEBwekqNyWvhLqC7nCAIhj3yvNQKBgQCqYbeec56LAhWP903Zwcj9VvG7sESqXUhIkUqoOkuIBTWFFIm54QLTA1tJxDQGb98heoCIWf5x/A3xNI98RsqNBX5JON6qNWjb7/dobitti3t99v/ptDp9u8JTMC7penoryLKK0Ty3bkan95Kn9SC42YxaSghzqkt+uvfVQgiNGQKBgGxU6P2aDAt6VNwWosHSe+d2WWXt8IZBhO9d6dn0f7ORvcjmCqNKTNGgrkewMZEuVcliueJquR47IROdY8qmwqcBAN7Vg2K7r7CPlTKAWTRYMJxCT1Hi5gwJb+CZF3+IeYqsJk2NF2s0w5WJTE70k1BSvQsfIzAIDz2yE1oPHvwVAoGAA6e+xQkVH4fMEph55RJIZ5goI4Y76BSvt2N5OKZKd4HtaV+eIhM3SDsVYRLIm9ZquJHMiZQGyUGnsvrKL6AAVNK7eQZCRDk9KQz+0GKOGqku0nOZjUbAu6A2/vtXAaAuFSFx1rUQVVjFulLexkXR3KcztL1Qu2k5pB6Si0K/uwQ=";
// 测试大容量报文
@Test
public void testGet() throws Exception {
// 公共请求参数
Map<String, String> params = new HashMap<String, String>();
params.put("app_id", appId);
params.put("method", "story.get.large");
params.put("format", "json");
params.put("charset", "utf-8");
params.put("sign_type", "RSA2");
params.put("timestamp", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
params.put("version", "1.0");
// 业务参数
Map<String, String> bizContent = new HashMap<>();
bizContent.put("id", "1");
String root = System.getProperty("user.dir");
File file = new File(root + "/src/main/resources/large_data.txt");
String fileContent = FileUtils.readFileToString(file, StandardCharsets.UTF_8);
bizContent.put("content", fileContent);
params.put("biz_content", JSON.toJSONString(bizContent));
String content = AlipaySignature.getSignContent(params);
String sign = AlipaySignature.rsa256Sign(content, privateKey, "utf-8");
params.put("sign", sign);
System.out.println("----------- 请求信息 -----------");
System.out.println("请求参数:" + buildParamQuery(params));
System.out.println("商户秘钥:" + privateKey);
System.out.println("待签名内容:" + content);
System.out.println("签名(sign):" + sign);
System.out.println("URL参数:" + buildUrlQuery(params));
System.out.println("----------- 返回结果 -----------");
String responseData = post(url, params);// 发送请求
System.out.println(responseData);
}
}

@ -38,16 +38,16 @@ public class SpringmvcDemoPostTest extends TestBase {
bizContent.put("goods_name", "iphone6");
params.put("biz_content", JSON.toJSONString(bizContent));
String content = AlipaySignature.getSignContent(params);
String sign = AlipaySignature.rsa256Sign(content, privateKey, "utf-8");
params.put("sign", sign);
System.out.println("----------- 请求信息 -----------");
System.out.println("请求参数:" + buildParamQuery(params));
System.out.println("商户秘钥:" + privateKey);
String content = AlipaySignature.getSignContent(params);
System.out.println("待签名内容:" + content);
String sign = AlipaySignature.rsa256Sign(content, privateKey, "utf-8");
System.out.println("签名(sign):" + sign);
params.put("sign", sign);
System.out.println("URL参数:" + buildUrlQuery(params));
System.out.println("----------- 返回结果 -----------");
String responseData = post(url, params);// 发送请求

@ -39,16 +39,16 @@ public class ThrowExceptionDemoTest extends TestBase {
bizContent.put("goods_comment", "1111");
params.put("biz_content", JSON.toJSONString(bizContent));
String content = AlipaySignature.getSignContent(params);
String sign = AlipaySignature.rsa256Sign(content, privateKey, "utf-8");
params.put("sign", sign);
System.out.println("----------- 请求信息 -----------");
System.out.println("请求参数:" + buildParamQuery(params));
System.out.println("商户秘钥:" + privateKey);
String content = AlipaySignature.getSignContent(params);
System.out.println("待签名内容:" + content);
String sign = AlipaySignature.rsa256Sign(content, privateKey, "utf-8");
System.out.println("签名(sign):" + sign);
params.put("sign", sign);
System.out.println("URL参数:" + buildUrlQuery(params));
System.out.println("----------- 返回结果 -----------");
String responseData = post(url, params);// 发送请求

@ -247,7 +247,7 @@ SET FOREIGN_KEY_CHECKS = 0;
LOCK TABLES `admin_user_info` WRITE;
ALTER TABLE `admin_user_info` DISABLE KEYS;
INSERT INTO `admin_user_info` (`id`, `username`, `password`, `status`, `gmt_create`, `gmt_modified`) VALUES
(1,'admin','14e1b600b1fd579f47433b88e8d85291',1,'2019-04-02 19:55:26','2019-04-02 19:55:26');
(1,'admin','a62cd510fb9a8a557a27ef279569091f',1,'2019-04-02 19:55:26','2019-04-02 19:55:26');
ALTER TABLE `admin_user_info` ENABLE KEYS;
UNLOCK TABLES;

Loading…
Cancel
Save