shiro一之例項運用
參考:http://hnbcjzj.iteye.com/blog/1706600
簡介: Shiro 是一個 Apache Incubator 專案,旨在簡化身份驗證和授權。是一個很不錯的安全框架。
借用別人寫的一個shiro框架的例項來對shiro的應用做下記錄。
1.基本概念
首先理解兩個概念:認證和授權
我開始接觸許可權的時候,並沒有明確區分認證和授權,把這兩個概念混為一團,導致在開始運用shi'ro的時候有些地方沒有理解透徹。
認證,簡單來說,就是指使用者身份是否合法,通俗的理解就是使用者是否是登入使用者,登入的使用者為認證使用者,為登入的使用者為非認證使用者。
授權,一般來說,認證是第一步,使用者經過認證以後,只能說明使用者是系統的合法使用者,但是許可權是分級別的,使用者身份合法並不代表使用者就對所有資源擁有操作許可權,要對某一個資源操作,還需要相應的許可權管理,這就是授權要就解決的問題。
2.配置
結合DEMO進行說明。
先將shiro引入web.xml
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml,classpath:spring-shiro.xml</param-value> </context-param> <!-- apache shiro許可權 --> <filter> <filter-name>shiroFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <param-name>targetFilterLifecycle</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>shiroFilter</filter-name> <url-pattern>*.do</url-pattern> </filter-mapping> <filter-mapping> <filter-name>shiroFilter</filter-name> <url-pattern>*.jsp</url-pattern> </filter-mapping>
shiro的配置檔案是spring-shiro.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd"> <description>Shiro 配置</description> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager" /> <property name="loginUrl" value="/login.jsp" /> <property name="successUrl" value="/login.jsp" /> <property name="unauthorizedUrl" value="/error/noperms.jsp" /> <property name="filterChainDefinitions"> <value> /login.jsp* = anon /login.do* = anon /index.jsp*= anon /error/noperms.jsp*= anon /*.jsp* = authc /*.do* = authc </value> </property> </bean> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <!--設定自定義realm --> <property name="realm" ref="monitorRealm" /> </bean> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" /> <!--自定義Realm 繼承自AuthorizingRealm --> <bean id="monitorRealm" class="com.shiro.service.MonitorRealm"></bean> <!-- securityManager --> <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> <property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager" /> <property name="arguments" ref="securityManager" /> </bean> <!-- Enable Shiro Annotations for Spring-configured beans. Only run after --> <!-- the lifecycleBeanProcessor has run: --> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor" /> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager" /> </bean> </beans>
在這個配置檔案中有兩個重點要關注的地方:
1)過濾器配置。在裡面有兩個型別的過濾器配置,anon和authc,anon,要注意的是anon必須配置在authc配置項的前面,否則anon配置項會失效。
Anon:不指定過濾器,不錯是這個過濾器是空的,什麼都沒做,跟沒有一樣。
Authc:驗證,這些頁面必須驗證後才能訪問,也就是我們說的登入後才能訪問。
簡單來講,anon就是指所有使用者(匿名使用者)都可以訪問的資源,authc是指認證使用者(登入使用者)才可以訪問的資源。
這裡還有其他的過濾器,我沒用,比如說授權,這個比較重要,但是這個過濾器有個不好的地方,就是要帶一個引數,所以如果配在這裡就不是很合適,因為每個頁面,或是.do的許可權不一樣,而我們也沒法事先知道他需要什麼許可權。所以這裡不配,我們在程式碼中再授權。這裡.do和.jsp後面的*表示引數,比如login.jsp?main這種,是為了匹配這種。好行了,繼續往下吧。
2)自定義realm
realm的作用就是自己來定義認證和授權的業務邏輯。可以靈活的控制權限。這裡自定義了一個monitorRealm用來自己寫許可權業務,這個後面慢慢說明。
3.認證
現在先看第一步認證。
開啟登入頁面,輸入使用者名稱/密碼點選提交,就會提交到後臺的登入actIon的方法:
@RequestMapping(params = "main")
public ModelAndView login(User user,HttpSession session, HttpServletRequest request) {
ModelAndView modelView = new ModelAndView();
Subject currentUser = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(
user.getUsercode(), EncryptUtils.encryptMD5(user.getPassword()));
token.setRememberMe(true);
try {
currentUser.login(token);
} catch (AuthenticationException e) {
modelView.addObject("message", "login errors");
modelView.setViewName("/login");
e.printStackTrace();
}
if(currentUser.isAuthenticated()){
user.setUserName("張三");
session.setAttribute("userinfo", user);
modelView.setViewName("/main");
}else{
modelView.addObject("message", "login errors");
modelView.setViewName("/login");
}
return modelView;
}
這裡有兩條語句需要說明:
Subject currentUser = SecurityUtils.getSubject() ;
currentUser就是代表當前的使用者。
UsernamePasswordToken token = new UsernamePasswordToken(user.getUsercode(),EncryptUtils.encryptMD5(user.getPassword()));
token大家叫他令牌,也就相當於一張表格,你要去驗證,你就得填個表,裡面寫好使用者名稱密碼,交給公安局的同志給。
currentUser.login(token);
這個就是校驗token,校驗過程會回撥realm裡面的認證方法:
protected AuthenticationInfo doGetAuthenticationInfo()
這裡就是自己寫認證邏輯的地方,看下這裡的業務
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken authcToken) throws AuthenticationException {
/* 這裡編寫認證程式碼 */
UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
// User user = securityApplication.findby(upToken.getUsername());
User user = new User();
user.setUsercode(token.getUsername());
user.setUserName("admin");
user.setPassword(EncryptUtils.encryptMD5("admin"));
// if (user != null) {
return new SimpleAuthenticationInfo(user.getUserName(),
user.getPassword(), getName());
}
是否呼叫這裡是在前面的配置檔案中配置的,我們前面spring裡配了這個
/*.jsp* = authc
/*.do* = authc
你配了authc過濾器,shiro會自動調currentUser.isAuthenticated()這個方法,沒有登入的將被返回
<property name="unauthorizedUrl" value="/error/noperms.jsp" />
配置的頁面。
呼叫這個認證方法後,會對使用者的使用者名稱密碼進行校驗,校驗失敗跳轉到登入頁面,校驗成功處理登入方法後的邏輯。這裡是跳轉到main主頁。
4.授權
再看第二步,授權
再main頁面上點選myjsp,會提交到user.do?myjsp,看下後臺的這個方法:
@RequestMapping(params = "myjsp")
public String home() {
Subject currentUser = SecurityUtils.getSubject();
if(currentUser.isPermitted("user.do?myjsp")){
return "my";
}else{
return "error/noperms";
}
}
這裡
Subject currentUser = SecurityUtils.getSubject();
currentUser.isPermitted("user.do?myjsp");
這兩行語句的意思是判斷當前使用者是否有訪問user.do?myjsp的許可權
呼叫currentUser.isPermitted("user.do?myjsp");此方法後會回撥realm中的授權驗證方法
protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principals)
看下這裡是如何授權的:
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals) {
/* 這裡編寫授權程式碼 */
Set<String> roleNames = new HashSet<String>();
Set<String> permissions = new HashSet<String>();
roleNames.add("admin");
permissions.add("user.do?myjsp");
permissions.add("login.do?main");
permissions.add("login.do?logout");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roleNames);
info.setStringPermissions(permissions);
return info;
}
這裡沒有用到資料庫,直接將許可權硬編碼在程式碼裡面,新增admin角色,然後給角色新增許可權,當然正常肯定是預先存放在資料庫裡面,直接從資料庫裡面查詢出來,構造成一個許可權物件返回,再與currentUser.isPermitted()中傳入的許可權引數進行對比,看看使用者是否有訪問此地址引數的許可權,沒有許可權就返回到沒有許可權的頁面。
5.總結
通過前面的demo流程,可以分析出如下幾點:
1)認證的配置一般都是配置再shiro的配置檔案,而授權設定比較靈活,一般不直接寫在shiro配置檔案裡面。
2)認證是授權的前提,通過通過了認證,才會用相應的許可權管理。
3)認證過程會呼叫reanlm裡面的認證方法doGetAuthenticationInfo(),授權會呼叫realm裡面的授權方法doGetAuthenticationInfo()
一般認證方法只會在登入的時候呼叫一次,而授權則在需要授權的地方都會呼叫,會被多次呼叫。