關於Apache Shiro許可權架構的一些使用誤區的解釋

來源:互聯網
上載者:User

標籤:turn   pac   .net   net   很多   構造方法   collect   http   ash   

   多了不說了,進入正題,shiro是個許可權架構提供許可權管理等功能,網上的教程一般都是互相抄,比如<shiro:principal property="xxx"/>這個標籤,網上教程告訴你可以用來擷取登入使用者的任何屬性,但現實中如果你這麼寫,並且按照開濤教程上寫的登陸邏輯,肯定百分百報錯,這是為什麼呢?因為網上教程的登入部分一般這麼寫:

  這是重寫authorizingrealm的dogetAuthenticationinfo方法:

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {// TODO Auto-generated method stubString username=(String) token.getPrincipal();User user=userService.getUserByUsername(username);if(user==null){throw new UnknownAccountException();}if(user.getUserState()==4){throw new LockedAccountException();}SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(),ByteSource.Util.bytes(user.getCredentialsSalt()),this.getName()); return info;


網上很多教程都是這麼教給大家的, 然後在controller裡用:
UsernameAndPasswordToken token=new UsernameAndPasswordToken(username,password);
subject.login(token);
....
這一系列邏輯來進行登陸,登陸成功後在頁面通過<shiro:principal property="username"/>來擷取登入使用者屬性,然後你這麼做了就報錯了。
為什嗎?來看源碼,源碼一是一手鞋,官方文檔是二手鞋,網上教程是三手鞋,鞋都穿三手了,興許會有腳氣。。。

    public SimpleAuthenticationInfo(Object principal, Object hashedCredentials, ByteSource credentialsSalt, String realmName) {        this.principals = new SimplePrincipalCollection(principal, realmName);        this.credentials = hashedCredentials;        this.credentialsSalt = credentialsSalt;

    }
上面是SimpleAuthenticationInfo源碼的一個構造方法,這裡第一個參數就是你剛才傳入的使用者名稱,第二個參數就是你傳入的密碼,但是方法定義中這兩個參數都是Object類型,尤其是第一個principal參數,它的意義遠遠不止使用者名稱那麼簡單,它是使用者的所有認證資訊集合,登陸成功後,<shiro:principal/>標籤一旦有property屬性,PrincipalTag類也就是標籤的支援類,會從Subject的principalcollection裡將principal取出,取出的就是你傳入的第一個參數,如果你傳了個string類型的使用者名稱,那麼你只能擷取使用者名稱。
仔細看那個this.principals=new SimplePrincipalCollection,這一行,這一行構造了一個新的對象並將引用給了principals,而principals就是principalcollection。
再來看Principal那個標籤<shiro:principal property=""/>那個,開啟PrincipalTag類,看到onDoStartTag方法:

    public int onDoStartTag() throws JspException {        String strValue = null;         if (getSubject() != null) {             // Get the principal to print out            Object principal;             if (type == null) {                principal = getSubject().getPrincipal();            } else {                principal = getPrincipalFromClassName();            }             // Get the string value of the principal            if (principal != null) {                if (property == null) {                    strValue = principal.toString();                } else {                    strValue = getPrincipalProperty(principal, property);                }            }         }         // Print out the principal value if not null        if (strValue != null) {            try {                pageContext.getOut().write(strValue);            } catch (IOException e) {                throw new JspTagException("Error writing [" + strValue + "] to JSP.", e);            }        }         return SKIP_BODY;

    } 
看到那個Object principal這個方法變數了嗎?如果標籤裡沒有type屬性,那麼就直接調用getPrincipal方法,再開啟這個方法,看到subject裡是這麼寫的:

    public Object getPrincipal() {        return getPrimaryPrincipal(getPrincipals());

    } 
getPrincipals是擷取principalcollection,getprimaryprincipal就是擷取這個principalcollection的第一個元素,用的是迭代器的方式擷取。具體的我不列出來了,請參見SimplePrincipalcollection的原始碼。
第一個元素你最初放的是使用者名稱,所以你可以取得這個使用者名稱。 如果type不為空白,就會去principalcollection中找和這個type類型一致的一個對象,看源碼:

    private Object getPrincipalFromClassName() {        Object principal = null;         try {            Class cls = Class.forName(type);            principal = getSubject().getPrincipals().oneByType(cls);        } catch (ClassNotFoundException e) {            if (log.isErrorEnabled()) {                log.error("Unable to find class for name [" + type + "]");            }        }        return principal;

    } 
那個oneByType方法就是在principalcollection中找和type屬性一致的那個類的對象,將其作為principal,如果<shiro:property/>標籤有了property屬性,然後用java內省機制擷取bean的屬性,參見getPrincipalProperty方法,因為方法體太長我就不列出了。 
所以你現在知道該怎麼做了吧?
在你的realm裡這麼寫:

List<Object> principals=new ArrayList<Object>();principals.add(user.getUsername());principals.add(user);SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(principals,user.getPassword(),ByteSource.Util.bytes(user.getCredentialsSalt()),this.getName()

);
構建一個list,第一個元素是使用者名稱,第二個元素是user對象。第一個元素用來登陸,第二個元素用來擷取登陸後user對象的屬性。
他們到底怎麼判斷登陸的呢?
先參見HashedCredentialsMatcher類的這個方法,這是用來判斷是否可以登入成功的方法:  
  public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {

        Object tokenHashedCredentials = hashProvidedCredentials(token, info);        Object accountCredentials = getCredentials(info);        return equals(tokenHashedCredentials, accountCredentials);

    } 
其中第一個參數token就是controller裡封裝的token,第二個參數info就是你剛才的simpleauthenticationinfo,因為源碼層次比較深所以簡單點說,這個方法就是再判斷兩個密碼是否相等,因為兩個使用者名稱肯定是相等的info的使用者名稱是token傳過來的,所以只對比密碼就可以了。 
就寫這麼多吧,希望大家看了能有啟發。 

   不是我寫的,是我在開發中遇到問題,特此複製別人解決辦法的部落格  附上 大牛的連結  http://blog.csdn.net/ccdust/article/details/52300287

關於Apache Shiro許可權架構的一些使用誤區的解釋

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.