How does Java's SpringBoot integrate shiro for single sign-on and realize the kicking function

premise:

We sometimes have such a business scenario in the project, that is, if user A logs in to the admin account at one point, and user B logs in at 1:30, they will find that both are successfully logged in, but for safety reasons, we can’t To make the login successful, either kick the former A or block the latter B, so we need to use single sign-on in the project at this time.

Sometimes you can also specify how many people can be online for an account at the same time. These will be reflected in the code, and the logic in the corresponding code can be modified for your own business.

The framework chosen by the project is : SpringBoot + Shiro + Redis (other caches can also be used)

text:

We need to check whether the account he is using is already used by someone or whether it is already in a logged-in state every time a user initiates a request. If it is a logged-in state, we will kick the former out and prompt The user changes the password. So we need to inherit Shiro's AccessControllerFilter (access control filter).

Then rewrite the two methods of this class, the isAccessAllowed and onAccessDenied methods.

isAccessAllowed is to judge whether to log in. The return value is a boolean type. If it returns true, it will not enter the onAccessDenied method and will directly enter the controller. If it returns false, it will continue to execute the onAccessDenied method.

We directly return false to isAccessAllowed, and make logical judgments in onAccessDenied.

Not much to say, the code:


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

In this way, the proposed function can be realized. If the user is kicked out with a parameter in the head when the login page is proposed, it can also be adjusted according to the situation of your own project. If you find it useful, please give me a point of attention. Hahahaha.

 

 

 

     

Guess you like

Origin blog.csdn.net/qq_38821574/article/details/111180162