1. 程式人生 > >關於shiro授權不呼叫(或者需要重啟伺服器才生效)的陷阱

關於shiro授權不呼叫(或者需要重啟伺服器才生效)的陷阱

 關於shiro的配置我不多說了,現在說一下更改許可權後要重啟伺服器的問題

web.xml

  <filter>
    <filter-name>myShiroFilter</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    <init-param>
      <param-name>targetFilterLifecycle</param-name>
      <param-value>true</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>myShiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

ShiroConfig.java

    @Bean(name = "myShiroFilter")
    public ShiroFilterFactoryBean myShiroFilter() {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //設定 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //設定登入連結(前後端分離方案中這裡不返回頁面,返回403報文,供前端跳轉到登入頁面)
        //如果不配置,預設跳到登入頁面
        shiroFilterFactoryBean.setLoginUrl("/403");
        // 登入成功後要跳轉的連結(前後端分離方案這個不需要)
        //shiroFilterFactoryBean.setSuccessUrl("/home");
        // 未授權跳轉連結;
        shiroFilterFactoryBean.setUnauthorizedUrl("/401");

        //攔截鏈配置
        Map<String, String> filterChainDefinitionMap = constructFilterChainDefinitionMap();
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        log.info("ShiroFilterFactoryBean注入成功!");
        return shiroFilterFactoryBean;
    }
  /**
     * 構造shiro過濾鏈配置
     * */
    private Map<String,String> constructFilterChainDefinitionMap(){
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        //動態載入url許可權配置,從資料庫獲取
        List<SysAccessPermissionTest> list = sysAccessPermissionServiceImpl.getAll();
        for (SysAccessPermissionTest item : list) {
            filterChainDefinitionMap.put(item.getUrl(),item.getRoles());
        }

        return filterChainDefinitionMap;
    }

ShiroConfigServer.java的更新許可權的方法

public void updateMyPermission() {
        synchronized (shiroFilterFactoryBean) {
        	
            AbstractShiroFilter shiroFilter = null;
            try {
                shiroFilter = (AbstractShiroFilter) shiroFilterFactoryBean
                        .getObject();
            } catch (Exception 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(loadFilterChainDefinitions());

            // 重新構建生成
            Map<String, String> chains = shiroFilterFactoryBean.getFilterChainDefinitionMap();
            //System.out.println("chains-------"+chains);
            for (Map.Entry<String, String> entry : chains.entrySet()) {
                String url = entry.getKey();
                String chainDefinition = entry.getValue().trim().replace(" ", "");
                manager.createChain(url, chainDefinition);
            }
            log.info("更新許可權成功!!");
        }
    }
注意:
updateMyPermission()的方法是更新許可權的操作,紅色的部分要去掉,這樣才可以不用重啟服務,ShiroConfigServer.java 完整的方法
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.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.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.support.WebApplicationContextUtils;


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


import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;


/**
 * shiro 動態許可權配置相關服務
 * @author gameloft9
 */
@Service
@Slf4j
public class ShiroConfigService {


    @Autowired
    ShiroFilterFactoryBean shiroFilterFactoryBean;
    
    


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


    /**
     * 更新許可權(已廢棄),會造成要重啟才能生效許可權
     */
    @Deprecated
    public void updateMyPermission() {
        synchronized (shiroFilterFactoryBean) {
        	
            AbstractShiroFilter shiroFilter = null;
            try {
                shiroFilter = (AbstractShiroFilter) shiroFilterFactoryBean
                        .getObject();
            } catch (Exception 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(loadFilterChainDefinitions());


            // 重新構建生成
            Map<String, String> chains = shiroFilterFactoryBean.getFilterChainDefinitionMap();
            //System.out.println("chains-------"+chains);
            for (Map.Entry<String, String> entry : chains.entrySet()) {
                String url = entry.getKey();
                String chainDefinition = entry.getValue().trim().replace(" ", "");
                manager.createChain(url, chainDefinition);
            }
            log.info("更新許可權成功!!");
        }
    }
    
    /**
     * 更新許可權,解決需要重啟tomcat才能生效許可權的問題
     */
    public void updatePermission() {
    	try {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            ServletContext servletContext = request.getSession().getServletContext();
            AbstractShiroFilter shiroFilter = (AbstractShiroFilter) WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext).getBean("myShiroFilter");          
            
            //ShiroFilterFactoryBean shiroFilter = (ShiroFilterFactoryBean) 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();
               // System.out.println("chains-------"+chains);
                for (Map.Entry<String, String> entry : chains.entrySet()) {
                    String url = entry.getKey();
                    String chainDefinition = entry.getValue().trim().replace(" ", "");
                    //System.out.println(url+"\t-----> "+chainDefinition);
                    manager.createChain(url, chainDefinition);
                }
                log.info("更新許可權成功!!");
            }
        } catch (Exception e) {
            log.error(e.getMessage());


    


	}
   }
}

再說一下不走授權doGetAuthorizationInfo方法,我的ShiroRealm完整檔案如下

package com.gameloft9.demo.security;

import com.gameloft9.demo.service.api.system.SysUserService;
import com.gameloft9.demo.dataaccess.model.system.UserTest;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationException;
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 java.util.List;

/**
 * 認證授權。
 * @author gameloft9
 */
@Slf4j
@Data
public class ShiroRealm extends AuthorizingRealm {

	/**
	 * 通過setter注入,這裡沒有通過@Autowired注入
	 * */
	private SysUserService userServiceImpl;

	/**
	 * 獲取授權資訊方法,返回使用者角色資訊
	 * */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(
			PrincipalCollection principals) {
		if (principals == null) {
			throw new AuthorizationException("PrincipalCollection method argument cannot be null.");
		}

		UserTest user = (UserTest) principals.getPrimaryPrincipal();
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		if (user != null) {//獲取使用者角色資訊
			List<String> roles = userServiceImpl.getRoleNames(user.getId());
			info.addRoles(roles);
		} else {
			SecurityUtils.getSubject().logout();
		}
		return info;
	}

	/**
	 * 重寫回調認證方法,subject.login()呼叫後回撥此方法,獲取認證資訊。
	 * 如果是與第三方使用者系統整合,可在此處進行身份認證,成功後可構造一個同登入token一致的認證資訊。
	 * 或者乾脆跳過shiro的認證,自己實現認證邏輯,成功後將使用者資訊放入session、cookie.
	 * */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken authcToken) throws AuthenticationException {
		UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
		UserTest user = userServiceImpl.getByLoginName(token.getUsername());

		if (user == null) {//使用者不存在
			throw new UnknownAccountException();
		}

		//構造一個使用者認證資訊並返回,後面會通過這個和token的pwd進行對比。
		return new SimpleAuthenticationInfo(user,user.getPassword(),user.getRealName());
	}
}

網上所說的都不適用,例如:

1、subject.hasRole(“admin”) 或 subject.isPermitted(“admin”):自己去呼叫這個是否有什麼角色或者是否有什麼許可權的時候;

2、@RequiresRoles("admin") :在方法上加註解的時候;

3、[@shiro.hasPermission name = "admin"][/@shiro.hasPermission]:在頁面上加shiro標籤的時候,即進這個頁面的時候掃描到有這個標籤的時候。

在所有的程式碼檢查完後,如果不是這方面的原因,很可能是你配置的許可權url規則有問題,請看圖


小夥伴們發現了什麼問題了嗎,下面的是執行後臺請求的路徑字首,例如sysUser/list.do,一定要在前面加/


沒錯就是url配置的問題,字首一定加/,所以路徑很重要,重要的事情再說一遍,字首一定要加/,否則你的攔截會失效!!!!