前言:在項目中,我們需要讓每個賬戶只能有一個在線,如果一個賬號在一個系統中登錄多次並且這些同時 對系統操作是非常不安全的,因此需要做出限制,在登陸前可以先判斷當前賬戶在系統中是否處於已登錄狀態,如果有登錄的可以直接將其他的當前同一賬戶擠下線,在之前我首先想到的是直接用HttpSession獲取到session在其setAttribute方法中傳入當前賬戶信息進行判斷當前賬戶是否過期或者是否處於已登錄狀態,后來測試發現這個session只能在同一個瀏覽器可以使用getAttribute方法拿到值,而在不同瀏覽器是拿不到值,返回null,故而無法判斷,因此下面方法使用的是通過設置全局變量map<賬號,Session>來存儲信息,這樣就可以跨瀏覽器取值進行判斷了,之前覺得為啥要傳Session進去,直接傳字符串不也一樣?后來需要實現當前用戶在規定時間沒任何操作使其下線,而Session可以通過設置過期時間正好實現此需求,時間一到session中存的信息會被銷毀,並且Session可以用setAttribute傳入用戶信息,我們進行登錄前的判斷時直接獲取map中的session,然后獲取出session中的用戶信息進行判斷,完美解決這里的需求,如果項目使用shiro同樣可以使用,親測可用。
正文:
一、創建實體類,在實體類中添加getter,setter方法,記得加注解@Repository,否則使用@Autowired注解注入時會報錯
1 import org.springframework.stereotype.Repository; 2 import javax.servlet.http.HttpSession; 3 import java.util.HashMap; 4 import java.util.Map; 5 6 @Repository 7 public class ManageSession { 8 Map<String, HttpSession> manageSession=new HashMap<>(); 9 10 //setter 11 public Map<String, HttpSession> getManageSession() { 12 return manageSession; 13 } 14 //getter 15 public void setManageSession(Map<String, HttpSession> manageSession) { 16 this.manageSession = manageSession; 17 } 18 }
二、在啟動類中添加全局session變量
1 @SpringBootApplication 2 public class BtkbringomgApplication { 4 public static void main(String[] args) { 5 SpringApplication.run(BtkbringomgApplication.class, args); 6 } 7 /* 全局session變量 */ 8 public static ManageSession manageSession; 9 }
三、在Controller中注入
1 @Autowired 2 private ManageSession manageSession;
四、在Controller中的登錄方法下添加,為了簡便下面的登錄方法返回值設置為void,讀者可根據自己的需求進行修改返回自己想要的數據
@RequestMapping("/dologin")
public void login(@RequestBody User user,HttpServletRequest request){
HttpSession session = request.getSession();//獲取session
session.setMaxInactiveInterval(60*30*2);//1h //session過期時間 單位:s 也可以不設置或傳入參數0或負數表示永久有效,這里設置是為了判斷在這么多時間未對系統進行任何操作讓它下線
session.setAttribute(user.username,user.username);//session想傳入的東西,隨便傳,這里傳入了登錄賬號,達到上面時間自動銷毀
/** *每個賬號只能在線一個 * *當前登錄后其他的踢掉 **/ HttpSession httpSession = manageSession.getManageSession().get(user.getUsername()); //System.out.println("session:"+httpSession); //if (httpSession!=null && !httpSession.isNew()){ if (httpSession!=null){ //第一種方法:session銷毀 httpSession.invalidate(); // 第二種方法:清除session---賬戶已經登錄時另一個此賬號直接登不進去,不建議使用 //Enumeration<String> enumeration = request.getSession().getAttributeNames(); //while (enumeration.hasMoreElements()) { //String key = enumeration.nextElement().toString(); //request.getSession().removeAttribute(key); //} } //寫入session信息
manageSession.getManageSession().put(user.getUsername(),session);
}
五、有時候登錄時session已經銷毀導致我們銷毀時出現異常信息:IllegalStateException:Session already invalidated,如果使用shiro進行登錄認證時認證失敗會走異常,會造成即使認證成功也會進異常而導致認證失敗的假象,所以如果使用shiro登錄的話我們使用try/catch將其包裹對此異常進行放行。
........ catch (Exception e){ e.printStackTrace(); String messagename = e.getMessage();// 扒出異常內容 if(messagename.contains("Session already invalidated")){ //session已經銷毀,放行
//返回認證成功的信息--這里的和步驟六result為我自定義的結果集Result result = new Result();來的--可直接刪掉按照自己需求返回自己想要的
result.setSuccess(true);
result.setMessage("認證成功");
//寫入session信息
manageSession.getManageSession().put(user.getUsername(),session);
}else{ e.printStackTrace(); /*認證失敗*/ result.setSuccess(false); result.setMessage("認證失敗!"); }
六、有時我們有這樣一個需求:在登陸前判斷當前賬戶是否處於已經登錄狀態並給出提示,上面代碼中session.setMaxInactiveInterval(60*30*2);可以設置過期時間,時間一到銷毀session中存儲的值
@Autowired private ManageSession manageSession; @RequestMapping("/beforelogin") public Result beforelogin(@RequestBody User user) { Result result = new Result(); //判斷是否有人正在使用此帳號(此帳號是否處於已登錄狀態)--獲取map中的session HttpSession httpSession = manageSession.getManageSession().get(user.getUsername()); try {
//扒出session中的用戶信息 if(httpSession.getAttribute(user.username)!=null){ //當前session有值,說明1.此帳號處於已登錄狀態有人正在使用,2.session還在有效期未被銷毀 //System.out.println(httpSession.getAttribute(user.username)); //此賬號有人正在使用-----返回提示信息 result.setSuccess(false); result.setMessage("此帳號有人正在使用,是否繼續登錄將其擠掉?"); }else { result.setSuccess(true); result.setMessage("此賬號空閑,可直接登錄使用!"); } return result; } catch (Exception e) { //掉進異常,說明當前賬戶要么空閑,要么超時過期,直接登錄就好 //首次登錄或賬號處於空閑狀態(未登錄)報空指針NullPointerException //報異常getAttribute: Session already invalidated說明此帳號之前的登錄已經過期,現在可直接登錄 result.setSuccess(true); result.setMessage("此賬號空閑,可直接登錄!"); System.out.println("此賬號空閑"); return result; } }