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


Shiro認證、授權案例講解

 

一、認證

 1、 認證流程

   

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

 (1)測試類

  // 用戶登陸和退出,這里我自定了一個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配置了加密的規則,這個規則要和用戶注冊保存的密碼加密規則一致。

[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;
    }
}

運行效果

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

 3、加密算法

 因為在ini里面配置的加密規則,那我第一次用戶注冊的時候,肯定也需要按它的規則進行加密后,把密碼保存到數據庫

  /**
  * 加密算法
  */
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中的密文也就是該用戶保存到數據庫中的密文。

 

 二、授權(基於資源的授權)

 1、授權流程

 

2、授權方式

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

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

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

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

 (3)JSP/GSP 標簽:在JSP/GSP 頁面通過相應的標簽完成

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

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

 3、授權測試

 (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】

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM