shiro從入門到放棄-初學者筆記

背景:使用SpringBoot + shiro + vue 搭建一個權限控制的系統html

問題: 遇到的一些可能感興趣的問題
一、doGetAuthorizationInfo不會被調用
二、自定義過濾器致使訪問資源認證出現混亂(過濾器失效)
三、使用shiro進行動態的權限配置
四、更改用戶角色,如何讓shiro知道並在下一次訪問能作出調整
五、先後端分離如何作到讓unauthorizedUrl和loginUrl能訪問到正確的路徑(交由vue前端控制)
六、定義一個過濾器進行受權校驗前端

問題1、doGetAuthorizationInfo不會被調用

若是是採用註解或者配置文件寫死那麼不會這麼糾結,若是是一個beginner而且沒學會跑就要飛,那麼確定在doGetAuthorizationInfo不調用吃過很大的苦頭,官網指出了有幾種途徑可讓doGetAuthorizationInfo執行,這裏須要動態權限,因此採用編碼的方式
clipboard.pngvue

角色檢查java

//get the current Subject 
Subject currentUser = SecurityUtils.getSubject();

if (currentUser.hasRole("administrator")) {
    //show a special button‏
} else {
    //don’t show the button?)‏
}

權限檢查ios

If (currentUser.isPermitted(printPermission)) {
    //do one thing (show the print button?)‏
} else {
    //don’t show the button?
}

上面兩種hasRole/isPermitted都會觸發doGetAuthorizationInfo調用,可是這種觸發方式並非很好,由於多角色有相同的資源路徑,會屢次調用doGetAuthorizationInfo影響性能
具體參考文章Shiro受權連接shiro動態權限spring

自定義過濾器致使訪問資源認證出現混亂(過濾器失效)

下面代碼重寫過濾器的時候須要特別注意要用new CustomRolesAuthorizationFilter() 這麼建立過濾器而不是使用@Bean,具體緣由很複雜,能夠看看這篇文章爲何這樣子
而後講講這篇文章沒有講到的內容,爲何會這樣? springboot使用ApplicationFilterChain管理過濾器,shiro的11個默認過濾器和自定義shiro過濾器(自定義過濾器能夠覆蓋默認過濾器,由於默認過濾器先加入Map緩存)是放在一個名爲shiroFilter的對象交由ApplicationFilterChain管理,若是給自定義過濾器加上@Bean,那麼springboot會將這些自定義的過濾器放到ApplicationFilterChain上管理。
有什麼問題? 簡單說就是shiroFilter對象執行完裏面的shiro過濾器之後(login/xx = anon驗證經過了)會繼續執行ApplicationFilterChain對象剩餘的過濾器,而自定義過濾器(重寫authc也就是FormAuthenticationFilter)恰好就是這些剩餘的過濾器中的一個,這時會這個login/xx又會調用自定義過濾器再驗證一遍,這致使受權不經過。apache

/**
ProxiedFilterChain類doFilter方法能夠了解一下
做用執行完當前shiro過濾器後回到springboot的過濾器

*/
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
        if (this.filters == null || this.filters.size() == this.index) {
            //we've reached the end of the wrapped chain, so invoke the original one:
            if (log.isTraceEnabled()) {
                log.trace("Invoking original filter chain.");
            }
            this.orig.doFilter(request, response);
        } else {
            if (log.isTraceEnabled()) {
                log.trace("Invoking wrapped filter at index [" + this.index + "]");
            }
            this.filters.get(this.index++).doFilter(request, response, this);
        }
    }
/**
其實不用重寫ShiroFilterFactoryBean,下面一個bean瞭解一下
*/
@Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager, ShiroService shiroService) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
         // 必須設置 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 設置無權限時跳轉的 url;
        shiroFilterFactoryBean.setUnauthorizedUrl("/login/unAuthor.do");

        // 重寫roles攔截規則 path = roles['role,role2']
        Map<String, Filter> extraFilter = shiroFilterFactoryBean.getFilters();
        extraFilter.put("roles", new CustomRolesAuthorizationFilter());
        extraFilter.put("authc", new CustomFormAuthenticationFilter());
        shiroFilterFactoryBean.setFilters(extraFilter);
        // 加載攔截資源
        shiroFilterFactoryBean.setFilterChainDefinitionMap(shiroService.loadFilterChainDefinitions());
        return shiroFilterFactoryBean;
    }

使用shiro進行動態的權限配置

參考:shiro動態權限axios

更改用戶角色,如何讓shiro知道並在下一次訪問能作出調整

寫個service交由spring管理,提供加載清除shiro緩存的受權內容的功能後端

public void updatePermission(ShiroFilterFactoryBean shiroFilterFactoryBean) {
        synchronized (this) {
            AbstractShiroFilter shiroFilter;
            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();
            for (Map.Entry<String, String> entry : chains.entrySet()) {
                String url = entry.getKey();
                String chainDefinition = entry.getValue().trim()
                        .replace(" ", "");
                manager.createChain(url, chainDefinition);
            }
        }
    }

先後端分離如何作到讓unauthorizedUrl和loginUrl能訪問到正確的路徑(交由vue前端控制)

最簡單作法就是指定一個能訪問後臺的路徑,好比 shiroFilterFactoryBean.setUnauthorizedUrl("/login/unAuthor.do");,而後寫一個controller方法返回狀態碼,交給前端路由axios的攔截器進行攔截處理跳轉。固然這裏對登陸處理能夠重寫FormAuthenticationFilter,在這個類中進行狀態碼的返回緩存

定義一個過濾器進行受權校驗

public class CustomRolesAuthorizationFilter extends AuthorizationFilter {

    private HttpServletRequest request;

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
        Subject subject = getSubject(request, response);
        CmsUserEntity user = (CmsUserEntity) subject.getPrincipals().getPrimaryPrincipal();

        if (null == user) {
            return true;
        }
        String[] rolesArray = (String[]) mappedValue;
        if (rolesArray == null || rolesArray.length == 0) {
            return true;
        }
        List<String> rolesList = Arrays.asList(rolesArray);
        UserBiz userBiz = SpringUtil.getBean(UserBiz.class);
        List<String> roleNames = userBiz.findRoleNameByUserId(user.getId());
        Set<String> roleSet = new HashSet<>(roleNames);

        boolean disjoint = Collections.disjoint(roleSet, rolesList);
        return !disjoint;
    }
}
相關文章
相關標籤/搜索