shiro
創(chuàng)新互聯(lián)長期為成百上千客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊從業(yè)經(jīng)驗10年,關(guān)注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為邢臺企業(yè)提供專業(yè)的成都網(wǎng)站設(shè)計、做網(wǎng)站,邢臺網(wǎng)站改版等技術(shù)服務(wù)。擁有10余年豐富建站經(jīng)驗和眾多成功案例,為您定制開發(fā)。
apache shiro 是一個輕量級的身份驗證與授權(quán)框架,與spring security 相比較,簡單易用,靈活性高,springboot本身是提供了對security的支持,畢竟是自家的東西。springboot暫時沒有集成shiro,這得自己配。
1 . 添加依賴
org.apache.shiro shiro-spring 1.2.5 org.apache.shiro shiro-ehcache 1.2.5
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來實現(xiàn)權(quán)限控制和攔截 , 就好像SpringMVC通過DispachServlet來主控制請求分發(fā)一樣 . * 既然是使用Filter , 即是通過URL規(guī)則來進(jìn)行過濾和權(quán)限校驗 , 所以我們需要定義一系列關(guān)于URL的規(guī)則和訪問權(quán)限 */ @Configuration public class ShiroConfiguration { /** * DefaultAdvisorAutoProxyCreator , Spring的一個bean , 由Advisor決定對哪些類的方法進(jìn)行AOP代理 . */ @Bean @ConditionalOnMissingBean public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator(); defaultAAP.setProxyTargetClass(true); return defaultAAP; } /** * ShiroFilterFactoryBean : 為了生成ShiroFilter , 處理攔截資源文件問題 . * 它主要保持了三項數(shù)據(jù) , securityManager , filters , filterChainDefinitionManager . * 注意 : 單獨(dú)一個ShiroFilterFactoryBean配置是或報錯的 , 因為在初始化ShiroFilterFactoryBean的時候需要注入:SecurityManager * * FilterChain定義說明 * 1 . 一個URL可以配置多個Filter , 使用逗號分隔 * 2 . 當(dāng)設(shè)置多個過濾器時 , 全部驗證通過 , 才視為通過 * 3 . 部分過濾器可指定參數(shù) , 如perms , roles * */ @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean() { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager()); shiroFilterFactoryBean.setLoginUrl("/login");//不設(shè)置默認(rèn)找web工程根目錄下的login.jsp頁面 shiroFilterFactoryBean.setSuccessUrl("/index");//登錄成功之后要跳轉(zhuǎn)的連接 shiroFilterFactoryBean.setUnauthorizedUrl("/403");//未授權(quán)跳轉(zhuǎn)頁面 /* //自定義攔截器 , 多個filter的設(shè)置 */ // Mapfilters = new LinkedHashMap<>(); // LogoutFilter logoutFilter = new LogoutFilter();//限制同一帳號同時在線的個數(shù)?;騿吸c登錄等 // logoutFilter.setRedirectUrl("/login"); // filters.put("logout",null); // shiroFilterFactoryBean.setFilters(filters); Map filterChainDefinitionMap = new LinkedHashMap<>(); //filterChainDefinitionManager必須是LinkedHashMap因為它必須保證有序 filterChainDefinitionMap.put("/css/**", "anon");//靜態(tài)資源不要求權(quán)限 , 若有其他目錄下文件(如js,img等)也依此設(shè)置 filterChainDefinitionMap.put("/", "anon"); filterChainDefinitionMap.put("/login", "anon");//配置不需要權(quán)限訪問的部分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]");// 這里為了測試 , 固定寫死的值 , 也可以從數(shù)據(jù)庫或其他配置中讀取 , 此處是用權(quán)限控制 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(){ //這個參數(shù)是cookie的名稱,對應(yīng)前端的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加密的密鑰 建議每個項目都不一樣 默認(rèn)AES算法 密鑰長度(128 256 512 位) return cookieRememberMeManager; } @Bean SessionDAO sessionDAO() { return new MemorySessionDAO(); } @Bean public SessionManager sessionManager() { DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); Collection listeners = new ArrayList<>(); listeners.add(new BDSessionListener()); sessionManager.setSessionListeners(listeners); sessionManager.setSessionDAO(sessionDAO()); return sessionManager; } // ==================== Cookie及Session管理 end ==================== //endregion /** * SecurityManager : 核心安全事務(wù)管理器 , 權(quán)限管理 * 這個類組合了登陸 , 登出 , 權(quán)限 , session的處理 . 是個比較重要的類 . */ @Bean(name = "securityManager") public DefaultWebSecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(shiroRealm()); securityManager.setCacheManager(ehCacheManager());////用戶授權(quán)/認(rèn)證信息Cache, 采用EhCache 緩存 // 自定義session管理 使用redis securityManager.setSessionManager(sessionManager()); //注入記住我管理器; securityManager.setRememberMeManager(rememberMeManager()); return securityManager; } /** * ShiroRealm , 這是個自定義的認(rèn)證類 , 繼承自AuthorizingRealm , * 負(fù)責(zé)用戶的認(rèn)證和權(quán)限的處理 , 可以參考JdbcRealm的實現(xiàn) . */ @Bean @DependsOn("lifecycleBeanPostProcessor") public ShiroRealm shiroRealm(CredentialsMatcher matcher) { ShiroRealm realm = new ShiroRealm(); realm.setCredentialsMatcher(matcher);//密碼校驗實現(xiàn) return realm; } /** * EhCacheManager , 緩存管理 * 用戶登陸成功后 , 把用戶信息和權(quán)限信息緩存起來 , 然后每次用戶請求時 , 放入用戶的session中 , 如果不設(shè)置這個bean , 每個請求都會查詢一次數(shù)據(jù)庫 . */ @Bean @DependsOn("lifecycleBeanPostProcessor") public EhCacheManager ehCacheManager() { EhCacheManager em = new EhCacheManager(); em.setCacheManagerConfigFile("classpath:config/ehcache.xml");//配置文件路徑 return em; } /** * LifecycleBeanPostProcessor , 這是個DestructionAwareBeanPostProcessor的子類 , * 負(fù)責(zé)org.apache.shiro.util.Initializable類型bean的生命周期的 , 初始化和銷毀 . * 主要是AuthorizingRealm類的子類 , 以及EhCacheManager類 . */ @Bean(name = "lifecycleBeanPostProcessor") public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } /** * HashedCredentialsMatcher , 這個類是為了對密碼進(jìn)行編碼的 , * 防止密碼在數(shù)據(jù)庫里明碼保存 , 當(dāng)然在登陸認(rèn)證的時候 , * 這個類也負(fù)責(zé)對form里輸入的密碼進(jìn)行編碼 * 處理認(rèn)證匹配處理器:如果自定義需要實現(xiàn)繼承HashedCredentialsMatcher */ @Bean(name = "hashedCredentialsMatcher") public HashedCredentialsMatcher hashedCredentialsMatcher() { HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher(); credentialsMatcher.setHashAlgorithmName("MD5");//指定加密方式方式,也可以在這里加入緩存,當(dāng)用戶超過五次登陸錯誤就鎖定該用戶禁止不斷嘗試登陸 credentialsMatcher.setHashIterations(2); credentialsMatcher.setStoredCredentialsHexEncoded(true); return credentialsMatcher; } /** * AuthorizationAttributeSourceAdvisor , shiro里實現(xiàn)的Advisor類 , * 內(nèi)部使用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.*; /** * 獲取用戶的角色和權(quán)限信息 */ public class ShiroRealm extends AuthorizingRealm { // 一般這里都寫的是servic @Autowired private UserDao userMapper; @Autowired private MenuService menuService; /** * 登錄認(rèn)證 一般情況下 , 登錄成功之后就給當(dāng)前用戶進(jìn)行權(quán)限賦予了 * 根據(jù)用戶的權(quán)限信息做授權(quán)判斷,這一步是以doGetAuthenticationInfo為基礎(chǔ)的,只有在有用戶信息后才能根據(jù)用戶的角色和授權(quán)信息做判斷是否給用戶授權(quán),因此這里的Roles和Permissions是用戶的兩個重點判斷依據(jù) * @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){ // 若存在,將此用戶存放到登錄認(rèn)證info中,無需自己做密碼對比,Shiro會為我們進(jìn)行密碼對比校驗 Listrlist = uRoleDao.findRoleByUid(user.getId());//獲取用戶角色 List plist = uPermissionDao.findPermissionByUid(user.getId());//獲取用戶權(quán)限 List roleStrlist=new ArrayList ();////用戶的角色集合 List perminsStrlist=new ArrayList ();//用戶的權(quán)限集合 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 // 若存在,將此用戶存放到登錄認(rèn)證info中,無需自己做密碼對比,Shiro會為我們進(jìn)行密碼對比校驗 return new SimpleAuthenticationInfo(user, user.getPassword(), getName()); } return null; } /** * 權(quán)限認(rèn)證 * 獲取用戶的權(quán)限信息,這是為下一步的授權(quán)做判斷,獲取當(dāng)前用戶的角色和這些角色所擁有的權(quán)限信息 * @param principalCollection * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //獲取當(dāng)前登錄輸入的用戶名,等價于(String) principalCollection.fromRealm(getName()).iterator().next(); // String loginName = (String) super.getAvailablePrincipal(principalCollection); UserDo user = (UserDo) principalCollection.getPrimaryPrincipal(); // //到數(shù)據(jù)庫查是否有此對象 // User user = null;// 實際項目中,這里可以根據(jù)實際情況做緩存,如果不做,Shiro自己也是有時間間隔機(jī)制,2分鐘內(nèi)不會重復(fù)執(zhí)行該方法 // user = userMapper.findByName(loginName); if (user != null) { //權(quán)限信息對象info,用來存放查出的用戶的所有的角色(role)及權(quán)限(permission) SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); //用戶的角色集合 info.addRoles(user.getRoleStrlist()); //用戶的權(quán)限集合 info.addStringPermissions(user.getPerminsStrlist()); return info; } // 返回null的話,就會導(dǎo)致任何用戶訪問被攔截的請求時,都會自動跳轉(zhuǎn)到unauthorizedUrl指定的地址 return null; } }
4 . 最后看一下ehcache的配置文件
<?xml version="1.0" encoding="UTF-8"?>
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。