shiro实现用户踢出功能
shiro实现用户踢出功能
KickoutSessionControlFilte
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Serializable;
import java.util.ArrayDeque;
import java.util.Deque; import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest; import com.fasterxml.jackson.databind.ObjectMapper;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory; /**
*
*
* @类名称:KickoutSessionFilter
* @类描述:自定义过滤器,进行用户访问控制
*
*/
public class KickoutSessionControlFilter extends AccessControlFilter { private static final Logger logger = LoggerFactory
.getLogger(KickoutSessionControlFilter.class); private final static ObjectMapper objectMapper = new ObjectMapper(); private String kickoutUrl; // 踢出后到的地址
private boolean kickoutAfter = true; // 踢出之前登录的/之后登录的用户 默认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;
} // 设置Cache的key的前缀
public void setCacheManager(CacheManager cacheManager) {
//必须和ehcache缓存配置中的缓存name一致
this.cache = cacheManager.getCache("shiro-activeSessionCache");
} @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);
Session session = subject.getSession();
// 没有登录授权 且没有记住我
if (!subject.isAuthenticated() && !subject.isRemembered()) {
// 如果没有登录,直接进行之后的流程
//判断是不是Ajax请求,异步请求,直接响应返回未登录
if (ShiroFilterUtils.isAjax(request) ) {
out(response,"超时");
return false;
}else{
request.setAttribute("forceLogout", "1");
session.setAttribute("forceLogout", "forceLogout");
return true;
} } // 获得用户请求的URI
HttpServletRequest req=(HttpServletRequest) request;
String path = req.getRequestURI();
//放行登录
if(path.equals("/login")){
return true;
}
try {
// 当前用户
String username = (String) subject.getPrincipal();
Serializable sessionId = session.getId();
// 读取缓存用户 没有就存入
Deque<Serializable> deque = cache.get(username);
if (deque == null) {
// 初始化队列
deque = new ArrayDeque<Serializable>();
}
// 如果队列里没有此sessionId,且用户没有被踢出;放入队列
if (!deque.contains(sessionId)
&& session.getAttribute("kickout") == null) {
// 将sessionId存入队列
deque.push(sessionId);
// 将用户的sessionId队列缓存
cache.put(username, deque);
}
// 如果队列里的sessionId数超出最大会话数,开始踢人
while (deque.size() > maxSession) {
Serializable kickoutSessionId = null;
// 是否踢出后来登录的,默认是false;即后者登录的用户踢出前者登录的用户;
if (kickoutAfter) { // 如果踢出后者
kickoutSessionId = deque.removeFirst(); } else { // 否则踢出前者
kickoutSessionId = deque.removeLast(); }
// 踢出后再更新下缓存队列
cache.put(username, deque); try {
// 获取被踢出的sessionId的session对象
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) {
// 会话被踢出了
subject.logout();
try {
// 退出登录("退出登录" + kickoutUrl);
} catch (Exception e) { // ignore
logger.debug("==踢出后用户重定向的路径kickoutUrl:" + kickoutUrl);
}
saveRequest(request);
// 重定向
return isAjaxResponse(request,response);
}
return true;
} catch (Exception e) { // ignore
logger.debug("控制用户在线数量【lyd-admin-->KickoutSessionFilter.onAccessDenied】异常!");
}
return isAjaxResponse(request,response);
}
public boolean out(ServletResponse response,String status ){
PrintWriter out = null;
try {
response.setCharacterEncoding("UTF-8");//设置编码
//response.setContentType("application/json");//设置返回类型
out = response.getWriter();
out.println("<html>");
out.println("<script>");
if("重复".equals(status)) {
out.println("alert('当前用户已经在其他地方登录,请您修改密码或重新登录!');"); }else if("超时".equals(status)) {
out.println("alert('当前用户操作超时或被踢出登录,请您修改密码或重新登录!');"); }
//out.println("history.back();");
out.println("</script>");
out.println("</html>");
return true; } catch (Exception e) {
logger.debug("用户在线数量限制【wyait-manager-->KickoutSessionFilter.out1】响应json信息出错");
}finally{
if(null != out){
out.flush();
out.close();
}
}
return true;
} private boolean isAjaxResponse(ServletRequest request,
ServletResponse response) throws IOException {
// ajax请求
/**
* 判断是否已经踢出
* 1.如果是Ajax 访问,那么给予json返回值提示。
* 2.如果是普通请求,直接跳转到登录页
*/
//判断是不是Ajax请求
if (ShiroFilterUtils.isAjax(request) ) {
out(response,"重复");
}else{
// 重定向
WebUtils.issueRedirect(request, response, kickoutUrl);
}
return false;
} }
jsp页面
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<!--jQuery js-->
<script src="<%=request.getContextPath()%>/resources/scripts2/jquery.min.js" type="text/javascript"></script>
<!--MiniUI-->
<link href="<%=request.getContextPath()%>/resources/scripts2/miniui/themes/default/miniui.css" rel="stylesheet" type="text/css" />
<!-- <link href="<%=request.getContextPath()%>/resources/scripts/miniui/themes/blue/skin.css" rel="stylesheet" type="text/css" /> -->
<link href="<%=request.getContextPath()%>/resources/scripts2/miniui/themes/icons.css" rel="stylesheet" type="text/css" />
<script src="<%=request.getContextPath()%>/resources/scripts2/miniui/miniui.js" type="text/javascript"></script>
<!-- <script src="../../scripts/boot.js" type="text/javascript"></script> -->
<style type="text/css">
html, body{
margin:0;padding:0;border:0;width:100%;height:100%;overflow:hidden;
}
</style>
</head>
<body>
<div style="padding:10px; background: #EDF0F3">
<form id="queryForm">
<table style="table-layout:fixed;">
<tr>
<td style="width:100px;">当前在线人数:</td> <td style="width:200px;">
<input id="sesessionCount" name="sesessionCount" property="editor" class="mini-textbox" allowInput="true" autoPopup="true" enabled="flase" style="width:80%"
valueFromSelect="true" />
</td>
</tr>
</table> </form>
</div>
<!-- Start Tool bar -->
<div id="toolbar" style="width:100%; ">
<div class="mini-toolbar" style="border-bottom:0;padding:0px;">
<table style="width:100%;">
<tr>
<td style="width:100%;">
<a class="mini-button" iconCls="icon-reload" onclick="refreshForm()" plain="true">刷新</a>
<!-- <a class="mini-button" iconCls="icon-print" onclick="output()" plain="true" tooltip="输出Excel">输出Excel</a> -->
</td>
</tr>
</table>
</div>
</div>
<!-- End Tool bar -->
<div class="mini-fit">
<div id="datagrid" class="mini-datagrid" ondrawcell="drawCellStatus"
style="width:100%;height:100%;" allowResize="true" fitColumns="false" showPager="false" showFilterRow="false" multiSelect="true" virtualScroll="true" >
<div property="columns">
<div type="indexcolumn" width="30"></div>
<!-- <div type="checkcolumn" width="15"></div>-->
<div name="id" id="id" field="id" align="left" headerAlign="center" width="100">会话id
<input id="id" name="id" property="editor" class="mini-spinner" format="n3" vtype="float" style="width:100%;" required="true" enabled="true" />
</div>
<div name="operatorId" field="operatorId" align="left" headerAlign="center" width="100">用户id
<input id="id" name="id" property="editor" class="mini-spinner" format="n3" vtype="float" style="width:100%;" required="true" enabled="true" />
</div>
<div name="username" field="username" align="left" headerAlign="center" width="150">用户名</div>
<div name="lastAccessTime" field="lastAccessTime" width="180" headerAlign="center" dateFormat="yyyy-MM-dd HH:mm:ss">最后访问时间</div>
<div name="host" field="host" headerAlign="center" width="100" >主机地址</div>
<div name="tichu" field="tichu" headerAlign="center" width="100" >是否踢出</div>
</div>
</div>
</div>
</body>
<script src="<%=request.getContextPath()%>/resources/js/common.js" type="text/javascript"></script>
<script type="text/javascript">
mini.parse();
var grid = mini.get("datagrid");
loadData();
function loadData() {
var form = new mini.Form("queryForm");
var o = form.getData(true);
var json = mini.encode(o); //grid.load({json: json});
$.ajax({
url: 'ajax/getSessions',
type: 'get',
success: function(text) {
console.log(text);
var o = mini.decode(text);
var sesessionCount = o.sesessionCount;
mini.get("sesessionCount").setValue(sesessionCount);
grid.setData(o.sessions);
var data = mini.get('datagrid').getData();
for (i = 0; i < sesessionCount; i++) {
var operatorId = o.sessions[i].attributes.USER_ID;
var sessionId = o.sessions[i].id;
var username = o.sessions[i].attributes.USER_NAME;
var row = mini.get('datagrid').getRow(i);
var tichu = "<a class='mini-button' iconCls='icon-search' onclick='tichu(\""+sessionId+"\")' tooltip='踢出...'>踢出</a>"
mini.get('datagrid').updateRow(row, {'tichu': tichu});
mini.get('datagrid').updateRow(row, {'operatorId': operatorId});
mini.get('datagrid').updateRow(row, {'username': username});
} },
error: function(err) {
mini.alert("读取数据失败!")
}
});
} function refreshForm() {
clearForm();
loadData();
} function query() {
loadData();
} function tichu(sessionId) {
$.ajax({
url: 'ajax/forceLogout',
type: 'post',
data: {sessionId: sessionId},
cache: false,
success: function(text) {
removeRow();
mini.alert("成功踢出用户xx!");
},
error: function(err) {
mini.alert("读取数据失败")
}
});
}
function clearForm() {
var form = new mini.Form("queryForm");
form.clear();
} function onQuyuNodeSelect(e) {
//mini.alert(e.isLeaf);
if(e.isLeaf == false) {
e.cancel=true;
}
}
function removeRow() {
var datagrid = mini.get("datagrid");
var rows = datagrid.getSelecteds();
if (rows.length > 0) {
datagrid.removeRows(rows, true);
}
} function drawCellStatus(e) {
var column = e.column;
var field = e.field;
var value = 1;
var grid = mini.get("datagrid");
if (field === 'operatorId') {
if (value == 1) {
console.log("行数为"+e.row);
grid.removeRows(e.row, true);
}
}
}
</script>
</html>
applicationContext-security.xml配置页面
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:util="http://www.springframework.org/schema/util" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean id="cacheManager" class="com.infotech.security.filter.SpringCacheManagerWrapper">
<property name="cacheManager" ref="springCacheManager"/>
</bean> <bean id="credentialsMatcher" class="com.infotech.security.filter.RetryLimitHashedCredentialsMatcher">
<constructor-arg ref="cacheManager"/>
<property name="hashAlgorithmName" value="md5"/>
<property name="hashIterations" value="2"/>
<property name="storedCredentialsHexEncoded" value="true"/>
</bean> <bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"/> <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg name="name" value="JSESSID"/>
<property name="path" value="/"/> </bean> <bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg value="rememberMe"/>
<property name="httpOnly" value="true"/>
<property name="maxAge" value="2592000"/><!-- 30天 -->
</bean> <bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">
<property name="activeSessionsCacheName" value="shiro-activeSessionCache"/>
<property name="sessionIdGenerator" ref="sessionIdGenerator"/>
</bean> <bean id="sessionValidationScheduler" class="org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler">
<property name="sessionValidationInterval" value="1800000"/>
<property name="sessionManager" ref="sessionManager"/>
</bean> <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<property name="globalSessionTimeout" value="1800000"/>
<property name="deleteInvalidSessions" value="true"/>
<property name="sessionValidationSchedulerEnabled" value="true"/>
<property name="sessionValidationScheduler" ref="sessionValidationScheduler"/>
<property name="sessionDAO" ref="sessionDAO"/>
<property name="sessionIdCookieEnabled" value="true"/>
<property name="sessionIdCookie" ref="sessionIdCookie"/>
<property name="sessionIdUrlRewritingEnabled" value="false" />
</bean> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realms">
<list><ref bean="permissionAuthenticationRealm"></ref></list>
</property>
<property name="sessionManager" ref="sessionManager"/>
</bean> <bean id="formAuthenticationFilter" class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter">
<property name="usernameParam" value="username"/>
<property name="passwordParam" value="password"/>
<property name="rememberMeParam" value="rememberMe"/>
<property name="loginUrl" value="/index"/>
</bean> <bean id="kickoutSessionControlFilter" class="com.infotech.security.filter.KickoutSessionControlFilter">
<property name="cacheManager" ref="cacheManager"/>
<property name="sessionManager" ref="sessionManager"/>
<property name="kickoutAfter" value="false"/>
<property name="maxSession" value="1"/>
<property name="kickoutUrl" value="/index?kickout=1"/>
</bean> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="/index" />
<property name="successUrl" value="/main" />
<property name="unauthorizedUrl" value="/404" />
<property name="filters">
<util:map>
<entry key="authc" value-ref="formAuthenticationFilter"/>
<entry key="sysUser" value-ref="sysUserFilter"/>
<entry key="kickout" value-ref="kickoutSessionControlFilter"/>
</util:map>
</property>
<property name="filterChainDefinitions">
<value>
/resources/** = anon<!--设置静态资源文件为游客可访问-->
/mp/** = anon <!-- 微信接口设为可访问 -->
/wechat/** = anon <!-- 微信验证接口 -->
/css/** = anon
/data/** = anon
/images2/** = anon
/images/** = anon
/img/** = anon
/scripts2/** = anon
/js/** = anon
/login/** = anon
/login = authc
/logout = logout
/authenticated = authc
/** = kickout,user,sysUser
</value>
</property>
</bean> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
</beans>
AjaxSysController 控制类
@Autowired
private SessionDAO sessionDAO;
/**
* ############################# SYS04 在线用户统计
* ######################################
*/ /**
* 根据条件分页查询日志信息
*
* @param goods
* @param page
* @param rows
* @return
* @throws Exception
*/ @RequestMapping("/ajax/getSessions")
public @ResponseBody Map<String, Object> getSessions() {
Map<String, Object> result = new HashMap<String, Object>();
try {
Collection<Session> sessions = sessionDAO.getActiveSessions();
result.put("sessions", sessions);
result.put("sesessionCount", sessions.size());
} catch (Exception e) {
result.put(MsgConstants.ErrCode, MsgConstants.Err1);
result.put(MsgConstants.ErrMsg, MsgConstants.ErrText99);
LOGGER.error(e.getMessage(), e);
}
return result;
} @RequestMapping("/ajax/forceLogout")
public @ResponseBody String forceLogout(@RequestParam("sessionId") String sessionId) {
try {
Subject subject = SecurityUtils.getSubject();
Collection<Session> sessions = sessionDAO.getActiveSessions();
for(Session session:sessions){
if(session.getId().equals(sessionId)) {
session.setTimeout(0);//设置session立即失效,即将其踢出系统
}
}
} catch (Exception e) {/*ignore*/}
return "error2";
}
shiro实现用户踢出功能的更多相关文章
- 《怎样实现通过shell脚本将用户踢出系统》
下面是一个将用户踢出系统的脚本: #!/bin/bashread -p "input your username " userps aux | grep "^$user& ...
- 踢出非法Linux用户
非法添加用户及非法进去的远程操作用户! 01.非法用户闯入系统 最简单的办法就是用 w 命令来检查. 如果确认有非法用户出现在系统内,可以立即 kill 用户相关进程. kill -9 `lsof ...
- Linux系统下查看已经登录用户并踢出的方法
LINUX是个多用户系统,一旦连接到网络中,它可以同时为多个登录用户提供服务. 查看用户的操作 查看当前用户: [ROOT@LOCALHOST ROOT] # W ...
- linux 踢出用户方法
linux系统root用户可强制踢制其它登录用户, 首先以root登录以便查看全部的在线用户信息,可用w命令查看登录用户信息 强制踢人命令格式:pkill -kill -t tty 解释: pkill ...
- linux查看在线用户并踢出用户
linux查看在线用户并踢出用户 1.查看在线用户 w [root@dbserver01 ~]# w 16:45:04 up 16 days, 8:48, 1 user, load average: ...
- CentOS查看登录用户以及踢出用户
查看登录用户,使用w命令 [root@lnmp ~]# w 18:51:18 up 35 min, 2 users, load average: 0.00, 0.00, 0.00 USER ...
- Linux查看当前登录用户并踢出用户
1.查看当前登录用户 [wilsh@lcl ~]$ whatis w w (1) - Show who is logged on and what they a ...
- Linux下管理员强行踢出用户的命令使用方法
Linux强制踢出用户命令: 一.输入w命令查看已登录用户信息 [root@KW_S01_192.168.1.106_A ~]# w 19:22:31 up 2:11, 3 users, loa ...
- Linux踢出已登录用户
1.使用w命令可以查看当前登录系统的所有用户 USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT root tty1 - 08:05 4:29 0.09s 0.09s - ...
随机推荐
- HTML—学习笔记
1 .表格 <br/>换行 <p> align top<img src="./julizi.png" align="top" &g ...
- 《ArcGIS Runtime SDK for .Net开发笔记》--介绍与环境搭建
一. ArcGIS Runtime SDK for .NET介绍 ArcGIS Runtime SDK for .net是一款针对windows平台的开发包.能够在开发出在windows phone, ...
- TP5.0 where数组高级查询
多条件模糊查询多条件比较查询使用数组可以方便得将一些比较复杂的查询条件 , 组合到一个数组之内 如以下数据库查询 $subjectList = Db::name('user_apply') -> ...
- HTML5: HTML5 WebSocket
ylbtech-HTML5: HTML5 WebSocket 1.返回顶部 1. HTML5 WebSocket WebSocket是HTML5开始提供的一种在单个 TCP 连接上进行全双工通讯的协议 ...
- seleniumIDE command命令
语法组成要素:command.target.value. command命令 三大类型:(action.Accessor.assertion) 操作 存储 断言 操作类型——Action 浏览 ...
- spring相关注解
spring相关注解: 使用之前需要<context:annotation-config/>在配置文件中启用 @Required 应用于类属性的set方法,并且要求该属性必须在xml容器里 ...
- java Wrapper包装类
什么是包装类?为什么需要包装类? 这是大家要了解的 对于java中的基本数据类型我们如果想要把当当做类来使用怎么办呢? 如果想要对这些基本数据类型直接调用一些方法来操作的话 怎么做呢? 由以上就产生了 ...
- Linux命令 shutdown
1. 简介 shutdown是用来让计算机处于暂停,关机,重启的指令 2.语法 shutdown [krhHPc] [时间] [警告信息] 时间的格式: hh:mm 表示多长时间以后执行 ...
- 03、python的基础-->str字符串的使用
1.字符串首字母大写 s = "aksjdjjhfhdhjaGGGGkkk" s1 = s.capitalize() print(s1) 2.字符串全部字母大写 s = " ...
- Windows10安装好Visual Studio2017后,找不到MFC向导
前段时候在Windows10中安装好Visual Studio2017后,想创建一个基于MFC的对话框应用,发现无法找到MFC开发向导选项,很是奇怪,以前使用VC6.0或者Visual Studio2 ...