美文网首页SpringBoot极简教程 · Spring Boot SpringbootSpringHome
SpringBoot+Shiro定义拦截器管理在线用户

SpringBoot+Shiro定义拦截器管理在线用户

作者: yellow_han | 来源:发表于2018-08-15 08:56 被阅读202次

自定义访问控制拦截器:AccessControlFilter,集成这个接口后要实现下面这三个方法。

abstract boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception;  

boolean onAccessDenied(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception; 

abstract boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception;  

在ShiroConfig

 /**
     * 限制同一账号登录同时登录人数控制
     * @return
     */
    //@Bean
    public GunsUserFilter gunsUserFilter(){
        GunsUserFilter gunsUserFilter = new GunsUserFilter();

        //使用cacheManager获取相应的cache来缓存用户登录的会话;用于保存用户—会话之间的关系的;
        //这里我们还是用之前shiro使用的redisManager()实现的cacheManager()缓存管理
        //也可以重新另写一个,重新配置缓存时间之类的自定义缓存属性
        gunsUserFilter.setCacheManager(getCacheShiroManager(new EhCacheManagerFactoryBean()));
        gunsUserFilter.setKickoutAfter(false);
        //同一个用户最大的会话数,默认1;比如2的意思是同一个用户允许最多同时两个人登录;
        gunsUserFilter.setMaxSession(1);
        //被踢出后重定向到的地址;
        gunsUserFilter.setKickoutUrl("/login");
        return gunsUserFilter;
    }

自定义GunsUserFilter

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package com.stylefeng.guns.core.intercept;

import com.stylefeng.guns.config.web.ShiroConfig;
import com.stylefeng.guns.core.cache.CacheKit;
import com.stylefeng.guns.core.shiro.ShiroKit;
import com.stylefeng.guns.core.shiro.ShiroUser;

import com.stylefeng.guns.core.common.constant.cache.Cache;
import com.stylefeng.guns.core.util.SpringContextHolder;
import com.stylefeng.guns.core.util.ToolUtil;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.SessionException;
import org.apache.shiro.session.mgt.DefaultSessionKey;
import org.apache.shiro.session.mgt.SessionContext;
import org.apache.shiro.session.mgt.SessionKey;
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.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.util.WebUtils;


import javax.annotation.Resource;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.Serializable;
import java.util.Deque;
import java.util.LinkedList;

/**
 * Filter that allows access to resources if the accessor is a known user, which is defined as
 * having a known principal.  This means that any user who is authenticated or remembered via a
 * 'remember me' feature will be allowed access from this filter.
 * <p/>
 * If the accessor is not a known user, then they will be redirected to the {@link #setLoginUrl(String) loginUrl}</p>
 *
 * @since 0.9
 */
public class GunsUserFilter extends AccessControlFilter {

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

    private org.apache.shiro.cache.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;
    }

    //设置Cache的key的前缀
    public void setCacheManager(CacheManager cacheManager) {
        this.cache = cacheManager.getCache("shiro_redis_cache");
    }




    /**
     * Returns <code>true</code> if the request is a
     * {@link #isLoginRequest(javax.servlet.ServletRequest, javax.servlet.ServletResponse) loginRequest} or
     * if the current {@link #getSubject(javax.servlet.ServletRequest, javax.servlet.ServletResponse) subject}
     * is not <code>null</code>, <code>false</code> otherwise.
     *
     * @return <code>true</code> if the request is a
     * {@link #isLoginRequest(javax.servlet.ServletRequest, javax.servlet.ServletResponse) loginRequest} or
     * if the current {@link #getSubject(javax.servlet.ServletRequest, javax.servlet.ServletResponse) subject}
     * is not <code>null</code>, <code>false</code> otherwise.
     */
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {

        return false;
        /*if (isLoginRequest(request, response)) {
            return true;
        } else {
            Subject subject = getSubject(request, response);
            // If principal is not null, then the user is known and should be allowed access.
            return subject.getPrincipal() != null;
        }*/
    }

    /**
     * This default implementation simply calls
     * {@link #saveRequestAndRedirectToLogin(javax.servlet.ServletRequest, javax.servlet.ServletResponse) saveRequestAndRedirectToLogin}
     * and then immediately returns <code>false</code>, thereby preventing the chain from continuing so the redirect may
     * execute.
     */
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
        HttpServletResponse httpServletResponse = WebUtils.toHttp(response);


        /**
         * 如果是ajax请求则不进行跳转
         */
        if (httpServletRequest.getHeader("x-requested-with") != null
                && httpServletRequest.getHeader("x-requested-with").equalsIgnoreCase("XMLHttpRequest")) {
            httpServletResponse.setHeader("sessionstatus", "timeout");
            return false;
        } else {

            /*-----------------单机用户唯一登陆------------------------------*/
            /**
             *
             * @date 创建时间:2018年3月27日
             * 1.读取当前登录用户名,获取在缓存中的sessionId队列
             * 2.判断队列的长度,大于最大登录限制的时候,按踢出规则
             *  将之前的sessionId中的session域中存入kickout:true,并更新队列缓存
             * 3.判断当前登录的session域中的kickout如果为true,
             * 想将其做退出登录处理,然后再重定向到踢出登录提示页面
             */
            Subject subject = getSubject(request, response);
            if(!subject.isAuthenticated() && !subject.isRemembered()) {
                //如果没有登录,直接进行之后的流程
                return true;
            }

            Session session = subject.getSession();
            ShiroUser user = ShiroKit.getUser();
            String username = user.getAccount();
            Serializable sessionId = session.getId();

            //读取缓存   没有就存入
            Deque<Serializable> deque = cache.get(username);
            //如果此用户没有session队列,也就是还没有登录过,缓存中没有
            //就new一个空队列,不然deque对象为空,会报空指针
            if(deque==null){
                deque = new LinkedList<Serializable>();
            }


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

                //将sessionId存入队列
                deque.push(sessionId);
                //将用户的sessionId队列缓存
                cache.put(username, deque);
            }

            //如果队列里的sessionId数超出最大会话数,开始踢人
            while(deque.size() > maxSession) {
                Serializable kickoutSessionId = null;
                if(kickoutAfter) { //如果踢出后者
                    kickoutSessionId = deque.removeFirst();
                    //踢出后再更新下缓存队列
                    cache.put(username, deque);
                } else { //否则踢出前者
                    kickoutSessionId = deque.removeLast();
                    //踢出后再更新下缓存队列
                    cache.put(username, deque);
                }



                try {
                    //获取被踢出的sessionId的session对象
                    Session kickoutSession = SecurityUtils.getSecurityManager().getSession(new DefaultSessionKey(kickoutSessionId));

                    if(kickoutSession != null) {
                        //设置会话的kickout属性表示踢出了

                        kickoutSession.setAttribute("kickout", true);
                    }
                } catch (Exception e) {//ignore exception
                }
            }



            //如果被踢出了,直接退出,重定向到踢出后的地址
            if ((Boolean)session.getAttribute("kickout")!=null&&(Boolean)session.getAttribute("kickout") == true) {
                System.out.println("被踢出");
                //会话被踢出了
                try {
                    //退出登录
                    subject.logout();
                } catch (Exception e) { //ignore
                }
                saveRequest(request);
                //重定向
                WebUtils.issueRedirect(request, response, kickoutUrl);
                return false;
            }
            return true;

            /*--------------------------------------------------------*/

            /**
             * 第一次点击页面
             */
            /*String referer = httpServletRequest.getHeader("Referer");
            if (referer == null) {
                System.out.println("进来7");
                redirectToLogin(request, response);

                return false;
            } else {

                System.out.println("进来8");


                 //从别的页面跳转过来的

                if (ShiroKit.getSession().getAttribute("sessionFlag") == null) {
                    System.out.println("进来9");

                    httpServletRequest.setAttribute("tips", "session超时");
                    httpServletRequest.getRequestDispatcher("/login.html").forward(request, response);
                    return false;
                } else {
                    System.out.println("进来10");

                    redirectToLogin(request, response);
                    return false;
                }
            }*/
        }
    }
}

相关文章

  • SpringBoot+Shiro定义拦截器管理在线用户

    自定义访问控制拦截器:AccessControlFilter,集成这个接口后要实现下面这三个方法。 在ShiroC...

  • SpringBoot+Shiro学习之自定义拦截器管理在线用户(

    应用场景 我们经常会有用到,当A 用户在北京登录 ,然后A用户在天津再登录 ,要踢出北京登录的状态。如果用户在北京...

  • SpringMVC学习笔记 | SpringMVC拦截器详解:自

    自定义拦截器 SpringMVC可以使用拦截器对请求进行拦截处理,用户可以自定义拦截器来实现特定的功能,自定义拦截...

  • Spring MVC拦截器

    自定义拦截器 Spring MVC可以使用拦截器对请求进行拦截处理,用户可以自定义拦截器来实现特定的功能,自定义拦...

  • SpringMVC(十二)使用拦截器

    一、自定义拦截器 Spring MVC也可以使用拦截器对请求进行拦截处理,用户可以自定义拦截器来实现特定的功能,自...

  • springMVC自定义拦截器

    Spring MVC使用拦截器对请求进行拦截处理,用户可以自定义拦截器来实现特定的功能,自定义的拦截器必须实现Ha...

  • SpringMVC拦截器

    SpingMvc拦截器的配置和应用 SpringMvc用户在使用自定义拦截器必须实现HandlerIntercep...

  • SpringMVC之拦截器

    十、拦截器 目录:简述、自定义拦截器、认证用户 1.简述 SpringMVC的处理器拦截器类似于Servlet开发...

  • Spring15-拦截器

    定义拦截器 定义拦截器需要实现HandlerInterceptor 配置拦截器 注意:spring mvc的拦截器...

  • TDSQL-数据库管理

    数据库管理用户管理 在线DDL 在线SQL 在线SQL日志 数据库参数 防火墙 ...

网友评论

    本文标题:SpringBoot+Shiro定义拦截器管理在线用户

    本文链接:https://www.haomeiwen.com/subject/knribftx.html