前提:
私たちのプロジェクトでは、このようなビジネスシナリオが発生することがあります。つまり、ユーザーAが管理者アカウントにある時点でログインし、ユーザーBが1:30にログインすると、両方が正常にログインしていることがわかりますが、安全上の理由から、ログインを成功させるには、前者のAをキックするか、後者のBをブロックする必要があります。そのため、この時点でプロジェクトでシングルサインオンを使用する必要があります。
アカウントで同時にオンラインにできる人数を指定できる場合もあります。これらはコードに反映され、対応するコードのロジックを自分のビジネスに合わせて変更できます。
プロジェクトで選択されたフレームワークは次のとおりです:SpringBoot + Shiro + Redis(他のキャッシュも使用できます)
テキスト:
ユーザーがリクエストを開始するたびに、彼が使用しているアカウントがすでに誰かによって使用されているかどうか、またはすでにログイン状態にあるかどうかを確認する必要があります。ログイン状態の場合は、前者をキックしてユーザーにプロンプトを表示します。パスワードを変更します。したがって、ShiroのAccessControllerFilter(アクセス制御フィルター)を継承する必要があります。
次に、このクラスの2つのメソッド、isAccessAllowedメソッドとonAccessDeniedメソッドを書き直します。
isAccessAllowedは、ログインするかどうかを判断します。戻り値はブール型です。trueを返す場合は、onAccessDeniedメソッドに入力せず、コントローラーに直接入力します。falseを返す場合は、onAccessDeniedメソッドを実行し続けます。
isAccessAllowedに直接falseを返し、onAccessDeniedで論理的な判断を下します。
言うまでもありませんが、コードは次のとおりです。
import com.zjxf.base.bean.po.StudentInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.cache.Cache;
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 javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.Serializable;
import java.util.Deque;
import java.util.LinkedList;
/**
* created with IntelliJ IDEA
*
* @author: create by limu
* Date: 2020/12/14
* Time:17:31
*/
@Slf4j
public class KickOutSessionControlFilter extends AccessControlFilter {
private String kickOutUrl; //提出用户之后跳转的页面
private boolean kickOutAfter = false; //踢出前者还是后者
private int maxSession = 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;
}
@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 Boolean.TRUE;
}
Session session = subject.getSession(); //获取会话session
Object principal = subject.getPrincipal();
Serializable sessionId = session.getId();
StudentInfo studentInfo = (StudentInfo) principal;
String userName = studentInfo.getName() + studentInfo.getId();
Deque<Serializable> deque = cache.get(userName);
if (deque == null) {
deque = new LinkedList<>();
}
if (!deque.contains(sessionId) && session.getAttribute("kickOut") == null) {
deque.push(sessionId);
cache.put(userName, deque);
}
while (deque.size() > maxSession) {
Serializable kickOutSessionId;
if (kickOutAfter) {
kickOutSessionId = deque.removeFirst();
cache.put(userName, deque);
} else {
kickOutSessionId = deque.removeLast();
cache.put(userName, deque);
}
try {
Session kickOutSession = sessionManager.getSession(new DefaultSessionKey(kickOutSessionId));
if (kickOutSession != null) {
kickOutSession.setAttribute("kickOut", Boolean.TRUE);
}
} catch (Exception e) {
log.error("踢出异常未踢出");
}
}
if (session.getAttribute("kickOut") != null) {
try {
subject.logout();
} catch (Exception e) {
log.error("踢出异常");
}
saveRequest(request);
WebUtils.issueRedirect(request, response, kickOutUrl);
return Boolean.FALSE;
}
return Boolean.TRUE;
}
}
このようにして、提案された機能を実現することができます。ログインページが提案されたときにユーザーが頭のパラメータで追い出された場合、それはあなた自身のプロジェクトの状況に応じて調整することもできます。役に立ったら、注意点を教えてください。ハハハッハッハ。