shiro 自定义拦截器继承AccessControllerFilter后无法获取用户登录信息

2 篇文章 0 订阅
2 篇文章 0 订阅

shiro自定义拦截器继承AccessControllerFilter,在ShiroConfig中加入到拦截器链中。

package com.lwp.website.shiro;

import com.alibaba.fastjson.JSON;
import com.lwp.website.entity.Vo.UserVo;
import com.lwp.website.res.ResourceManage;
import com.lwp.website.utils.RedisUtil;
import org.apache.shiro.SecurityUtils;
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.filter.authc.UserFilter;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.beans.factory.annotation.Autowired;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.util.LinkedList;

/**
 * Created with IntelliJ IDEA.
 *
 * @Auther: liweipeng
 * @Date: 2021/02/01/10:59
 * @Description:
 */
public class KickoutSessionControlFilter extends AccessControlFilter{

    //踢出后的地址
    private String kickoutUrl;
    //踢出之前登陆的/之后登陆的用户 默认踢出之前登陆的用户
    private boolean kickoutAfter = false;
    //同一个账号最大会话数 默认 1
    private int maxSession = 1;

    public static final String DEFAULT_KICKOUT_CACHE_KEY_PREFIX = "shiro:cache:kickout:";

    private String keyPrefix = DEFAULT_KICKOUT_CACHE_KEY_PREFIX;

    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 setKeyPrefix(String keyPrefix){
        this.keyPrefix = keyPrefix;
    }
    public String getKeyPrefix(){
        return this.keyPrefix;
    }

    private String getRedisKickoutKey(String username){
        return this.keyPrefix+username;
    }

    /**
     * 是否允许访问 返回true表示允许访问
     * @param servletRequest
     * @param servletResponse
     * @param o
     * @return
     * @throws Exception
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o){
        return false;
    }

    /**
     * 表示访问拒绝时 是否自己处理 如果返回true表示自己不处理且继续拦截器链执行,返回false表示自己已经处理(比如重定向到另一个页面)
     * @param servletRequest
     * @param servletResponse
     * @return
     * @throws Exception
     */
    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        Subject subject = this.getSubject(servletRequest, servletResponse);
        Boolean b = subject.isAuthenticated();
        if(!b){
            //没有登录,直接进行之后的流程 不进行拦截
            return true;
        }
        //如果有登录,判断是否访问静态资源 如果允许访问静态资源则返回true
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        String path = httpServletRequest.getServletPath();
        if(isStaticFile(path)){
            return true;
        }
        RedisUtil redisUtil = (RedisUtil) ResourceManage.getBean("redisUtil");
        SessionManager sessionManager = (SessionManager) ResourceManage.getBean("sessionManager");
        Session session = subject.getSession();
        //这里获取的User是实体 因为我在 自定义ShiroRealm中的doGetAuthenticationInfo方法中
        //new SimpleAuthenticationInfo(user, password, getName()); 传的是 User实体 所以这里拿到的也是实体,如果传的是userName 这里拿到的就是userName
        String username = ((UserVo)subject.getPrincipal()).getUsername();
        Serializable sessionId = session.getId();
        //初始化用户的队列到缓存
        LinkedList<Serializable> deque = null;
        String name = getRedisKickoutKey(username);
        Object object = redisUtil.get(name);
        if(object == null){
            deque = new LinkedList<Serializable>();
        }else {
            deque = JSON.parseObject(JSON.toJSONString(object),LinkedList.class);
        }

        //如果队列中没有此sessionid,且用户没有被提出 放入队列
        if(!deque.contains(sessionId) && session.getAttribute("kickout") == null){
            deque.push(sessionId);
        }

        //如果队列里的session id数量超出最大会话数,开始踢人
        while (deque.size() > maxSession){
            Serializable kickoutSessionId = null;
            if(kickoutAfter){
                kickoutSessionId = deque.removeFirst();
            }else {
                kickoutSessionId = deque.removeLast();
            }
            try{
                Session kickoutSession = sessionManager.getSession(new DefaultSessionKey(kickoutSessionId));
                if(null != kickoutSession){
                    kickoutSession.setAttribute("kickout",true);
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        redisUtil.set(getRedisKickoutKey(username),deque);
        if(session.getAttribute("kickout") != null){
            try {
                subject.logout();
            }catch (Exception e){
                e.printStackTrace();
            }
            WebUtils.issueRedirect(servletRequest,servletResponse,kickoutUrl);
            return false;
        }

        return true;
    }

    private boolean isStaticFile(String path){
        return false;
    }

}

 ShiroConfig中添加到链。

/**
     * 并发登录控制
     * @return
     */
    @Bean
    public KickoutSessionControlFilter kickoutSessionControlFilter(){
        KickoutSessionControlFilter kickoutSessionControlFilter = new KickoutSessionControlFilter();
        //用于根据会话ID,获取会话进行踢出操作的;
        //是否踢出后来登录的,默认是false;即后者登录的用户踢出前者登录的用户;
        kickoutSessionControlFilter.setKickoutAfter(false);
        //同一个用户最大的会话数,默认1;比如2的意思是同一个用户允许最多同时两个人登录;
        kickoutSessionControlFilter.setMaxSession(1);
        //被踢出后重定向到的地址
        kickoutSessionControlFilter.setKickoutUrl("/a/login");
        return kickoutSessionControlFilter;
    }
    @Bean(name = "shiroFilterFactoryBean")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier(value = "securityManager") SecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //设置SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        //访问的是后端url的地址,这里要写base 服务的公用登录接口。 使用toLogin进行拦截,跳转到/a/login //不使用toLogin,整体调转在html页面进行
        shiroFilterFactoryBean.setLoginUrl("/a/login");
        // 登录成功后要跳转的链接;现在应该没用
        //shiroFilterFactoryBean.setSuccessUrl("/index");
        // 未授权界面;可以写个公用的403页面
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");
        //自定义拦截器限制并发人数 //限制同一帐号同时在线的个数
        LinkedHashMap<String, Filter> filtersMap = new LinkedHashMap<>();
        filtersMap.put("custom", customUserFilter());
        filtersMap.put("kickout",kickoutSessionControlFilter());
        //filtersMap.put("toLogin",loginFilter());

        shiroFilterFactoryBean.setFilters(filtersMap);

        //拦截器
        Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        //配置不会被拦截的链接 顺序判断
        filterChainDefinitionMap.put("/static/**","anon");
        filterChainDefinitionMap.put("/login","anon");
        //对 /a/login 不进行拦截
        filterChainDefinitionMap.put("/a/login","anon");
        //对toLogin进行拦截
        //filterChainDefinitionMap.put("/toLogin","toLogin");
        //logout是shiro提供的过滤器
        filterChainDefinitionMap.put("/logout", "logout");
        filterChainDefinitionMap.put("/index","authc");
        //filterChainDefinitionMap.put("/a/**","kickout,authc");
        filterChainDefinitionMap.put("/a/**","custom,kickout");
        filterChainDefinitionMap.put("/**","anon");
        //filterChainDefinitionMap.put("/","kickout,authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        LOGGER.info("Shiro拦截器工厂类注入成功");
        return shiroFilterFactoryBean;

    }

在自定义KickoutSessionControlFilter 中使用

Subject subject = this.getSubject(servletRequest, servletResponse);

或者

SecurityUtils.getSubject();获取到的subject都是空的。

同时经过查找ServletRequest里面的 cookie等值也是为空的。

后检查发现在实例化自定义拦截器的时候,在方法上面又加了一个@Bean导致

定义拦截器的时候不需要加@Bean;

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值