java的SpringBoot如何集成shiro做单点登录并且实现踢人功能

15 篇文章 2 订阅
7 篇文章 0 订阅

前提:

我们有时候在项目中,会有这样一个业务场景,就是如果A用户在一点登录admin账号,B用户在一点半登录的时候,会发现两者都登录成功了,但是为了安全考虑我们是不可以让登录成功的,要么踢掉前者A要么阻止后者B,所以这会儿项目中我们就需要用到单点登录了。

有时候也可以指定一个账号可以多少个人同时在线,这些都会体现在代码中,对应的代码中的逻辑,针对自己的业务去修改就可以了。

项目选择的框架是:SpringBoot + Shiro + Redis(也可以使用其他的cache)

正文:

我们需要在每次用户发起请求的时候去检测他使用的这个账号是否已经是被人使用的,或者是否已经是登录的一个状态,如果是一个登录的状态的,就踢去前者,并且提示前使用者修改密码。所以我们需要继承Shiro的AccessControllerFilter(访问控制过滤器)。

然后重写该类的两个方法,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;
    }
}

这样就可以实现提出的功能了,如果提出到登录页面的时候在头部携带一个参数作为用户被踢出去,也可以针对自己的项目情况去调整。觉得有用的大家给点个关注点个赞吧。哈哈哈哈。

 

 

 

     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

树很忙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值