spring-boot + shiro + mybatis 的 demo
這裡總結下用springboot實現shiro的幾個要點,如果要下載完整的專案,請到********(下載後先在本地建立test_shiro資料庫,然後執行resources的sql包下的5個sql檔案),啟動專案即可看到網頁並且測試
說一下要點:
資料庫一般至少有五張表(本專案是用mybaits)
1- user:使用者賬號密碼
2-role:角色ID,一個賬戶可以有很多角色
3. permission許可權ID,一個角色可以有很多許可權
4-user_role關係對照表:記錄每個userID有的角色
5-role_permission關係對照表,記錄每個role有的permission
shiro授權的時候,就是先配置那些url需要授權,那些是某個角色可以訪問的,那些是擁有某個許可權可以訪問的,另外還有不需要登入不需要許可權就可以訪問的路徑(包含靜態資源)。然後遇到需要許可權的url,就迴圈登入的userId,查出他的所有角色和所有許可權,驗證是否可以訪問這個url,如果不能就會說沒有角色或許可權,例如以下
1. ShiroConfig類的配置,替代之前xml
其中很多配置可以用註解實現,不一定要在config裡設定
@Configuration public class ShiroConfig { /*無需再另外設定filter*/ @Bean public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) { System.err.println("ShiroConfiguration.shirFilter()"); ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); // 必須設定 SecurityManager shiroFilterFactoryBean.setSecurityManager(securityManager); //新增shiro內建過濾器 /* 具體參考shiro的enum DefaultFilter * anon:表示可以匿名使用。 authc:表示需要認證(登入)才能使用,沒有引數 roles:引數可以寫多個,多個時必須加上引號,並且引數之間用逗號分割,當有多個引數時,例如admins/user/**=roles["admin,guest"],每個引數通過才算通過,相當於hasAllRoles()方法。 perms:引數可以寫多個,多個時必須加上引號,並且引數之間用逗號分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],當有多個引數時必須每個引數都通過才通過,想當於isPermitedAll()方法。 rest:根據請求的方法,相當於/admins/user/**=perms[user:method] ,其中method為post,get,delete等。 port:當請求的url的埠不是8081是跳轉到schemal://serverName:8081?queryString,其中schmal是協議http或https等,serverName是你訪問的host,8081是url配置裡port的埠,queryString是你訪問的url裡的?後面的引數。 authcBasic:沒有引數表示httpBasic認證 ssl:表示安全的url請求,協議為https user:當登入操作時不做檢查 */ Map<String, String> fMap = new HashMap<String, String>(); /*user:必須登入才能訪問的頁面 效力同@RequiresUser*/ // fMap.put("/users", "user"); /*authc:表示需要認證(登入)才能使用,這裡不能包含anon的部分,否則anno的設定無效,導致靜態資源無法訪問*/ // fMap.put("/admin", "authc"); // fMap.put("/user", "authc"); /*anon:表示可以匿名使用*/ /*靜態資源*/ fMap.put("/assets/**", "anon"); fMap.put("/css/**", "anon"); fMap.put("/images/**", "anon"); fMap.put("/img/**", "anon"); fMap.put("/js/**", "anon"); /*登入介面*/ fMap.put("/getGifCode", "anon");//驗證碼 fMap.put("/ajaxLogin", "anon");//ajax登入介面 fMap.put("/main", "anon");//for test fMap.put("/logout", "anon"); fMap.put("/login", "anon"); // fMap.put("/*", "anon"); // fMap.put("/**/*", "anon"); /*設定可以登入的人員 可用註解配置*/ // fMap.put("/admin", "perms[admin:*]"); // fMap.put("/add", "perms[*:add]"); // fMap.put("/user", "perms[user:*]"); /*設定可以登入的角色,可用註解配置,效力同@RequiresRoles("admin")*/ // fMap.put("/user", "roles[user,admin]"); // fMap.put("/admin", "roles[admin]"); // Shiro攔截器工廠類注入 shiroFilterFactoryBean.setFilterChainDefinitionMap(fMap); //被攔截返回登入頁面 shiroFilterFactoryBean.setLoginUrl("/login"); // 登入成功後要跳轉的連結 shiroFilterFactoryBean.setSuccessUrl("/main"); //未授權介面; shiroFilterFactoryBean.setUnauthorizedUrl("/noAuth"); return shiroFilterFactoryBean; } @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //設定realm.,必須是@Bean securityManager.setRealm(myShiroRealm()); //注入快取管理器; securityManager.setCacheManager(ehCacheManager());//這個如果執行多次,也是同樣的一個物件; securityManager.setRememberMeManager(rememberMeManager()); /*把securityManager注入SecurityUtils*/ SecurityUtils.setSecurityManager(securityManager); return securityManager; } /** * 身份認證realm; * (這個需要自己寫,賬號密碼校驗;許可權等) * * @return */ @Bean public UserAuthenticationRealm myShiroRealm() { UserAuthenticationRealm myShiroRealm = new UserAuthenticationRealm(); myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());; return myShiroRealm; } /** * shiro快取管理器; * 需要注入對應的其它的實體類中: * 1、安全管理器:securityManager * 可見securityManager是整個shiro的核心; * * @return */ @Bean public EhCacheManager ehCacheManager() { System.out.println("ShiroConfiguration.getEhCacheManager()"); EhCacheManager cacheManager = new EhCacheManager(); cacheManager.setCacheManagerConfigFile("classpath:config/ehcache-shiro.xml"); return cacheManager; } /** * cookie管理物件; * * @return */ @Bean public CookieRememberMeManager rememberMeManager() { System.out.println("ShiroConfiguration.rememberMeManager()"); CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager(); cookieRememberMeManager.setCookie(rememberMeCookie()); return cookieRememberMeManager; } /** * cookie物件; * * @return */ @Bean public SimpleCookie rememberMeCookie() { System.out.println("ShiroConfiguration.rememberMeCookie()"); //這個引數是cookie的名稱,對應前端的checkbox的name = rememberMe SimpleCookie simpleCookie = new SimpleCookie("rememberMe"); //<!-- 記住我cookie生效時間30天 ,單位秒;--> simpleCookie.setMaxAge(259200); return simpleCookie; } /** * 憑證匹配器 * (由於我們的密碼校驗交給Shiro的SimpleAuthenticationInfo進行處理了 * 所以我們需要修改下doGetAuthenticationInfo中的程式碼; * ) * * @return */ @Bean public HashedCredentialsMatcher hashedCredentialsMatcher(){ HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); hashedCredentialsMatcher.setHashAlgorithmName(Md5Hash.ALGORITHM_NAME);//雜湊演算法:這裡使用MD5演算法; hashedCredentialsMatcher.setHashIterations(1);//雜湊的次數,比如雜湊兩次,相當於 md5(md5("")); // hashedCredentialsMatcher.setHashAlgorithmName("SHA-256"); // hashedCredentialsMatcher.setHashIterations(1024); // hashedCredentialsMatcher.setStoredCredentialsHexEncoded(false); // 這一行決定hex還是base64,true=Hex,false=base64 hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true); // 這一行決定hex還是base64,true=Hex,false=base64 hashedCredentialsMatcher.setHashSalted(false); return hashedCredentialsMatcher; } @Bean public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } @Bean @DependsOn({"lifecycleBeanPostProcessor"}) public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() { // <!-- 配置是否啟動過慮器的init/destory方法 --> // <!-- 保證實現了Shiro內部lifecycle函式的bean執行 --> DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); advisorAutoProxyCreator.setProxyTargetClass(true); return advisorAutoProxyCreator; } /** * 開啟shiro aop註解支援. * 使用代理方式;所以需要開啟程式碼支援; * * @param securityManager * @return */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } }
ehcache-shiro.xml檔案裡的配置
<?xml version="1.0" encoding="UTF-8"?> <ehcache name="es"> <diskStore path="java.io.tmpdir"/> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="false" diskPersistent="false" diskExpiryThreadIntervalSeconds="120" /> <!-- 登入記錄快取鎖定10分鐘 --> <cache name="passwordRetryCache" maxEntriesLocalHeap="2000" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="0" overflowToDisk="false" statistics="true"> </cache> </ehcache>
2. relm的加密,資料庫密碼必須加密,加密方式在Config裡配置要注意到是,實現AuthorizingRealm類的登入認證裡要和自己加密方式搭配,以下是用Hex,不加鹽的情況
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(userInfo.getUserId(),
userInfo.getPassword(), userInfo.getUserName());
3. 註解和ShiroFilterFactoryBean的config都可以配置許可權,個人覺得註解好用一點
4. 實現AuthorizingRealm的類裡有兩個方法doGetAuthenticationInfo是登入驗證,在SecurityUtils.getSubject().login(token);的時候呼叫,另外一個doGetAuthorizationInfo是有驗證要求的時候才會執行,例如加了@RequiresAuthentication註解的Controller
如果要下載完整的專案,請到********(下載後先在本地建立test_shiro資料庫,然後執行resources的sql包下的5個sql檔案),啟動專案即可看到網頁並且測試