Como o SpringBoot do Java integra o shiro para logon único e realiza a função de chute

premissa:

Às vezes, em nossos projetos, haverá um cenário de negócios, ou seja, se o usuário A efetuar login na conta de administrador em um ponto, e o usuário B efetuar login às 1:30, eles descobrirão que ambos estão logados com sucesso, mas por motivos de segurança, não podemos Para fazer o login bem-sucedido, chute o primeiro A ou bloqueie o último B, portanto, precisamos usar o logon único no projeto neste momento.

Às vezes, você também pode especificar quantas pessoas podem estar online para uma conta ao mesmo tempo. Isso será refletido no código e a lógica no código correspondente pode ser modificada para seu próprio negócio.

O framework escolhido pelo projeto é : SpringBoot + Shiro + Redis (outros caches também podem ser usados)

texto:

Precisamos verificar se a conta que ele está usando já está sendo usada por alguém ou se já está em um estado conectado toda vez que um usuário inicia uma solicitação. Se for um estado conectado, chute a anterior e indique o usuário muda a senha. Portanto, precisamos herdar o AccessControllerFilter (filtro de controle de acesso) de Shiro.

Em seguida, reescreva os dois métodos dessa classe, os métodos isAccessAllowed e onAccessDenied.

isAccessAllowed avalia se deve efetuar login. O valor de retorno é um tipo booleano. Se retornar verdadeiro, não entrará no método onAccessDenied e entrará diretamente no controlador. Se retornar falso, continuará a executar o método onAccessDenied.

Retornamos false diretamente para isAccessAllowed e fazemos julgamentos lógicos em onAccessDenied.

Não há muito a dizer, o código:


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

Desta forma, a função proposta pode ser realizada, caso o usuário seja expulso com um parâmetro no head quando a página de login é proposta, ela também pode ser ajustada de acordo com a situação do seu próprio projeto. Se você achar útil, por favor, me dê um ponto de atenção. Hahahaha.

 

 

 

     

Acho que você gosta

Origin blog.csdn.net/qq_38821574/article/details/111180162
Recomendado
Clasificación