<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<org.apache.shiro.mgt.securitymanager> 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() && 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 <tt>securitymanager</tt> 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<permission> permissions);</code>
<code> </code><code>boolean</code> <code>ispermittedall(principalcollection subjectprincipal, string... permissions);</code>
<code> </code><code>boolean</code> <code>ispermittedall(principalcollection subjectprincipal, collection<permission> 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<permission> 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<string> roleidentifiers);</code>
<code> </code><code>boolean</code> <code>hasallroles(principalcollection subjectprincipal, collection<string> 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<string> 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方法則是驗證不通過時抛出異常。
接口實作類圖為:
可以看到很多的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<realm> 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<permission> resolvepermissionsinrole(string rolestring);</code>
将角色字元串解析成permission 集合。
來看下這幾個方法:
29
30
31
32
33
34
35
36
37
38
<code>public</code> <code>modularrealmauthorizer(collection<realm> realms) {</code>
<code> </code><code>setrealms(realms);</code>
<code>public</code> <code>void</code> <code>setrealms(collection<realm> 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<realm> realms = getrealms();</code>
<code> </code><code>if</code> <code>(resolver !=</code><code>null</code> <code>&& realms !=</code><code>null</code> <code>&& !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<realm> realms集合是否為空,然後就是将那些實作了authorizer接口的realm 來判斷是否具有某個權限,也就是modularrealmauthorizer本身并不去權限驗證,而是交給那些具有權限驗證功能的realm去驗證(即那些realm實作了authorizer接口)。是以
modularrealmauthorizer并不具有太多實際内容,我們轉戰那些實作了authorizer接口的realm,去看看他們的驗證過程。
這時候,就需要看authorizer接口的另一個分支即下圖authorizingrealm分支:
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 && 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緩存的整體結構圖:
我們要先看下cachemanager是幹嘛的:
<code>public</code> <code>interface</code> <code>cachemanager {</code>
<code> </code><code>public</code> <code><k, v> cache<k, v> getcache(string name)</code><code>throws</code> <code>cacheexception;</code>
根據name擷取一個cache<k, v>這樣的結構,看起來像hashmap的結構,這裡的name到底是什麼呢?
cachingrealm的子類authenticatingrealm有一個authenticationcachename屬性,而這裡的authenticationcachename就是我們剛才要找的目标,證據如下:
<code>private</code> <code>cache<object, authenticationinfo> 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 ></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>&& 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<k, v>這個結構:
<code>public</code> <code>interface</code> <code>cache<k, v> {</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<k> keys();</code>
<code> </code><code>public</code> <code>collection<v> 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>* <p>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<k, v></code><code>implements</code> <code>cache<k, v> {</code>
<code> </code><code>private</code> <code>final</code> <code>map<k, v> map;</code>
<code> </code><code>private</code> <code>final</code> <code>string name;</code>
<code> </code><code>public</code> <code>mapcache(string name, map<k, v> 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<k, v>知道了,又有哪些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<string, cache> caches;</code>
<code> </code><code>public</code> <code>abstractcachemanager() {</code>
<code> </code><code>this</code><code>.caches =</code><code>new</code> <code>concurrenthashmap<string, cache>();</code>
<code> </code><code>public</code> <code><k, v> cache<k, v> 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<object, object>(name,</code><code>new</code> <code>softhashmap<object, object>());</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<object, authenticationinfo> getavailableauthenticationcache() {</code>
<code> </code><code>cache<object, authenticationinfo> cache = getauthenticationcache();</code>
<code> </code><code>boolean</code> <code>authccachingenabled = isauthenticationcachingenabled();</code>
<code> </code><code>if</code> <code>(cache ==</code><code>null</code> <code>&& authccachingenabled) {</code>
<code> </code><code>cache = getauthenticationcachelazy();</code>
首先會擷取cache<object, authenticationinfo> 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>&& 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<object, authorizationinfo> 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<permission>的。我們來看下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<object, authorizationinfo> 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>&& 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<permission> getpermissions(authorizationinfo info) {</code>
<code> </code><code>set<permission> permissions =</code><code>new</code> <code>hashset<permission>();</code>
<code> </code><code>collection<permission> 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<permission> resolvepermissions(collection<string> stringperms) {</code>
<code> </code><code>collection<permission> perms = collections.emptyset();</code>
<code> </code><code>if</code> <code>(resolver !=</code><code>null</code> <code>&& !collectionutils.isempty(stringperms)) {</code>
<code> </code><code>perms =</code><code>new</code> <code>linkedhashset<permission>(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)具體的授權方法。