This commit is contained in:
xiangwb
2018-04-08 16:13:38 +08:00
parent 058caaf434
commit 7f679837aa
4 changed files with 162 additions and 8 deletions
@@ -0,0 +1,125 @@
package com.xwbing.shiro;
import com.xwbing.domain.entity.sys.SysUser;
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 javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.Serializable;
import java.util.Deque;
import java.util.LinkedList;
/**
* 项目名称: boot-module-demo
* 创建时间: 2018/4/3 18:02
* 作者: xiangwb
* 说明: 防止二次登陆的功能
*/
public class KickoutSessionControlFilter extends AccessControlFilter {
private String kickoutUrl; // 踢出后到的地址
private boolean kickoutAfter; // 踢出之前登录的/之后登录的用户 默认踢出之前登录的用户
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");
}
/**
* 是否允许访问,返回true表示允许
*
* @param request
* @param response
* @param mappedValue
* @return
* @throws Exception
*/
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
return false;
}
/**
* 表示访问拒绝时是否自己处理,如果返回true表示自己不处理且继续拦截器链执行,返回false表示自己已经处理了(比如重定向到另一个页面)。
*
* @param request
* @param response
* @return
* @throws Exception
*/
@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();
Object object = subject.getPrincipal();
SysUser sysUser = (SysUser) object;
Serializable sessionId = session.getId();
// 初始化用户的队列放到缓存里
Deque<Serializable> deque = cache.get(sysUser.getUserName());
if (deque == null || deque.size() == 0) {
deque = new LinkedList<>();
cache.put(sysUser.getUserName(), deque);
}
// 如果队列里没有此sessionId,且用户没有被踢出;放入队列
if (!deque.contains(sessionId) && session.getAttribute("kickout") == null) {
deque.push(sessionId);
}
// 如果队列里的sessionId数超出最大会话数,开始踢人
while (deque.size() > maxSession) {
Serializable kickoutSessionId;
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);
WebUtils.issueRedirect(request, response, kickoutUrl);
return false;
}
return true;
}
}
@@ -8,8 +8,6 @@ import com.xwbing.service.sys.SysAuthorityService;
import com.xwbing.service.sys.SysRoleService;
import com.xwbing.service.sys.SysUserService;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
@@ -92,11 +90,11 @@ public class MyShiroRealm extends AuthorizingRealm {
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) {
UsernamePasswordCaptchaToken captchaToken = (UsernamePasswordCaptchaToken) authenticationToken;
//1.校验验证码
String captcha = captchaToken.getCaptcha();
String imgCode = (String) SecurityUtils.getSubject().getSession().getAttribute(CommonConstant.KEY_CAPTCHA);
if (StringUtils.isEmpty(captcha) || !captcha.equalsIgnoreCase(imgCode)) {
throw new AuthenticationException("验证码错误");
}
// String captcha = captchaToken.getCaptcha();
// String imgCode = (String) SecurityUtils.getSubject().getSession().getAttribute(CommonConstant.KEY_CAPTCHA);
// if (StringUtils.isEmpty(captcha) || !captcha.equalsIgnoreCase(imgCode)) {
// throw new AuthenticationException("验证码错误");
// }
//2.查是否有此用户
SysUser user = sysUserService.getByUserName(captchaToken.getUsername());
if (user == null) {
@@ -67,6 +67,7 @@ public class ShiroConfig {
Map<String, Filter> filters = new HashMap<>();
filters.put("sessionFilter", sessionFilter());
filters.put("kickout", kickoutSessionControlFilter());
filters.put("authc", urlPermissionsFilter());
filters.put("anon", new AnonymousFilter());
shiroFilterFactoryBean.setFilters(filters);
@@ -95,7 +96,7 @@ public class ShiroConfig {
//登出
chains.put("/user/logout", "anon");
//必须通过验证或者rememberMe
chains.put("/**", "user,authc,sessionFilter");
chains.put("/**", "user,authc,sessionFilter,kickout");
shiroFilterFactoryBean.setFilterChainDefinitionMap(chains);
return shiroFilterFactoryBean;
}
@@ -203,6 +204,26 @@ public class ShiroConfig {
return new SessionFilter();
}
///////////////////////kickoutSessionControlFilter///////////////////
@Bean
public FilterRegistrationBean kickoutSessionControlFilterRegistrationBean(KickoutSessionControlFilter kickoutSessionControlFilter) {
FilterRegistrationBean registration = new FilterRegistrationBean(kickoutSessionControlFilter);
//设置不自动在在配置时执行,默认是true
registration.setEnabled(false);
return registration;
}
@Bean
public KickoutSessionControlFilter kickoutSessionControlFilter() {
KickoutSessionControlFilter kickoutSessionControlFilter = new KickoutSessionControlFilter();
kickoutSessionControlFilter.setCacheManager(cacheManager());
kickoutSessionControlFilter.setSessionManager(sessionManager());
kickoutSessionControlFilter.setKickoutAfter(false);
kickoutSessionControlFilter.setMaxSession(1);
kickoutSessionControlFilter.setKickoutUrl("/kickout.html");
return kickoutSessionControlFilter;
}
/**
* Shiro生命周期处理器
*
@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>kickout</title>
</head>
<body>
<h2>你的账号在另一个地方登录,请确认是否本人登录!</h2>
</body>
</html>