10-shiro

簡介

這里新開一篇文章坐梯,記錄下shiro的相關(guān)知識震嫉。
Shiro不會去維護(hù)用戶森瘪、維護(hù)權(quán)限;這些需要我們自己去設(shè)計(jì)/提供票堵;然后通過相應(yīng)的接口注入給Shiro即可扼睬。
首先,我們從外部來看Shiro吧悴势,即從應(yīng)用程序角度的來觀察如何使用Shiro完成工作窗宇。如下圖:


image.png
  • Subject:主體,代表了當(dāng)前“用戶”特纤,這個用戶不一定是一個具體的人军俊,與當(dāng)前應(yīng)用交互的任何東西都是Subject,如網(wǎng)絡(luò)爬蟲捧存,機(jī)器人等粪躬;即一個抽象概念担败;所有Subject都綁定到SecurityManager,與Subject的所有交互都會委托給SecurityManager镰官;可以把Subject認(rèn)為是一個門面提前;SecurityManager才是實(shí)際的執(zhí)行者;

  • SecurityManager:安全管理器朋魔;即所有與安全有關(guān)的操作都會與SecurityManager交互岖研;且它管理著所有Subject卿操;可以看出它是Shiro的核心警检,它負(fù)責(zé)與后邊介紹的其他組件進(jìn)行交互,如果學(xué)習(xí)過SpringMVC害淤,你可以把它看成DispatcherServlet前端控制器扇雕;

  • Realm:域,Shiro從從Realm獲取安全數(shù)據(jù)(如用戶窥摄、角色镶奉、權(quán)限),就是說SecurityManager要驗(yàn)證用戶身份崭放,那么它需要從Realm獲取相應(yīng)的用戶進(jìn)行比較以確定用戶身份是否合法哨苛;也需要從Realm得到用戶相應(yīng)的角色/權(quán)限進(jìn)行驗(yàn)證用戶是否能進(jìn)行操作;可以把Realm看成DataSource币砂,即安全數(shù)據(jù)源建峭。

  • 從以上也可以看出,Shiro不提供維護(hù)用戶/權(quán)限决摧,而是通過Realm讓開發(fā)人員自己注入亿蒸。

身份驗(yàn)證

身份驗(yàn)證,即在應(yīng)用中誰能證明他就是他本人掌桩。一般提供如他們的身份ID一些標(biāo)識信息來表明他就是他本人边锁,如提供身份證,用戶名/密碼來證明波岛。

在shiro中茅坛,用戶需要提供principals (身份)和credentials(證明)給shiro,從而應(yīng)用能驗(yàn)證用戶身份:

  • principals:身份则拷,即主體的標(biāo)識屬性灰蛙,可以是任何東西,如用戶名隔躲、郵箱等摩梧,唯一即可。一個主體可以有多個principals宣旱,但只有一個Primary principals仅父,一般是用戶名/密碼/手機(jī)號。

  • credentials:證明/憑證,即只有主體知道的安全值笙纤,如密碼/數(shù)字證書等耗溜。
    最常見的principals和credentials組合就是用戶名/密碼了。接下來先進(jìn)行一個基本的身份認(rèn)證省容。

  • Realm Realm:域抖拴,Shiro從從Realm獲取安全數(shù)據(jù)(如用戶、角色腥椒、權(quán)限)阿宅,就是說SecurityManager要驗(yàn)證用戶身份,那么它需要從Realm獲取相應(yīng)的用戶進(jìn)行比較以確定用戶身份是否合法笼蛛;也需要從Realm得到用戶相應(yīng)的角色/權(quán)限進(jìn)行驗(yàn)證用戶是否能進(jìn)行操作洒放;可以把Realm看成DataSource,即安全數(shù)據(jù)源滨砍。

  • 多Realm配置往湿。 會按照realm聲明的順序進(jìn)行使用

  • 自定義的Realm一般繼承AuthorizingRealm即可。

授權(quán)

Shiro支持三種方式的權(quán)限控制

  • 編程式:通過寫if/else授權(quán)代碼塊完成:
    Subject subject = SecurityUtils.getSubject();
    if(subject.hasRole(“admin”)) { ... }

  • 注解式:通過在執(zhí)行的Java方法上放置相應(yīng)的注解完成: 沒有權(quán)限將拋出相應(yīng)的異常惋戏;
    @RequiresRoles("admin")
    public void hello() {
    //有權(quán)限
    }

  • JSP/GSP標(biāo)簽:在JSP/GSP頁面通過相應(yīng)的標(biāo)簽完成:
    <shiro:hasRole name="admin">
    <!— 有權(quán)限 —>
    </shiro:hasRole>

角色/資源
Shiro提供的checkRole/checkRoles和hasRole/hasAllRoles领追,不同的地方是它在判斷為假的情況下會拋出UnauthorizedException異常
Shiro提供了isPermitted和isPermittedAll用于判斷用戶是否擁有某個權(quán)限或所有權(quán)限

與Web集成

Shiro提供了與Web集成的支持,其通過一個ShiroFilter入口來攔截需要安全控制的URL响逢,然后進(jìn)行相應(yīng)的控制绒窑,ShiroFilter類似于如Strut2/SpringMVC這種web框架的前端控制器,其是安全控制的入口點(diǎn)龄句,其負(fù)責(zé)讀取配置(如ini配置文件)回论,然后判斷URL是否需要登錄/權(quán)限等工作。

  • mvc的話就配置filter
  • springboot的話就ShiroFilterFactoryBean

會話管理

Shiro提供了完整的企業(yè)級會話管理功能分歇,不依賴于底層容器(如web容器tomcat)傀蓉,不管JavaSE還是JavaEE環(huán)境都可以使用,提供了會話管理职抡、會話事件監(jiān)聽葬燎、會話存儲/持久化、容器無關(guān)的集群缚甩、失效/過期支持谱净、對Web的透明支持、SSO單點(diǎn)登錄的支持等特性擅威。即直接使用Shiro的會話管理可以直接替換如Web容器的會話管理壕探。

會話管理器管理著應(yīng)用中所有Subject的會話的創(chuàng)建、維護(hù)郊丛、刪除李请、失效瞧筛、驗(yàn)證等工作。是Shiro的核心組件导盅,頂層組件SecurityManager直接繼承了SessionManager较幌,且提供了SessionsSecurityManager實(shí)現(xiàn)直接把會話管理委托給相應(yīng)的SessionManager,DefaultSecurityManager及DefaultWebSecurityManager默認(rèn)SecurityManager都繼承了SessionsSecurityManager

Shiro提供了三個默認(rèn)實(shí)現(xiàn):

  • DefaultSessionManager:DefaultSecurityManager使用的默認(rèn)實(shí)現(xiàn)白翻,用于JavaSE環(huán)境乍炉;

  • ServletContainerSessionManager:DefaultWebSecurityManager使用的默認(rèn)實(shí)現(xiàn),用于Web環(huán)境滤馍,其直接使用Servlet容器的會話岛琼;并且可以設(shè)置session相關(guān)cookies的name等信息。

  • DefaultWebSessionManager:用于Web環(huán)境的實(shí)現(xiàn)纪蜒,可以替代ServletContainerSessionManager衷恭,自己維護(hù)著會話此叠,直接廢棄了Servlet容器的會話管理纯续。

  • 可以正常配置會話監(jiān)聽器

  • 會話存儲/持久化 Shiro提供SessionDAO用于會話的CRUD,即DAO(Data Access Object)模式實(shí)現(xiàn):

  • 會話驗(yàn)證

單點(diǎn)登錄

Shiro 1.2開始提供了Jasig CAS單點(diǎn)登錄的支持灭袁,單點(diǎn)登錄主要用于多系統(tǒng)集成猬错。
大體流程如下:
1、訪問客戶端需要登錄的頁面http://localhost:9080/ client/茸歧,此時會跳到單點(diǎn)登錄服務(wù)器https://localhost:8443/ server/login?service=https://localhost:9443/ client/cas倦炒;
2、如果此時單點(diǎn)登錄服務(wù)器也沒有登錄的話软瞎,會顯示登錄表單頁面逢唤,輸入用戶名/密碼進(jìn)行登錄;
3涤浇、登錄成功后服務(wù)器端會回調(diào)客戶端傳入的地址:https://localhost:9443/client/cas?ticket=ST-1-eh2cIo92F9syvoMs5DOg-cas01.example.org鳖藕,且?guī)е粋€ticket;
4只锭、客戶端會把ticket提交給服務(wù)器來驗(yàn)證ticket是否有效著恩;如果有效服務(wù)器端將返回用戶身份;
5蜻展、客戶端可以再根據(jù)這個用戶身份獲取如當(dāng)前系統(tǒng)用戶/角色/權(quán)限信息喉誊。

接入流程:

  • 導(dǎo)入shiro-cas 的依賴
  • 自定義CasRealm
public class MyCasRealm extends CasRealm {  
    private UserService userService;  
    public void setUserService(UserService userService) {  
        this.userService = userService;  
    }  
    @Override  
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {  
        String username = (String)principals.getPrimaryPrincipal();  
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();  
        authorizationInfo.setRoles(userService.findRoles(username));  
        authorizationInfo.setStringPermissions(userService.findPermissions(username));  
        return authorizationInfo;  
    }  
}   
  • 配置
<bean id="casRealm" class="com.github.zhangkaitao.shiro.chapter13.realm.MyCasRealm">  
    <property name="userService" ref="userService"/>  
    ……  
    <property name="casServerUrlPrefix" value="https://localhost:8443/chapter14-server"/>  
    <property name="casService" value="https://localhost:9443/chapter14-client/cas"/>  
</bean>  

casServerUrlPrefix:是CAS Server服務(wù)器端地址;
casService:是當(dāng)前應(yīng)用CAS服務(wù)URL纵顾,即用于接收并處理登錄成功后的Ticket的伍茄;

  • cas的token驗(yàn)證
<bean id="casFilter" class="org.apache.shiro.cas.CasFilter">  
    <property name="failureUrl" value="/casFailure.jsp"/>  
</bean>  
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">  
    <property name="securityManager" ref="securityManager"/>  
    <property name="loginUrl" value="https://localhost:8443/chapter14-server/login?service=https://localhost:9443/chapter14-client/cas"/>  
    <property name="successUrl" value="/"/>  
    <property name="filters">  
        <util:map>  
            <entry key="cas" value-ref="casFilter"/>  
        </util:map>  
    </property>  
    <property name="filterChainDefinitions">  
        <value>  
            /casFailure.jsp = anon  
            /cas = cas  
            /logout = logout  
            /** = user  
        </value>  
    </property>  
</bean>   

loginUrl:https://localhost:8443/chapter15-server/login表示服務(wù)端端登錄地址,登錄成功后跳轉(zhuǎn)到?service參數(shù)對于的地址進(jìn)行客戶端驗(yàn)證及登錄施逾;

“/cas=cas”:即/cas地址是服務(wù)器端回調(diào)地址敷矫,使用CasFilter獲取Ticket進(jìn)行登錄贞盯。

OAuth2集成

OAuth角色

  • 資源擁有者(resource owner):能授權(quán)訪問受保護(hù)資源的一個實(shí)體,可以是一個人沪饺,那我們稱之為最終用戶躏敢;如新浪微博用戶zhangsan;

  • 資源服務(wù)器(resource server):存儲受保護(hù)資源整葡,客戶端通過access token請求資源件余,資源服務(wù)器響應(yīng)受保護(hù)資源給客戶端;存儲著用戶zhangsan的微博等信息遭居。

  • 授權(quán)服務(wù)器(authorization server):成功驗(yàn)證資源擁有者并獲取授權(quán)之后啼器,授權(quán)服務(wù)器頒發(fā)授權(quán)令牌(Access Token)給客戶端。

  • 客戶端(client):如新浪微博客戶端weico俱萍、微格等第三方應(yīng)用端壳,也可以是它自己的官方應(yīng)用;其本身不存儲資源枪蘑,而是資源擁有者授權(quán)通過后损谦,使用它的授權(quán)(授權(quán)令牌)訪問受保護(hù)資源,然后客戶端把相應(yīng)的數(shù)據(jù)展示出來/提交到服務(wù)器岳颇≌占瘢“客戶端”術(shù)語不代表任何特定實(shí)現(xiàn)(如應(yīng)用運(yùn)行在一臺服務(wù)器、桌面话侧、手機(jī)或其他設(shè)備)栗精。

認(rèn)證服務(wù)流程

資源服務(wù)流程

  • 1粤策、首先通過如http://localhost:8080/chapter17-server/userInfo? access_token=828beda907066d058584f37bcfd597b6進(jìn)行訪問;
  • 2误窖、該控制器會驗(yàn)證access token的有效性叮盘;如果無效了將返回相應(yīng)的錯誤秩贰,客戶端再重新進(jìn)行授權(quán);
  • 3柔吼、如果有效毒费,則返回當(dāng)前登錄用戶的用戶名。

客戶端

  • OAuth2AuthenticationFilter愈魏。 該filter的作用類似于FormAuthenticationFilter用于oauth2客戶端的身份驗(yàn)證控制觅玻;如果當(dāng)前用戶還沒有身份驗(yàn)證,首先會判斷url中是否有code(服務(wù)端返回的auth code)培漏,如果沒有則重定向到服務(wù)端進(jìn)行登錄并授權(quán)溪厘,然后返回auth code;接著OAuth2AuthenticationFilter會用auth code創(chuàng)建OAuth2Token牌柄,然后提交給Subject.login進(jìn)行登錄畸悬;接著OAuth2Realm會根據(jù)OAuth2Token進(jìn)行相應(yīng)的登錄邏輯。
  • 1珊佣、首先判斷有沒有服務(wù)端返回的error參數(shù)蹋宦,如果有則直接重定向到失敗頁面;
  • 2彩扔、接著如果用戶還沒有身份驗(yàn)證妆档,判斷是否有auth code參數(shù)(即是不是服務(wù)端授權(quán)之后返回的)僻爽,如果沒有則重定向到服務(wù)端進(jìn)行授權(quán)虫碉;
  • 3、否則調(diào)用executeLogin進(jìn)行登錄胸梆,通過auth code創(chuàng)建OAuth2Token提交給Subject進(jìn)行登錄敦捧;
  • 4、登錄成功將回調(diào)onLoginSuccess方法重定向到成功頁面碰镜;
  • 5兢卵、登錄失敗則回調(diào)onLoginFailure重定向到失敗頁面。

OAuth2Realm

  • 此Realm首先只支持OAuth2Token類型的Token绪颖;然后通過傳入的auth code去換取access token秽荤;再根據(jù)access token去獲取用戶信息(用戶名),然后根據(jù)此信息創(chuàng)建AuthenticationInfo柠横;如果需要AuthorizationInfo信息窃款,可以根據(jù)此處獲取的用戶名再根據(jù)自己的業(yè)務(wù)規(guī)則去獲取。

總結(jié)

  • 認(rèn)證服務(wù)提供登錄功能牍氛,返回authCode
  • 客戶端的Realm通過authCode去認(rèn)證服務(wù)器獲取accessToken
  • 用accessToken訪問資源服務(wù)器中的資源晨继,獲取用戶信息(這步是不是可以換成從token中提取用戶名,這樣就不必要調(diào)用資源服務(wù)接口了)
  • 拿到用戶信息后客戶端shiro做登錄操作搬俊,本地處理角色等

JWT

下面介紹一下無狀態(tài)的紊扬,基于jwt的認(rèn)證處理方式蜒茄。
比如使用了oauth2,得到了一個accessToken餐屎,就當(dāng)作是jwt檀葛,里邊包含了用戶名。然后沒有資源服務(wù)提供什么用戶信息接口了腹缩。

有狀態(tài)方式

  • 有個jwt驻谆,每次請求驗(yàn)證這個jwt(可能有緩存,每次不需要真正執(zhí)行查詢等操作)
  • 驗(yàn)證之后庆聘,存到session中一些東西胜臊,以后會用

無狀態(tài)方式

  • 有個jwt,每次請求驗(yàn)證jwt伙判,從中獲取用戶名象对,角色等,這些都在jwt中本身包含(也可能有緩存和上邊是一樣的)
  • 每次驗(yàn)證宴抚,可以刷新token有效期
  • 可以使用redis等存儲jwt用來驗(yàn)證
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末勒魔,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子菇曲,更是在濱河造成了極大的恐慌冠绢,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,607評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件常潮,死亡現(xiàn)場離奇詭異弟胀,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)喊式,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評論 3 395
  • 文/潘曉璐 我一進(jìn)店門孵户,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人岔留,你說我怎么就攤上這事夏哭。” “怎么了献联?”我有些...
    開封第一講書人閱讀 164,960評論 0 355
  • 文/不壞的土叔 我叫張陵竖配,是天一觀的道長。 經(jīng)常有香客問我里逆,道長进胯,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,750評論 1 294
  • 正文 為了忘掉前任运悲,我火速辦了婚禮龄减,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘班眯。我一直安慰自己希停,他們只是感情好烁巫,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著宠能,像睡著了一般亚隙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上违崇,一...
    開封第一講書人閱讀 51,604評論 1 305
  • 那天阿弃,我揣著相機(jī)與錄音,去河邊找鬼羞延。 笑死渣淳,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的伴箩。 我是一名探鬼主播入愧,決...
    沈念sama閱讀 40,347評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼嗤谚!你這毒婦竟也來了棺蛛?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,253評論 0 276
  • 序言:老撾萬榮一對情侶失蹤巩步,失蹤者是張志新(化名)和其女友劉穎旁赊,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體椅野,經(jīng)...
    沈念sama閱讀 45,702評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡终畅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了鳄橘。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片声离。...
    茶點(diǎn)故事閱讀 40,015評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖瘫怜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情本刽,我是刑警寧澤鲸湃,帶...
    沈念sama閱讀 35,734評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站子寓,受9級特大地震影響暗挑,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜斜友,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評論 3 330
  • 文/蒙蒙 一炸裆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鲜屏,春花似錦烹看、人聲如沸国拇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽酱吝。三九已至,卻和暖如春土思,著一層夾襖步出監(jiān)牢的瞬間务热,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評論 1 270
  • 我被黑心中介騙來泰國打工己儒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留崎岂,地道東北人。 一個月前我還...
    沈念sama閱讀 48,216評論 3 371
  • 正文 我出身青樓闪湾,卻偏偏與公主長得像该镣,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子响谓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評論 2 355