【shiro】(4)---Shiro認證、受權案例講解

Shiro認證、受權案例講解

 

1、認證

 一、 認證流程

   

二、用戶密碼已經加密、加鹽的用戶認證

 (1)測試類web

  // 用戶登錄和退出,這裏我自定了一個realm(開發確定須要自定義realm獲取數據庫密碼和權限)
    @Test
    public void testCustomRealmMd5() {

        // 建立securityManager工廠,經過ini配置文件建立securityManager工廠
        Factory<SecurityManager> factory = new IniSecurityManagerFactory(
                "classpath:shiro-realm-md5.ini");

        // 建立SecurityManager
        SecurityManager securityManager = factory.getInstance();

        // 將securityManager設置當前的運行環境中
        SecurityUtils.setSecurityManager(securityManager);

        // 從SecurityUtils裏邊建立一個subject
        Subject subject = SecurityUtils.getSubject();

        // 在認證提交前準備token(令牌)
        // 這裏的帳號和密碼 未來是由用戶輸入進去
        UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "111111");

        try {
            // 執行認證提交
            subject.login(token);
        } catch (AuthenticationException e) {
            e.printStackTrace();
        }

        // 是否定證經過
        boolean isAuthenticated = subject.isAuthenticated();

        System.out.println("加密後是否定證經過:" + isAuthenticated);
        // 退出操做
        subject.logout();
        // 是否定證經過
          isAuthenticated = subject.isAuthenticated();
      System.out.println("退出後是否定證經過:" + isAuthenticated);
    }

(2)shiro-realm-md5.ini (有關散列加密下面會舉例子)算法

    我在ini配置了加密的規則,這個規則要和用戶註冊保存的密碼加密規則一致。spring

[main]
#定義憑證匹配器
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
#散列算法
credentialsMatcher.hashAlgorithmName=md5
#散列次數
credentialsMatcher.hashIterations=1

#將憑證匹配器設置到realm
customRealm=com.jincou.shiro.realm.CustomRealmMd5
customRealm.credentialsMatcher=$credentialsMatcher
securityManager.realms=$customRealm

(3)自定義CustomRealmMd5類數據庫

/**
 *自定義Realm特色
 *(1)繼承AuthorizingRealm類
 *(2)重寫AuthenticationInfo(認證)
 *     doGetAuthorizationInfo(受權)兩個方法
 * 注意:
 * 1:這裏password = "f3694f162729b7d0254c6e40260bf15c"確定也是經過明文(這裏指zhangsan)+鹽(這裏是qwerty)進行加密後的字符串
 * 2:實際應用是將鹽和散列後的值存在數據庫中,自動realm從數據庫取出鹽和加密後的值由shiro完成密碼校驗。
 * 3:這裏要注意它們兩的加密規則必定要一致,不然沒法比較。
 */
public class CustomRealmMd5 extends AuthorizingRealm {

    // 設置realm的名稱(任意和其它也無關聯)
    @Override
    public void setName(String name) {
        super.setName("customRealmMd5");
    }

    // 用於認證
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken token) throws AuthenticationException {
        
         System.out.println("測試..自定義CustomRealmMd5方法裏面......");
         
        // token是用戶輸入的
        // 第一步從token中取出身份信息
        String userCode = (String) token.getPrincipal();

        // 第二步:根據用戶輸入的userCode從數據庫查詢
        // ....

        //若是數據庫查詢不到zhangsan用戶信息,則返回null
            /*if(user==null){
                    return null;
            }*/

        // 模擬從數據庫查詢到密碼,散列值
        String password = "f3694f162729b7d0254c6e40260bf15c";
        // 從數據庫獲取salt
        String salt = "qwerty";
        //上邊散列值和鹽對應的明文:111111

        // 若是查詢到返回認證信息AuthenticationInfo,這裏的密碼加密判斷交給底層處理
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
                userCode, password, ByteSource.Util.bytes(salt), this.getName());

        return simpleAuthenticationInfo;
    }

    // 用於受權(後面寫)
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(
            PrincipalCollection principals) {
        return null;
    }
}

運行效果apache

結論:subject.login(token)我在提交認證,它就會去調用我自定義的CustomRealm,由於我在ini進行配置自定義CustomRealm編程

 三、加密算法

 由於在ini裏面配置的加密規則,那我第一次用戶註冊的時候,確定也須要按它的規則進行加密後,把密碼保存到數據庫ide

  /**
  * 加密算法
  */
public class MD5Test {    
    public static void main(String[] args) {     
        //原始 密碼 
        String source = "111111";
        //
        String salt = "qwerty";
        
        //散列次數(和ini中credentialsMatcher.hashIterations=1一致)
        int hashIterations = 1;
        //上邊散列1次:f3694f162729b7d0254c6e40260bf15c
        //上邊散列2次:36f2dfa24d0a9fa97276abbe13e596fc
        
        //第一個參數:散列算法 
        //這裏的散列算法是md5要和ini中credentialsMatcher.hashAlgorithmName=md5一致)
        //第二個參數:明文,原始密碼 
        //第三個參數:鹽,經過使用隨機數
        //第四個參數:散列的次數,好比散列兩次,至關 於md5(md5(''))
        SimpleHash simpleHash = new SimpleHash("md5", source, salt, hashIterations);
        System.out.println(simpleHash.toString());    
    }
}
  //這個結果:f3694f162729b7d0254c6e40260bf15c就是在自定義realm中的密文也就是該用戶保存到數據庫中的密文。

 

 2、受權(基於資源的受權)

 一、受權流程

 

二、受權方式

Shiro 支持三種方式的受權:
(1)編程式:經過寫if/else 受權代碼塊完成:測試

Subject subject = SecurityUtils.getSubject();
if(subject.hasRole(「admin」)) {
//有權限
} else {
//無權限
}

 (2)註解式:經過在執行的Java方法上放置相應的註解完成ui

@RequiresRoles("admin")
public void hello() {
//有權限
}

 (3)JSP/GSP 標籤:在JSP/GSP 頁面經過相應的標籤完成this

<shiro:hasRole name="admin">
<!— 有權限—>
</shiro:hasRole>

 這裏受權測試使用第一種編程方式,實際與web系統集成使用後兩種方式。

 三、受權測試

 (1)測試類

// 自定義realm進行資源受權測試
    @Test
    public void testAuthorizationCustomRealm() {

        // 建立SecurityManager工廠
        Factory<SecurityManager> factory = new IniSecurityManagerFactory(
                "classpath:shiro-realm.ini");

        // 建立SecurityManager
        SecurityManager securityManager = factory.getInstance();

        // 將SecurityManager設置到系統運行環境,和spring後將SecurityManager配置spring容器中,通常單例管理
        SecurityUtils.setSecurityManager(securityManager);

        // 建立subject
        Subject subject = SecurityUtils.getSubject();

        // 建立token令牌
        UsernamePasswordToken token = new UsernamePasswordToken("zhangsan",
                "111111");

        // 執行認證
        try {
            subject.login(token);
        } catch (AuthenticationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        System.out.println("認證狀態:" + subject.isAuthenticated());
        // 認證經過後執行受權

        // 基於資源的受權,調用isPermitted方法會調用CustomRealm從數據庫查詢正確權限數據
        // isPermitted傳入權限標識符,判斷user:create:1是否在CustomRealm查詢到權限數據以內
        boolean isPermitted = subject.isPermitted("user:create:1");
        System.out.println("單個權限判斷" + isPermitted);

        boolean isPermittedAll = subject.isPermittedAll("user:create:1",
                "user:create");
        System.out.println("多個權限判斷" + isPermittedAll);

        // 使用check方法進行受權,若是受權不經過會拋出異常
        subject.checkPermission("items:add:1");

    }

(2)shiro-realm.ini

[main]
#自定義 realm
customRealm=com.jincou.shiro.realm.CustomRealm
#將realm設置到securityManager
securityManager.realms=$customRealm

(3)自定義CustomRealm類

public class CustomRealm extends AuthorizingRealm {

    // 用於認證(上面寫過)
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken token) throws AuthenticationException {
        return null;
    }

    // 用於受權
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(
            PrincipalCollection principals) {
        
        //從 principals獲取主身份信息
        //將getPrimaryPrincipal方法返回值轉爲真實身份類型(在上邊的doGetAuthenticationInfo認證經過填充到SimpleAuthenticationInfo中身份類型),
        String userCode =  (String) principals.getPrimaryPrincipal();
        
        //根據身份信息獲取權限信息
        //鏈接數據庫...
        //模擬從數據庫獲取到數據
        List<String> permissions = new ArrayList<String>();
        permissions.add("user:create");//用戶的建立
        permissions.add("items:add");//商品添加權限
        //....
        
        //查到權限數據,返回受權信息(要包括 上邊的permissions)
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        //將上邊查詢到受權信息填充到simpleAuthorizationInfo對象中
        simpleAuthorizationInfo.addStringPermissions(permissions);

        return simpleAuthorizationInfo;
    }
}

運行結果:

 

 想太多,作太少,中間的落差就是煩惱。想沒有煩惱,要麼別想,要麼多作。少校【5】

相關文章
相關標籤/搜索