SpringBoot + Redis + Shiro 實現權限管理


文章主要是針對shiro進行權限配置,只針對角色進行了權限過濾。

GitHub:https://github.com/stevencxb/blog

數據庫腳本

-- ----------------------------
-- Table structure for sys_menu
-- ----------------------------
DROP TABLE IF EXISTS `sys_menu`;
CREATE TABLE `sys_menu`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `pid` bigint(20) NULL DEFAULT 0 COMMENT '父id',
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '名稱',
  `type` int(11) NULL DEFAULT NULL COMMENT '類型(1:模塊;2:菜單)',
  `url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '路徑',
  `sort` int(11) NULL DEFAULT NULL COMMENT '排序',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 20 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '系統菜單' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '角色id',
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '名稱',
  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '描述',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '角色' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for sys_role_menu
-- ----------------------------
DROP TABLE IF EXISTS `sys_role_menu`;
CREATE TABLE `sys_role_menu`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `role_id` bigint(20) NOT NULL,
  `menu_id` bigint(20) NOT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE INDEX `role_id`(`role_id`, `menu_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 171 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '角色-菜單' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用戶id',
  `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '姓名',
  `user_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用戶名',
  `nick_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '昵稱',
  `birthday` date NULL DEFAULT NULL COMMENT '出生日期',
  `phone` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '手機號',
  `email` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '郵箱',
  `pwd` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '密碼',
  `sex` tinyint(2) NULL DEFAULT NULL COMMENT '性別(1:男;2:女)',
  `intro` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '個人簡介',
  `head_img` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '頭像地址',
  `status` tinyint(2) NOT NULL DEFAULT 1 COMMENT '狀態(1:正常:2:凍結)',
  `last_login_time` datetime(0) NULL DEFAULT NULL COMMENT '最后登錄時間',
  `create_time` datetime(0) NOT NULL COMMENT '創建時間',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE INDEX `username`(`user_name`) USING BTREE,
  UNIQUE INDEX `phone`(`phone`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用戶表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for sys_user_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `user_id` bigint(20) NOT NULL COMMENT '用戶id',
  `role_id` bigint(20) NULL DEFAULT NULL COMMENT '角色id',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 12 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用戶-角色' ROW_FORMAT = Dynamic;

maven配置

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.crazycake</groupId>
            <artifactId>shiro-redis</artifactId>
            <version>2.4.2.1-RELEASE</version>
        </dependency>
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>

 

ShiroConfig

package com.cxb.blog.config;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.cxb.blog.service.ISysMenuService;
import com.cxb.blog.utils.ShiroUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @Title: ShiroConfig
 * @Description: ShiroConfig
 * @Author <a href="mailto:chenxb1993@126.com">陳曉博</a>
 * @Date 2019-05-14 14:01
 * @Version V1.0
 */
@Slf4j
@Configuration
public class ShiroConfig {

    @Value("${spring.redis.host}")
    private String host;

    @Value("${spring.redis.password}")
    private String password;

    @Value("${spring.redis.port}")
    private String port;

    @Value("${spring.redis.timeout}")
    private String timeout;

    @Value("${spring.redis.database}")
    private String database;

    @Autowired
    private ISysMenuService sysMenuService;

    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 必須設置 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // setLoginUrl 如果不設置值,默認會自動尋找Web工程根目錄下的"/login.jsp"頁面 或 "/login" 映射
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 設置無權限時跳轉的 url;
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");
        Map<String, Filter> filtersMap = new LinkedHashMap<>();
        filtersMap.put("roleOrFilter",new RolesOrFilterAuthorizationFilter());//可以配置RoleOrFilter的Bean

        filtersMap.put("kickout",kickoutSessionControlFilter());

        shiroFilterFactoryBean.setFilters(filtersMap);
        // 設置攔截器
        Map<String, String> filterChainDefinitionMap = ShiroUtil.getFilterChainDefinitionMap(sysMenuService);

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        log.info("Shiro攔截器工廠類注入成功");
        return shiroFilterFactoryBean;
    }
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 設置realm.
        securityManager.setRealm(shiroRealm());
        // 自定義緩存實現 使用redis
        securityManager.setCacheManager(cacheManager());
        // 自定義session管理 使用redis
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }

    /**
     * 身份認證realm; (這個需要自己寫,賬號密碼校驗;權限等)
     *
     * @return
     */
    @Bean
    public RealmConfig shiroRealm() {
        RealmConfig realmConfig = new RealmConfig();
        return realmConfig;
    }

    /**
     * cacheManager 緩存 redis實現
     * 使用的是shiro-redis開源插件
     *
     * @return
     */
    public RedisCacheManager cacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        return redisCacheManager;
    }

    /**
     * 配置shiro redisManager
     * 使用的是shiro-redis開源插件
     *
     * @return
     */
    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        redisManager.setHost(host);
        redisManager.setPort(Integer.parseInt(port));
        redisManager.setExpire(1800);// 配置緩存過期時間
        redisManager.setTimeout(Integer.parseInt(timeout));
        redisManager.setPassword(password);
        return redisManager;
    }

    /**
     * Session Manager
     * 使用的是shiro-redis開源插件
     */
    @Bean
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionDAO(redisSessionDAO());
        return sessionManager;
    }

    /**
     * RedisSessionDAO shiro sessionDao層的實現 通過redis
     * 使用的是shiro-redis開源插件
     */
    @Bean
    public RedisSessionDAO redisSessionDAO() {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager());
        return redisSessionDAO;
    }

    /**
     * 限制同一賬號登錄同時登錄人數控制
     *
     * @return
     */
    @Bean
    public KickoutSessionControlFilter kickoutSessionControlFilter() {
        KickoutSessionControlFilter kickoutSessionControlFilter = new KickoutSessionControlFilter();
        kickoutSessionControlFilter.setCacheManager(cacheManager());
        kickoutSessionControlFilter.setSessionManager(sessionManager());
        kickoutSessionControlFilter.setKickoutAfter(false);
        kickoutSessionControlFilter.setMaxSession(1);
        kickoutSessionControlFilter.setKickoutUrl("/kickout");
        return kickoutSessionControlFilter;
    }


    /***
     * 授權所用配置
     *
     * @return
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }

    /***
     * 使授權注解起作用不如不想配置可以在pom文件中加入
     * <dependency>
     *<groupId>org.springframework.boot</groupId>
     *<artifactId>spring-boot-starter-aop</artifactId>
     *</dependency>
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * Shiro生命周期處理器
     *
     */
    @Bean
    public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * ShiroDialect,為了在thymeleaf里使用shiro的標簽的bean
     *
     * @return
     */
    @Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }
}

RealmConfig

package com.cxb.blog.config;

import com.cxb.blog.dao.SysMenuDao;
import com.cxb.blog.domain.DO.SysMenuDO;
import com.cxb.blog.domain.DO.SysUserDO;
import com.cxb.blog.enums.SysUserStatus;
import com.cxb.blog.exception.SysUserStatusException;
import com.cxb.blog.service.ISysUserRoleService;
import com.cxb.blog.service.ISysUserService;
import com.cxb.blog.utils.RegexUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @Title: RealmConfig
 * @Description: shiro realm
 * @Author <a href="mailto:chenxb1993@126.com">陳曉博</a>
 * @Date 2019-05-14 14:01
 * @Version V1.0
 */
@Slf4j
public class RealmConfig extends AuthorizingRealm {
    //如果項目中用到了事物,@Autowired注解會使事物失效,可以自己用get方法獲取值

    @Autowired
    private ISysUserService sysUserService;

    @Autowired
    private ISysUserRoleService sysUserRoleService;

    @Autowired
    private SysMenuDao sysMenuDao;

    /**
     * 認證信息.(身份驗證) : Authentication 是用來驗證用戶身份
     *
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authctoken) throws AuthenticationException {
        log.info("---------------- 執行 Shiro 憑證認證 ----------------------");
        UsernamePasswordToken token = (UsernamePasswordToken) authctoken;
        String name = token.getUsername();
        String password = String.valueOf(token.getPassword());

        // 從數據庫獲取對應用戶名密碼的用戶
        SysUserDO userDO;
        if (RegexUtil.checkMobile(name)) {
            userDO = sysUserService.getByPhone(name);
        }else {
            userDO = sysUserService.getByUserName(name);
        }

        if (userDO == null) {
            throw new UnknownAccountException("賬號或密碼不正確");
        }

        // 密碼錯誤
        if (!password.equals(userDO.getPwd())) {
            throw new IncorrectCredentialsException("賬號或密碼不正確");
        }

        if (userDO.getStatus() != SysUserStatus.NORMAL.getStatus()){
            throw new SysUserStatusException("用戶狀態異常");
        }

        Subject subject = SecurityUtils.getSubject();
        subject.getSession().setAttribute("userName", name);
        subject.getSession().setAttribute("id", userDO.getId());
        log.info("---------------- Shiro 憑證認證成功 ----------------------");
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                name, //用戶
                password, //密碼
                getName()  //realm name
        );
        return authenticationInfo;
    }

    /**
     * 授權
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        log.info("---------------- 執行 Shiro 權限獲取 ---------------------");
        String principal = (String)principals.getPrimaryPrincipal();

        SysUserDO  sysUserDO = sysUserService.getByUserName(principal);

        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        Set<String> roles = sysUserRoleService.getRoleIdByUserId(sysUserDO.getId());
        authorizationInfo.addRoles(roles);
//
//        List<SysMenuDO> list =  sysMenuDao.getSysMenuByUserId(sysUserDO.getId());
//        Set<String> set=new HashSet<>();
//        for (SysMenuDO sysMenuDO : list){
//            set.add(sysMenuDO.getUrl()+"/**");
//        }
//        authorizationInfo.addStringPermissions(set);
        log.info("---------------- Shiro 權限獲取成功 ----------------------");
        return authorizationInfo;
    }
}
RolesOrFilterAuthorizationFilter
package com.cxb.blog.config;

import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.CollectionUtils;
import org.apache.shiro.web.filter.authz.RolesAuthorizationFilter;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.util.Set;

/**
 * @Title: RolesOrFilterAuthorizationFilter
 * @Description: 角色過濾
 * @Author <a href="mailto:chenxb1993@126.com">陳曉博</a>
 * @Date 2019-05-23 15:01
 * @Version V1.0
 */
public class RolesOrFilterAuthorizationFilter extends RolesAuthorizationFilter{
    public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {
        Subject subject = this.getSubject(request, response);
        String[] rolesArray = (String[])((String[])mappedValue);
        if (rolesArray != null && rolesArray.length != 0) {
            Set<String> roles = CollectionUtils.asSet(rolesArray);
            for (String role : roles){
                if (subject.hasRole(role)){
                    return true;
                }
            }
            return false;
        } else {
            return true;
        }
    }
}
5.KickoutSessionControlFilter
package com.cxb.blog.config;

import com.alibaba.fastjson.JSON;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
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 javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Serializable;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;

/**
 * @Title: KickoutSessionControlFilter
 * @Description: 被踢出控制
 * @Author <a href="mailto:chenxb1993@126.com">陳曉博</a>
 * @Date 2019-05-14 14:03
 * @Version V1.0
 */
public class KickoutSessionControlFilter extends AccessControlFilter {

    private String kickoutUrl; //踢出后到的地址
    private boolean kickoutAfter = false; //踢出之前登錄的/之后登錄的用戶 默認踢出之前登錄的用戶
    private int maxSession = 1; //同一個帳號最大會話數 默認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;
    }
    //設置Cache的key的前綴
    public void setCacheManager(CacheManager cacheManager) {
        this.cache = cacheManager.getCache("shiro_redis_cache");
    }

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


        Session session = subject.getSession();
        String userName = (String) subject.getPrincipal();
        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 = sessionManager.getSession(new DefaultSessionKey(kickoutSessionId));
                if(kickoutSession != null) {
                    //設置會話的kickout屬性表示踢出了
                    kickoutSession.setAttribute("kickout", true);
                }
            } catch (Exception e) {//ignore exception
            }
        }

        //如果被踢出了,直接退出,重定向到踢出后的地址
        if (session.getAttribute("kickout") != null) {
            //會話被踢出了
            try {
                //退出登錄
                subject.logout();
            } catch (Exception e) { //ignore
            }
            saveRequest(request);

            Map<String, String> resultMap = new HashMap<String, String>();
            //判斷是不是Ajax請求
            if ("XMLHttpRequest".equalsIgnoreCase(((HttpServletRequest) request).getHeader("X-Requested-With"))) {
                resultMap.put("user_status", "300");
                resultMap.put("message", "您已經在其他地方登錄,請重新登錄!");
                //輸出json串
                out(response, resultMap);
            }else{
                //重定向
                WebUtils.issueRedirect(request, response, kickoutUrl);
            }
            return false;
        }
        return true;
    }
    private void out(ServletResponse hresponse, Map<String, String> resultMap)
            throws IOException {
        try {
            hresponse.setCharacterEncoding("UTF-8");
            PrintWriter out = hresponse.getWriter();
            out.println(JSON.toJSONString(resultMap));
            out.flush();
            out.close();
        } catch (Exception e) {
            System.err.println("KickoutSessionFilter.class 輸出JSON異常,可以忽略。");
        }
    }

}
ShiroFilerChainManager
package com.cxb.blog.config;

import com.cxb.blog.service.ISysMenuService;
import com.cxb.blog.utils.ShiroUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

/**
 * @Title: ShiroFilerChainManager
 * @Description: ShiroFilerChainManager
 * @Author <a href="mailto:chenxb1993@126.com">陳曉博</a>
 * @Date 2019-05-23 15:30
 * @Version V1.0
 */
@Slf4j
@Component("shiroFilerChainManager")
@Transactional(readOnly=true)
public class ShiroFilerChainManager {

    @Autowired
    private ShiroFilterFactoryBean shiroFilterFactoryBean;

    @Autowired
    private ISysMenuService sysMenuService;

    /**
     * 重載過濾鏈
     */
    public void reloadFilterChains() {
        AbstractShiroFilter shiroFilter = null;
        try {
            shiroFilter = (AbstractShiroFilter) shiroFilterFactoryBean.getObject();
        } catch (Exception e) {
            log.error("getShiroFilter from shiroFilterFactoryBean error!", e);
            throw new RuntimeException("get ShiroFilter from shiroFilterFactoryBean error!");
        }

        PathMatchingFilterChainResolver filterChainResolver = (PathMatchingFilterChainResolver) shiroFilter.getFilterChainResolver();
        DefaultFilterChainManager manager = (DefaultFilterChainManager) filterChainResolver.getFilterChainManager();

        // 清空老的權限控制
        manager.getFilterChains().clear();

        shiroFilterFactoryBean.getFilterChainDefinitionMap().clear();

        shiroFilterFactoryBean.setFilterChainDefinitionMap(ShiroUtil.getFilterChainDefinitionMap(sysMenuService));
        System.out.println("Shiro攔截器工廠類注入成功");
    }

}
ShiroUtil
package com.cxb.blog.utils;

import com.cxb.blog.domain.VO.SysMenuFilterVO;
import com.cxb.blog.service.ISysMenuService;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * @Title: ShiroUtil
 * @Description: shiro工具類
 * @Author <a href="mailto:chenxb1993@126.com">陳曉博</a>
 * @Date 2019-05-23 15:37
 * @Version V1.0
 */
public class ShiroUtil {

    public static Map<String, String> getFilterChainDefinitionMap(ISysMenuService sysMenuService){
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        List<SysMenuFilterVO> list = sysMenuService.selectAll();
//        //游客,開發權限
//        filterChainDefinitionMap.put("/guest/**", "anon");
//        //用戶,需要角色權限 “user”
//        filterChainDefinitionMap.put("/user/**", "roles[user]");
//        //管理員,需要角色權限 “admin”
//        filterChainDefinitionMap.put("/admin/**", "roles[admin]");
//        //開放登陸接口
//        filterChainDefinitionMap.put("/login", "anon");
//        //其余接口一律攔截
        //主要這行代碼必須放在所有權限設置的最后,不然會導致所有 url 都被攔截
        for(SysMenuFilterVO filter : list){
            filterChainDefinitionMap.put(filter.getUrl()+"/**","roleOrFilter["+(filter.getRoles()==null?"":filter.getRoles())+"]");
        }
        filterChainDefinitionMap.put("/**", "kickout,anon");
        return filterChainDefinitionMap;
    }

}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM