diff --git a/.gitignore b/.gitignore index 9bb8bbf66..f35982eab 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ out *.rdb .checkstyle !etc/eclipse/.checkstyle +gradle/ !**/src/**/build .DS_Store spring-session-docs/package-lock.json diff --git a/build.gradle b/build.gradle index 782fcd722..f89588b9f 100644 --- a/build.gradle +++ b/build.gradle @@ -26,6 +26,7 @@ buildscript { plugins { id "com.github.ben-manes.versions" + id 'maven-publish' } apply plugin: 'io.spring.convention.root' @@ -66,3 +67,23 @@ springRelease { apiDocUrl = "https://docs.spring.io/spring-session/reference/{version}/api/java/index.html" replaceSnapshotVersionInReferenceDocUrl = true } + + +configure(subprojects) {project -> { + apply plugin: 'maven-publish' + + publishing { + repositories { + maven { + url = release_url + name = 'kyligence' + if (project.hasProperty('kyligenceRepoUsername')) { + credentials { + username "$kyligenceRepoUsername" + password "$kyligenceRepoPassword" + } + } + } + } + } +}} diff --git a/gradle.properties b/gradle.properties index 3f1517d65..e890893f2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,7 @@ org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 org.gradle.parallel=true -version=3.5.6 +version=3.5.6-kylin-r1-SNAPSHOT + +group_id=org.springframework.session +artifact_id=spring-session-core +release_url=https://repo-ofs.kyligence.com/repository/maven-releases/ diff --git a/spring-session-core/src/main/java/org/springframework/session/MapSession.java b/spring-session-core/src/main/java/org/springframework/session/MapSession.java index 4f6949e08..cfb348698 100644 --- a/spring-session-core/src/main/java/org/springframework/session/MapSession.java +++ b/spring-session-core/src/main/java/org/springframework/session/MapSession.java @@ -17,14 +17,19 @@ package org.springframework.session; import java.io.Serializable; +import java.security.SecureRandom; import java.time.Duration; import java.time.Instant; +import java.util.Base64; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.UUID; +import org.springframework.session.config.annotation.web.http.SpringHttpSessionConfiguration; +import org.springframework.util.DigestUtils; + /** *
* A {@link Session} implementation that is backed by a {@link java.util.Map}. The
@@ -242,7 +247,25 @@ public int hashCode() {
}
private static String generateId() {
- return UUID.randomUUID().toString();
+ String id;
+ if (SpringHttpSessionConfiguration.secureRandomCreateEnabled) {
+ byte[] salt1 = new byte[36];
+ byte[] salt2 = new byte[36];
+ SecureRandom secureRandom = new SecureRandom();
+ secureRandom.setSeed(System.currentTimeMillis());
+ secureRandom.nextBytes(salt1);
+ secureRandom.nextBytes(salt2);
+ id = DigestUtils.md5DigestAsHex(salt1) + DigestUtils.md5DigestAsHex(salt2);
+ }
+ else {
+ id = UUID.randomUUID().toString();
+ }
+
+ if (SpringHttpSessionConfiguration.jdbcEncodeEnable) {
+ return new String(Base64.getEncoder().encode(id.getBytes()));
+ }
+
+ return id;
}
/**
diff --git a/spring-session-core/src/main/java/org/springframework/session/config/annotation/web/http/SpringHttpSessionConfiguration.java b/spring-session-core/src/main/java/org/springframework/session/config/annotation/web/http/SpringHttpSessionConfiguration.java
index ecf6b588c..016800cef 100644
--- a/spring-session-core/src/main/java/org/springframework/session/config/annotation/web/http/SpringHttpSessionConfiguration.java
+++ b/spring-session-core/src/main/java/org/springframework/session/config/annotation/web/http/SpringHttpSessionConfiguration.java
@@ -28,6 +28,7 @@
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
@@ -106,11 +107,31 @@ public class SpringHttpSessionConfiguration implements InitializingBean, Applica
private List SessionRepositoryFilter extends Session> springSess
SessionRepository sessionRepository) {
SessionRepositoryFilter sessionRepositoryFilter = new SessionRepositoryFilter<>(sessionRepository);
sessionRepositoryFilter.setHttpSessionIdResolver(this.httpSessionIdResolver);
+ sessionRepositoryFilter.setSessionTimeout(this.sessionTimeout);
+ sessionRepositoryFilter.setSkipCommitSessionHeaderName(this.skipUpdateSessionHeaderName);
return sessionRepositoryFilter;
}
diff --git a/spring-session-core/src/main/java/org/springframework/session/web/http/SaveSessionException.java b/spring-session-core/src/main/java/org/springframework/session/web/http/SaveSessionException.java
new file mode 100644
index 000000000..81ee615a7
--- /dev/null
+++ b/spring-session-core/src/main/java/org/springframework/session/web/http/SaveSessionException.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2014-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.session.web.http;
+
+public class SaveSessionException extends RuntimeException {
+
+ public SaveSessionException() {
+ super();
+ }
+
+ public SaveSessionException(String s) {
+ super(s);
+ }
+
+ public SaveSessionException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public SaveSessionException(Throwable cause) {
+ super(cause);
+ }
+
+}
diff --git a/spring-session-core/src/main/java/org/springframework/session/web/http/SessionRepositoryFilter.java b/spring-session-core/src/main/java/org/springframework/session/web/http/SessionRepositoryFilter.java
index 32415a211..ab5eeffe7 100644
--- a/spring-session-core/src/main/java/org/springframework/session/web/http/SessionRepositoryFilter.java
+++ b/spring-session-core/src/main/java/org/springframework/session/web/http/SessionRepositoryFilter.java
@@ -17,6 +17,7 @@
package org.springframework.session.web.http;
import java.io.IOException;
+import java.time.Duration;
import java.time.Instant;
import java.util.List;
@@ -105,6 +106,18 @@ public class SessionRepositoryFilter extends OncePerRequestFi
private HttpSessionIdResolver httpSessionIdResolver = new CookieHttpSessionIdResolver();
+ private String skipCommitSessionHeaderName = "Auto";
+
+ public void setSkipCommitSessionHeaderName(String skipCommitSessionHeaderName) {
+ this.skipCommitSessionHeaderName = skipCommitSessionHeaderName;
+ }
+
+ private int sessionTimeout = -1;
+
+ public void setSessionTimeout(int sessionTimeout) {
+ this.sessionTimeout = sessionTimeout;
+ }
+
/**
* Creates a new instance.
* @param sessionRepository the SessionRepository to use. Cannot be null.
@@ -223,10 +236,30 @@ private void commitSession() {
}
}
else {
+ if (Boolean.valueOf(this.getHeader(SessionRepositoryFilter.this.skipCommitSessionHeaderName))) {
+ return;
+ }
+
S session = wrappedSession.getSession();
String requestedSessionId = getRequestedSessionId();
clearRequestedSessionCache();
- SessionRepositoryFilter.this.sessionRepository.save(session);
+ try {
+ S sourceSession = SessionRepositoryFilter.this.sessionRepository.findById(session.getId());
+
+ if (null == sourceSession) {
+ SessionRepositoryFilter.this.sessionRepository.save(session);
+ }
+ else {
+ long halfExpireMillis = sourceSession.getLastAccessedTime().toEpochMilli()
+ + session.getMaxInactiveInterval().toMillis() / 2;
+ if (System.currentTimeMillis() > halfExpireMillis) {
+ SessionRepositoryFilter.this.sessionRepository.save(session);
+ }
+ }
+ }
+ catch (Exception ex) {
+ throw new SaveSessionException("Failed to save session!", ex);
+ }
String sessionId = session.getId();
if (!isRequestedSessionIdValid() || !sessionId.equals(requestedSessionId)) {
SessionRepositoryFilter.this.httpSessionIdResolver.setSessionId(this, this.response, sessionId);
@@ -317,6 +350,12 @@ public HttpSessionWrapper getSession(boolean create) {
new RuntimeException("For debugging purposes only (not an error)"));
}
S session = SessionRepositoryFilter.this.sessionRepository.createSession();
+ if (SessionRepositoryFilter.this.sessionTimeout > 60) {
+ session.setMaxInactiveInterval(Duration.ofSeconds(SessionRepositoryFilter.this.sessionTimeout));
+ }
+ else if (SessionRepositoryFilter.this.sessionTimeout > 0) {
+ session.setMaxInactiveInterval(Duration.ofSeconds(60));
+ }
session.setLastAccessedTime(Instant.now());
currentSession = new HttpSessionWrapper(session, getServletContext());
setCurrentSession(currentSession);
diff --git a/spring-session-jdbc/src/main/java/org/springframework/session/jdbc/JdbcIndexedSessionRepository.java b/spring-session-jdbc/src/main/java/org/springframework/session/jdbc/JdbcIndexedSessionRepository.java
index 98b46de85..59e50cda3 100644
--- a/spring-session-jdbc/src/main/java/org/springframework/session/jdbc/JdbcIndexedSessionRepository.java
+++ b/spring-session-jdbc/src/main/java/org/springframework/session/jdbc/JdbcIndexedSessionRepository.java
@@ -207,6 +207,25 @@ public class JdbcIndexedSessionRepository implements
WHERE EXPIRY_TIME < ?
""";
+ // @formatter:off
+ private static final String DELETE_SESSION_ATTRIBUTE_BY_SESSION_ID_QUERY = ""
+ + "DELETE FROM %TABLE_NAME%_ATTRIBUTES "
+ + "WHERE SESSION_PRIMARY_ID in ("
+ + " SELECT PRIMARY_ID FROM %TABLE_NAME%"
+ + " WHERE SESSION_ID = ? "
+ + " AND MAX_INACTIVE_INTERVAL >= 0"
+ + ")";
+ // @formatter:on
+
+ // @formatter:off
+ private static final String DELETE_SESSION_ATTRIBUTE_BY_EXPIRY_TIME_QUERY = ""
+ + "DELETE FROM %TABLE_NAME%_ATTRIBUTES "
+ + "WHERE SESSION_PRIMARY_ID in ("
+ + " SELECT PRIMARY_ID FROM %TABLE_NAME%"
+ + " WHERE EXPIRY_TIME < ?"
+ + ")";
+ // @formatter:on
+
private static final Log logger = LogFactory.getLog(JdbcIndexedSessionRepository.class);
private final JdbcOperations jdbcOperations;
@@ -240,6 +259,10 @@ public class JdbcIndexedSessionRepository implements
private Duration defaultMaxInactiveInterval = Duration.ofSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS);
+ private String deleteSessionAttributeBySessionIdQuery;
+
+ private String deleteSessionAttributeByExpiryTimeQuery;
+
private IndexResolver> {
diff --git a/spring-session-jdbc/src/test/java/org/springframework/session/jdbc/JdbcIndexedSessionRepositoryTests.java b/spring-session-jdbc/src/test/java/org/springframework/session/jdbc/JdbcIndexedSessionRepositoryTests.java
index 341b5e350..27e34b6e0 100644
--- a/spring-session-jdbc/src/test/java/org/springframework/session/jdbc/JdbcIndexedSessionRepositoryTests.java
+++ b/spring-session-jdbc/src/test/java/org/springframework/session/jdbc/JdbcIndexedSessionRepositoryTests.java
@@ -239,6 +239,34 @@ void setDeleteSessionsByLastAccessTimeQueryEmpty() {
.withMessage("Query must not be empty");
}
+ @Test
+ void setDeleteSessionAttributeBySessionIdQueryNull() {
+ assertThatIllegalArgumentException()
+ .isThrownBy(() -> this.repository.setDeleteSessionAttributeBySessionIdQuery(null))
+ .withMessage("Query must not be empty");
+ }
+
+ @Test
+ void setDeleteSessionAttributeBySessionIdQueryEmpty() {
+ assertThatIllegalArgumentException()
+ .isThrownBy(() -> this.repository.setDeleteSessionAttributeBySessionIdQuery(" "))
+ .withMessage("Query must not be empty");
+ }
+
+ @Test
+ void setDeleteSessionAttributeByExpiryTimeQueryNull() {
+ assertThatIllegalArgumentException()
+ .isThrownBy(() -> this.repository.setDeleteSessionAttributeByExpiryTimeQuery(null))
+ .withMessage("Query must not be empty");
+ }
+
+ @Test
+ void setDeleteSessionAttributeByExpiryTimeQueryEmpty() {
+ assertThatIllegalArgumentException()
+ .isThrownBy(() -> this.repository.setDeleteSessionAttributeByExpiryTimeQuery(" "))
+ .withMessage("Query must not be empty");
+ }
+
@Test
void setLobHandlerNull() {
assertThatIllegalArgumentException().isThrownBy(() -> this.repository.setLobHandler(null))
@@ -573,7 +601,7 @@ void getSessionExpired() {
assertThat(session).isNull();
verify(this.jdbcOperations, times(1)).query(isA(String.class), isA(PreparedStatementSetter.class),
isA(ResultSetExtractor.class));
- verify(this.jdbcOperations, times(1)).update(startsWith("DELETE"), eq(expired.getId()));
+ verify(this.jdbcOperations, times(2)).update(startsWith("DELETE"), eq(expired.getId()));
}
@Test
@@ -600,7 +628,7 @@ void delete() {
this.repository.deleteById(sessionId);
- verify(this.jdbcOperations, times(1)).update(startsWith("DELETE"), eq(sessionId));
+ verify(this.jdbcOperations, times(2)).update(startsWith("DELETE"), eq(sessionId));
}
@Test
@@ -658,7 +686,7 @@ void findByIndexNameAndIndexValuePrincipalIndexNameFound() {
void cleanupExpiredSessions() {
this.repository.cleanUpExpiredSessions();
- verify(this.jdbcOperations, times(1)).update(startsWith("DELETE"), anyLong());
+ verify(this.jdbcOperations, times(2)).update(startsWith("DELETE"), anyLong());
}
@Test // gh-1120