shiro
apache shiro 是一個輕量級的身份驗證與授權框架,與spring security 相比較,簡單易用,靈活性高,springboot本身是提供了對security的支持,畢竟是自家的東西。 springboot暫時沒有集成shiro,這得自己配。
1 . 添加依賴
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.2.5</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>1.2.5</version> </dependency>
2 . 編寫Shiro配置類
package com.xbz.web.system.config; import at.pollux.thymeleaf.shiro.dialect.ShiroDialect; import org.apache.shiro.authc.credential.CredentialsMatcher; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.cache.ehcache.EhCacheManager; import org.apache.shiro.codec.Base64; import org.apache.shiro.session.SessionListener; import org.apache.shiro.session.mgt.SessionManager; import org.apache.shiro.session.mgt.eis.MemorySessionDAO; import org.apache.shiro.session.mgt.eis.SessionDAO; import org.apache.shiro.spring.LifecycleBeanPostProcessor; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.CookieRememberMeManager; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.apache.shiro.web.servlet.SimpleCookie; import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.DependsOn; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; import java.util.Map; /** * shiro配置類* ApacheShiro核心通過Filter來實現權限控制和攔截, 就好像SpringMVC通過DispachServlet來主控制請求分發一樣. * 既然是使用Filter , 即是通過URL規則來進行過濾和權限校驗, 所以我們需要定義一系列關於URL的規則和訪問權限*/ @Configuration public class ShiroConfiguration { /** * DefaultAdvisorAutoProxyCreator , Spring的一個bean , 由Advisor決定對哪些類的方法進行AOP代理. */ @Bean @ConditionalOnMissingBean public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator(); defaultAAP.setProxyTargetClass(true); return defaultAAP; } /** * ShiroFilterFactoryBean : 為了生成ShiroFilter , 處理攔截資源文件問題. * 它主要保持了三項數據, securityManager , filters , filterChainDefinitionManager . * 注意: 單獨一個ShiroFilterFactoryBean配置是或報錯的, 因為在初始化ShiroFilterFactoryBean的時候需要注入:SecurityManager * * FilterChain定義說明* 1 . 一個URL可以配置多個Filter , 使用逗號分隔* 2 . 當設置多個過濾器時, 全部驗證通過, 才視為通過* 3 . 部分過濾器可指定參數, 如perms , roles * */ @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean() { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager()); shiroFilterFactoryBean.setLoginUrl("/login");//不設置默認找web工程根目錄下的login.jsp頁面shiroFilterFactoryBean.setSuccessUrl("/index");//登錄成功之後要跳轉的連接shiroFilterFactoryBean.setUnauthorizedUrl("/403");//未授權跳轉頁面/* //自定義攔截器, 多個filter的設置*/ // Map<String, Filter> filters = new LinkedHashMap<>(); // LogoutFilter logoutFilter = new LogoutFilter();//限制同一帳號同時在線的個數。或單點登錄等// logoutFilter.setRedirectUrl("/login"); // filters.put("logout",null); // shiroFilterFactoryBean.setFilters(filters); Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); //filterChainDefinitionManager必須是LinkedHashMap因為它必須保證有序filterChainDefinitionMap.put("/css/**", "anon");//靜態資源不要求權限, 若有其他目錄下文件(如js,img等)也依此設置filterChainDefinitionMap.put("/", "anon"); filterChainDefinitionMap.put("/login", "anon");//配置不需要權限訪問的部分url filterChainDefinitionMap.put("/logout", "logout"); filterChainDefinitionMap.put("/user/**", "authc,roles[ROLE_USER]");//用戶為ROLE_USER 角色可以訪問. 由用戶角色控制用戶行為. filterChainDefinitionMap.put("/events/**", "authc,roles[ROLE_ADMIN]"); // filterChainDefinitionMap.put("/user/edit/**", "authc,perms[user:edit]");// 這里為了測試, 固定寫死的值, 也可以從數據庫或其他配置中讀取, 此處是用權限控制filterChainDefinitionMap.put("/**", "authc");//需要登錄訪問的資源, 一般將/*放在最下邊shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } //region Cookie及Session // ==================== Cookie及Session管理begin ==================== private static final String COOKIE_NAME = "rememberMe"; /** cookie對像管理*/ public SimpleCookie rememberMeCookie(){ //這個參數是cookie的名稱,對應前端的checkbox的name = rememberMe SimpleCookie simpleCookie = new SimpleCookie(COOKIE_NAME); simpleCookie.setMaxAge(604800);//記住我cookie生效時間7天,單位秒return simpleCookie; } /** cookie管理對象: 記住我功能*/ public CookieRememberMeManager rememberMeManager(){ CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager(); cookieRememberMeManager.setCookie(rememberMeCookie()); cookieRememberMeManager.setCipherKey(Base64.decode("3AvVhmFLUs0KTA3Kprsdag=="));//rememberMe cookie加密的密鑰建議每個項目都不一樣默認AES算法密鑰長度(128 256 512 位) return cookieRememberMeManager; } @Bean SessionDAO sessionDAO() { return new MemorySessionDAO(); } @Bean public SessionManager sessionManager() { DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); Collection<SessionListener> listeners = new ArrayList<>(); listeners.add(new BDSessionListener()); sessionManager.setSessionListeners(listeners); sessionManager.setSessionDAO(sessionDAO()); return sessionManager; } // ==================== Cookie及Session管理end ==================== //endregion /** * SecurityManager : 核心安全事務管理器, 權限管理* 這個類組合了登陸, 登出, 權限, session的處理. 是個比較重要的類. */ @Bean(name = "securityManager") public DefaultWebSecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(shiroRealm()); securityManager.setCacheManager(ehCacheManager());////用戶授權/認證信息Cache, 採用EhCache 緩存// 自定義session管理使用redis securityManager.setSessionManager(sessionManager()); //注入記住我管理器; securityManager.setRememberMeManager(rememberMeManager()); return securityManager; } /** * ShiroRealm , 這是個自定義的認證類, 繼承自AuthorizingRealm , * 負責用戶的認證和權限的處理, 可以參考JdbcRealm的實現. */ @Bean @DependsOn("lifecycleBeanPostProcessor") public ShiroRealm shiroRealm(CredentialsMatcher matcher) { ShiroRealm realm = new ShiroRealm(); realm.setCredentialsMatcher(matcher);//密碼校驗實現return realm; } /** * EhCacheManager , 緩存管理* 用戶登陸成功後, 把用戶信息和權限信息緩存起來, 然後每次用戶請求時, 放入用戶的session中, 如果不設置這個bean , 每個請求都會查詢一次數據庫. */ @Bean @DependsOn("lifecycleBeanPostProcessor") public EhCacheManager ehCacheManager() { EhCacheManager em = new EhCacheManager(); em.setCacheManagerConfigFile("classpath:config/ehcache.xml");//配置文件路徑return em; } /** * LifecycleBeanPostProcessor , 這是個DestructionAwareBeanPostProcessor的子類, * 負責org.apache.shiro.util.Initializable類型bean的生命週期的, 初始化和銷毀. * 主要是AuthorizingRealm類的子類, 以及EhCacheManager類. */ @Bean(name = "lifecycleBeanPostProcessor") public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } /** * HashedCredentialsMatcher , 這個類是為了對密碼進行編碼的, * 防止密碼在數據庫裡明碼保存, 當然在登陸認證的時候, * 這個類也負責對form裡輸入的密碼進行編碼* 處理認證匹配處理器:如果自定義需要實現繼承HashedCredentialsMatcher */ @Bean(name = "hashedCredentialsMatcher") public HashedCredentialsMatcher hashedCredentialsMatcher() { HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher(); credentialsMatcher.setHashAlgorithmName("MD5");//指定加密方式方式,也可以在這裡加入緩存,當用戶超過五次登陸錯誤就鎖定該用戶禁止不斷嘗試登陸credentialsMatcher.setHashIterations(2); credentialsMatcher.setStoredCredentialsHexEncoded(true); return credentialsMatcher; } /** * AuthorizationAttributeSourceAdvisor , shiro裡實現的Advisor類, * 內部使用AopAllianceAnnotationsAuthorizingMethodInterceptor來攔截用以下註解的方法. */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() { AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); advisor.setSecurityManager(securityManager()); return advisor; } //thymeleaf模板引擎和shiro整合時使用@Bean public ShiroDialect shiroDialect() { return new ShiroDialect(); } } 3 . 自定義Realm驗證類
package com.yiyun.web.system.config; import com.yiyun.dao.master.UserDao; import com.yiyun.domain.UserDO; import com.yiyun.web.common.utils.ShiroUtils; import com.yiyun.web.system.service.MenuService; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.*; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.session.Session; import org.apache.shiro.subject.PrincipalCollection; import org.springframework.beans.factory.annotation.Autowired; import java.util.*; /** * 獲取用戶的角色和權限信息*/ public class ShiroRealm extends AuthorizingRealm { // 一般這裡都寫的是servic @Autowired private UserDao userMapper; @Autowired private MenuService menuService; /** * 登錄認證一般情況下, 登錄成功之後就給當前用戶進行權限賦予了* 根據用戶的權限信息做授權判斷,這一步是以doGetAuthenticationInfo為基礎的,只有在有用戶信息後才能根據用戶的角色和授權信息做判斷是否給用戶授權,因此這裡的Roles和Permissions是用戶的兩個重點判斷依據* @param authenticationToken * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken; UserDo user = userMapper.findByName(token.getUsername());//查出是否有此用戶if(user != null){ // 若存在,將此用戶存放到登錄認證info中,無需自己做密碼對比,Shiro會為我們進行密碼對比校驗List<URole> rlist = uRoleDao.findRoleByUid(user.getId());//獲取用戶角色List<UPermission> plist = uPermissionDao.findPermissionByUid(user.getId());//獲取用戶權限List<String> roleStrlist=new ArrayList<String>();////用戶的角色集合List<String> perminsStrlist=new ArrayList<String>();//用戶的權限集合for (URole role : rlist) { roleStrlist.add(role.getName()); } for (UPermission uPermission : plist) { perminsStrlist.add(uPermission.getName()); } user.setRoleStrlist(roleStrlist); user.setPerminsStrlist(perminsStrlist); Session session = SecurityUtils.getSubject().getSession(); session.setAttribute("user", user);//成功則放入session // 若存在,將此用戶存放到登錄認證info中,無需自己做密碼對比,Shiro會為我們進行密碼對比校驗return new SimpleAuthenticationInfo(user, user.getPassword(), getName()); } return null; } /** * 權限認證* 獲取用戶的權限信息,這是為下一步的授權做判斷,獲取當前用戶的角色和這些角色所擁有的權限信息* @param principalCollection * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //獲取當前登錄輸入的用戶名,等價於(String) principalCollection.fromRealm(getName()).iterator().next(); // String loginName = (String) super.getAvailablePrincipal(principalCollection); UserDo user = (UserDo) principalCollection.getPrimaryPrincipal(); // //到數據庫查是否有此對象// User user = null;// 實際項目中,這裡可以根據實際情況做緩存,如果不做,Shiro自己也是有時間間隔機制,2分鐘內不會重複執行該方法// user = userMapper.findByName(loginName); if (user != null) { //權限信息對象info,用來存放查出的用戶的所有的角色(role)及權限(permission) SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); //用戶的角色集合info.addRoles(user.getRoleStrlist()); //用戶的權限集合info.addStringPermissions(user.getPerminsStrlist()); return info; } // 返回null的話,就會導致任何用戶訪問被攔截的請求時,都會自動跳轉到unauthorizedUrl指定的地址return null; } }4 . 最後看一下ehcache的配置文件
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" updateCheck="false"> <diskStore path="java.io.tmpdir/Tmp_EhCache" /> <!-- name:緩存名稱。 maxElementsInMemory:緩存最大數目maxElementsOnDisk:硬盤最大緩存個數。 eternal:對像是否永久有效,一但設置了,timeout將不起作用。 overflowToDisk:是否保存到磁盤,當系統當機時timeToIdleSeconds:設置對像在失效前的允許閒置時間(單位:秒)。僅當eternal=false對像不是永久有效時使用,可選屬性,默認值是0,也就是可閒置時間無窮大。 timeToLiveSeconds:設置對像在失效前允許存活時間(單位:秒)。最大時間介於創建時間和失效時間之間。僅當eternal=false對像不是永久有效時使用,默認是0.,也就是對象存活時間無窮大。 diskPersistent:是否緩存虛擬機重啟期數據Whether the disk store persists between restarts of the Virtual Machine. The default value is false. diskSpoolBufferSizeMB:這個參數設置DiskStore(磁盤緩存)的緩存區大小。默認是30MB。每個Cache都應該有自己的一個緩衝區。 diskExpiryThreadIntervalSeconds:磁盤失效線程運行時間間隔,默認是120秒。 memoryStoreEvictionPolicy:當達到maxElementsInMemory限制時,Ehcache將會根據指定的策略去清理內存。默認策略是LRU(最近最少使用)。你可以設置為FIFO(先進先出)或是LFU(較少使用)。 clearOnFlush:內存數量最大時是否清除。 memoryStoreEvictionPolicy: Ehcache的三種清空策略; FIFO,first in first out,這個是大家最熟的,先進先出。 LFU, Less Frequently Used,就是上面例子中使用的策略,直白一點就是講一直以來最少被使用的。如上面所講,緩存的元素有一個hit屬性,hit值最小的將會被清出緩存。 LRU,Least Recently Used,最近最少使用的,緩存的元素有一個時間戳,當緩存容量滿了,而又需要騰出地方來緩存新的元素的時候,那麼現有緩存元素中時間戳離當前時間最遠的元素將被清出緩存。 --> <defaultCache eternal="false" maxElementsInMemory="1000" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="0" timeToLiveSeconds="600" memoryStoreEvictionPolicy="LRU" /> <!-- 登錄記錄緩存鎖定10分鐘--> <cache name="passwordRetryCache" maxEntriesLocalHeap="2000" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="0" overflowToDisk="false" statistics="true"> </cache> </ehcache>
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。