shiro动态更新权限失败

一、概述

    在之前的spring集成shiro权限控制一文中,实现了动态更新的权限的方法,即对权限表做修改后,需要更新到shiro的filterchain。后来测试的时候,发现两个问题:
1、更改权限后,并没有动态更新到shiro中去,取消权限的用户仍然可以正常访问。
2、权限的写法authec,roles[admin,guest]是逻辑AND的关系,必须用户同时满足两种角色才有权限,而我们现实中是满足其中一个就可以了,也就是逻辑OR的关系。

二、动态更新

先展示正确的更新代码:

package com.gameloft9.demo.security;

import com.gameloft9.demo.service.api.system.SysAccessPermissionService;
import com.gameloft9.demo.dataaccess.model.system.SysAccessPermissionTest;
import lombok.extern.slf4j.Slf4j;
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.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.support.WebApplicationContextUtils;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * shiro 动态权限配置相关服务
 *
 * @author gameloft9
 */
@Service
@Slf4j
public class ShiroConfigService {

    @Autowired
    SysAccessPermissionService sysAccessPermissionServiceImpl;

    /**
     * 从数据库加载权限列表
     */
    public Map<String, String> loadFilterChainDefinitions() {
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        List<SysAccessPermissionTest> list = sysAccessPermissionServiceImpl.getAll();
        for (SysAccessPermissionTest item : list) {
            filterChainDefinitionMap.put(item.getUrl(), item.getRoles());
        }
        return filterChainDefinitionMap;
    }

    /**
     * 更新权限,解决需要重启tomcat才能生效权限的问题
     */
    public void updatePermission() {
        try {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            ServletContext servletContext = request.getSession().getServletContext();
            AbstractShiroFilter shiroFilter = (AbstractShiroFilter) WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext).getBean("myShiroFilter");

            synchronized (shiroFilter) {
                // 获取过滤管理器
                PathMatchingFilterChainResolver filterChainResolver = (PathMatchingFilterChainResolver) shiroFilter.getFilterChainResolver();
                DefaultFilterChainManager manager = (DefaultFilterChainManager) filterChainResolver.getFilterChainManager();
                // 清空初始权限配置
                manager.getFilterChains().clear();
                // 重新获取资源
                Map<String, String> chains = loadFilterChainDefinitions();

                for (Map.Entry<String, String> entry : chains.entrySet()) {
                    String url = entry.getKey();
                    String chainDefinition = entry.getValue().trim().replace(" ", "");

                    manager.createChain(url, chainDefinition);
                }
                log.info("更新权限成功!!");
            }
        } catch (Exception e) {
            log.error("更新权限到shiro异常", e);
        }
    }
}

    原来的错误的代码里面主要是ShiroFactoryBean的获取有问题,之前是通过@Autowired的方式获取的,这种方式是获取不到Spring MVC 上下文环境里的bean的。Spring MVC和spring上下文不是同一个,spring是用listener初始化的上下文,称为root上下文,Spring MVC是通过servlet初始化的上下文,称为servlet上下文,servlet里的能获取root里的,反之就获取不到。servlet应该是root的子上下文,而我获取bean的地方是处于root上下文的。

    正确的获取方式是通过SelvetContext去拿到Spring MVC的上下文,然后再获取相应的bean。如下所示:

 HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
 ServletContext servletContext = request.getSession().getServletContext();
 AbstractShiroFilter shiroFilter = (AbstractShiroFilter) 
WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext).getBean("myShiroFilter");

然后就是从数据库读取最新的权限配置,重新加载到shiro中来。

二、逻辑OR的实现

 shiro的roles filter只能是逻辑与的关系,想要实现逻辑或,就需要自定义一个filter。我们通过继承AuthorizationFilter并重写isAccessAllowed方法来实现。逻辑很简单了,只要有一个角色满足就返回true。

package com.gameloft9.demo.security;

import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

/**
 * shiro的url定义中的roles参数,必须满足所有角色才通过(也就是and关系)
 * 这里自定义一个权限满足角色之一的Or关系的filter
 * Created by gameloft9 on 2018/7/23.
 */
public class RoleOrAuthorizationFilter extends AuthorizationFilter {

    @Override
    protected boolean isAccessAllowed(ServletRequest request,
                                      ServletResponse response, Object mappedValue) throws Exception {
        Subject subject = getSubject(request, response);
        String[] rolesArray = (String[]) mappedValue;

        if (rolesArray == null || rolesArray.length == 0) {
            return true;
        }

        for(int i=0;i<rolesArray.length;i++) {
            if(subject.hasRole(rolesArray[i])) { // 有一个满足即可
                return true;
            }
        }

        return false;
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值