項(xiàng)目背景
在最近重構(gòu)后的項(xiàng)目中使用了springboot+shiro的技術(shù)棧,shiro是一個(gè)強(qiáng)大且易用的Java安全框架,執(zhí)行身份驗(yàn)證、授權(quán)、密碼學(xué)和會(huì)話管理.shiro包含三個(gè)核心組件subject,securityManager和realm.subject即當(dāng)前操作用戶,代表了當(dāng)前用戶的安全操作,shiro底層使用了threadlocal來(lái)存取當(dāng)前用戶的subject.
securityManager則是管理所有用戶的安全操作,屬于框架的核心組件,shiro通過(guò)它來(lái)管理內(nèi)部組件與提供各種服務(wù)。
realm充當(dāng)了shiro與數(shù)據(jù)之間的橋梁,實(shí)質(zhì)上就是封裝好的一個(gè)安全相關(guān)的DAO。
本系統(tǒng)使用了shiro來(lái)處理用戶的身份認(rèn)證與權(quán)限校驗(yàn)。
具體實(shí)現(xiàn)
首先在項(xiàng)目的pom文件中引入shiro的核心包.
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>${org.apache.shiro-version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>${org.apache.shiro-version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${org.apache.shiro-version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>${org.apache.shiro-version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-aspectj</artifactId>
<version>${org.apache.shiro-version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-quartz</artifactId>
<version>${org.apache.shiro-version}</version>
</dependency>
然后創(chuàng)建一個(gè)shiro的配置類ShiroConfiguration,shiro使用了過(guò)濾器shiroFilter來(lái)作為入口點(diǎn),用于攔截需要安全控制的請(qǐng)求.
/**
* ShiroFilterFactoryBean 處理攔截資源文件問(wèn)題。
* Filter Chain定義說(shuō)明 1、一個(gè)URL可以配置多個(gè)Filter,使用逗號(hào)分隔
2、當(dāng)設(shè)置多個(gè)過(guò)濾器時(shí),全部驗(yàn)證通過(guò),才視為通過(guò)
* 3、部分過(guò)濾器可指定參數(shù),如perms,roles
*/
@Bean
public ShiroFilterFactoryBean shirFilter() {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 必須設(shè)置 SecurityManager
shiroFilterFactoryBean.setSecurityManager(getDefaultWebSecurityManager());
// 如果不設(shè)置默認(rèn)會(huì)自動(dòng)尋找Web工程根目錄下的"/login.jsp"頁(yè)面
shiroFilterFactoryBean.setLoginUrl("/login.html");
// 登錄成功后要跳轉(zhuǎn)的鏈接
shiroFilterFactoryBean.setSuccessUrl("/admin/index.html");
// 未授權(quán)界面;
shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");
//Shiro自定義認(rèn)證過(guò)濾器
Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();//獲取filters
filters.put("authc", new CustomerFormAuthenticationFilter());
shiroFilterFactoryBean.setFilters(filters);
// 攔截器.
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
//攔截的,從上向下順序執(zhí)行,匹配成功就不再繼續(xù)往下走
filterChainDefinitionMap.put("/static/**", "anon");
filterChainDefinitionMap.put("/img/favicon.ico", "anon,noSessionCreation");
filterChainDefinitionMap.put("/templates/system/public/**", "anon");
filterChainDefinitionMap.put("/vertifyCode/**", "anon");
filterChainDefinitionMap.put("/login", "anon");
filterChainDefinitionMap.put("/toLogin", "anon");
// <!-- authc:所有url都必須認(rèn)證通過(guò)才可以訪問(wèn); anon:所有url都都可以匿名訪問(wèn)-->
filterChainDefinitionMap.put("/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
設(shè)置系統(tǒng)的sessionid,框架默認(rèn)為: JSESSIONID,可改成其他的如token.
@Bean
public SimpleCookie wapsession() {
SimpleCookie simpleCookie = new SimpleCookie("token");
simpleCookie.setMaxAge(2592000);
return simpleCookie;
}
接下來(lái)配置cookie管理器.
@Bean
public SimpleCookie rememberMeCookie() {
SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
simpleCookie.setMaxAge(2592000);
simpleCookie.setHttpOnly(true);
return simpleCookie;
}
提供了記住我(RememberMe)的功能.
@Bean
public CookieRememberMeManager rememberMeManager() {
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
byte[] cipherKey = Base64.decode("4AvVhmFLUs0KTA3Kprsdag==");
cookieRememberMeManager.setCookie(rememberMeCookie());
cookieRememberMeManager.setCipherKey(cipherKey);
return cookieRememberMeManager;
}
配置securityManager安全管理器.
@Bean(name = "securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager() {
logger.info("注入Shiro的Web過(guò)濾器-->securityManager", ShiroFilterFactoryBean.class);
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setAuthenticator(authenticator());
//設(shè)置多Realm,用于獲取認(rèn)證憑證
Collection<Realm> realms = new ArrayList<>();
realms.add(appShiroRealm());
realms.add(pcShiroRealm());
realms.add(thirdPathShiroRealm());
securityManager.setRealms(realms);
//注入緩存管理器
//securityManager.setCacheManager(ehCacheManager());
//注入會(huì)話管理器
securityManager.setSessionManager(sessionManager());
//注入Cookie(記住我)管理器(remenberMeManager)
securityManager.setRememberMeManager(rememberMeManager());
return securityManager;
}
定義數(shù)據(jù)源Realm.
/*
* @describe 自定義AppRealm
* @param []
* @return com.hongsui.win.home.controller.admin.web.realm.AppShiroRealm
*/
@Bean
public AppShiroRealm appShiroRealm() {
AppShiroRealm appShiroRealm = new AppShiroRealm();
appShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return appShiroRealm;
}
/*
* @describe 自定義AppRealm
* @param []
* @return com.hongsui.win.home.controller.admin.web.realm.PcShiroRealm
*/
@Bean
public PcShiroRealm pcShiroRealm() {
PcShiroRealm pcShiroRealm = new PcShiroRealm();
pcShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return pcShiroRealm;
}
/*
* @describe 自定義ThirdRealm
* @param []
* @return com.hongsui.win.home.controller.admin.web.realm.ThirdPathShiroRealm
*/
@Bean
public ThirdPathShiroRealm thirdPathShiroRealm() {
ThirdPathShiroRealm thirdPathShiroRealm = new ThirdPathShiroRealm();
thirdPathShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return thirdPathShiroRealm;
}
配置會(huì)話管理器.
@Bean
public CustomerWebSessionManager sessionManager() {
CustomerWebSessionManager sessionManager = new CustomerWebSessionManager();
//會(huì)話驗(yàn)證器調(diào)度時(shí)間
sessionManager.setSessionValidationInterval(1800000);
//定時(shí)檢查失效的session
sessionManager.setSessionValidationSchedulerEnabled(true);
//是否在會(huì)話過(guò)期后會(huì)調(diào)用SessionDAO的delete方法刪除會(huì)話 默認(rèn)true
sessionManager.setDeleteInvalidSessions(true);
sessionManager.setSessionDAO(redisSessionDAO());
sessionManager.setSessionIdUrlRewritingEnabled(false);
sessionManager.setSessionIdCookie(wapsession());
sessionManager.setSessionIdCookieEnabled(true);
return sessionManager;
}
保證實(shí)現(xiàn)了Shiro內(nèi)部lifecycle函數(shù)的bean執(zhí)行.
@Bean(name = "lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
開啟Shiro的注解(如@RequiresRoles,@RequiresPermissions等等),需借助SpringAOP掃描使用Shiro注解的類,并在必要時(shí)進(jìn)行安全邏輯驗(yàn)證,配置以下兩個(gè)bean(DefaultAdvisorAutoProxyCreatorAuthorizationAttributeSourceAdvisor)即可實(shí)現(xiàn)此功能.
@Bean
@DependsOn({"lifecycleBeanPostProcessor"})
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor =
new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(getDefaultWebSecurityManager());
return authorizationAttributeSourceAdvisor;
}
到此shiro框架已經(jīng)與springboot集成到項(xiàng)目中了,之后就可以編寫相應(yīng)的realm代碼以及在controller層實(shí)現(xiàn)登錄認(rèn)證了.