java鬼混笔记:shiro 10、在线用户、踢人下线、唯一登录

完整的项目下载路径(项目下载网上的hui框架来着,什么鬼文件都在里面,暂清除,所以很多):
http://download.csdn.net/download/u013845177/9992748
相关的lib包路径:

http://download.csdn.net/download/u013845177/9992728


一、获取在线用户:

/**
 * 在线用户
 * 
 * @param model
 * @return
 */
@RequestMapping("online")
@RequiresPermissions("user:online")
public Object online(Model model, Pager pager) {
	
	try {
		Collection<Session> ss = sessionDAO.getActiveSessions();// 获取在线用户信息 下面的代码封装成list,好做分页查询功能
		List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
		Map<String, Object> m;
		for (Session s : ss) {
			m = new HashMap<String, Object>();
			m.put("sessionId", s.getId());
			Object obj = s.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
			if (obj != null) {
				SimplePrincipalCollection spc = (SimplePrincipalCollection) obj;
				if (spc.getPrimaryPrincipal() != null) {
					User u = (User) spc.getPrimaryPrincipal();// 转成User
					m.put("user", u);
				}
			} else {
				continue;
			}

			list.add(m);// 装成list,好做分页查询功能
		}
		if(pager != null) {
			int start = (pager.getPageNumber()-1)*pager.getPageSize();
			int end = start + pager.getPageSize();
			model.addAttribute("list", list.subList(start, end > list.size() ? list.size() : end));
			pager.setTotalRow(Long.valueOf(list.size()));
			Long tp = pager.getTotalRow() % pager.getPageSize() == 0 ?
					(pager.getTotalRow() / pager.getPageSize()) : (pager.getTotalRow()/ pager.getPageSize()+1);
			pager.setTotalPage(tp.intValue());
		}else {
			model.addAttribute("list", list);
		}
		return "user/online";
	} catch (Exception e) {
		e.printStackTrace();
		return "redirect:noPower.jsp";
	}
}

二、踢人下线

/**
 * 踢在线用户
 * 
 * @param model
 * @return
 */
@RequestMapping("offline")
@ResponseBody
@RequiresPermissions("user:offline")
public Object offline(String sessionId) {
	try {
		Session session = sessionDAO.readSession(sessionId);// sessionId:在获取在线用户那里可以获取得到
		if (session != null) {
			session.stop();
			sessionDAO.delete(session);
		}
		return new BackMessage(true, "", "");
	} catch (Exception e) {
		e.printStackTrace();
	}
	return new BackMessage(true, "", "");

}

三、唯一登录(这个方法基本复制网上的)

自定义一个KickoutSessionControlFilter.java 代码如下:

package cn.common;

import java.io.Serializable;
import java.util.Deque;
import java.util.LinkedList;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;


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 cn.entity.User;

public class KickoutSessionControlFilter extends AccessControlFilter {

    private String kickoutUrl; // 被踢出的跳转的url
    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();
        User user = (User)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)) {
            deque.push(sessionId);
            ((HttpServletRequest)request).getSession().setAttribute("currentUser", user);
        }
 
        //如果队列里的sessionId数超出最大会话数,开始踢人
        while(deque.size() > maxSession) {
            Serializable kickoutSessionId = null;
            if(kickoutAfter) { //如果踢出后者 同一个用户第二次在别的地方登录,报错org.apache.shiro.session.UnknownSessionException
                kickoutSessionId = deque.removeFirst();
            } else { //否则踢出前者
                kickoutSessionId = deque.removeLast();
            }
            try {
                Session kickoutSession = sessionManager.getSession(new DefaultSessionKey(kickoutSessionId));
                if(kickoutSession != null) {
                    kickoutSession.stop();// 停止这个session,重新登录
                }
            } catch (Exception e) {
            	e.printStackTrace();
            }
        }
 
        return true;
    }

}

接着配置applicationContext-shiro.xml
加入下面的代码:

<!-- 踢人功能 -->
<bean id="kickoutSessionControlFilter" class="cn.common.KickoutSessionControlFilter">
 <property name="cacheManager" ref="cacheManager"/>
 <property name="sessionManager" ref="sessionManager"/>
 <!-- 后者登录踢出前者登录:比如同一个账号:A电脑登录后,接着在B电脑登录,这时A电脑操作时会跳转登录页面 -->
 <property name="kickoutAfter" value="false"/>
 <!-- 一个用户只能有1个登录在线 -->
 <property name="maxSession" value="1"/>
 <!-- 被踢出的跳转的url -->
 <property name="kickoutUrl" value="/login.do?m=kick"/>       
</bean>

最后在<bean id="shiroFilter">中配置<property name="filters">中配置:

<property name="filters">
	<map>
		<entry key="authc" value-ref="LSFormAuthenticationFilter" /><!-- 配置自定义的form过滤器 -->
		<entry key="kickout" value-ref="kickoutSessionControlFilter"/><!-- 踢人 -->
	</map>
</property>

同时在<property name="filterChainDefinitions">中配置:

/** = authc,kickout(之前只有/** = authc)

OK!

阅读更多

没有更多推荐了,返回首页