天天看點

shiro源碼分析(三)授權、認證、緩存的接口設計

<a href="http://my.oschina.net/pingpangkuangmo/blog/376298#">?</a>

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

<code>public</code> <code>class</code> <code>shirotest {</code>

<code>    </code><code>@test</code> 

<code>    </code><code>public</code> <code>void</code> <code>testhelloworld() { </code>

<code>        </code><code>init();</code>

<code>        </code><code>subject subject=login(</code><code>"zhang"</code><code>,</code><code>"123"</code><code>);</code>

<code>        </code><code>assert.asserttrue(subject.hasrole(</code><code>"role1"</code><code>));</code>

<code>        </code><code>assert.asserttrue(subject.hasrole(</code><code>"role2"</code><code>));</code>

<code>        </code><code>assert.asserttrue(subject.hasrole(</code><code>"role3"</code><code>));</code>

<code>    </code><code>}</code>

<code>    </code> 

<code>    </code><code>private</code> <code>subject login(string username,string password){</code>

<code>         </code><code>//3、得到subject及建立使用者名/密碼身份驗證token(即使用者身份/憑證) </code>

<code>        </code><code>subject subject = securityutils.getsubject(); </code>

<code>        </code><code>usernamepasswordtoken token =</code><code>new</code> <code>usernamepasswordtoken(username,password); </code>

<code>        </code><code>subject.login(token);</code>

<code>        </code><code>return</code> <code>subject;</code>

<code>    </code><code>private</code> <code>void</code> <code>init(){</code>

<code>         </code><code>//1、擷取securitymanager工廠,此處使用ini配置檔案初始化securitymanager </code>

<code>        </code><code>factory&lt;org.apache.shiro.mgt.securitymanager&gt; factory = </code>

<code>                </code><code>new</code> <code>inisecuritymanagerfactory(</code><code>"classpath:shiro.ini"</code><code>); </code>

<code>        </code><code>//2、得到securitymanager執行個體 并綁定給securityutils </code>

<code>        </code><code>org.apache.shiro.mgt.securitymanager securitymanager = factory.getinstance(); </code>

<code>        </code><code>securityutils.setsecuritymanager(securitymanager); </code>

<code>}</code>

ini配置檔案如下: 

<code>[users]</code>

<code>zhang=</code><code>123</code><code>,role1,role2 </code>

<code>wang=</code><code>123</code><code>,role1</code>

從subject.hasrole開始入手,預設的subject為delegatingsubject: 

<code>public</code> <code>boolean</code> <code>hasrole(string roleidentifier) {</code>

<code>        </code><code>return</code> <code>hasprincipals() &amp;&amp; securitymanager.hasrole(getprincipals(), roleidentifier);</code>

首先就是該使用者是否已登入,驗證角色的地方在securitymanager的hasrole方法中: 

<code>public</code> <code>boolean</code> <code>hasrole(principalcollection principals, string roleidentifier) {</code>

<code>        </code><code>return</code> <code>this</code><code>.authorizer.hasrole(principals, roleidentifier);</code>

authorizingsecuritymanager實作了authorizer接口,但是authorizingsecuritymanager是通過内部authorizer引用來完成具體的功能,預設采用的是modularrealmauthorizer。如下: 

<code>public</code> <code>abstract</code> <code>class</code> <code>authorizingsecuritymanager</code><code>extends</code> <code>authenticatingsecuritymanager {</code>

<code>    </code><code>/**</code>

<code>     </code><code>* the wrapped instance to which all of this &lt;tt&gt;securitymanager&lt;/tt&gt; authorization calls are delegated.</code>

<code>     </code><code>*/</code>

<code>    </code><code>private</code> <code>authorizer authorizer;</code>

<code>    </code><code>public</code> <code>authorizingsecuritymanager() {</code>

<code>        </code><code>super</code><code>();</code>

<code>        </code><code>this</code><code>.authorizer =</code><code>new</code> <code>modularrealmauthorizer();</code>

<code>//略</code>

來看看這個authorizer子產品的接口設計: 

<code>public</code> <code>interface</code> <code>authorizer {</code>

<code>    </code><code>boolean</code> <code>ispermitted(principalcollection principals, string permission);</code>

<code>    </code><code>boolean</code> <code>ispermitted(principalcollection subjectprincipal, permission permission);</code>

<code>    </code><code>boolean</code><code>[] ispermitted(principalcollection subjectprincipal, string... permissions);</code>

<code>    </code><code>boolean</code><code>[] ispermitted(principalcollection subjectprincipal, list&lt;permission&gt; permissions);</code>

<code>    </code><code>boolean</code> <code>ispermittedall(principalcollection subjectprincipal, string... permissions);</code>

<code>    </code><code>boolean</code> <code>ispermittedall(principalcollection subjectprincipal, collection&lt;permission&gt; permissions);</code>

<code>    </code><code>void</code> <code>checkpermission(principalcollection subjectprincipal, string permission)</code><code>throws</code> <code>authorizationexception;</code>

<code>    </code><code>void</code> <code>checkpermission(principalcollection subjectprincipal, permission permission)</code><code>throws</code> <code>authorizationexception;</code>

<code>    </code><code>void</code> <code>checkpermissions(principalcollection subjectprincipal, string... permissions)</code><code>throws</code> <code>authorizationexception;</code>

<code>    </code><code>void</code> <code>checkpermissions(principalcollection subjectprincipal, collection&lt;permission&gt; permissions)</code><code>throws</code> <code>authorizationexception;</code>

<code>    </code><code>boolean</code> <code>hasrole(principalcollection subjectprincipal, string roleidentifier);</code>

<code>    </code><code>boolean</code><code>[] hasroles(principalcollection subjectprincipal, list&lt;string&gt; roleidentifiers);</code>

<code>    </code><code>boolean</code> <code>hasallroles(principalcollection subjectprincipal, collection&lt;string&gt; roleidentifiers);</code>

<code>    </code><code>void</code> <code>checkrole(principalcollection subjectprincipal, string roleidentifier)</code><code>throws</code> <code>authorizationexception;</code>

<code>    </code><code>void</code> <code>checkroles(principalcollection subjectprincipal, collection&lt;string&gt; roleidentifiers)</code><code>throws</code> <code>authorizationexception;</code>

<code>    </code><code>void</code> <code>checkroles(principalcollection subjectprincipal, string... roleidentifiers)</code><code>throws</code> <code>authorizationexception;</code>

從上面的接口中,可以分成兩大類,第一類是驗證使用者的某個或某些權限,第二類是驗證使用者的某個角色或某些角色。角色則是一組權限的集合,是以後者是粗粒度的驗證,而前者是細粒度的驗證。對于那些check方法則是驗證不通過時抛出異常。 

接口實作類圖為: 

shiro源碼分析(三)授權、認證、緩存的接口設計

可以看到很多的realm都實作了該接口,即這些realm不僅提供登陸驗證,還提供權限驗證。 

先來看下預設使用的modularrealmauthorizer: 

<code>public</code> <code>class</code> <code>modularrealmauthorizer</code><code>implements</code> <code>authorizer, permissionresolveraware, rolepermissionresolveraware {</code>

<code>    </code><code>protected</code> <code>collection&lt;realm&gt; realms;</code>

<code>    </code><code>protected</code> <code>permissionresolver permissionresolver;</code>

<code>    </code><code>protected</code> <code>rolepermissionresolver rolepermissionresolver;</code>

<code>    </code><code>//略</code>

可以看到,它有三個重要屬性,realm集合和permissionresolver 、rolepermissionresolver 。permissionresolver 是什麼呢? 

<code>public</code> <code>interface</code> <code>permissionresolver {</code>

<code>    </code><code>permission resolvepermission(string permissionstring);</code>

就是将權限字元串解析成permission 對象,同理rolepermissionresolver 如下: 

<code>public</code> <code>interface</code> <code>rolepermissionresolver {</code>

<code>    </code><code>collection&lt;permission&gt; resolvepermissionsinrole(string rolestring);</code>

将角色字元串解析成permission 集合。 

來看下這幾個方法: 

29

30

31

32

33

34

35

36

37

38

<code>public</code> <code>modularrealmauthorizer(collection&lt;realm&gt; realms) {</code>

<code>        </code><code>setrealms(realms);</code>

<code>public</code> <code>void</code> <code>setrealms(collection&lt;realm&gt; realms) {</code>

<code>        </code><code>this</code><code>.realms = realms;</code>

<code>        </code><code>applypermissionresolvertorealms();</code>

<code>        </code><code>applyrolepermissionresolvertorealms();</code>

<code>public</code> <code>void</code> <code>setpermissionresolver(permissionresolver permissionresolver) {</code>

<code>        </code><code>this</code><code>.permissionresolver = permissionresolver;</code>

<code>public</code> <code>void</code> <code>setrolepermissionresolver(rolepermissionresolver rolepermissionresolver) {</code>

<code>        </code><code>this</code><code>.rolepermissionresolver = rolepermissionresolver;</code>

<code>protected</code> <code>void</code> <code>applyrolepermissionresolvertorealms() {</code>

<code>        </code><code>rolepermissionresolver resolver = getrolepermissionresolver();</code>

<code>        </code><code>collection&lt;realm&gt; realms = getrealms();</code>

<code>        </code><code>if</code> <code>(resolver !=</code><code>null</code> <code>&amp;&amp; realms !=</code><code>null</code> <code>&amp;&amp; !realms.isempty()) {</code>

<code>            </code><code>for</code> <code>(realm realm : realms) {</code>

<code>                </code><code>if</code> <code>(realm</code><code>instanceof</code> <code>rolepermissionresolveraware) {</code>

<code>                    </code><code>((rolepermissionresolveraware) realm).setrolepermissionresolver(resolver);</code>

<code>                </code><code>}</code>

<code>            </code><code>}</code>

<code>        </code><code>}</code>

<code>protected</code> <code>void</code> <code>applypermissionresolvertorealms() {</code>

<code>        </code><code>permissionresolver resolver = getpermissionresolver();</code>

<code>                </code><code>if</code> <code>(realm</code><code>instanceof</code> <code>permissionresolveraware) {</code>

<code>                    </code><code>((permissionresolveraware) realm).setpermissionresolver(resolver);</code>

看下這幾個set方法,其目的都是如果哪些realm 想要permissionresolver 或rolepermissionresolver 參數,則将modularrealmauthorizer 的對應參數傳給它。 

再來看modularrealmauthorizer 是如何實作authorizer接口的: 

<code>protected</code> <code>void</code> <code>assertrealmsconfigured()</code><code>throws</code> <code>illegalstateexception {</code>

<code>        </code><code>if</code> <code>(realms ==</code><code>null</code> <code>|| realms.isempty()) {</code>

<code>            </code><code>string msg =</code><code>"configuration error:  no realms have been configured!  one or more realms must be "</code> <code>+</code>

<code>                    </code><code>"present to execute an authorization operation."</code><code>;</code>

<code>            </code><code>throw</code> <code>new</code> <code>illegalstateexception(msg);</code>

<code>public</code> <code>boolean</code> <code>ispermitted(principalcollection principals, string permission) {</code>

<code>        </code><code>assertrealmsconfigured();</code>

<code>        </code><code>for</code> <code>(realm realm : getrealms()) {</code>

<code>            </code><code>if</code> <code>(!(realm</code><code>instanceof</code> <code>authorizer))</code><code>continue</code><code>;</code>

<code>            </code><code>if</code> <code>(((authorizer) realm).ispermitted(principals, permission)) {</code>

<code>                </code><code>return</code> <code>true</code><code>;</code>

<code>        </code><code>return</code> <code>false</code><code>;</code>

首先是判斷collection&lt;realm&gt; realms集合是否為空,然後就是将那些實作了authorizer接口的realm 來判斷是否具有某個權限,也就是modularrealmauthorizer本身并不去權限驗證,而是交給那些具有權限驗證功能的realm去驗證(即那些realm實作了authorizer接口)。是以 

modularrealmauthorizer并不具有太多實際内容,我們轉戰那些實作了authorizer接口的realm,去看看他們的驗證過程。 

這時候,就需要看authorizer接口的另一個分支即下圖authorizingrealm分支: 

shiro源碼分析(三)授權、認證、緩存的接口設計

authorizingrealm涉及到realm,是以再把realm說清楚。realm接口如下: 

<code>public</code> <code>interface</code> <code>realm {</code>

<code>    </code><code>string getname();</code>

<code>    </code><code>boolean</code> <code>supports(authenticationtoken token);</code>

<code>    </code><code>authenticationinfo getauthenticationinfo(authenticationtoken token)</code><code>throws</code> <code>authenticationexception;</code>

realm 本身隻具有驗證使用者是否合法的功能,不具有授權的功能。再看它的實作者cachingrealm,從名字上就可以知道加入了緩存功能: 

<code>private</code> <code>static</code> <code>final</code> <code>atomicinteger instance_count =</code><code>new</code> <code>atomicinteger();</code>

<code>    </code><code>private</code> <code>string name;</code>

<code>    </code><code>private</code> <code>boolean</code> <code>cachingenabled;</code>

<code>    </code><code>private</code> <code>cachemanager cachemanager;</code>

<code>public</code> <code>cachingrealm() {</code>

<code>        </code><code>this</code><code>.cachingenabled =</code><code>true</code><code>;</code>

<code>        </code><code>this</code><code>.name = getclass().getname() +</code><code>"_"</code> <code>+ instance_count.getandincrement();</code>

有3個對象屬性和一個類屬性,instance_count 主要是用來計數realm的個數的,同時追加到name屬性中,cachingenabled對外提供get、set方法,這裡的cachingenabled就相當于一個總開關,它的子類都有子開關,共同決定着是否進行緩存,如它的子類authenticatingrealm: 

<code>private</code> <code>boolean</code> <code>authenticationcachingenabled;</code>

<code>public</code> <code>boolean</code> <code>isauthenticationcachingenabled() {</code>

<code>        </code><code>return</code> <code>this</code><code>.authenticationcachingenabled &amp;&amp; iscachingenabled();</code>

<code> </code><code>public</code> <code>void</code> <code>setauthenticationcachingenabled(</code><code>boolean</code> <code>authenticationcachingenabled) {</code>

<code>        </code><code>this</code><code>.authenticationcachingenabled = authenticationcachingenabled;</code>

<code>        </code><code>if</code> <code>(authenticationcachingenabled) {</code>

<code>            </code><code>setcachingenabled(</code><code>true</code><code>);</code>

從這裡就可以看到兩個cacheenabled的作用。也對外提供cachemanager的get、set方法, cachingrealm本身并沒有做太多内容,就是把這幾個參數收集起來,供子類去使用。 

接下來看下cache緩存的整體結構圖: 

shiro源碼分析(三)授權、認證、緩存的接口設計

我們要先看下cachemanager是幹嘛的: 

<code>public</code> <code>interface</code> <code>cachemanager {</code>

<code>    </code><code>public</code> <code>&lt;k, v&gt; cache&lt;k, v&gt; getcache(string name)</code><code>throws</code> <code>cacheexception;</code>

根據name擷取一個cache&lt;k, v&gt;這樣的結構,看起來像hashmap的結構,這裡的name到底是什麼呢? 

cachingrealm的子類authenticatingrealm有一個authenticationcachename屬性,而這裡的authenticationcachename就是我們剛才要找的目标,證據如下: 

<code>private</code> <code>cache&lt;object, authenticationinfo&gt; getauthenticationcachelazy() {</code>

<code>        </code><code>if</code> <code>(</code><code>this</code><code>.authenticationcache ==</code><code>null</code><code>) {</code>

<code>            </code><code>log.trace(</code><code>"no authenticationcache instance set.  checking for a cachemanager..."</code><code>);</code>

<code>            </code><code>cachemanager cachemanager = getcachemanager();</code>

<code>            </code><code>if</code> <code>(cachemanager !=</code><code>null</code><code>) {</code>

<code>      </code><code>//這裡的getauthenticationcachename()就是擷取authenticationcachename</code>

<code>                </code><code>string cachename = getauthenticationcachename();</code>

<code>                </code><code>log.debug(</code><code>"cachemanager [{}] configured.  building authentication cache '{}'"</code><code>, cachemanager, cachename);</code>

<code>                </code><code>this</code><code>.authenticationcache = cachemanager.getcache(cachename);</code>

<code>        </code><code>return</code> <code>this</code><code>.authenticationcache;</code>

再看下authenticationcachename的構成: 

<code>public</code> <code>authenticatingrealm(cachemanager cachemanager, credentialsmatcher matcher) {</code>

<code>        </code><code>authenticationtokenclass = usernamepasswordtoken.</code><code>class</code><code>;</code>

<code>        </code><code>//retain backwards compatibility for shiro 1.1 and earlier.  setting to true by default will probably cause</code>

<code>        </code><code>//unexpected results for existing applications:</code>

<code>        </code><code>this</code><code>.authenticationcachingenabled =</code><code>false</code><code>;</code>

<code>        </code><code>int</code> <code>instancenumber = instance_count.getandincrement();</code>

<code>        </code><code>this</code><code>.authenticationcachename = getclass().getname() + default_authorization_cache_suffix;</code>

<code>        </code><code>if</code> <code>(instancenumber &gt;</code><code>0</code><code>) {</code>

<code>            </code><code>this</code><code>.authenticationcachename =</code><code>this</code><code>.authenticationcachename +</code><code>"."</code> <code>+ instancenumber;</code>

<code>        </code><code>if</code> <code>(cachemanager !=</code><code>null</code><code>) {</code>

<code>            </code><code>setcachemanager(cachemanager);</code>

<code>        </code><code>if</code> <code>(matcher !=</code><code>null</code><code>) {</code>

<code>            </code><code>setcredentialsmatcher(matcher);</code>

在建立authenticatingrealm時,authenticationcachename 預設是目前類名+default_authorization_cache_suffix(為.authenticationcache)+數量。這個數量也是用來統計authenticatingrealm的個數的,這種方式僅僅是預設的,也可以去修改: 

<code>public</code> <code>void</code> <code>setauthenticationcachename(string authenticationcachename) {</code>

<code>        </code><code>this</code><code>.authenticationcachename = authenticationcachename;</code>

<code>public</code> <code>void</code> <code>setname(string name) {</code>

<code>        </code><code>super</code><code>.setname(name);</code>

<code>        </code><code>string authccachename =</code><code>this</code><code>.authenticationcachename;</code>

<code>        </code><code>if</code> <code>(authccachename !=</code><code>null</code> <code>&amp;&amp; authccachename.startswith(getclass().getname())) {</code>

<code>            </code><code>//get rid of the default heuristically-created cache name.  create a more meaningful one</code>

<code>            </code><code>//based on the application-unique realm name:</code>

<code>            </code><code>this</code><code>.authenticationcachename = name + default_authorization_cache_suffix;</code>

這兩種方式都可以去修改。回到cachemanager: 

然後就需要了解下cache&lt;k, v&gt;這個結構: 

<code>public</code> <code>interface</code> <code>cache&lt;k, v&gt; {</code>

<code>    </code><code>public</code> <code>v get(k key)</code><code>throws</code> <code>cacheexception;</code>

<code>    </code><code>public</code> <code>v put(k key, v value)</code><code>throws</code> <code>cacheexception;</code>

<code>    </code><code>public</code> <code>v remove(k key)</code><code>throws</code> <code>cacheexception;</code>

<code>    </code><code>public</code> <code>void</code> <code>clear()</code><code>throws</code> <code>cacheexception;</code>

<code>    </code><code>public</code> <code>int</code> <code>size();</code>

<code>    </code><code>public</code> <code>set&lt;k&gt; keys();</code>

<code>    </code><code>public</code> <code>collection&lt;v&gt; values();</code>

這基本上不就是map的結構嗎?為什麼還要單獨設計這樣的結構呢?來看下它的文檔介紹就知道了: 

<code>/**</code>

<code> </code><code>* a cache efficiently stores temporary objects primarily to improve an application's performance.</code>

<code> </code><code>*</code>

<code> </code><code>* &lt;p&gt;shiro doesn't implement a full cache mechanism itself, since that is outside the core competency of a</code>

<code> </code><code>* security framework.  instead, this interface provides an abstraction (wrapper) api on top of an underlying</code>

<code> </code><code>* cache framework's cache instance (e.g. jcache, ehcache, jcs, oscache, jbosscache, terracotta, coherence,</code>

<code> </code><code>* gigaspaces, etc, etc), allowing a shiro user to configure any cache mechanism they choose.</code>

<code> </code><code>* @since 0.2</code>

<code> </code><code>*/</code>

shiro并不打算自己實作一個完整的緩存機制,因為這并不是安全架構的主要職責,相反它應該提供一個統一的api接口,可以加入不同緩存架構。而對于我們使用者來說,隻需針對這一層統一api進行程式設計,不再針對某個具體的緩存架構程式設計,這樣就更加容易切換不同的緩存架構。 

再看下,它的實作類mapcache和ehcache,mapcache很簡單就是通過map結構來實作 

<code>public</code> <code>class</code> <code>mapcache&lt;k, v&gt;</code><code>implements</code> <code>cache&lt;k, v&gt; {</code>

<code>    </code><code>private</code> <code>final</code> <code>map&lt;k, v&gt; map;</code>

<code>    </code><code>private</code> <code>final</code> <code>string name;</code>

<code>    </code><code>public</code> <code>mapcache(string name, map&lt;k, v&gt; backingmap) {</code>

<code>        </code><code>if</code> <code>(name ==</code><code>null</code><code>) {</code>

<code>            </code><code>throw</code> <code>new</code> <code>illegalargumentexception(</code><code>"cache name cannot be null."</code><code>);</code>

<code>        </code><code>if</code> <code>(backingmap ==</code><code>null</code><code>) {</code>

<code>            </code><code>throw</code> <code>new</code> <code>illegalargumentexception(</code><code>"backing map cannot be null."</code><code>);</code>

<code>        </code><code>this</code><code>.name = name;</code>

<code>        </code><code>this</code><code>.map = backingmap;</code>

<code>    </code><code>public</code> <code>v get(k key)</code><code>throws</code> <code>cacheexception {</code>

<code>        </code><code>return</code> <code>map.get(key);</code>

<code>    </code><code>public</code> <code>v put(k key, v value)</code><code>throws</code> <code>cacheexception {</code>

<code>        </code><code>return</code> <code>map.put(key, value);</code>

<code>    </code><code>public</code> <code>v remove(k key)</code><code>throws</code> <code>cacheexception {</code>

<code>        </code><code>return</code> <code>map.remove(key);</code>

ehcache則是通過net.sf.ehcache.ehcache架構來來實作,不再涉及。 

cache&lt;k, v&gt;知道了,又有哪些cachemanager的實作呢? 

abstractcachemanager如下: 

<code>public</code> <code>abstract</code> <code>class</code> <code>abstractcachemanager</code><code>implements</code> <code>cachemanager, destroyable {</code>

<code>    </code><code>private</code> <code>final</code> <code>concurrentmap&lt;string, cache&gt; caches;</code>

<code>    </code><code>public</code> <code>abstractcachemanager() {</code>

<code>        </code><code>this</code><code>.caches =</code><code>new</code> <code>concurrenthashmap&lt;string, cache&gt;();</code>

<code>    </code><code>public</code> <code>&lt;k, v&gt; cache&lt;k, v&gt; getcache(string name)</code><code>throws</code> <code>illegalargumentexception, cacheexception {</code>

<code>        </code><code>if</code> <code>(!stringutils.hastext(name)) {</code>

<code>            </code><code>throw</code> <code>new</code> <code>illegalargumentexception(</code><code>"cache name cannot be null or empty."</code><code>);</code>

<code>        </code><code>cache cache;</code>

<code>        </code><code>cache = caches.get(name);</code>

<code>        </code><code>if</code> <code>(cache ==</code><code>null</code><code>) {</code>

<code>            </code><code>cache = createcache(name);</code>

<code>            </code><code>cache existing = caches.putifabsent(name, cache);</code>

<code>            </code><code>if</code> <code>(existing !=</code><code>null</code><code>) {</code>

<code>                </code><code>cache = existing;</code>

<code>        </code><code>return</code> <code>cache;</code>

也很簡單,内部擁有一個concurrenthashmap集合,存取都是對該集合的操作,而把真正建立cache的操作留給具體的子類來實作,即createcache方法。看下它的子類memoryconstrainedcachemanager的createcache實作: 

<code>public</code> <code>class</code> <code>memoryconstrainedcachemanager</code><code>extends</code> <code>abstractcachemanager {</code>

<code>    </code><code>@override</code>

<code>    </code><code>protected</code> <code>cache createcache(string name) {</code>

<code>        </code><code>return</code> <code>new</code> <code>mapcache&lt;object, object&gt;(name,</code><code>new</code> <code>softhashmap&lt;object, object&gt;());</code>

就是建立了一個mapcache對象作為cache,至于softhashmap則需要單獨去介紹其中的設計。 

cachingrealm就大緻介紹完了,回到它的子類,看它的子類authenticatingrealm是怎麼去使用cachemanager。該子類主要完成認證流程,首先是其的初始化,authenticatingrealm及其子類都實作了initializable接口,初始化的時候會首先擷取其緩存,如下: 

<code>public</code> <code>final</code> <code>void</code> <code>init() {</code>

<code>        </code><code>//trigger obtaining the authorization cache if possible</code>

<code>        </code><code>getavailableauthenticationcache();</code>

<code>        </code><code>oninit();</code>

<code>private</code> <code>cache&lt;object, authenticationinfo&gt; getavailableauthenticationcache() {</code>

<code>        </code><code>cache&lt;object, authenticationinfo&gt; cache = getauthenticationcache();</code>

<code>        </code><code>boolean</code> <code>authccachingenabled = isauthenticationcachingenabled();</code>

<code>        </code><code>if</code> <code>(cache ==</code><code>null</code> <code>&amp;&amp; authccachingenabled) {</code>

<code>            </code><code>cache = getauthenticationcachelazy();</code>

首先會擷取cache&lt;object, authenticationinfo&gt; cache屬性,如果沒有,再判斷是否允許緩存,如果允許,則通過cachemanager 來擷取,之前已分析過,如果還沒有則會建立一個cache,然後傳回。 

再看下認證過程如下: 

<code>public</code> <code>final</code> <code>authenticationinfo getauthenticationinfo(authenticationtoken token)</code><code>throws</code> <code>authenticationexception {</code>

<code>        </code><code>authenticationinfo info = getcachedauthenticationinfo(token);</code>

<code>        </code><code>if</code> <code>(info ==</code><code>null</code><code>) {</code>

<code>            </code><code>//otherwise not cached, perform the lookup:</code>

<code>            </code><code>info = dogetauthenticationinfo(token);</code>

<code>            </code><code>log.debug(</code><code>"looked up authenticationinfo [{}] from dogetauthenticationinfo"</code><code>, info);</code>

<code>            </code><code>if</code> <code>(token !=</code><code>null</code> <code>&amp;&amp; info !=</code><code>null</code><code>) {</code>

<code>                </code><code>cacheauthenticationinfoifpossible(token, info);</code>

<code>        </code><code>}</code><code>else</code> <code>{</code>

<code>            </code><code>log.debug(</code><code>"using cached authentication info [{}] to perform credentials matching."</code><code>, info);</code>

<code>        </code><code>if</code> <code>(info !=</code><code>null</code><code>) {</code>

<code>            </code><code>assertcredentialsmatch(token, info);</code>

<code>            </code><code>log.debug(</code><code>"no authenticationinfo found for submitted authenticationtoken [{}].  returning null."</code><code>, token);</code>

<code>        </code><code>return</code> <code>info;</code>

首先從緩存中嘗試是否能找到authenticationinfo ,如果找不到,則需要子類去完成具體的認證細節,然後再存儲到緩存中,因為本類并沒有具體的資料源,隻有緩存源,是以本類隻是搭建了認證流程,具體的認證細節則由具體的子類來完成,是以 dogetauthenticationinfo(token)是一個protected的抽象方法,如下: 

<code>protected</code> <code>abstract</code> <code>authenticationinfo dogetauthenticationinfo(authenticationtoken token)</code><code>throws</code> <code>authenticationexception;</code>

當緩存中存在或者子類進行具體的認證後,下一步的操作是要進行密碼比對的過程,authenticatingrealm有一個屬性credentialsmatcher credentialsmatcher,接口如下: 

<code>public</code> <code>interface</code> <code>credentialsmatcher {</code>

<code>    </code><code>boolean</code> <code>docredentialsmatch(authenticationtoken token, authenticationinfo info);</code>

就是比對我們認證時的authenticationtoken 和剛才已找到的authenticationinfo 是否比對。有如下的實作類:allowallcredentialsmatcher、passwordmatcher、simplecredentialsmatcher等等。authenticatingrealm的構造函數預設使用的是simplecredentialsmatcher: 

<code>public</code> <code>authenticatingrealm() {</code>

<code>        </code><code>this</code><code>(</code><code>null</code><code>,</code><code>new</code> <code>simplecredentialsmatcher());</code>

<code>    </code><code>public</code> <code>authenticatingrealm(cachemanager cachemanager) {</code>

<code>        </code><code>this</code><code>(cachemanager,</code><code>new</code> <code>simplecredentialsmatcher());</code>

<code>    </code><code>public</code> <code>authenticatingrealm(credentialsmatcher matcher) {</code>

<code>        </code><code>this</code><code>(</code><code>null</code><code>, matcher);</code>

這一塊内容先暫時不講,後續文章再來詳細說明。 

當你比對通過了,則就算認證成功了。認證流程就在authenticatingrealm中完成了。 

我們再向它的子類authorizingrealm研究,這個就有涉及到授權的功能了。authenticatingrealm是将整個認證流程架構化,authorizingrealm則是将整個授權流程架構化,authorizingrealm也有授權緩存,是以會通過父類cachingrealm來擷取cachemanager,同時也有一個子緩存開關authorizationcachingenabled,和authenticatingrealm基本類似,屬性如下: 

39

40

41

42

43

<code>public</code> <code>abstract</code> <code>class</code> <code>authorizingrealm</code><code>extends</code> <code>authenticatingrealm</code>

<code>        </code><code>implements</code> <code>authorizer, initializable, permissionresolveraware, rolepermissionresolveraware {</code>

<code>    </code><code>private</code> <code>static</code> <code>final</code> <code>string default_authorization_cache_suffix =</code><code>".authorizationcache"</code><code>;</code>

<code>    </code><code>private</code> <code>static</code> <code>final</code> <code>atomicinteger instance_count =</code><code>new</code> <code>atomicinteger();</code>

<code>    </code><code>private</code> <code>boolean</code> <code>authorizationcachingenabled;</code>

<code>    </code><code>private</code> <code>cache&lt;object, authorizationinfo&gt; authorizationcache;</code>

<code>    </code><code>private</code> <code>string authorizationcachename;</code>

<code>    </code><code>private</code> <code>permissionresolver permissionresolver;</code>

<code>    </code><code>private</code> <code>rolepermissionresolver permissionroleresolver;</code>

<code>    </code><code>public</code> <code>authorizingrealm() {</code>

<code>        </code><code>this</code><code>(</code><code>null</code><code>,</code><code>null</code><code>);</code>

<code>    </code><code>public</code> <code>authorizingrealm(cachemanager cachemanager) {</code>

<code>        </code><code>this</code><code>(cachemanager,</code><code>null</code><code>);</code>

<code>    </code><code>public</code> <code>authorizingrealm(credentialsmatcher matcher) {</code>

<code>    </code><code>public</code> <code>authorizingrealm(cachemanager cachemanager, credentialsmatcher matcher) {</code>

<code>        </code><code>if</code> <code>(cachemanager !=</code><code>null</code><code>) setcachemanager(cachemanager);</code>

<code>        </code><code>if</code> <code>(matcher !=</code><code>null</code><code>) setcredentialsmatcher(matcher);</code>

<code>        </code><code>this</code><code>.authorizationcachingenabled =</code><code>true</code><code>;</code>

<code>        </code><code>this</code><code>.permissionresolver =</code><code>new</code> <code>wildcardpermissionresolver();</code>

<code>        </code><code>this</code><code>.authorizationcachename = getclass().getname() + default_authorization_cache_suffix;</code>

<code>            </code><code>this</code><code>.authorizationcachename =</code><code>this</code><code>.authorizationcachename +</code><code>"."</code> <code>+ instancenumber;</code>

atomicinteger 同樣是用于對那些具有授權功能的realm進行數量統計的,authorizationcachingenabled緩存子開關,authorizationcache緩存,authorizationcachename緩存名字。 permissionresolver permissionresolver、rolepermissionresolver permissionroleresolver這兩個則是對字元串進行解析對應的permission和collection&lt;permission&gt;的。我們來看下authorizingrealm的主要功能,對于授權接口authorizer的實作: 

<code>public</code> <code>boolean</code> <code>hasrole(principalcollection principal, string roleidentifier) {</code>

<code>        </code><code>authorizationinfo info = getauthorizationinfo(principal);</code>

<code>        </code><code>return</code> <code>hasrole(roleidentifier, info);</code>

首先就是擷取授權資訊,看下getauthorizationinfo: 

44

<code>protected</code> <code>authorizationinfo getauthorizationinfo(principalcollection principals) {</code>

<code>        </code><code>if</code> <code>(principals ==</code><code>null</code><code>) {</code>

<code>            </code><code>return</code> <code>null</code><code>;</code>

<code>        </code><code>authorizationinfo info =</code><code>null</code><code>;</code>

<code>        </code><code>if</code> <code>(log.istraceenabled()) {</code>

<code>            </code><code>log.trace(</code><code>"retrieving authorizationinfo for principals ["</code> <code>+ principals +</code><code>"]"</code><code>);</code>

<code>        </code><code>cache&lt;object, authorizationinfo&gt; cache = getavailableauthorizationcache();</code>

<code>        </code><code>if</code> <code>(cache !=</code><code>null</code><code>) {</code>

<code>            </code><code>if</code> <code>(log.istraceenabled()) {</code>

<code>                </code><code>log.trace(</code><code>"attempting to retrieve the authorizationinfo from cache."</code><code>);</code>

<code>            </code><code>object key = getauthorizationcachekey(principals);</code>

<code>            </code><code>info = cache.get(key);</code>

<code>                </code><code>if</code> <code>(info ==</code><code>null</code><code>) {</code>

<code>                    </code><code>log.trace(</code><code>"no authorizationinfo found in cache for principals ["</code> <code>+ principals +</code><code>"]"</code><code>);</code>

<code>                </code><code>}</code><code>else</code> <code>{</code>

<code>                    </code><code>log.trace(</code><code>"authorizationinfo found in cache for principals ["</code> <code>+ principals +</code><code>"]"</code><code>);</code>

<code>            </code><code>// call template method if the info was not found in a cache</code>

<code>            </code><code>info = dogetauthorizationinfo(principals);</code>

<code>            </code><code>// if the info is not null and the cache has been created, then cache the authorization info.</code>

<code>            </code><code>if</code> <code>(info !=</code><code>null</code> <code>&amp;&amp; cache !=</code><code>null</code><code>) {</code>

<code>                </code><code>if</code> <code>(log.istraceenabled()) {</code>

<code>                    </code><code>log.trace(</code><code>"caching authorization info for principals: ["</code> <code>+ principals +</code><code>"]."</code><code>);</code>

<code>                </code><code>object key = getauthorizationcachekey(principals);</code>

<code>                </code><code>cache.put(key, info);</code>

同樣很容易了解,先得到緩存,從緩存中去找有沒有授權資訊,如果沒有,則需要子類去完成具體的授權細節即dogetauthorizationinfo,授權完成後放置緩存中。同樣dogetauthorizationinfo是protected的抽象方法,由子類去實作。permissionresolver permissionresolver、rolepermissionresolver permissionroleresolver則是發揮如下作用: 

<code>private</code> <code>collection&lt;permission&gt; getpermissions(authorizationinfo info) {</code>

<code>        </code><code>set&lt;permission&gt; permissions =</code><code>new</code> <code>hashset&lt;permission&gt;();</code>

<code>            </code><code>collection&lt;permission&gt; perms = info.getobjectpermissions();</code>

<code>            </code><code>if</code> <code>(!collectionutils.isempty(perms)) {</code>

<code>                </code><code>permissions.addall(perms);</code>

<code>            </code><code>perms = resolvepermissions(info.getstringpermissions());</code>

<code>            </code><code>perms = resolverolepermissions(info.getroles());</code>

<code>        </code><code>if</code> <code>(permissions.isempty()) {</code>

<code>            </code><code>return</code> <code>collections.emptyset();</code>

<code>            </code><code>return</code> <code>collections.unmodifiableset(permissions);</code>

即有了授權資訊authorizationinfo 後,擷取所有的權限permission,有三種途徑來收集,第一種就是info.getobjectpermissions() info中直接含有permission對象集合,第二種就是info.getstringpermissions() info中有字元串形式的權限表示,第三種就是info.getroles() info中含有角色集合,角色也是一組權限的集合,看下resolvepermissions(info.getstringpermissions()): 

<code>private</code> <code>collection&lt;permission&gt; resolvepermissions(collection&lt;string&gt; stringperms) {</code>

<code>        </code><code>collection&lt;permission&gt; perms = collections.emptyset();</code>

<code>        </code><code>if</code> <code>(resolver !=</code><code>null</code> <code>&amp;&amp; !collectionutils.isempty(stringperms)) {</code>

<code>            </code><code>perms =</code><code>new</code> <code>linkedhashset&lt;permission&gt;(stringperms.size());</code>

<code>            </code><code>for</code> <code>(string strpermission : stringperms) {</code>

<code>                </code><code>permission permission = getpermissionresolver().resolvepermission(strpermission);</code>

<code>                </code><code>perms.add(permission);</code>

<code>        </code><code>return</code> <code>perms;</code>

也很簡單,對于每一個strpermission 通過permissionresolver 轉化成permission 對象,對于resolverolepermissions也同理,不再說明。這裡具體的轉化細節先暫且不說,後續再将。 

現在終于把認證流程和授權架構流程大緻說完了,即authenticatingrealm和authorizingrealm的内容,他們分别留給子類protected abstract authenticationinfo dogetauthenticationinfo(authenticationtoken token) throws authenticationexception;具體的認證方法和protected abstract authorizationinfo dogetauthorizationinfo(principalcollection principals)具體的授權方法。 

繼續閱讀