1. 程式人生 > >(轉) shiro權限框架詳解04-shiro認證

(轉) shiro權限框架詳解04-shiro認證

software protected .get 打開 net 文件的 apach stc cdc

http://blog.csdn.net/facekbook/article/details/54906635

shiro認證

本文介紹shiro的認證功能

  • 認證流程
  • 入門程序(用戶登錄和退出)
  • 自定義Realm
  • 散列算法

認證流程

開始構造SecurityManager環境subject.login();提交認證securityManager.login()執行認證Authenticator執行認證Realm根據身份獲取驗證信息結束

入門程序(用戶登錄和退出)

創建Java項目

jdk版本:1.7.0_67

加入shiro的jar包以及依賴包

技術分享

log4j.properties日誌文件配置

log4j.rootLogger=debug, stdout

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n

eclipse中ini文件打開方式配置

shiro使用ini文件作為配置文件。所以需要修改eclipse中ini文件的打開方式,默認的話ini文件是使用記事本打開。具體如下圖:

技術分享

創建ini配置文件

在classpath路徑下創建shiro-first.ini文件,文件內容是測試用戶的賬號和密碼。內容如下:

[users]
zhangsan=123
lisi=456

認證代碼

@Test
    public void testLoginAndLogOut() {
        // 構建SecurityManager工廠,IniSecurityManagerFactory可以從ini文件中初始化SecurityManager環境
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-first.ini");
        // 通過工廠創建SecurityManager
        SecurityManager securityManager = factory.getInstance();
        // 將SecurityManager設置到運行環境中
        SecurityUtils.setSecurityManager(securityManager);
        //創建一個Subject實例,該實例認證需要使用上面創建的SecurityManager
        Subject subject = SecurityUtils.getSubject();
        //創建token令牌,賬號和密碼是ini文件中配置的
        AuthenticationToken token = new UsernamePasswordToken("zhangsan", "123");
        try {
            //用戶登錄
            subject.login(token);
        } catch (AuthenticationException e) {
            e.printStackTrace();
        }
        //用戶認證狀態
        Boolean isAuthenticated = subject.isAuthenticated();
        System.out.println("用戶認證狀態:"+isAuthenticated);//輸出true

        //用戶退出
        subject.logout();

        isAuthenticated = subject.isAuthenticated();
        System.out.println("用戶認證狀態:"+isAuthenticated);//輸出false
    }

認證執行流程

1.創建token令牌,token中有用戶提交的認證信息即賬號和密碼。
2.執行subject.login(token),最終由securityManager通過 Authenticator進行認證。
3.Authenticator的實現ModuleRealmAuthenticator調用realm從init文件讀取用戶真實的賬號和密碼,這裏使用的是IniRealm(Shiro自帶)
4.IniRealm先根據token中的賬號去ini中找該賬號,如果找不到則給ModuleRealmAuthenticator返回null,如果找到則匹配密碼,匹配密碼成功則認證通過。

常見的異常

  • UnknownAccountException
    賬號不存在異常如下:
org.apache.shiro.authc.UnknownAccountException: Realm [org.apache.shiro.realm.text.IniRealm@9cdc393] was unable to find account data for the submitted AuthenticationToken [org.apache.shiro.authc.UsernamePasswordToken - zhangsan1
  • IncorrectCredentialsException
    當輸入密碼錯誤會拋出此異常,如下:
org.apache.shiro.authc.IncorrectCredentialsException: Submitted credentials for token [org.apache.shiro.authc.UsernamePasswordToken - zhangsan, rememberMe=false] did not match the expected credentials.

更多異常信息如下:
DisabledAccountException(帳號被禁用)
LockedAccountException(帳號被鎖定)
ExcessiveAttemptsException(登錄失敗次數過多)
ExpiredCredentialsException(憑證過期)等
類結構如下圖
技術分享

自定義Realm

上面的程序使用的是Shiro自帶的IniRealm,IniRealm從ini配置文件中讀取用戶的信息。但是實際情況中大部分情況下是從數據庫中獲取用戶信息,所以需要自定義realm。

Shiro中Realm

技術分享
最基礎的是Realm接口,CachingRealm負責緩存管理,AuthenticatingRealm負責認證,AuthorizingRealm負責授權,通常自定義的Realm繼承AuthorizingRealm。

自定義Realm代碼

通過繼承AuthorizingRealm類實現

public class CustomRealm extends AuthorizingRealm {

    /**
     * 認證方法
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        // 從token中獲取用戶身份信息
        String username = (String) token.getPrincipal();

        // 正常邏輯應該是通過username查詢數據庫。
        // 如果查詢不到返回null
        if (!"zhangsan".equals(username)) {// 這裏模仿查詢不到
            return null;
        }

        // 模擬從數據獲取密碼
        String password = "123";

        // 返回認證信息交由父類AuthorizingRealm認證
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username, password, "");

        return authenticationInfo;
    }

    /**
     * 授權方法
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }
}

ini配置文件

新建shiro-realm.ini文件。內容如下:

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

這裏不需要配置users,是因為我們這裏模擬users的獲取來自數據庫。

測試代碼

@Test
    public void testCustomeRealm() {
        // 構建SecurityManager工廠,IniSecurityManagerFactory可以從ini文件中初始化SecurityManager環境
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-realm.ini");
        // 通過工廠創建SecurityManager
        SecurityManager securityManager = factory.getInstance();
        // 將SecurityManager設置到運行環境中
        SecurityUtils.setSecurityManager(securityManager);
        //創建一個Subject實例,該實例認證需要使用上面創建的SecurityManager
        Subject subject = SecurityUtils.getSubject();
        //創建token令牌,賬號和密碼是ini文件中配置的
        //AuthenticationToken token = new UsernamePasswordToken("zhangsan", "123");//賬號密碼正確token
        //AuthenticationToken token = new UsernamePasswordToken("zhangsan", "1234");//密碼錯誤異常token
        AuthenticationToken token = new UsernamePasswordToken("zhangsan1", "123");//賬號錯誤異常token
        try {
            //用戶登錄
            subject.login(token);
        } catch (AuthenticationException e) {
            e.printStackTrace();
        }
        //用戶認證狀態
        Boolean isAuthenticated = subject.isAuthenticated();
        System.out.println("用戶認證狀態:"+isAuthenticated);//輸出true
    }

散列算法

散列算法一般用於生成一段文本的摘要信息,散列算法不可逆,也就是將內容生成摘要,但是反過來通過摘要生成內容是不可以的。散列算法常用於對密碼進行散列,常用的散列算法有MD5、SHA。一般散列算法需要提供一個salt(鹽)與原始內容生成摘要,這樣做的目的是為了安全性。

例子

        Md5Hash  md5Hash = new Md5Hash("111111");
        System.out.println("md5加密,不加鹽:"+md5Hash.toString());

        //md5加密,加鹽,一次hash
        String password_md5_sale_1 = new Md5Hash("11111", "aga23", 1).toString();
        System.out.println("md5加密,加鹽,一次hash:"+password_md5_sale_1);

        //md5加密,加鹽,兩次hash
        String password_md5_sale_2 = new Md5Hash("11111", "aga23", 2).toString();
        System.out.println("md5加密,加鹽,兩次hash:"+password_md5_sale_2);//相當於md5(md5(‘1111‘))

        //使用simpleHash
        String simpleHash = new SimpleHash("MD5", "11111", "aga23", 1).toString();
        System.out.println(simpleHash);

在realm中使用散列算法

實際系統中是將鹽和散列後的值存儲在數據庫中,自定義realm從數據庫取出鹽和加密後的值由shiro完成密碼校驗。

自定義支持散列的realm

public class CustomRealmMd5 extends AuthorizingRealm {

    /**
     * 認證方法
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        // 從token中獲取用戶身份信息
        String username = (String) token.getPrincipal();

        // 正常邏輯應該是通過username查詢數據庫。
        // 如果查詢不到返回null
        if (!"zhangsan".equals(username)) {// 這裏模仿查詢不到
            return null;
        }

        // 模擬從數據獲取密碼
        String password = "fdf907b0d3f427b9ffa2f86f213d1afd";
        // 鹽
        String salt = "aga23";
        // 返回認證信息交由父類AuthorizingRealm認證
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username, password,
                ByteSource.Util.bytes(salt), "");

        return authenticationInfo;
    }

    /**
     * 授權方法
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }
}

支持散列的realm配置

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

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

測試代碼

註意修改配置文件的路徑

    @Test
    public void testCustomeRealmMd5() {
        // 構建SecurityManager工廠,IniSecurityManagerFactory可以從ini文件中初始化SecurityManager環境
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-realm-md5.ini");
        // 通過工廠創建SecurityManager
        SecurityManager securityManager = factory.getInstance();
        // 將SecurityManager設置到運行環境中
        SecurityUtils.setSecurityManager(securityManager);
        //創建一個Subject實例,該實例認證需要使用上面創建的SecurityManager
        Subject subject = SecurityUtils.getSubject();
        //創建token令牌,賬號和密碼是ini文件中配置的
        //AuthenticationToken token = new UsernamePasswordToken("zhangsan", "11111");//賬號密碼正確token
        AuthenticationToken token = new UsernamePasswordToken("zhangsan", "1234");//密碼錯誤異常token
        //AuthenticationToken token = new UsernamePasswordToken("zhangsan1", "11111");//賬號錯誤異常token
        try {
            //用戶登錄
            subject.login(token);
        } catch (AuthenticationException e) {
            e.printStackTrace();
        }
        //用戶認證狀態
        Boolean isAuthenticated = subject.isAuthenticated();
        System.out.println("用戶認證狀態:"+isAuthenticated);//輸出true
    }

該文章涉及的代碼

代碼地址

(轉) shiro權限框架詳解04-shiro認證