Share Session In Multiple Web Project
Suppose there are two web projects: test4
and test5
, the login page is in test4
and when user access resources of test5
, he/she will
be redirected to login page of test4
if he/she has not login yet.
Therefore, the session should be shared between test4
and test5
.
There are some workarounds to implement this goal, e.g. spring session
, apache shiro
, this post is about apache shiro
.
Custom Session DAO
In order to share session, we need to write a custom session dao which extends AbstractSessionDAO
.
Keep/read the session info in redis
.
public class ShiroRedisSessionDao extends AbstractSessionDAO {
private long expireTime = 120000;
@Autowired
private RedisTemplate redisTemplate;
public ShiroRedisSessionDao() {
super();
}
public ShiroRedisSessionDao(long expireTime, RedisTemplate redisTemplate) {
super();
this.expireTime = expireTime;
this.redisTemplate = redisTemplate;
}
@Override
public void update(Session session) throws UnknownSessionException {
if (session == null || session.getId() == null) {
return;
}
session.setTimeout(expireTime);
redisTemplate.opsForValue().set(session.getId(), session, expireTime, TimeUnit.MILLISECONDS);
}
@Override
public void delete(Session session) {
if (null == session) {
return;
}
redisTemplate.opsForValue().getOperations().delete(session.getId());
}
@Override
public Collection<Session> getActiveSessions() {
return redisTemplate.keys("*");
}
@Override
protected Serializable doCreate(Session session) {
Serializable sessionId = this.generateSessionId(session);
this.assignSessionId(session, sessionId);
redisTemplate.opsForValue().set(session.getId(), session, expireTime, TimeUnit.MILLISECONDS);
return sessionId;
}
@Override
protected Session doReadSession(Serializable sessionId) {
if (sessionId == null) {
return null;
}
return (Session) redisTemplate.opsForValue().get(sessionId);
}
//ommit setters/gettters
}
Custom Realm Service
You can implements a own realm service by extends AuthorizingRealm
, please refer to previous post for detail.
Note that if your want to store POJO in redis, it has to be serializable. In addition, multiple web projects have to use same model, e.g. com.example.model.User.
Shiro config
Add custom session dao and cookie to session manager.
The shiro config in test4
looks like next:
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="/login.html" />
<!-- it has to be /logout = logout, cannot be /test4/logout = logout -->
<property name="filterChainDefinitions">
<value>
/login.action = anon
/*.css = anon
/*.js = anon
/logout = logout
/** = authc
</value>
</property>
</bean>
<bean id="logout" class="org.apache.shiro.web.filter.authc.LogoutFilter">
<property name="redirectUrl" value="/login.html" />
</bean>
<bean name="myRealm" class="example.test4.service.RealmService" >
<property name="credentialsMatcher" ref="passwordMatcher" />
</bean>
<bean id="customSessionDao" class="example.test4.dao.ShiroRedisSessionDao" />
<bean id="simpleCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg name="name" value="shiro.sesssion"/>
<property name="path" value="/"/>
</bean>
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<property name="sessionValidationSchedulerEnabled" value="true" />
<property name="globalSessionTimeout" value="1800000" />
<property name="sessionDAO" ref="customSessionDao" />
<property name="sessionIdCookie" ref="simpleCookie"/>
<!-- set false to remove jsessionid = xxx at the end of url -->
<property name="sessionIdUrlRewritingEnabled" value="false"/>
</bean>
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="myRealm" />
<property name="sessionManager" ref="sessionManager" />
</bean>
<bean id="hashService" class="org.apache.shiro.crypto.hash.DefaultHashService">
<property name="hashAlgorithmName" value="MD5" />
<property name="hashIterations" value="1" />
<property name="generatePublicSalt" value="false" />
</bean>
<bean id="hexFormat" class="org.apache.shiro.crypto.hash.format.HexFormat">
</bean>
<bean id="passwordService" class="org.apache.shiro.authc.credential.DefaultPasswordService">
<property name="hashService" ref="hashService" />
<property name="hashFormat" ref="hexFormat" />
</bean>
<bean id="passwordMatcher" class="org.apache.shiro.authc.credential.PasswordMatcher">
<property name="passwordService" ref="passwordService" />
</bean>
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
While the config in test5
is:
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<!-- has to be http://192.168.88.6:8080/test4/login.html, cannot be 192.168.88.6:8080/test4/login.html,otherwise the browser address bar will changed to http://localhost:8080/test5/192.168.88.6:8080/test4/192.168.88.6:8080/test4/192.168.88.6:8080/test4/192.168.88.6:8080/test4/192.168.88.6:8080/test4/192.168.88.6:8080/test4/192.168.88.6:8080/test4/192.168.88.6:8080/test4/192.168.88.6:8080/test4/192.168.88.6:8080/test4/192.168.88.6:8080/test4/192.168.88.6:8080/test4/192.168.88.6:8080/test4/192.168.88.6:8080/test4/192.168.88.6:8080/test4/192.168.88.6:8080/test4/192.168.88.6:8080/test4/192.168.88.6:8080/test4/192.168.88.6:8080/test4/192.168.88.6:8080/test4/login.html -->
<property name="loginUrl" value="http://192.168.88.6:8080/test4/login.html" />
<property name="filterChainDefinitions">
<value>
/test4/login.html = anon
/login.action = anon
/*.css = anon
/*.js = anon
/logout = logout
/** = authc
</value>
</property>
</bean>
<bean id="logout" class="org.apache.shiro.web.filter.authc.LogoutFilter">
<property name="redirectUrl" value="http://192.168.88.6:8080/test4/login.html" />
</bean>
<bean id="simpleCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg name="name" value="shiro.sesssion"/>
<property name="path" value="/"/>
</bean>
<!-- others are same as config of test4 -->
Note: cookie path has to be /