最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Shiro 控制并發(fā)登錄人數(shù)限制及登錄踢出的實(shí)現(xiàn)代碼

 更新時間:2017年09月20日 10:31:22   作者:AinUser  
本文通過shiro實(shí)現(xiàn)一個賬號只能同時一個人使用,本文重點(diǎn)給大家分享Shiro 控制并發(fā)登錄人數(shù)限制及登錄踢出的實(shí)現(xiàn)代碼,需要的朋友參考下吧

我們經(jīng)常會有用到,當(dāng)A 用戶在北京登錄 ,然后A用戶在天津再登錄 ,要踢出北京登錄的狀態(tài)。如果用戶在北京重新登錄,那么又要踢出天津的用戶,這樣反復(fù)。

這樣保證了一個帳號只能同時一個人使用。那么下面來講解一下 Shiro  怎么實(shí)現(xiàn)這個功能,現(xiàn)在是用到了緩存 Redis  。我們也可以用其他緩存。如果是單個點(diǎn),直接用一個靜態(tài)的Map<String,Object> 或者 Ehcache  即可。

XML配置。

<!-- session 校驗(yàn)單個用戶是否多次登錄 -->
<bean id="kickoutSessionFilter"  class="com.sojson.core.shiro.filter.KickoutSessionFilter"> 
  <property name="kickoutUrl" value="/u/login.shtml?kickout"/> 
</bean> 
<!-- 靜態(tài)注入 jedisShiroSessionRepository-->
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
  <property name="staticMethod" value="com.sojson.core.shiro.filter.KickoutSessionFilter.setShiroSessionRepository"/>
  <property name="arguments" ref="jedisShiroSessionRepository"/>
</bean>

 這里用到了靜態(tài)注入。如果不了解請看這篇:Spring 靜態(tài)注入講解(MethodInvokingFactoryBean)

加入到 shiro  的Filter 攔截序列

<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
 <property name="securityManager" ref="securityManager" />
 <property name="loginUrl" value="/u/login.shtml" />
 <!-- TODO 待提取 -->
<property name="successUrl" value="/" />
<property name="unauthorizedUrl" value="/?login" />
  <property name="filterChainDefinitions" value="#{shiroManager.loadFilterChainDefinitions()}"/>  
  <property name="filters">
    <util:map>
      <entry key="login" value-ref="login"></entry>
      <entry key="role" value-ref="role"></entry>
      <entry key="simple" value-ref="simple"></entry>
      <entry key="permission" value-ref="permission"></entry>
      <entry key="kickout" value-ref="kickoutSessionFilter"></entry>
    </util:map>
  </property>
</bean>

Java代碼,下面看實(shí)現(xiàn)的Filter代碼。

package com.sojson.core.shiro.filter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Serializable;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import net.sf.json.JSONObject;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.util.WebUtils;
import com.sojson.common.utils.LoggerUtils;
import com.sojson.core.shiro.cache.VCache;
import com.sojson.core.shiro.session.ShiroSessionRepository;
import com.sojson.core.shiro.token.manager.TokenManager;
/**
 * 
 * 開發(fā)公司:SOJSON在線工具 <p>
 * 版權(quán)所有:© www.sojson.com<p>
 * 博客地址:http://www.sojson.com/blog/ <p>
 * <p>
 * 
 * 相同帳號登錄控制
 * 
 * <p>
 * 
 * 區(qū)分 責(zé)任人 日期    說明<br/>
 * 創(chuàng)建 周柏成 2016年6月2日  <br/>
 *
 * @author zhou-baicheng
 * @email so@sojson.com
 * @version 1.0,2016年6月2日 <br/>
 * 
 */
@SuppressWarnings({"unchecked","static-access"})
public class KickoutSessionFilter extends AccessControlFilter {
 //靜態(tài)注入
 static String kickoutUrl;
 //在線用戶
 final static String ONLINE_USER = KickoutSessionFilter.class.getCanonicalName()+ "_online_user";
 //踢出狀態(tài),true標(biāo)示踢出
 final static String KICKOUT_STATUS = KickoutSessionFilter.class.getCanonicalName()+ "_kickout_status";
 static VCache cache;
 //session獲取
 static ShiroSessionRepository shiroSessionRepository;
 @Override
 protected boolean isAccessAllowed(ServletRequest request,
  ServletResponse response, Object mappedValue) throws Exception {
 HttpServletRequest httpRequest = ((HttpServletRequest)request);
 String url = httpRequest.getRequestURI();
 Subject subject = getSubject(request, response);
 //如果是相關(guān)目錄 or 如果沒有登錄 就直接return true
 if(url.startsWith("/open/") || (!subject.isAuthenticated() && !subject.isRemembered())){
  return Boolean.TRUE;
 }
 Session session = subject.getSession();
 Serializable sessionId = session.getId();
 /**
  * 判斷是否已經(jīng)踢出
  * 1.如果是Ajax 訪問,那么給予json返回值提示。
  * 2.如果是普通請求,直接跳轉(zhuǎn)到登錄頁
  */
 Boolean marker = (Boolean)session.getAttribute(KICKOUT_STATUS);
 if (null != marker && marker ) {
  Map<String, String> resultMap = new HashMap<String, String>();
  //判斷是不是Ajax請求
  if (ShiroFilterUtils.isAjax(request) ) {
  LoggerUtils.debug(getClass(), "當(dāng)前用戶已經(jīng)在其他地方登錄,并且是Ajax請求!");
  resultMap.put("user_status", "300");
  resultMap.put("message", "您已經(jīng)在其他地方登錄,請重新登錄!");
  out(response, resultMap);
  }
  return Boolean.FALSE;
 }
 //從緩存獲取用戶-Session信息 <UserId,SessionId>
 LinkedHashMap<Long, Serializable> infoMap = cache.get(ONLINE_USER, LinkedHashMap.class);
 //如果不存在,創(chuàng)建一個新的
 infoMap = null == infoMap ? new LinkedHashMap<Long, Serializable>() : infoMap;
 //獲取tokenId
 Long userId = TokenManager.getUserId();
 //如果已經(jīng)包含當(dāng)前Session,并且是同一個用戶,跳過。
 if(infoMap.containsKey(userId) && infoMap.containsValue(sessionId)){
  //更新存儲到緩存1個小時(這個時間最好和session的有效期一致或者大于session的有效期)
  cache.setex(ONLINE_USER, infoMap, 3600);
  return Boolean.TRUE;
 }
 //如果用戶相同,Session不相同,那么就要處理了
 /**
  * 如果用戶Id相同,Session不相同
  * 1.獲取到原來的session,并且標(biāo)記為踢出。
  * 2.繼續(xù)走
  */
 if(infoMap.containsKey(userId) && !infoMap.containsValue(sessionId)){
  Serializable oldSessionId = infoMap.get(userId);
  Session oldSession = shiroSessionRepository.getSession(oldSessionId);
  if(null != oldSession){
  //標(biāo)記session已經(jīng)踢出
  oldSession.setAttribute(KICKOUT_STATUS, Boolean.TRUE);
  shiroSessionRepository.saveSession(oldSession);//更新session
  LoggerUtils.fmtDebug(getClass(), "kickout old session success,oldId[%s]",oldSessionId);
  }else{
  shiroSessionRepository.deleteSession(oldSessionId);
  infoMap.remove(userId);
  //存儲到緩存1個小時(這個時間最好和session的有效期一致或者大于session的有效期)
  cache.setex(ONLINE_USER, infoMap, 3600);
  }
  return Boolean.TRUE;
 }
 if(!infoMap.containsKey(userId) && !infoMap.containsValue(sessionId)){
  infoMap.put(userId, sessionId);
  //存儲到緩存1個小時(這個時間最好和session的有效期一致或者大于session的有效期)
  cache.setex(ONLINE_USER, infoMap, 3600);
 }
 return Boolean.TRUE;
 }
 @Override
 protected boolean onAccessDenied(ServletRequest request,
  ServletResponse response) throws Exception {
 //先退出
 Subject subject = getSubject(request, response);
 subject.logout();
 WebUtils.getSavedRequest(request);
 //再重定向
 WebUtils.issueRedirect(request, response,kickoutUrl);
 return false;
 }
 private void out(ServletResponse hresponse, Map<String, String> resultMap)
  throws IOException {
 try {
  hresponse.setCharacterEncoding("UTF-8");
  PrintWriter out = hresponse.getWriter();
  out.println(JSONObject.fromObject(resultMap).toString());
  out.flush();
  out.close();
 } catch (Exception e) {
  LoggerUtils.error(getClass(), "KickoutSessionFilter.class 輸出JSON異常,可以忽略。");
 }
 }
 public static void setShiroSessionRepository(
  ShiroSessionRepository shiroSessionRepository) {
 KickoutSessionFilter.shiroSessionRepository = shiroSessionRepository;
 }
 public static String getKickoutUrl() {
 return kickoutUrl;
 }
 public static void setKickoutUrl(String kickoutUrl) {
 KickoutSessionFilter.kickoutUrl = kickoutUrl;
 }
}

前端頁面(登錄頁面)代碼。

try{
 var _href = window.location.href+"";
 if(_href && _href.indexOf('?kickout')!=-1){
 layer.msg('您已經(jīng)被踢出,請重新登錄!');
 }
}catch(e){
}

Ok了,這樣效果就出來了。(效果圖)

總結(jié)

以上所述是小編給大家介紹的Shiro 控制并發(fā)登錄人數(shù)限制及登錄踢出的實(shí)現(xiàn)代碼,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!

相關(guān)文章

  • Spring Security基于HttpRequest配置權(quán)限示例詳解

    Spring Security基于HttpRequest配置權(quán)限示例詳解

    這篇文章主要介紹了Spring Security基于HttpRequest配置權(quán)限示例詳解,我們在配置中配置的url被封裝成RequestMatcher,而hasRole被封裝成AuthorityAuthorizationManager,本文結(jié)合示例代碼講解的非常詳細(xì),需要的朋友可以參考下
    2024-03-03
  • Java線程池高頻面試題總結(jié)

    Java線程池高頻面試題總結(jié)

    在進(jìn)程和線程的相關(guān)面試題中還有一部分是關(guān)于多線程和線程池的,也是在這一部分中比較??疾斓膬?nèi)容。本篇文章就帶你了解一下,希望能給你帶來幫助
    2021-08-08
  • 詳解Java基礎(chǔ)知識——JDBC

    詳解Java基礎(chǔ)知識——JDBC

    這篇文章主要介紹了Java基礎(chǔ)知識——JDBC,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • java中unicode和中文相互轉(zhuǎn)換的簡單實(shí)現(xiàn)

    java中unicode和中文相互轉(zhuǎn)換的簡單實(shí)現(xiàn)

    下面小編就為大家?guī)硪黄猨ava中unicode和中文相互轉(zhuǎn)換的簡單實(shí)現(xiàn)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-08-08
  • Hibernate一級緩存和二級緩存詳解

    Hibernate一級緩存和二級緩存詳解

    今天小編就為大家分享一篇關(guān)于Hibernate一級緩存和二級緩存詳解,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-03-03
  • 詳解Java多線程和IO流的應(yīng)用

    詳解Java多線程和IO流的應(yīng)用

    這篇文章主要介紹了詳解Java多線程和IO流的應(yīng)用,無論是本地文件復(fù)制,還是網(wǎng)絡(luò)多線程下載,對于流的使用都是一樣的,需要的朋友可以參考下
    2023-04-04
  • 解決MyEclipse中Maven設(shè)置jdk版本jdk1.8報錯問題

    解決MyEclipse中Maven設(shè)置jdk版本jdk1.8報錯問題

    今天安裝了jdk1.8、tomcat8、和maven3.5.2,弄好后在myeclipse新建了一個maven項目,項目默認(rèn)是jdk1.5,改成jdk1.8后項目報錯
    2018-10-10
  • Spring Boot Web應(yīng)用程序配置詳解

    Spring Boot Web應(yīng)用程序配置詳解

    這篇文章主要介紹了Spring Boot Web應(yīng)用程序配置詳解,本文中將介紹一些Web應(yīng)用程序最常用的配置,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-05-05
  • idea使用jclasslib插件查看字節(jié)碼

    idea使用jclasslib插件查看字節(jié)碼

    這篇文章主要為大家介紹了idea使用jclasslib插件查看字節(jié)碼,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-05-05
  • springboot中引入日志文件生成的配置詳解

    springboot中引入日志文件生成的配置詳解

    本文主要介紹了springboot中引入日志文件生成的配置詳解,包括日志級別的設(shè)置、日志格式的配置以及日志輸出的位置等,從而幫助開發(fā)者更好地進(jìn)行開發(fā)與調(diào)試
    2023-10-10

最新評論

红桥区| 平罗县| 闽侯县| 昌吉市| 崇明县| 大方县| 依安县| 科尔| 珠海市| 商河县| 乐亭县| 马龙县| 太湖县| 中西区| 辽宁省| 浮山县| 调兵山市| 鸡东县| 五莲县| 惠水县| 惠安县| 华蓥市| 荥阳市| 佛山市| 霞浦县| 陈巴尔虎旗| 龙里县| 莱芜市| 星座| 莫力| 和田县| 泾阳县| 青浦区| 股票| 景德镇市| 常德市| 广德县| 秦皇岛市| 定安县| 延吉市| 壤塘县|