由於Shiro filterChainDefinitions中 roles默認是and, admin= user,roles[system,general]
比如:roles[system,general] ,表示同時需要“system”和“general” 2個角色(權限)才通過認證,缺一不可。
但是在實際業務中,一個端口往往同時對幾個角色開放。比如管理員賬號擁有訪問所有端口的權限。那么此時的and顯然是不合時宜的,與之替代的是or
一、重新實現AuthorizationFilter類
import org.apache.shiro.subject.Subject; import org.apache.shiro.web.filter.authz.AuthorizationFilter; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; /** * @Auther: lanhaifeng * @Date: 2019/4/18 0018 17:40 * @Description:支持多角色驗證 * 由於Shiro filterChainDefinitions中 roles默認是and, * = user,roles[system,general] * 比如:roles[system,general] ,表示同時需要“system”和“general” 2個角色才通過認證 * 但是在實際業務中,一個端口的權限往往對多個角色開放(比如管理員可以使用一切權利) */ public class MyRolesAuthorizationFilter 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++) { //若當前用戶是rolesArray中的任何一個,則有權限訪問 if (subject.hasRole(rolesArray[i])) { return true; } } return false; } }
二、在ShiroConfig引用,其實spring boot集成shiro也就這兩步,加上自定義權限驗證 和自定義登錄驗證。
import org.apache.shiro.authc.credential.HashedCredentialsMatcher; 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.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver; import javax.servlet.Filter; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import java.util.Properties; @Configuration public class ShiroConfig { @Bean public ShiroFilterFactoryBean shirFilter(DefaultWebSecurityManager securityManager) { //System.out.println("ShiroConfiguration.shirFilter()"); ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); //添加自定義驗證方法,注意這個Filter繼承javax.servlet.Filter Map<String, Filter> filterMap=new HashMap<>(); filterMap.put("rolesOr",roleFilter()); shiroFilterFactoryBean.setFilters(filterMap); //攔截器. Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>(); // 配置不會被攔截的鏈接 順序判斷 filterChainDefinitionMap.put("/static/**", "anon"); filterChainDefinitionMap.put("/gif/**", "anon"); //配置退出 過濾器,其中的具體的退出代碼Shiro已經替我們實現了 filterChainDefinitionMap.put("/sanyi/logout", "logout"); //<!-- 過濾鏈定義,從上向下順序執行,一般將/**放在最為下邊 -->:這是一個坑呢,一不小心代碼就不好使了; //<!-- authc:所有url都必須認證通過才可以訪問; anon:所有url都都可以匿名訪問--> filterChainDefinitionMap.put("/sanyi/index", "anon");/*swagger*/ filterChainDefinitionMap.put("/swagger-ui.html", "anon"); filterChainDefinitionMap.put("/swagger-resources/**", "anon"); filterChainDefinitionMap.put("/v2/**", "anon"); filterChainDefinitionMap.put("/webjars/**", "anon"); /*business業務端(1管理員,3業務員)*/ filterChainDefinitionMap.put("/contract/base/**", "authc,rolesOr[1,3]");
/*其他,一定要采取白名單*/ filterChainDefinitionMap.put("/**", "authc"); // 如果不設置默認會自動尋找Web工程根目錄下的"/login"頁面 shiroFilterFactoryBean.setLoginUrl("/sanyi/index"); // 登錄成功后要跳轉的鏈接 shiroFilterFactoryBean.setSuccessUrl("/"); //未授權界面; shiroFilterFactoryBean.setUnauthorizedUrl("/sanyi/nopermission"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } @Bean public MyRolesAuthorizationFilter roleFilter(){ MyRolesAuthorizationFilter roleFilter=new MyRolesAuthorizationFilter(); return roleFilter; } /** * 密碼憑證匹配器 * (由於我們的密碼校驗交給Shiro的SimpleAuthenticationInfo進行處理了 * ) * @return */ @Bean public HashedCredentialsMatcher hashedCredentialsMatcher(){ HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:這里使用MD5算法; hashedCredentialsMatcher.setHashIterations(10);//散列的次數10哈哈; return hashedCredentialsMatcher; } /** * 身份認證realm; (這個需要自己寫,賬號密碼校驗;權限等) * @return */ @Bean public MyShiroRealm myShiroRealm(){ MyShiroRealm myShiroRealm = new MyShiroRealm(); myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher()); return myShiroRealm; } /** * 會話管理器 * @return */ @Bean public DefaultWebSessionManager sessionManager(){ DefaultWebSessionManager sessionManager=new DefaultWebSessionManager(); sessionManager.setGlobalSessionTimeout(1800000);//單位是ms return sessionManager; } @Bean public DefaultWebSecurityManager securityManager(){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(myShiroRealm()); securityManager.setSessionManager(sessionManager()); return securityManager; } /** * 開啟shiro aop注解支持. * 使用代理方式;所以需要開啟代碼支持; * @param securityManager * @return */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager){ AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } @Bean(name="simpleMappingExceptionResolver") public SimpleMappingExceptionResolver createSimpleMappingExceptionResolver() { SimpleMappingExceptionResolver r = new SimpleMappingExceptionResolver(); Properties mappings = new Properties(); mappings.setProperty("DatabaseException", "databaseError");//數據庫異常處理 mappings.setProperty("UnauthorizedException","403"); r.setExceptionMappings(mappings); // None by default r.setDefaultErrorView("error"); // No default r.setExceptionAttribute("exception"); // Default is "exception" //r.setWarnLogCategory("example.MvcLogger"); // No default return r; } }