原文地址:https://www.cnblogs.com/leechenxiang/p/6171151.html
-
import org.apache.shiro.cache.Cache;
-
import org.apache.shiro.cache.CacheManager;
-
import org.apache.shiro.session.Session;
-
import org.apache.shiro.session.mgt.DefaultSessionKey;
-
import org.apache.shiro.session.mgt.SessionManager;
-
import org.apache.shiro.subject.Subject;
-
import org.apache.shiro.web.filter.AccessControlFilter;
-
import org.apache.shiro.web.util.WebUtils;
-
-
import com.agood.pojo.ActiveUser;
-
-
import javax.servlet.ServletRequest;
-
import javax.servlet.ServletResponse;
-
import javax.servlet.http.HttpServletRequest;
-
import javax.servlet.http.HttpServletResponse;
-
-
import java.io.Serializable;
-
import java.util.Deque;
-
import java.util.LinkedList;
-
-
/**
-
*
-
* @Title: KickoutSessionControlFilter.java
-
* @Package com.agood.bejavagod.controller.filter
-
* @Description: 同一用户后登陆踢出前面的用户
-
* Copyright: Copyright (c) 2016
-
* Company:Nathan.Lee.Salvatore
-
*
-
* @author leechenxiang
-
* @date 2016年12月12日 下午7:25:40
-
* @version V1.0
-
*/
-
public
class KickoutSessionControlFilter extends AccessControlFilter {
-
-
private String kickoutUrl;
//踢出后到的地址
-
private
boolean kickoutAfter =
false;
//踢出之前登录的/之后登录的用户 默认踢出之前登录的用户
-
private
int maxSession =
1;
//同一个帐号最大会话数 默认1
-
-
private SessionManager sessionManager;
-
private Cache<String, Deque<Serializable>> cache;
-
-
public void setKickoutUrl(String kickoutUrl) {
-
this.kickoutUrl = kickoutUrl;
-
}
-
-
public void setKickoutAfter(boolean kickoutAfter) {
-
this.kickoutAfter = kickoutAfter;
-
}
-
-
public void setMaxSession(int maxSession) {
-
this.maxSession = maxSession;
-
}
-
-
public void setSessionManager(SessionManager sessionManager) {
-
this.sessionManager = sessionManager;
-
}
-
-
public void setCacheManager(CacheManager cacheManager) {
-
this.cache = cacheManager.getCache(
"shiro-kickout-session");
-
}
-
-
@Override
-
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
-
return
false;
-
}
-
-
@Override
-
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
-
Subject subject = getSubject(request, response);
-
if(!subject.isAuthenticated() && !subject.isRemembered()) {
-
//如果没有登录,直接进行之后的流程
-
return
true;
-
}
-
-
Session session = subject.getSession();
-
ActiveUser user = (ActiveUser)subject.getPrincipal();
-
String username = user.getUserName();
-
Serializable sessionId = session.getId();
-
-
// 同步控制
-
Deque<Serializable> deque = cache.get(username);
-
if(deque ==
null) {
-
deque =
new LinkedList<Serializable>();
-
cache.put(username, deque);
-
}
-
-
//如果队列里没有此sessionId,且用户没有被踢出;放入队列
-
if(!deque.contains(sessionId) && session.getAttribute(
"kickout") ==
null) {
-
deque.push(sessionId);
-
}
-
-
//如果队列里的sessionId数超出最大会话数,开始踢人
-
while(deque.size() > maxSession) {
-
Serializable kickoutSessionId =
null;
-
if(kickoutAfter) {
//如果踢出后者
-
kickoutSessionId = deque.removeFirst();
-
}
else {
//否则踢出前者
-
kickoutSessionId = deque.removeLast();
-
}
-
try {
-
Session kickoutSession = sessionManager.getSession(
new DefaultSessionKey(kickoutSessionId));
-
if(kickoutSession !=
null) {
-
//设置会话的kickout属性表示踢出了
-
kickoutSession.setAttribute(
"kickout",
true);
-
}
-
}
catch (Exception e) {
//ignore exception
-
}
-
}
-
-
//如果被踢出了,直接退出,重定向到踢出后的地址
-
if (session.getAttribute(
"kickout") !=
null) {
-
//会话被踢出了
-
try {
-
subject.logout();
-
}
catch (Exception e) {
//ignore
-
}
-
saveRequest(request);
-
-
HttpServletRequest httpRequest = WebUtils.toHttp(request);
-
if (ShiroFilterUtils.isAjax(httpRequest)) {
-
HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
-
httpServletResponse.sendError(ShiroFilterUtils.HTTP_STATUS_SESSION_EXPIRE);
-
return
false;
-
}
else {
-
WebUtils.issueRedirect(request, response, kickoutUrl);
-
return
false;
-
}
-
}
-
-
return
true;
-
}
-
}
首先得要有个过滤器命名为:KickoutSessionControlFilter
然后在shiro.xml中需要这么定义:
-
<property name="filters">
-
<map>
-
<entry key="kickout" value-ref="kickoutSessionControlFilter"/>
-
</map>
-
</property>
-
<bean id="kickoutSessionControlFilter" class="com.agood.bejavagod.controller.filter.KickoutSessionControlFilter">
-
<property name="cacheManager" ref="shiroEhcacheManager"/>
-
<property name="sessionManager" ref="sessionManager"/>
-
<!-- 是否踢出后来登录的,默认是false;即后者登录的用户踢出前者登录的用户 -->
-
<property name="kickoutAfter" value="false"/>
-
<!-- 同一个用户最大的会话数,默认1;比如2的意思是同一个用户允许最多同时两个人登录 -->
-
<property name="maxSession" value="1"/>
-
<property name="kickoutUrl" value="/login.action"/>
-
</bean>
最后修改过滤器配置,拦截所有请求
/** = kickout,authc