Compare commits
13 Commits
Author | SHA1 | Date |
---|---|---|
![]() |
d0de8bb6e2 | |
![]() |
8593f88243 | |
![]() |
9da24f2328 | |
![]() |
e31ea59636 | |
![]() |
45b0b98725 | |
![]() |
2e505deee1 | |
![]() |
df80fa0da3 | |
![]() |
f4c6322793 | |
![]() |
a0eab9407e | |
![]() |
1f6e57c164 | |
![]() |
5d0c7aa7a1 | |
![]() |
3916e217a5 | |
![]() |
39df08832a |
53
README.md
53
README.md
|
@ -14,15 +14,31 @@ Going forward, we no need to enable sticky session (JSESSIONID) in Load Balancer
|
||||||
- Apache Tomcat 7
|
- Apache Tomcat 7
|
||||||
- Apache Tomcat 8
|
- Apache Tomcat 8
|
||||||
- Apache Tomcat 9
|
- Apache Tomcat 9
|
||||||
|
- Apache Tomcat 10
|
||||||
|
|
||||||
## Downloads: [](https://github.com/ran-jit/tomcat-cluster-redis-session-manager/wiki)
|
## Downloads: [](https://github.com/ran-jit/tomcat-cluster-redis-session-manager/wiki)
|
||||||
- [latest version (3.0.4)](https://github.com/ran-jit/tomcat-cluster-redis-session-manager/releases/tag/3.0.4)
|
- [latest version (4.0)](https://github.com/ran-jit/tomcat-cluster-redis-session-manager/releases/tag/4.0)
|
||||||
- [older versions](https://github.com/ran-jit/tomcat-cluster-redis-session-manager/wiki)
|
- [older versions](https://github.com/ran-jit/tomcat-cluster-redis-session-manager/wiki)
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://paypal.me/ranmanic1" target="_blank"><img alt="Donate" height="30%" width="30%" src="https://github.com/ran-jit/tomcat-cluster-redis-session-manager/blob/master/src/main/resources/donate.png"></a>
|
<a href="https://www.buymeacoffee.com/ranmanic" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-red.png" alt="Buy Me A Coffee" style="height: 60px !important;width: 217px !important;" ></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
## Maven configuration
|
||||||
|
```
|
||||||
|
<repository>
|
||||||
|
<id>repsy</id>
|
||||||
|
<name>tomcat-cluster-redis-session-manager-repo</name>
|
||||||
|
<url>https://repo.repsy.io/mvn/ranmanic/tomcat-session-manager</url>
|
||||||
|
</repository>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>tomcat-session-manager</groupId>
|
||||||
|
<artifactId>redis</artifactId>
|
||||||
|
<version>4.0</version>
|
||||||
|
</dependency>
|
||||||
|
```
|
||||||
|
|
||||||
#### Pre-requisite:
|
#### Pre-requisite:
|
||||||
1. jedis.jar
|
1. jedis.jar
|
||||||
2. commons-pool2.jar
|
2. commons-pool2.jar
|
||||||
|
@ -33,23 +49,33 @@ more details.. https://github.com/ran-jit/tomcat-cluster-redis-session-manager/w
|
||||||
|
|
||||||
#### Steps to be done,
|
#### Steps to be done,
|
||||||
1. Copy the downloaded jars to your tomcat/lib directory.
|
1. Copy the downloaded jars to your tomcat/lib directory.
|
||||||
- **tomcat/lib/**
|
```
|
||||||
|
tomcat/lib/
|
||||||
|
```
|
||||||
|
|
||||||
2. Add tomcat system property "catalina.base".
|
2. Add tomcat system property "catalina.base".
|
||||||
- **catalina.base="TOMCAT_LOCATION"**
|
```
|
||||||
* example: env "catalina.base=/opt/tomcat" bash
|
catalina.base="TOMCAT_LOCATION"
|
||||||
|
example: env "catalina.base=/opt/tomcat" bash
|
||||||
|
```
|
||||||
|
|
||||||
3. Copy the redis-data-cache.properties file to your tomcat/conf directory and update your Redis server details.
|
3. Copy the redis-data-cache.properties file to your tomcat/conf directory and update your Redis server details.
|
||||||
- **tomcat/conf/redis-data-cache.properties**
|
```
|
||||||
|
tomcat/conf/redis-data-cache.properties
|
||||||
|
```
|
||||||
|
|
||||||
4. Add the below two lines in your tomcat/conf/context.xml file.
|
4. Add the below two lines in your tomcat/conf/context.xml file.
|
||||||
- **<Valve className="tomcat.request.session.redis.SessionHandlerValve" />**
|
```
|
||||||
- **<Manager className="tomcat.request.session.redis.SessionManager" />**
|
<Valve className="tomcat.request.session.redis.SessionHandlerValve" />
|
||||||
|
<Manager className="tomcat.request.session.redis.SessionManager" />
|
||||||
|
```
|
||||||
|
|
||||||
5. Verify the session expiration time in tomcat/conf/web.xml file.
|
5. Verify the session expiration time in tomcat/conf/web.xml file.
|
||||||
- **<session-config>**
|
```
|
||||||
- **<session-timeout>60</session-timeout>**
|
<session-config>
|
||||||
- **</session-config>**
|
<session-timeout>60</session-timeout>
|
||||||
|
</session-config>
|
||||||
|
```
|
||||||
|
|
||||||
### Note:
|
### Note:
|
||||||
- **All your session attribute values must implement java.io.Serializable.**
|
- **All your session attribute values must implement java.io.Serializable.**
|
||||||
|
@ -67,7 +93,8 @@ more details.. https://github.com/ran-jit/tomcat-cluster-redis-session-manager/w
|
||||||
<tr><td>redis.sentinel.enabled</td><td>To enable redis sentinel mode<br/>- default: false<br>- supported values: true/false</td></tr>
|
<tr><td>redis.sentinel.enabled</td><td>To enable redis sentinel mode<br/>- default: false<br>- supported values: true/false</td></tr>
|
||||||
<tr><td>redis.sentinel.master</td><td>Redis sentinel master name<br/>- default: mymaster</td></tr>
|
<tr><td>redis.sentinel.master</td><td>Redis sentinel master name<br/>- default: mymaster</td></tr>
|
||||||
<tr><td>lb.sticky-session.enabled</td><td>To enable redis and standard session mode<br><br>If enabled,<ol><li>Must be enabled sticky session in your load balancer configuration. Else this manager may not return the updated session values</li><li>Session values are stored in local jvm and redis</li><li>If redis is down/not responding, requests uses jvm stored session values to process user requests. Redis comes back the values will be synced</li></ol>- default: false</td></tr>
|
<tr><td>lb.sticky-session.enabled</td><td>To enable redis and standard session mode<br><br>If enabled,<ol><li>Must be enabled sticky session in your load balancer configuration. Else this manager may not return the updated session values</li><li>Session values are stored in local jvm and redis</li><li>If redis is down/not responding, requests uses jvm stored session values to process user requests. Redis comes back the values will be synced</li></ol>- default: false</td></tr>
|
||||||
<tr><td>session.persistent.policies</td><td>session persistent policies.<br/><br/>- policies - DEFAULT, SAVE_ON_CHANGE, ALWAYS_SAVE_AFTER_REQUEST <br/><ol><li>SAVE_ON_CHANGE: every time session.setAttribute() or session.removeAttribute() is called the session will be saved.</li><li>ALWAYS_SAVE_AFTER_REQUEST: force saving after every request, regardless of whether or not the manager has detected changes to the session.</li></ol>- default: DEFAULT</td></tr>
|
<tr><td>session.persistent.policies</td><td>session persistent policies.<br/><br/>- policies - DEFAULT, SAVE_ON_CHANGE, ALWAYS_SAVE_AFTER_REQUEST <br/><ol><li>SAVE_ON_CHANGE: every time session.setAttribute() or session.removeAttribute() is called the session will be saved.</li><li>ALWAYS_SAVE_AFTER_REQUEST: force saving after every request, regardless of whether or not the manager has detected changes to the session.</li></ol>- default: DEFAULT</td></tr>
|
||||||
|
<tr><td>redis.sso.timeout</td><td>single-sign-on session timeout.<br/>- default: 0 ms (-no expiry)</td></tr>
|
||||||
</table>
|
</table>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
14
pom.xml
14
pom.xml
|
@ -2,9 +2,9 @@
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>tomcat-cluster-redis-session-manager</groupId>
|
<groupId>tomcat-session-manager</groupId>
|
||||||
<artifactId>tomcat-cluster-redis-session-manager</artifactId>
|
<artifactId>redis</artifactId>
|
||||||
<version>3.0.5</version>
|
<version>4.0</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<name>tomcat-cluster-redis-session-manager</name>
|
<name>tomcat-cluster-redis-session-manager</name>
|
||||||
|
@ -36,6 +36,14 @@
|
||||||
<!-- For local development properties end.. -->
|
<!-- For local development properties end.. -->
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
|
<distributionManagement>
|
||||||
|
<repository>
|
||||||
|
<id>repsy</id>
|
||||||
|
<name>tomcat-cluster-redis-session-manager-repo</name>
|
||||||
|
<url>https://repo.repsy.io/mvn/ranmanic/tomcat-session-manager</url>
|
||||||
|
</repository>
|
||||||
|
</distributionManagement>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>redis.clients</groupId>
|
<groupId>redis.clients</groupId>
|
||||||
|
|
|
@ -11,6 +11,8 @@ import static tomcat.request.session.annotation.Property.PropertyType.INTEGER;
|
||||||
/** author: Ranjith Manickam @ 5 Feb' 2020 */
|
/** author: Ranjith Manickam @ 5 Feb' 2020 */
|
||||||
public class Config implements Serializable {
|
public class Config implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 3480402257971437776L;
|
||||||
|
|
||||||
public static final String APPLICATION_PROPERTIES_FILE = "redis-data-cache.properties";
|
public static final String APPLICATION_PROPERTIES_FILE = "redis-data-cache.properties";
|
||||||
|
|
||||||
/** Redis config type. */
|
/** Redis config type. */
|
||||||
|
@ -77,6 +79,9 @@ public class Config implements Serializable {
|
||||||
@Property(name = "session.persistent.policies", defaultValue = "DEFAULT")
|
@Property(name = "session.persistent.policies", defaultValue = "DEFAULT")
|
||||||
private String sessionPersistentPolicies;
|
private String sessionPersistentPolicies;
|
||||||
|
|
||||||
|
@Property(name = "redis.sso.timeout", type = INTEGER, defaultValue = "0")
|
||||||
|
private Integer redisSSOTimeout;
|
||||||
|
|
||||||
public Config() {
|
public Config() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +103,8 @@ public class Config implements Serializable {
|
||||||
String redisSentinelMaster,
|
String redisSentinelMaster,
|
||||||
Integer redisSessionExpiryJobInterval,
|
Integer redisSessionExpiryJobInterval,
|
||||||
Integer redisSessionDataSyncJobInterval,
|
Integer redisSessionDataSyncJobInterval,
|
||||||
String sessionPersistentPolicies) {
|
String sessionPersistentPolicies,
|
||||||
|
Integer redisSSOTimeout) {
|
||||||
this.redisHosts = redisHosts;
|
this.redisHosts = redisHosts;
|
||||||
this.redisClusterEnabled = redisClusterEnabled;
|
this.redisClusterEnabled = redisClusterEnabled;
|
||||||
this.redisSentinelEnabled = redisSentinelEnabled;
|
this.redisSentinelEnabled = redisSentinelEnabled;
|
||||||
|
@ -118,6 +124,7 @@ public class Config implements Serializable {
|
||||||
this.redisSessionExpiryJobInterval = redisSessionExpiryJobInterval;
|
this.redisSessionExpiryJobInterval = redisSessionExpiryJobInterval;
|
||||||
this.redisSessionDataSyncJobInterval = redisSessionDataSyncJobInterval;
|
this.redisSessionDataSyncJobInterval = redisSessionDataSyncJobInterval;
|
||||||
this.sessionPersistentPolicies = sessionPersistentPolicies;
|
this.sessionPersistentPolicies = sessionPersistentPolicies;
|
||||||
|
this.redisSSOTimeout = redisSSOTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** To get 'redis.hosts' value. */
|
/** To get 'redis.hosts' value. */
|
||||||
|
@ -215,6 +222,11 @@ public class Config implements Serializable {
|
||||||
return sessionPersistentPolicies;
|
return sessionPersistentPolicies;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** To get 'redis.sso.timeout' value */
|
||||||
|
public Integer getRedisSSOTimeout() {
|
||||||
|
return redisSSOTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
/** {@inheritDoc} */
|
/** {@inheritDoc} */
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
@ -238,6 +250,7 @@ public class Config implements Serializable {
|
||||||
", redisSessionExpiryJobInterval=" + redisSessionExpiryJobInterval +
|
", redisSessionExpiryJobInterval=" + redisSessionExpiryJobInterval +
|
||||||
", redisSessionDataSyncJobInterval=" + redisSessionDataSyncJobInterval +
|
", redisSessionDataSyncJobInterval=" + redisSessionDataSyncJobInterval +
|
||||||
", sessionPersistentPolicies='" + sessionPersistentPolicies + '\'' +
|
", sessionPersistentPolicies='" + sessionPersistentPolicies + '\'' +
|
||||||
|
", redisSSOTimeout='" + redisSSOTimeout + '\'' +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,9 @@ public class SessionHandlerValve extends ValveBase {
|
||||||
LOGGER.error("Error processing request", ex);
|
LOGGER.error("Error processing request", ex);
|
||||||
throw new BackendException();
|
throw new BackendException();
|
||||||
} finally {
|
} finally {
|
||||||
this.manager.afterRequest();
|
if (this.manager != null) {
|
||||||
|
this.manager.afterRequest();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,10 +32,11 @@ public class SessionManager extends ManagerBase implements Lifecycle {
|
||||||
|
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(SessionManager.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(SessionManager.class);
|
||||||
|
|
||||||
|
private Integer ssoTimeout;
|
||||||
private DataCache dataCache;
|
private DataCache dataCache;
|
||||||
private SerializationUtil serializer;
|
private SerializationUtil serializer;
|
||||||
private ThreadLocal<SessionContext> sessionContext = new ThreadLocal<>();
|
private final ThreadLocal<SessionContext> sessionContext = new ThreadLocal<>();
|
||||||
private Set<SessionPolicy> sessionPolicy = EnumSet.of(SessionPolicy.DEFAULT);
|
private final Set<SessionPolicy> sessionPolicy = EnumSet.of(SessionPolicy.DEFAULT);
|
||||||
|
|
||||||
public boolean getSaveOnChange() {
|
public boolean getSaveOnChange() {
|
||||||
return this.sessionPolicy.contains(SessionPolicy.SAVE_ON_CHANGE);
|
return this.sessionPolicy.contains(SessionPolicy.SAVE_ON_CHANGE);
|
||||||
|
@ -78,14 +79,6 @@ public class SessionManager extends ManagerBase implements Lifecycle {
|
||||||
initializedValve = true;
|
initializedValve = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (valve instanceof SingleSignOnValve) {
|
|
||||||
SingleSignOnValve ssoValve = (SingleSignOnValve) valve;
|
|
||||||
ssoValve.setSessionManager(this);
|
|
||||||
ssoValve.setContext(context);
|
|
||||||
initializedValve = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!initializedValve) {
|
if (!initializedValve) {
|
||||||
|
@ -218,6 +211,7 @@ public class SessionManager extends ManagerBase implements Lifecycle {
|
||||||
private void initialize() {
|
private void initialize() {
|
||||||
try {
|
try {
|
||||||
Config config = ConfigUtil.getConfig();
|
Config config = ConfigUtil.getConfig();
|
||||||
|
this.ssoTimeout = config.getRedisSSOTimeout();
|
||||||
this.dataCache = new DataCacheFactory(config, getSessionTimeout(null)).getDataCache();
|
this.dataCache = new DataCacheFactory(config, getSessionTimeout(null)).getDataCache();
|
||||||
this.serializer = new SerializationUtil();
|
this.serializer = new SerializationUtil();
|
||||||
|
|
||||||
|
@ -353,6 +347,9 @@ public class SessionManager extends ManagerBase implements Lifecycle {
|
||||||
try {
|
try {
|
||||||
byte[] data = this.serializer.serializeSingleSignOnEntry(entry);
|
byte[] data = this.serializer.serializeSingleSignOnEntry(entry);
|
||||||
this.dataCache.set(ssoId, data);
|
this.dataCache.set(ssoId, data);
|
||||||
|
if (this.ssoTimeout > 0) {
|
||||||
|
this.dataCache.expire(ssoId, this.ssoTimeout);
|
||||||
|
}
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
LOGGER.error("Error occurred while serializing the single-sign-on entry..", ex);
|
LOGGER.error("Error occurred while serializing the single-sign-on entry..", ex);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@ package tomcat.request.session.redis;
|
||||||
|
|
||||||
import org.apache.catalina.Container;
|
import org.apache.catalina.Container;
|
||||||
import org.apache.catalina.Context;
|
import org.apache.catalina.Context;
|
||||||
|
import org.apache.catalina.Engine;
|
||||||
|
import org.apache.catalina.LifecycleException;
|
||||||
import org.apache.catalina.Manager;
|
import org.apache.catalina.Manager;
|
||||||
import org.apache.catalina.Realm;
|
import org.apache.catalina.Realm;
|
||||||
import org.apache.catalina.Session;
|
import org.apache.catalina.Session;
|
||||||
|
@ -26,14 +28,27 @@ public class SingleSignOnValve extends SingleSignOn {
|
||||||
|
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(SingleSignOnValve.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(SingleSignOnValve.class);
|
||||||
|
|
||||||
private Context context;
|
private Engine engine;
|
||||||
private SessionManager manager;
|
private SessionManager manager;
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
protected synchronized void startInternal() throws LifecycleException {
|
||||||
|
Container c;
|
||||||
|
for (c = this.getContainer(); c != null && !(c instanceof Engine); c = c.getParent()) {
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c instanceof Engine) {
|
||||||
|
this.engine = (Engine) c;
|
||||||
|
}
|
||||||
|
|
||||||
|
super.startInternal();
|
||||||
|
}
|
||||||
|
|
||||||
/** {@inheritDoc} */
|
/** {@inheritDoc} */
|
||||||
@Override
|
@Override
|
||||||
public void invoke(Request request, Response response) throws BackendException {
|
public void invoke(Request request, Response response) throws BackendException {
|
||||||
try {
|
try {
|
||||||
this.setContext(request.getContext());
|
|
||||||
this.setSessionManager(request.getContext().getManager());
|
this.setSessionManager(request.getContext().getManager());
|
||||||
|
|
||||||
request.removeNote("org.apache.catalina.request.SSOID");
|
request.removeNote("org.apache.catalina.request.SSOID");
|
||||||
|
@ -210,20 +225,17 @@ public class SingleSignOnValve extends SingleSignOn {
|
||||||
|
|
||||||
/** To set session manager. */
|
/** To set session manager. */
|
||||||
void setSessionManager(Manager manager) {
|
void setSessionManager(Manager manager) {
|
||||||
this.manager = (SessionManager) manager;
|
if (manager != null) {
|
||||||
}
|
this.manager = (SessionManager) manager;
|
||||||
|
}
|
||||||
/** To set context. */
|
|
||||||
void setContext(Context context) {
|
|
||||||
this.context = context;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** To expire session. */
|
/** To expire session. */
|
||||||
private void expire(SingleSignOnSessionKey key) {
|
private void expire(SingleSignOnSessionKey key) {
|
||||||
if (this.context == null) {
|
if (this.engine == null) {
|
||||||
LOGGER.warn("singleSignOn.sessionExpire.engineNull, key: {}", key);
|
LOGGER.warn("singleSignOn.sessionExpire.engineNull, key: {}", key);
|
||||||
} else {
|
} else {
|
||||||
Container host = this.context.findChild(key.getHostName());
|
Container host = this.engine.findChild(key.getHostName());
|
||||||
if (host == null) {
|
if (host == null) {
|
||||||
LOGGER.warn("singleSignOn.sessionExpire.hostNotFound, key: {}", key);
|
LOGGER.warn("singleSignOn.sessionExpire.hostNotFound, key: {}", key);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -34,3 +34,6 @@ lb.sticky-session.enabled=false
|
||||||
# 1. SAVE_ON_CHANGE: every time session.setAttribute() or session.removeAttribute() is called the session will be saved.
|
# 1. SAVE_ON_CHANGE: every time session.setAttribute() or session.removeAttribute() is called the session will be saved.
|
||||||
# 2. ALWAYS_SAVE_AFTER_REQUEST: force saving after every request, regardless of whether or not the manager has detected changes to the session.
|
# 2. ALWAYS_SAVE_AFTER_REQUEST: force saving after every request, regardless of whether or not the manager has detected changes to the session.
|
||||||
session.persistent.policies=DEFAULT
|
session.persistent.policies=DEFAULT
|
||||||
|
|
||||||
|
#- single-sign-on session timeout. (default value: 0 ms (-no expiry))
|
||||||
|
redis.sso.timeout=0
|
||||||
|
|
Loading…
Reference in New Issue