1. 程式人生 > >Java環境下shiro的測試-認證與授權

Java環境下shiro的測試-認證與授權

Java環境下shiro的測試

1.匯入依賴的核心jar包

<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-core</artifactId>
  <version>1.3.2</version>
</dependency>

2.認證程式

2.1 構建users配置檔案 xxx.ini doGetAuthenticationInfo方法從該配置檔案中獲取資料與token中比對

[users]
test=123456
lisi=123456

測試程式

public class TestShiro {
    public static void main(String[] args) {
        //獲取安全管理器工廠
        IniSecurityManagerFactory iniSecurityManagerFactory = new IniSecurityManagerFactory("classpath:shiro.ini");
        //獲取安全管理器
        SecurityManager securityManager = iniSecurityManagerFactory.getInstance();
        //set認證器
        SecurityUtils.setSecurityManager(securityManager);
        //subject發起認證,獲取subject
        Subject subject = SecurityUtils.getSubject();
        AuthenticationToken authenticationToken = new UsernamePasswordToken("test","123456");

        //認證失敗會丟擲異常  密碼:CredentialsException   賬戶:UnknownAccountException
        try {
            subject.login(authenticationToken);
        } catch (AuthenticationException e) {
            e.printStackTrace();
        }

        //當前subject是否認證通過
        boolean authenticated = subject.isAuthenticated();
        System.out.println(authenticated);
    }
}

認證流程:

token攜帶身份和憑證資訊--->subject發起認證--->SimpleAccountRealm(doGetAuthenticationInfo)獲取配置檔案中的使用者資訊---->CredentialsMatcher介面的實現類SimpleCredentialsMatcher:doCredentialsMatch方法對配置檔案中的資訊與token攜帶的資訊進行比對--->認證成功或者失敗。

3.Shiro框架中的關鍵物件:

AuthenticatingRealm  //抽象類
    //3.關鍵屬性 該屬性為憑證匹配器
    CredentialsMatcher credentialsMatcher;
    //1.該方法為抽象方法 其作用使用來獲取資料
    protected abstract AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken var1) throws AuthenticationException;
SimpleAccountRealm
//2.實現了AuthenticatingRealm抽象方法,用來獲取配置檔案中的使用者資訊,該類不做資料比對
SimpleCredentialsMatcher
//4.shiro中預設的憑證匹配器
  public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        Object tokenCredentials = this.getCredentials(token);
        Object accountCredentials = this.getCredentials(info);
        return this.equals(tokenCredentials, accountCredentials);
    }

必須要知道的類與介面,不然很難理解自定義Realm時屬性為什麼設定,使用哪種實現類方法等等:

以下程式碼或者截圖貼出最重要的地方.

類繼承關係

AuthenticatingRealm類

abstract class AuthenticatingRealm{
    //憑證匹配器 介面,其實現類做資料比對
    private CredentialsMatcher credentialsMatcher;
    //獲取配置檔案中的使用者資訊
    protected abstract AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken var1) throws AuthenticationException;
} 

該抽象方法返回型別AuthenticationInfo介面:

AuthorizingRealm類 抽象方法後面測試授權時使用

abstract class AuthorizingRealm{
    //
    //該抽象方法  獲取資料  獲取授權的資料   
    protected abstract AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection var1);
}

該抽象方法返回型別AuthorizationInfo介面:

CredentialsMatcher憑證匹配器介面:

其中:SimpleCredentialsMatcher是shiro中預設的憑證匹配器,其子類Hashxxx等都是做加密認證時使用

4.開發自定義Realm

public class MyRealm extends AuthenticatingRealm {
    //實現抽象方法doGetAuthenticationInfo
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
            throws AuthenticationException {
        String principal =(String) authenticationToken.getPrincipal();
        //查庫取回User物件
        SqlSession sqlSession = null;
        try {
            sqlSession = MySqlSession.getSqlSession();
        } catch (IOException e) {
            e.printStackTrace();
        }
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = mapper.queryUserByUserName(principal);
        AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(principal,user.getPassword(),this.getName());
        return authenticationInfo;
    }
}

4.1通知shiro使用自定義realm

[main]
#自定義 realm
customRealm=com.nyist.test.MyRealm
#將realm設定到securityManager
securityManager.realms=$customRealm

注意:需要匯入一個jar

<dependency>
    <groupId>commons-logging</groupId>
    <artifactId>commons-logging</artifactId>
    <version>1.2</version>
</dependency>

5.shiro的加密認證方式

5.1.使用shiro提供的Md5Hash類為一個字串加密進行測試

public class TestMD5 {
    public static void main(String[] args) {
        Md5Hash hash = new Md5Hash("123456","salt",1024);
        String s = hash.toHex();
        System.out.println(s);
        //a18d2133f593d7b0e3ed488560404083
    }
}

5.2.修改配置檔案,加入憑證匹配器的相關配置

[main]
#自定義憑證匹配器
hashedCredentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
#憑證匹配器通知AuthenticatingRealm,由於自定義realm繼承了AuthenticatingRealm,直接設定已有的MyRealm屬性即可

#自定義 realm
customRealm=com.nyist.test.MyRealm
customRealm.credentialsMatcher=$hashedCredentialsMatcher
hashedCredentialsMatcher.hashAlgorithmName=MD5
hashedCredentialsMatcher.hashIterations=1024

#將realm設定到securityManager .realms使用set方式賦值
securityManager.realms=$customRealm

坑:Caused by: java.lang.IllegalStateException: Required 'hashAlgorithmName' property has not been set. This is required to execute the hashing algorithm.

HashedCredentialsMatcher類中set方法非常規,set方法為:setHashAlgorithmName

為什麼這麼設定憑證匹配器?

自定義MyRealm extends AuthorizingRealm,實現兩個抽象方法

AuthenticationInfo doGetAuthenticationInfo()來自於AuthenticatingRealm類,獲取認證資料

AuthorizationInfo doGetAuthorizationInfo()來自於AuthorizingRealm類,獲取授權資料

public class MyRealm extends AuthorizingRealm {

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
            throws AuthenticationException {
        String principal =(String) authenticationToken.getPrincipal();
        //userDao.queryUserByUserName
        SqlSession sqlSession = null;
        try {
            sqlSession = MySqlSession.getSqlSession();
        } catch (IOException e) {
            e.printStackTrace();
        }
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = mapper.queryUserByUserName(principal);

        /*
        * ByteSource.Util.bytes("salt") 鹽欄位來自資料庫,憑證匹配器不能寫死鹽值
        * 安全管理器可以獲取到AuthenticationInfo中的鹽值 對使用者介面的憑證加密
        * */
        AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(principal,user.getPassword(),ByteSource.Util.bytes("salt"),this.getName());
        return authenticationInfo;
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //獲取身份資訊 Principal使用者名稱、手機號、郵箱地址等 一個主體可以有多個身份,但是必須有一個主身份(Primary Principal)
        String primaryPrincipal = (String) principalCollection.getPrimaryPrincipal();
        /*
        * 使用者----角色----許可權
        *   中間表   中間表
        * */
        //由primaryPrincipal查庫--->獲得角色info ---->獲取許可權info
        SqlSession sqlSession = null;
        try {
            sqlSession = MySqlSession.getSqlSession();
        } catch (IOException e) {
            e.printStackTrace();
        }
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = mapper.queryUserByUserName(primaryPrincipal);
        //測試基於角色的授權
        /*if (primaryPrincipal.equals(user.getUsername())){
            // class SimpleAuthorizationInfo implements AuthorizationInfo
            SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
            authorizationInfo.addRole("super");
            return authorizationInfo;
        }*/
        //測試基於資源的授權
        if(primaryPrincipal.equals(user.getUsername())){
            SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
            authorizationInfo.addStringPermission("user:delete");
            authorizationInfo.addStringPermissions(Arrays.asList("admin:delete","admin:add"));
            return authorizationInfo;
        }
        return null;
    }
}

一張圖看懂認證授權關係:[待修改,圖太劣質]

授權的api

  • 基於角色

                //判斷當前主體是否包含此角色
                boolean b = subject.hasRole("super");
                List<String> list = Arrays.asList("super", "admin");
                //判斷當前主體是否包含某個角色
                boolean[] booleans = subject.hasRoles(list);
                //判斷當前主體是否包含全部的角色
                boolean b = subject.hasAllRoles(list);
  • 基於資源

                boolean b = subject.isPermitted("admin:delete");
                String[] strs={"admin:delete", "admin:add"};
                boolean[] permitted = subject.isPermitted(strs);
                boolean permittedAll = subject.isPermittedAll(strs);

資源許可權的識別符號

許可權字串的規則是:“資源識別符號:操作:資源例項識別符號”,意思是對哪個資源的哪個例項具有什麼操作,“:”是資源/操作/例項的分割符,許可權字串也可以使用*萬用字元。

例子:

  • 使用者建立許可權:user:create,或user:create:*
  • 使用者修改例項001的許可權:user:update:001
  • 使用者例項001的所有許可權:user:*:001