這篇博客中沒有什么高深的技術(shù),,只是一個(gè)簡(jiǎn)單的Shiro框架的搭建...因?yàn)橹耙恢睕]找到正確的搭建方法、或是因?yàn)閳?bào)錯(cuò)搭建失敗...所以我寫下了這篇,。希望可以給有需要的人幫助,。
我用的編輯工具是Idea。
首先,我們從三個(gè)方面來(lái)認(rèn)識(shí)Shiro,。
1:什么是Shiro,?
Shiro是Apache旗下的一個(gè)開源的安全框架,我認(rèn)為他的主要功能就是2點(diǎn):認(rèn)證,、授權(quán),。
認(rèn)證:從字面意思上來(lái)看也就是那樣,認(rèn)證,、辨別你的身份,。
授權(quán):也從字面意思上開看的話是授予、賦予權(quán)限,。
(- -...我感覺我在說(shuō)廢話...)
2:Shiro有什么用,?
上面也說(shuō)了,主要是為了認(rèn)證,、授權(quán),,當(dāng)然,僅僅是個(gè)人初步的理解(撓頭)...歡迎指教,。
3:Shiro的組件,?
雖然百度一下就有一大堆,但是我還是寫出來(lái)吧...至少我又寫了一遍,,加深了一點(diǎn)印象...
Subject:主體,,可以把它看做是任何可以與應(yīng)用交互的對(duì)象;
SecurityManager:安全管理器,,是Shiro的核心,,管理所有的Subject;
Authenticator:認(rèn)證器,,負(fù)責(zé)主體的認(rèn)證,;
Authrizer:授權(quán)器,可以給認(rèn)證完的主體賦予所擁有的相應(yīng)的權(quán)限,;
Realm:數(shù)據(jù)源,,使用期間認(rèn)證和授權(quán)需要從這里獲取數(shù)據(jù);
SessionManager:Shiro自己抽象出來(lái)的一個(gè)Session,,與Tomcat自帶是不一樣的,,用來(lái)管理應(yīng)用和主體之間交互的數(shù)據(jù);
SessionDAO:用來(lái)做(Shiro的)Session的增刪改查(CRUD),。
CacheManager:緩存管理器,,可以用來(lái)存儲(chǔ)用戶的角色、權(quán)限等信息,。
Cryptography:密碼模塊,,Shiro自己提供了一些加密碼組件,;
(好吧,我承認(rèn)這一段是簡(jiǎn)述了
開濤的博客-跟我學(xué)Shiro ,,嘿嘿嘿...)
4:Shiro框架的簡(jiǎn)單搭建。
好了,,現(xiàn)在要開始主體了,!
第一步:
導(dǎo)入相關(guān)的Shiro的Jar包,我是導(dǎo)了這五個(gè)包:
<groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <groupId>org.apache.shiro</groupId> <artifactId>shiro-quartz</artifactId>
第二步:
a:創(chuàng)建一個(gè)spring-shiro.xml
<beans xmlns="http://www./schema/beans" xmlns:xsi="http://www./2001/XMLSchema-instance" xsi:schemaLocation="http://www./schema/beans http://www./schema/beans/spring-beans-3.2.xsd"> <!-- web.xml中shiro的filter對(duì)應(yīng)的bean --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager" /> <!-- loginUrl認(rèn)證提交地址,,如果沒有認(rèn)證將會(huì)請(qǐng)求此地址進(jìn)行認(rèn)證,,請(qǐng)求此地址將由formAuthenticationFilter進(jìn)行表單認(rèn)證 --> <property name="loginUrl" value="/login/login.do" /> <!--shiro登錄成功后自己跳轉(zhuǎn)的網(wǎng)頁(yè)--> <property name="successUrl" value="/homePage.jsp" /> <!-- 自定義filter配置 自定義認(rèn)證和授權(quán)功能 --> <property name="filters"> <!-- 將自定義 的FormAuthenticationFilter注入shiroFilter中 authc 所有需要權(quán)限認(rèn)證的 都會(huì)走此filter <entry key="authc" value-ref="formAuthenticationFilter" /> <!-- 過慮器鏈定義,從上向下順序執(zhí)行,,一般將/**放在最下邊 --> <property name="filterChainDefinitions"> <!--authc 為全部攔截,,注意,這里的authc是上面配置的那個(gè)map中的authc--> <!-- 登錄方法攔截,,最好和上面的loginUrl配置為一致的,,不需要寫方法 --> <!-- 請(qǐng)求 logout.action地址,shiro去清除session,,想要注銷的話直接訪問logout.do這個(gè)路徑就可以進(jìn)行注銷,,不需要去寫方法--> <!--對(duì)所有的信息進(jìn)行攔截,一般配置在最下面--> <!-- securityManager安全管理器,,引入realm --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="shiroRealm" /> <bean id="shiroRealm" class="com.jk.shiro.ShiroRealm"> <!-- 自定義form認(rèn)證過慮器 --> <!-- 基于Form表單的身份驗(yàn)證過濾器,,不配置將也會(huì)注冊(cè)此過慮器,表單中的用戶賬號(hào),、密碼及l(fā)oginurl將采用默認(rèn)值,,建議配置 --> <bean id="formAuthenticationFilter" class="com.jk.filter.formAuthenticationInfoFilter"> <!-- 表單中賬號(hào)的input名稱 --> <property name="usernameParam" value="username" /> <property name="passwordParam" value="password" /> <property name="rememberMeParam" value="rememberMe"/>
b:在web.xml中添加spring-shiro.xml的聲明 <param-name>contextConfigLocation</param-name> <param-value>classpath:spring*.xml</param-value>
<!-- shiro過慮器,DelegatingFilterProxy通過代理模式將spring容器中的bean和filter關(guān)聯(lián)起來(lái) --> <filter-name>shiroFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <!-- 設(shè)置true由servlet容器控制filter的生命周期 --> <param-name>targetFilterLifecycle</param-name> <param-value>true</param-value> <!-- 設(shè)置spring容器filter的bean id,,如果不設(shè)置則找與filter-name一致的bean--> <param-name>targetBeanName</param-name> <param-value>shiroFilter</param-value> <filter-name>shiroFilter</filter-name> <url-pattern>/*</url-pattern>
c:創(chuàng)建一個(gè)類繼承FormAuthenticationFilter,,重寫里面的onLoginSuccess方法 protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request,ServletResponse response) throws Exception { WebUtils.getAndClearSavedRequest(request);//清除原先的地址 shiro權(quán)限框架在認(rèn)證時(shí)認(rèn)證通過之后 就會(huì)跳轉(zhuǎn)到上一次訪問的路徑 //之前有個(gè)bug,若上一次請(qǐng)求為注銷請(qǐng)求 則會(huì)無(wú)限死循環(huán) 認(rèn)證成功會(huì)立即發(fā)送注銷請(qǐng)求 WebUtils.redirectToSavedRequest(request, response, "/homePage.jsp");
d:創(chuàng)建一個(gè)類繼承AuthorizingRealm,,實(shí)現(xiàn)里面的setName,、doGetAuthenticationInfo和doGetAuthorizationInfo方法。public class ShiroRealm extends AuthorizingRealm { //使用注解注入service,所以說(shuō),,要掃描到service層 private LoginService loginService; public void setName(String name) { super.setName("shiroRealm"); }//注意,這個(gè)括號(hào)里寫的是你在spring-shiro.xml中的realm的bean中的Id名 /**doGetAuthenticationInfo 這個(gè)方法為認(rèn)證方法*/ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String username = (String) token.getPrincipal(); //根據(jù)用戶名去數(shù)據(jù)控中查詢用戶 User user = loginService.selUserByUserAccount(username); //如果用戶信息為空則返回找不到賬號(hào)異常 throw new UnknownAccountException(); //設(shè)置鹽,,也可以不設(shè)置,。鹽通常為登錄用戶名+時(shí)間戳,添加到數(shù)據(jù)庫(kù)當(dāng)中,。 //一個(gè)簡(jiǎn)單的認(rèn)證器,,如果不設(shè)置鹽的話就不能添加第三個(gè)參數(shù),; // 第一個(gè)參數(shù)可以放查詢出來(lái)的對(duì)象、也可以放獲取的登錄用戶名,,第二個(gè)參數(shù)為查詢出來(lái)的密碼,,第三個(gè)參數(shù)為鹽,第四個(gè)參數(shù)為上面設(shè)置的setName SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user,user.getUserpwd(), ByteSource.Util.bytes(salt),this.getName()); return simpleAuthenticationInfo; /**doGetAuthorizationInfo 這個(gè)方法為授權(quán)方法*/ protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) { User user = (User) principal.getPrimaryPrincipal(); //根據(jù)已經(jīng)獲取的用戶的信息查詢當(dāng)前用戶所擁有的所有的權(quán)限 List<String> permissionList = loginService.getUserPermissionByUserName(user.getUseraccount()); //New一個(gè)簡(jiǎn)單的授權(quán)器 SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); //把查詢到的用戶的權(quán)限的信息放入到授權(quán)器當(dāng)中 simpleAuthorizationInfo.addStringPermissions(permissionList); return simpleAuthorizationInfo;
第三步:
emmmmmm,,到這里配置文件差不多就配置完了,,運(yùn)行流程的話我簡(jiǎn)單說(shuō)兩句,并沒有太深入只是簡(jiǎn)單的使用,,項(xiàng)目啟動(dòng)加載web.xml,,加載spring的各種配置文件,然后進(jìn)入登錄頁(yè)面,,在進(jìn)入的時(shí)候會(huì)優(yōu)先去訪問一遍spring-shiro.xml中配置的logunUrl中的地址,,在那里面會(huì)先通過request.getAttribute()方法獲取Shiro的異常類全限定名判斷下是否為空,如果為空則繼續(xù)進(jìn)入登錄頁(yè)面,,如果不為空則去判斷查看是用戶名,、密碼或者驗(yàn)證碼中的那個(gè)錯(cuò)誤。
loginUrl的配置:(loginUrl的那個(gè)路徑就是訪問Controller中的指定方法的路徑)
public String login(HttpServletRequest request) throws Exception { //如果登錄失敗從requst中獲取認(rèn)證異常信息,,shiroLoginFailure為shiro 的異常類全限定名,。該方法不處理認(rèn)證成功,只有在認(rèn)證失敗的時(shí)候才會(huì)進(jìn)入,。 String exceptionClassName = (String) request.getAttribute("shiroLoginFailure"); if(exceptionClassName!=null){ if(UnknownAccountException.class.getName().equals(exceptionClassName)){ throw new CustomException("賬號(hào)不存在"); }else if(IncorrectCredentialsException.class.getName().equals(exceptionClassName)){ throw new CustomException("用戶名/密碼不存在"); }else if("randomCodeError".equals(exceptionClassName)){ throw new CustomException("驗(yàn)證碼錯(cuò)誤");
在輸入賬號(hào)密碼(賬號(hào)密碼的name值分別為:username和password,,在spring-shiro.xml中引入了一個(gè)自定義form認(rèn)證過濾器,他繼承了shiro的form認(rèn)證過濾器,,在里面配置了username和password,,他會(huì)轉(zhuǎn)成相應(yīng)的格式去shiro的認(rèn)證過濾器里面進(jìn)行存儲(chǔ)數(shù)據(jù)),點(diǎn)擊登錄的時(shí)候,,會(huì)驗(yàn)證一下登錄路徑是否是在spring-shiro.xml中配置的過濾器鏈中攔截的方法路徑,,如果不是的話會(huì)繼續(xù)進(jìn)入loginUrl地址,然后繼續(xù)返回登錄頁(yè)面,。如果路徑正確,,則會(huì)進(jìn)入realm中,在realm中通過token獲取到登錄的用戶名,,然后拿用戶名去數(shù)據(jù)庫(kù)進(jìn)行查詢,,接著判斷一下,如果查詢返回的對(duì)象為空則證明用戶名錯(cuò)誤,,throw
new UnknownAccountExcecption()拋出一個(gè)用戶名錯(cuò)誤的異常,,如果不為空則繼續(xù)往下走,新new 一個(gè)SimpleAuthenticationInfo對(duì)象,,在對(duì)象中放入查詢出來(lái)的對(duì)象或者用戶名,,查詢出來(lái)的密碼,,鹽(加密處理),this.getName(獲取上面set的Name),,Shiro會(huì)比較登錄的賬號(hào)密碼和數(shù)據(jù)庫(kù)的賬號(hào)密碼是否一致,,不一致走loginurl中的路徑,一致說(shuō)明認(rèn)證通過,,走loginUrl下面的successUrl路徑,,successUrl路徑配置的是認(rèn)證通過后要跳轉(zhuǎn)的頁(yè)面。
在賬號(hào)密碼認(rèn)證成功之后會(huì)進(jìn)入realm下面的授權(quán)器的方法(doGetAuthorizationInfo),,使用形式參數(shù)對(duì)象的PrincipalCollection對(duì)象中的getPrimaryPrincipal方法獲取到在認(rèn)證器里面的SimpleAuthenticationInfo里面的第一個(gè)參數(shù),拿該參數(shù)去數(shù)據(jù)庫(kù)獲取該用戶所擁有的權(quán)限放入一個(gè)List<String>的集合當(dāng)中,,然后在new
SimpleAuthorizationInfo,,使用simpleAuthorizationInfo對(duì)象的addStringPermission方法把獲取到的權(quán)限的集合放進(jìn)去。然后返回該對(duì)象,。
上述一切都是Ok的之后會(huì)進(jìn)入spring-shiro.xml中的successUrl所指向的頁(yè)面,,在這個(gè)頁(yè)面可以使用Shiro的各種Jsp標(biāo)簽,當(dāng)然前提是需要導(dǎo)入Shiro的標(biāo)簽庫(kù),。
判斷該用戶是否有權(quán)限訪問某一個(gè)方法的時(shí)候需要在方法的上面加入@RequiresPermission("user:add")之類的注解,,括號(hào)里放的是權(quán)限表中的權(quán)限字段,有權(quán)限則能訪問,,無(wú)權(quán)限就報(bào)一個(gè)異常,。
要判斷是否可以使用方法的話就需要開啟對(duì)Shiro注解的支持了,我是在spring-mvc.xml中加入了
<!-- 開啟aop,,對(duì)類代理 --> <aop:config proxy-target-class="true"></aop:config> org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager" />
還有無(wú)權(quán)限訪問的話可以在加上<!-- 使用shiro注解 由于源碼問題需要自己捕獲異常 并且跳轉(zhuǎn)頁(yè)面 若當(dāng)前用戶無(wú)權(quán)限 則跳轉(zhuǎn)到無(wú)權(quán)限頁(yè)面 <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <property name="exceptionMappings"> <prop key="org.apache.shiro.authz.UnauthorizedException">exception</prop>
就可以報(bào)異常自動(dòng)跳到exception這個(gè)異常頁(yè)面了,。
https://blog.csdn.net/lylflyy_Ljc/article/details/78967075
|