Spring-Shiro介紹及其使用

What is Apache Shiro?

????Apache Shiro是一個(gè)功能強(qiáng)大、靈活的,開源的安全框架。它可以干凈利落地處理身份驗(yàn)證、授權(quán)、企業(yè)會(huì)話管理和加密。
????Apache Shiro的首要目標(biāo)是易于使用和理解。安全通常很復(fù)雜,甚至讓人感到很痛苦,但是Shiro卻不是這樣子的。一個(gè)好的安全框架應(yīng)該屏蔽復(fù)雜性,向外暴露簡(jiǎn)單、直觀的API,來簡(jiǎn)化開發(fā)人員實(shí)現(xiàn)應(yīng)用程序安全所花費(fèi)的時(shí)間和精力。

Shiro能做什么呢?

  • 驗(yàn)證用戶身份
  • 用戶訪問權(quán)限控制,比如:1、判斷用戶是否分配了一定的安全角色。2、判斷用戶是否被授予完成某個(gè)操作的權(quán)限
  • 在非 web 或 EJB 容器的環(huán)境下可以任意使用Session API
  • 可以響應(yīng)認(rèn)證、訪問控制,或者 Session 生命周期中發(fā)生的事件
  • 可將一個(gè)或以上用戶安全數(shù)據(jù)源數(shù)據(jù)組合成一個(gè)復(fù)合的用戶 “view”(視圖)
  • 支持單點(diǎn)登錄(SSO)功能
  • 支持提供“Remember Me”服務(wù),獲取用戶關(guān)聯(lián)信息而無需登錄

????等等——都集成到一個(gè)有凝聚力的易于使用的API。
????Shiro 致力在所有應(yīng)用環(huán)境下實(shí)現(xiàn)上述功能,小到命令行應(yīng)用程序,大到企業(yè)應(yīng)用中,而且不需要借助第三方框架、容器、應(yīng)用服務(wù)器等。當(dāng)然 Shiro 的目的是盡量的融入到這樣的應(yīng)用環(huán)境中去,但也可以在它們之外的任何環(huán)境下開箱即用。

Apache Shiro Features 特性

????Apache Shiro是一個(gè)全面的、蘊(yùn)含豐富功能的安全框架。下圖為描述Shiro功能的框架圖:


????Authentication(認(rèn)證), Authorization(授權(quán)), Session Management(會(huì)話管理), Cryptography(加密)被 Shiro 框架的開發(fā)團(tuán)隊(duì)稱之為應(yīng)用安全的四大基石。那么就讓我們來看看它們吧:

  • Authentication(認(rèn)證):用戶身份識(shí)別,通常被稱為用戶“登錄”
  • Authorization(授權(quán)):訪問控制。比如某個(gè)用戶是否具有某個(gè)操作的使用權(quán)限。
  • Session Management(會(huì)話管理):特定于用戶的會(huì)話管理,甚至在非web 或 EJB 應(yīng)用程序。
  • Cryptography(加密):在對(duì)數(shù)據(jù)源使用加密算法加密的同時(shí),保證易于使用。

????還有其他的功能來支持和加強(qiáng)這些不同應(yīng)用環(huán)境下安全領(lǐng)域的關(guān)注點(diǎn)。特別是對(duì)以下的功能支持

  • Web支持:Shiro 提供的 web 支持 api ,可以很輕松的保護(hù) web 應(yīng)用程序的安全。
  • 緩存:緩存是 Apache Shiro 保證安全操作快速、高效的重要手段。
  • 并發(fā):Apache Shiro 支持多線程應(yīng)用程序的并發(fā)特性。
  • 測(cè)試:支持單元測(cè)試和集成測(cè)試,確保代碼和預(yù)想的一樣安全。
  • “Run As”:這個(gè)功能允許用戶假設(shè)另一個(gè)用戶的身份(在許可的前提下)。
  • “Remember Me”:跨 session 記錄用戶的身份,只有在強(qiáng)制需要時(shí)才需要登錄。

注意: Shiro不會(huì)去維護(hù)用戶、維護(hù)權(quán)限,這些需要我們自己去設(shè)計(jì)/提供,然后通過相應(yīng)的接口注入給Shiro

High-Level Overview 高級(jí)概述

????在概念層,Shiro 架構(gòu)包含三個(gè)主要的理念:Subject,SecurityManager和 Realm。下面的圖展示了這些組件如何相互作用,我們將在下面依次對(duì)其進(jìn)行描述。

  • Subject:當(dāng)前用戶,Subject 可以是一個(gè)人,但也可以是第三方服務(wù)、守護(hù)進(jìn)程帳戶、時(shí)鐘守護(hù)任務(wù)或者其它–當(dāng)前和軟件交互的任何事件。
  • SecurityManager:管理所有Subject,SecurityManager 是 Shiro 架構(gòu)的核心,配合內(nèi)部安全組件共同組成安全傘。
  • Realms:用于進(jìn)行權(quán)限信息的驗(yàn)證,我們自己實(shí)現(xiàn)。Realm 本質(zhì)上是一個(gè)特定的安全 DAO:它封裝與數(shù)據(jù)源連接的細(xì)節(jié),得到Shiro 所需的相關(guān)的數(shù)據(jù)。在配置 Shiro 的時(shí)候,你必須指定至少一個(gè)Realm 來實(shí)現(xiàn)認(rèn)證(authentication)和/或授權(quán)(authorization)。

????我們需要實(shí)現(xiàn)Realms的Authentication 和 Authorization。其中 Authentication 是用來驗(yàn)證用戶身份,Authorization 是授權(quán)訪問控制,用于對(duì)用戶進(jìn)行的操作授權(quán),證明該用戶是否允許進(jìn)行當(dāng)前操作,如訪問某個(gè)鏈接,某個(gè)資源文件等。

快速上手
pom.xml
<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>net.sourceforge.nekohtml</groupId>
            <artifactId>nekohtml</artifactId>
            <version>1.9.22</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
    </dependencies>
Shiro 配置
@Configuration
public class ShiroConfig {
    @Bean
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
        System.out.println("ShiroConfiguration.shirFilter()");
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //攔截器.
        Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
        // 配置不會(huì)被攔截的鏈接 順序判斷
        filterChainDefinitionMap.put("/static/**", "anon");
        //配置退出 過濾器,其中的具體的退出代碼Shiro已經(jīng)替我們實(shí)現(xiàn)了
        filterChainDefinitionMap.put("/logout", "logout");
        //<!-- 過濾鏈定義,從上向下順序執(zhí)行,一般將/**放在最為下邊 -->:這是一個(gè)坑呢,一不小心代碼就不好使了;
        //<!-- authc:所有url都必須認(rèn)證通過才可以訪問; anon:所有url都都可以匿名訪問-->
        filterChainDefinitionMap.put("/**", "authc");
        // 如果不設(shè)置默認(rèn)會(huì)自動(dòng)尋找Web工程根目錄下的"/login.jsp"頁面
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 登錄成功后要跳轉(zhuǎn)的鏈接
        shiroFilterFactoryBean.setSuccessUrl("/index");

        //未授權(quán)界面;
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    @Bean
    public MyShiroRealm myShiroRealm(){
        MyShiroRealm myShiroRealm = new MyShiroRealm();
        return myShiroRealm;
    }


    @Bean
    public SecurityManager securityManager(){
        DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
        securityManager.setRealm(myShiroRealm());
        return securityManager;
    }
}
Filter Chain定義說明:
  1. 一個(gè)URL可以配置多個(gè)Filter,使用逗號(hào)分隔
  2. 當(dāng)設(shè)置多個(gè)過濾器時(shí),全部驗(yàn)證通過,才視為通過
  3. 部分過濾器可指定參數(shù),如perms,roles
Shiro內(nèi)置的FilterChain:
Filter Name Class
anon org.apache.shiro.web.filter.authc.AnonymousFilter
authc org.apache.shiro.web.filter.authc.FormAuthenticationFilter
authcBasic org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
perms org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
port org.apache.shiro.web.filter.authz.PortFilter
rest org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
roles org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
ssl org.apache.shiro.web.filter.authz.SslFilter
user org.apache.shiro.web.filter.authc.UserFilter
  • anon:所有url都都可以匿名訪問
  • authc: 需要認(rèn)證才能進(jìn)行訪問
  • user:配置記住我或認(rèn)證通過可以訪問

登錄認(rèn)證實(shí)現(xiàn)
????在認(rèn)證、授權(quán)內(nèi)部實(shí)現(xiàn)機(jī)制中都有提到,最終處理都將交給Real進(jìn)行處理。因?yàn)樵赟hiro中,最終是通過Realm來獲取應(yīng)用程序中的用戶、角色及權(quán)限信息的。通常情況下,在Realm中會(huì)直接從我們的數(shù)據(jù)源中獲取Shiro需要的驗(yàn)證信息。可以說,Realm是專用于安全框架的DAO.
Shiro的認(rèn)證過程最終會(huì)交由Realm執(zhí)行,這時(shí)會(huì)調(diào)用Realm的getAuthenticationInfo(token)方法。
????該方法主要執(zhí)行以下操作:

  1. 檢查提交的進(jìn)行認(rèn)證的令牌信息
  2. 根據(jù)令牌信息從數(shù)據(jù)源(通常為數(shù)據(jù)庫)中獲取用戶信息
  3. 對(duì)用戶信息進(jìn)行匹配驗(yàn)證。
  4. 驗(yàn)證通過將返回一個(gè)封裝了用戶信息的AuthenticationInfo實(shí)例。
  5. 驗(yàn)證失敗則拋出AuthenticationException異常信息。

????而在我們的應(yīng)用程序中要做的就是自定義一個(gè)Realm類,繼承AuthorizingRealm抽象類,重載doGetAuthenticationInfo(),重寫獲取用戶信息的方法。

doGetAuthenticationInfo的重寫

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
        throws AuthenticationException {
    System.out.println("MyShiroRealm.doGetAuthenticationInfo()");
    //獲取用戶的輸入的賬號(hào).
    String username = (String)token.getPrincipal();
    System.out.println(token.getCredentials());
    //通過username從數(shù)據(jù)庫中查找 User對(duì)象,如果找到,沒找到.
    //實(shí)際項(xiàng)目中,這里可以根據(jù)實(shí)際情況做緩存,如果不做,Shiro自己也是有時(shí)間間隔機(jī)制,2分鐘內(nèi)不會(huì)重復(fù)執(zhí)行該方法
    UserInfo userInfo = userInfoService.findByUsername(username);
    System.out.println("----->>userInfo="+userInfo);
    if(userInfo == null){
        return null;
    }
    SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
            userInfo, //用戶名
            userInfo.getPassword(), //密碼
            ByteSource.Util.bytes(userInfo.getCredentialsSalt()),//salt=username+salt
            getName()  //realm name
    );
    return authenticationInfo;
}

鏈接權(quán)限的實(shí)現(xiàn)
????shiro的權(quán)限授權(quán)是通過繼承AuthorizingRealm抽象類,重載doGetAuthorizationInfo()當(dāng)訪問到頁面的時(shí)候,鏈接配置了相應(yīng)的權(quán)限或者shiro標(biāo)簽才會(huì)執(zhí)行此方法否則不會(huì)執(zhí)行,所以如果只是簡(jiǎn)單的身份認(rèn)證沒有權(quán)限的控制的話,那么這個(gè)方法可以不進(jìn)行實(shí)現(xiàn),直接返回null即可。在這個(gè)方法中主要是使用類:SimpleAuthorizationInfo進(jìn)行角色的添加和權(quán)限的添加。

@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    System.out.println("權(quán)限配置-->MyShiroRealm.doGetAuthorizationInfo()");
    SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
    UserInfo userInfo  = (UserInfo)principals.getPrimaryPrincipal();
    for(SysRole role:userInfo.getRoleList()){
        authorizationInfo.addRole(role.getRole());
        for(SysPermission p:role.getPermissions()){
            authorizationInfo.addStringPermission(p.getPermission());
        }
    }
    return authorizationInfo;
}

????當(dāng)然也可以添加set集合:roles是從數(shù)據(jù)庫查詢的當(dāng)前用戶的角色,stringPermissions是從數(shù)據(jù)庫查詢的當(dāng)前用戶對(duì)應(yīng)的權(quán)限

authorizationInfo.setRoles(roles);
authorizationInfo.setStringPermissions(stringPermissions);

????就是說如果在shiro配置文件中添加了 filterChainDefinitionMap.put(“/add”, “perms[權(quán)限添加]”) 就說明訪問 /add 這個(gè)鏈接必須要有“權(quán)限添加”這個(gè)權(quán)限才可以訪問,如果在shiro配置文件中添加了 filterChainDefinitionMap.put(“/add”, “roles[100002],perms[權(quán)限添加]”) 就說明訪問 /add 這個(gè)鏈接必須要有“權(quán)限添加”這個(gè)權(quán)限和具有“100002”這個(gè)角色才可以訪問。

參考附錄:

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,119評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,382評(píng)論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,038評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,853評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,616評(píng)論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,112評(píng)論 1 323
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,192評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,355評(píng)論 0 288
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,869評(píng)論 1 334
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,727評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,928評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,467評(píng)論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,165評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,570評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,813評(píng)論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,585評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,892評(píng)論 2 372

推薦閱讀更多精彩內(nèi)容

  • 說明:本文很多觀點(diǎn)和內(nèi)容來自互聯(lián)網(wǎng)以及各種資料,如果侵犯了您的權(quán)益,請(qǐng)及時(shí)聯(lián)系我,我會(huì)刪除相關(guān)內(nèi)容。 權(quán)限管理 基...
    寇寇寇先森閱讀 7,612評(píng)論 8 76
  • 安全應(yīng)該是互聯(lián)網(wǎng)公司的一道生命線,幾乎任何的公司都會(huì)涉及到這方面的需求。在Java領(lǐng)域一般有Spring Secu...
    滄海一粟謙閱讀 2,459評(píng)論 0 3
  • title: 初識(shí)Shirotags: shirocategories: shiro 若圖片無法顯示,請(qǐng)前往我的博...
    codingXiaxw閱讀 579評(píng)論 1 0
  • http://blog.csdn.net/renfufei/article/details/49230943htt...
    博瑜閱讀 208評(píng)論 0 0
  • 李尋歡,尋歡,卻半生郁郁寡歡,讓人唏噓不已。他是江湖人稱“小李飛刀,例無虛發(fā)”的李大俠,一把平凡的飛刀在手,嚇破多...
    樸玄閱讀 1,092評(píng)論 5 0