shiro实现用户踢出功能


shiro实现用户踢出功能

KickoutSessionControlFilter

package com.infotech.security.filter;

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";  
        }  
    

猜你喜欢

转载自www.cnblogs.com/dahei96/p/11750861.html