shiro 框架的使用

網上有很多文章介紹如何使用shiro坛猪,如果你是第一次接觸shiro,各種文章可能看了好多篇也還是一頭霧水污秆。筆者當初也是這樣劈猪,想找一個簡單的入門教程看看,了解一下shiro是什么良拼,能做什么战得,以及怎么操作,結果看了很多也不太明白庸推,走了很多彎路常侦。因為網上的文章大多是以過來者的身份介紹浇冰,在學習一個新東西的時候膜蠢,如果你折騰了好長時間略号,過來了犀斋,就能明白那些文章在講什么否彩,哪些是重點,哪些是坑古沥;如果你折騰了好長時間,還是云里霧里,那么恭喜你致讥,因為你看到了這篇文章,我會用初學者的眼光器赞,帶你在最短時間內抓住shiro的本質垢袱。

在學新東西的時候,要問三個問題:這個新東西是什么港柜?能做什么请契?我怎么操作使用它?
如果有余力夏醉,最好再問一個為什么爽锥,看一看它的源碼,理解它的原理畔柔,這樣從原理到實現細節(jié)的方方面面就都掌握了氯夷。

Apache Shiro是一個強大且易用的Java安全框架,能做身份驗證、授權靶擦、密碼學和會話管理腮考。
請參考shiro官網介紹

Shiro 最主要的兩個部分就是認證和授權,Shiro通過過濾攔截請求實現認證或授權玄捕。

shiro 主要框架圖


shiro.png

Subject

Subject即主體踩蔚,外部應用與subject進行交互,subject記錄了當前操作用戶枚粘,將用戶的概念理解為當前操作的主體馅闽,可能是一個通過瀏覽器請求的用戶,也可能是一個運行的程序赌结。 Subject在shiro中是一個接口捞蛋,接口中定義了很多認證授相關的方法,外部程序通過subject進行認證授柬姚,而subject是通過SecurityManager安全管理器進行認證授權拟杉。

SecurityManager

SecurityManager即安全管理器,對全部的subject進行安全管理量承,它是shiro的核心搬设,負責對所有的subject進行安全管理穴店。通過SecurityManager可以完成subject的認證、授權等拿穴,實質上SecurityManager是通過Authenticator進行認證泣洞,通過Authorizer進行授權,通過SessionManager進行會話管理等默色。

SecurityManager是一個接口球凰,繼承了Authenticator, Authorizer, SessionManager這三個接口。

Authenticator

Authenticator即認證器腿宰,對用戶身份進行認證呕诉,Authenticator是一個接口,shiro提供ModularRealmAuthenticator實現類吃度,通過ModularRealmAuthenticator基本上可以滿足大多數需求甩挫,也可以自定義認證器。

Authorizer

Authorizer即授權器椿每,用戶通過認證器認證通過伊者,在訪問功能時需要通過授權器判斷用戶是否有此功能的操作權限。

realm

Realm即領域间护,相當于datasource數據源亦渗,securityManager進行安全認證需要通過Realm獲取用戶權限數據,比如:如果用戶身份數據在數據庫那么realm就需要從數據庫獲取用戶身份信息汁尺。realm不僅僅是從數據源取數據央碟,在realm中還有認證、授權校驗的相關的代碼均函。

sessionManager

sessionManager即會話管理亿虽,shiro框架定義了一套會話管理,它不依賴web容器的session苞也,所以shiro可以使用在非web應用上洛勉,也可以將分布式應用的會話集中在一點管理,此特性可使它實現單點登錄如迟。

SessionDAO

SessionDAO即會話dao收毫,是對session會話操作的一套接口,比如要將session存儲到數據庫殷勘,可以通過jdbc將會話存儲到數據庫此再。

CacheManager

CacheManager即緩存管理,將用戶權限數據存儲在緩存玲销,這樣可以提高性能输拇。

Cryptography

Cryptography即密碼管理,shiro提供了一套加密/解密的組件贤斜,方便開發(fā)策吠。比如提供常用的散列逛裤、加/解密等功能。

從應用程序的角度看猴抹,它的工作流程是這樣的:

shiro_flow.jpeg

簡單的來說带族,它的工作流程是:應用代碼被包裝成 subject,然后 SecurityManager 通過Realm獲取用戶權限數據對其實現登錄和授權的校驗蟀给,經過授權就可以正常訪問了蝙砌。

Shiro認證與授權在Web中的實現

第一步:添加jar包
第二步:配置web.xml shiro過濾器
第三步:自定義Realm ,繼承 AuthorizingRealm 跋理,重寫 AuthorizationInfo(授權) 拍霜,重寫 AuthenticationInfo(認證)
第四步:配置spring-shiro.xml
第五步:在spring-mvc.xml中配置權限的控制 異常的跳轉
第六步:在controller中使用

  • 第一步:添加jar包,版本1.3.2薪介,最近的穩(wěn)定版本
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.3.2</version>
        </dependency>
  • 第二步:配置web.xml shiro過濾器;targetFilterLifecycle 為 true 代表由 spring 控制該 filter 的生命周期越驻。shiro 源碼中的 demo 配的是/*汁政,因為我只想控制用戶訪問后端接口的權限,靜態(tài)資源可以隨便訪問缀旁,所以后端接口都用的api開頭记劈,讓shiro過濾器只對api開頭的請求生效。
<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>/api/*</url-pattern>
    </filter-mapping>
  • 第三步:自定義Realm 并巍;shiro 中 realm 是進行認證和授權的組件目木,自帶了幾種實現,比如 jdbcRealm 和 iniRealm懊渡,實際項目中肯定都是自己實現 realm, 這里自定義 MyRealm 繼承 AuthorizingRealm刽射,分別實現認證和授權的方法。
public class MyRealm extends AuthorizingRealm {
   //用來做測試的數據剃执,實際開發(fā)中都是從數據庫中取的
    private static final String USERNAME = "admin";
    private static final String PASSWORD = "123456";

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
      //principals.getPrimaryPrincipal()獲得的就是當前用戶名誓禁,權限取值在實際中是從數據庫中取出的
        Set<String> permissions = new HashSet<String>();
        permissions.add("sys:page1");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.setStringPermissions(permissions);
        return info;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username = (String) token.getPrincipal();
        String password = new String((char[]) token.getCredentials());
        //賬號錯誤
        if(!username.equals(USERNAME)) {
            throw new AuthenticationException("賬號不正確");
        }

        //密碼錯誤
        if(!password.equals(PASSWORD)) {
            throw new IncorrectCredentialsException("密碼不正確");
        }

//getName()返回該realm的名字,代表該認證信息的來源是該realm
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username, password, getName());
        return info;
    }
}

doGetAuthenticationInfo是認證的方法肾档,當用戶登陸的時候會調用
doGetAuthorizationInfo是授權的方法摹恰,在攔截器中進行權限校驗的時候會調用

  • 第四步:配置spring-shiro.xml
    配置 ShiroFilterFactoryBean ,注意 id 一定要和 web.xml 中 的 filter-name 一致怒见,否則 proxyFilter 找不到實際 filter俗慈。
    shiro 的內置過濾器有anon、authc遣耍、logout闺阱、perms、roles等等,可以分成兩類舵变,一類會調用 realm 中的認證方法馏颂,一類會調用 realm中 的授權方法示血,更詳細的請自行百度。
<!--配置全局權限過濾器-->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <!--設置沒有登錄時的跳轉地址-->
        <property name="loginUrl" value="/static/html/login.html"/>
        <!--設置權限不足時的跳轉地址-->
        <property name="unauthorizedUrl" value="/static/html/error.html"/>
        <!--對哪個后端接口使用哪個過濾器進行配置救拉,等號后邊是shiro內置過濾器的名字-->
        <property name="filterChainDefinitions">
            <value>
                <!--匿名訪問难审,/api/login是登陸接口,當然可以隨便訪問-->
                /api/login = anon
                <!--用戶退出登錄的接口亿絮,后端不需要實現該接口告喊,logout攔截到/api/logout的url后,就自動清除登錄狀態(tài)回到首頁了-->
                <!--因為在web.xml中設置的url-parttern是/api/*派昧,隨意只有api開頭的url才會被攔截-->
                /api/logout = logout
                <!--其他所有接口都需要認證黔姜,也就是需要之前輸入過賬號密碼登錄過-->
                /** = authc
            </value>
        </property>
    </bean>
  • 在 securityManager 中注入自定義的 realm
<!--非web環(huán)境使用DefaultSecurityManager-->
   <bean id="myRealm" class="cn.shirodemo.shiro.MyRealm"/>

    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="myRealm"/>
    </bean>
  • AOP式方法級權限檢查
   <!-- AOP式方法級權限檢查  -->
  <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
      <property name="proxyTargetClass" value="true" />
  </bean>
  <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
      <property name="securityManager" ref="securityManager"/>
  </bean>
  • 第五步:在 spring-mvc.xml 中配置異常的跳轉
<!-- 未認證或未授權時跳轉必須在springmvc里面配,spring-shiro里的shirofilter配不生效 -->
   <bean   class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
       <property name="exceptionMappings">
           <props>
               <!--沒有授權的異常 -->
               <prop key="org.apache.shiro.authz.UnauthorizedException">
                   <!--捕獲該異常時跳轉的路徑 -->
                   /error
               </prop>
               <!--沒有認證的異常 -->
               <prop key="org.apache.shiro.authz.UnauthenticatedException">
                   <!--捕獲該異常時跳轉的路徑 -->
                   /error
               </prop>
           </props>
       </property>
   </bean>
  • 第六步:在controller中測試使用的驗證登入
@RequestMapping(value="/login.do",method=RequestMethod.POST)
@ResponseBody
public String login(String username,String password) {
    Map<String, Object> result = new HashMap<String, Object>();
    result.put("success", true);

    Subject subjectecurityUtils.getSubject();
    UsernamePasswordToken token = new UsernamePasswordToken(username, password);
    subject.login(token);
    
    return JSONUtils.toJSONString(result);
}

現在只需要在相應方法前加上@RequiresPermissions就可以進行權限控制了

@RequiresPermissions("sys:page1")
@RequestMapping(value="/toPage1.do",method= RequestMethod.POST)
@ResponseBody
public String toPage1() {
    Map<String, Object> result = new HashMap<String, Object>();
    result.put("success", true);
    return JSONUtils.toJSONString(result);
}

  • 最后補充一點蒂萎,web 項目的前臺頁面如何用 shiro 進行配置呢秆吵。

  • 如果是jsp頁面,在 jsp 中引入 shiro 標簽
    <%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags"%>

<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>

<shiro:authenticated> <a href="xxx.jsp">xxxxxx</a> </shiro:authenticated> 

<shiro:hasRole name="admin"> <a href="xxx.jsp">xxxxxx</a> </shiro:hasRole> 

<shiro:hasPermission name="user:create"> <a href="xxx.jsp">xxxxxx</a> </shiro:hasPermission> 

  1. guest標簽 :驗證當前用戶是否為“訪客”五慈,即未認證(包含未記啄杉拧)的用戶。
  2. user標簽 :認證通過或已記住的用戶泻拦。
  3. authenticated標簽 :已認證通過的用戶毙芜。不包含已記住的用戶。
  4. notAuthenticated標簽 :未認證通過用戶争拐,與authenticated標簽相對應腋粥。與guest標簽的區(qū)別是,該標簽包含已記住用戶架曹。
  5. principal 標簽 :輸出當前用戶信息隘冲,通常為登錄帳號信息。
  6. hasRole標簽 :驗證當前用戶是否屬于該角色绑雄。
  7. lacksRole標簽 :與hasRole標簽邏輯相反对嚼,當用戶不屬于該角色時驗證通過。
  8. hasAnyRole標簽 :驗證當前用戶是否屬于以下任意一個角色绳慎。
  9. hasPermission標簽 :驗證當前用戶是否擁有指定權限纵竖。
  10. lacksPermission標簽 :與hasPermission標簽邏輯相反,當前用戶沒有制定權限時杏愤,驗證通過靡砌。
  • 如果前臺頁面是 html,且使用了 thymeleaf 的模板引擎
<dependency>
    <groupId>com.github.theborakompanioni</groupId>
    <artifactId>thymeleaf-extras-shiro</artifactId>
    <version>2.0.0</version>
</dependency>

配置一下 thymeleaf

<bean id="templateEngine" class="org.thymeleaf.spring3.SpringTemplateEngine">
  <property name="templateResolver" ref="templateResolver" />
  <property name="additionalDialects">
    <set>
      <bean class="at.pollux.thymeleaf.shiro.dialect.ShiroDialect"/>
    </set>
  </property>
</bean>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">

這樣就可以在頁面中使用了

<p shiro:hasRole="admin"></p>
<p shiro:hasPermission="userInfo:add"></p>
  • 如果前臺頁面是 html珊楼,沒有使用模板怎么辦通殃?
    這樣就無法使用shiro標簽了,不過這里有個解決思路,前臺頁面通過ajax獲取后臺某個角色或是權限的信息画舌,然后在前臺用改變標簽樣式display:none的方法讓相應內容隱顯堕担。
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市曲聂,隨后出現的幾起案子霹购,更是在濱河造成了極大的恐慌,老刑警劉巖朋腋,帶你破解...
    沈念sama閱讀 218,607評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件齐疙,死亡現場離奇詭異,居然都是意外死亡旭咽,警方通過查閱死者的電腦和手機贞奋,發(fā)現死者居然都...
    沈念sama閱讀 93,239評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來穷绵,“玉大人轿塔,你說我怎么就攤上這事≈倌” “怎么了勾缭?”我有些...
    開封第一講書人閱讀 164,960評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長宗收。 經常有香客問我,道長亚兄,這世上最難降的妖魔是什么混稽? 我笑而不...
    開封第一講書人閱讀 58,750評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮审胚,結果婚禮上匈勋,老公的妹妹穿的比我還像新娘。我一直安慰自己膳叨,他們只是感情好洽洁,可當我...
    茶點故事閱讀 67,764評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著菲嘴,像睡著了一般饿自。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上龄坪,一...
    開封第一講書人閱讀 51,604評論 1 305
  • 那天昭雌,我揣著相機與錄音,去河邊找鬼健田。 笑死烛卧,一個胖子當著我的面吹牛,可吹牛的內容都是我干的妓局。 我是一名探鬼主播总放,決...
    沈念sama閱讀 40,347評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼呈宇,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了局雄?” 一聲冷哼從身側響起甥啄,我...
    開封第一講書人閱讀 39,253評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎哎榴,沒想到半個月后型豁,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 45,702評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡尚蝌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,893評論 3 336
  • 正文 我和宋清朗相戀三年迎变,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片飘言。...
    茶點故事閱讀 40,015評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡衣形,死狀恐怖,靈堂內的尸體忽然破棺而出姿鸿,到底是詐尸還是另有隱情谆吴,我是刑警寧澤,帶...
    沈念sama閱讀 35,734評論 5 346
  • 正文 年R本政府宣布苛预,位于F島的核電站句狼,受9級特大地震影響,放射性物質發(fā)生泄漏热某。R本人自食惡果不足惜腻菇,卻給世界環(huán)境...
    茶點故事閱讀 41,352評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望昔馋。 院中可真熱鬧筹吐,春花似錦、人聲如沸秘遏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽邦危。三九已至洋侨,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間倦蚪,已是汗流浹背凰兑。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留审丘,地道東北人吏够。 一個月前我還...
    沈念sama閱讀 48,216評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親锅知。 傳聞我的和親對象是個殘疾皇子播急,可洞房花燭夜當晚...
    茶點故事閱讀 44,969評論 2 355

推薦閱讀更多精彩內容