shiro認識

一.認識shiro

Apache Shiro是一個強大且易用的Java安全框架,有身份驗證授權密碼學會話管理

  • 輕量級的權限框架
  • shiro(輕量級,粗粒度) , Spring security(細粒度)
  • RBAC:權限(登錄,授權) 用戶(n)-角色(n)-權限(n)(資源)

1.1 shiro的四大基石

  1. 身份認證(登錄) Authentication
  2. 授權(權限) Authorization
  3. 密碼學 Cryptography
    1. 過使用加密算法保持數據安全同時易於使用。

  4. 會話管理 Session Management
    1. 管理用戶特定的會話,即使在非 Web 或 EJB 應用程序。

image

1.2 重要的對象

  • Subject:當前用戶
  • SecurityManager:權限管理器(所有功能管理)
  • Realm:獲取權限數據

image

二.Hello案例

建一個普通的maven項目

2.1 導包

<!--使用shiro需要先導包-->
<dependencies>
    <!--shiro的核心包-->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-core</artifactId>
        <version>1.4.0</version>
    </dependency>
    <!--日誌包-->
    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.2</version>
    </dependency>
    <!--測試包-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.9</version>
    </dependency>
</dependencies>

2.2 ini文件

文件中有咱們的用戶角色權限

# ini文件裏面放的就是咱們的用戶,角色,權限,資源

# -----------------------------------------------------------------------------
#  users:用戶
#   root:用戶名  123456:密碼  admin:角色
# -----------------------------------------------------------------------------
[users]
root = 123456, admin
guest = guest, it

# -----------------------------------------------------------------------------
# roles:角色
#   admin = * :admin這個用戶擁有所有權限
#   it = employee:* :it這個角色擁有員工的所有權限
#   hr = employee:save :hr這個角色擁有員工添加權限
# -----------------------------------------------------------------------------
[roles]
admin = *
it = employee:*
hr = employee:save

2.3 功能測試

主要測試登錄,權限認證;認識API

@Test
public void testHello() throws Exception{
    //①.拿到權限管理對象
    /**
     * 讀取了shiro.ini的文件(隱藏了realm) -> 隱藏了iniRealm
     * SecurityManager:權限管理器,shiro的所有功能都放在裏面
     */
    Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
    SecurityManager securityManager = factory.getInstance();
    //②.相當於把SecurityManager放到了當前上下文
    /**
     * 可以讓我們在當前系統的任何位置都可以拿到SecurityManager對象
     */
    SecurityUtils.setSecurityManager(securityManager);
    //③.拿到當前用戶(沒有登錄就是遊客)
    Subject currentUser = SecurityUtils.getSubject();
    System.out.println("用戶是否登錄:"+currentUser.isAuthenticated());

    //④.如果沒有登錄,讓他進行登錄
    if(!currentUser.isAuthenticated()){
        //ctrl+alt+t :包含代碼
        try {
            //4.1 準備令牌(對象) 用戶名密碼令牌
            UsernamePasswordToken token = new UsernamePasswordToken("guest","guest");
            //4.2 進行登錄功能
            currentUser.login(token);
        } catch (UnknownAccountException e) {
            //Unknown(未知)Account(賬號)Exception:用戶名不存在
            e.printStackTrace();
            System.out.println("哥,你是傻子嘛?");
        }catch (IncorrectCredentialsException e){
            //Incorrect(不正確)Credentials(憑證)Exception:密碼錯誤
            e.printStackTrace();
            System.out.println("哥,密碼錯誤了?");
        }catch (AuthenticationException e){
            //AuthenticationException:登錄中最大的那個異常
            e.printStackTrace();
            System.out.println("發生了一個神祕的錯誤!!!");
        }
    }
    System.out.println("用戶是否登錄:"+currentUser.isAuthenticated());

    System.out.println("是否是管理員角色:"+currentUser.hasRole("admin"));
    System.out.println("是否是IT角色:"+currentUser.hasRole("it"));

    System.out.println("是否可以操作employee:save權限:"+currentUser.isPermitted("employee:save"));
    System.out.println("是否可以操作employee:index權限:"+currentUser.isPermitted("employee:index"));
    System.out.println("是否可以操作department:index權限:"+currentUser.isPermitted("department:index"));

    //⑤.還可以登出(註銷)
    currentUser.logout();
    System.out.println("用戶是否登錄:"+currentUser.isAuthenticated());
}

三.自定義Realm

剛纔咱們使用的是準備的好的一個Realm,在Shiro中,除了咱們 剛纔使用的iniRealm,還有普通SimpleAccountRealm,JDBCRealm等等.

但是這些Realm都不能滿足咱們的需求,而這種情況下,就需要我們自己定義一個Realm來實現相應的功能!

3.1 準備自定義Realm

  • 寫一個Realm,繼承AuthorizingRealm
  • 提供了兩個方法,一個是授權doGetAuthorizationInfo,一個是身份認證 doGetAuthenticationInfo
public class MyRealm extends AuthorizingRealm {

    //授權認證功能就寫在這裏面
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        //從數據庫中獲取角色並放且放到授權對象中
        Set<String> roles = getRoles();
        authorizationInfo.setRoles(roles);
        //從數據庫中獲取權限並放且放到授權對象中
        Set<String> perms = getPerms();
        authorizationInfo.setStringPermissions(perms);
        return authorizationInfo;
    }

    /**
     * 假設這裏獲取到當前用戶的角色
     */
    private Set<String> getRoles(){
        Set<String> roles = new HashSet<>();
        roles.add("admin");
        roles.add("it");
        return roles;
    }
    /**
     * 假設這裏獲取到當前用戶的權限
     */
    private Set<String> getPerms(){
        Set<String> perms = new HashSet<>();
        perms.add("employee:index");
        return perms;
    }

    /**
     * 記住:如果這個方法返回null,就代表是用戶名錯誤,shiro就會拋出:UnknownAccountException
     */
    //身份認證(登錄)就寫在這裏面
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //1.拿到令牌(UsernamePasswordToken)
        UsernamePasswordToken token =  (UsernamePasswordToken)authenticationToken;
        //2.拿到用戶名,判斷這個用戶是否存在
        // 2.1 拿到傳過來的用戶名
        String username = token.getUsername();
        // 2.2 根據用戶名從數據庫中拿到密碼(以後會拿用戶對象)
        String password = this.getUsers(username);
        // 2.3 如果沒有拿到密碼(沒有通過用戶名拿到相應的用戶->用戶不存在)
        if(password==null){
            return null;
        }

        //記住:我們只在正常完成這裏的功能,shiro會判斷密碼是否正確
        //3.返回 AuthenticationInfo這個對象
        /**
         * 咱們創建對象需要傳的參數:
         * Object principal:主體(可以亂寫) -> 登錄成功後,你想把哪一個對象存下來
         * Object credentials:憑證(就是密碼) -> 數據庫中的密碼
         * credentials(密碼)Salt:鹽值
         * String realmName : realm的名稱(可以亂寫)
         */
        //拿到咱們的鹽值對象(ByteSource)
        ByteSource salt = ByteSource.Util.bytes("itsource");
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username,password,salt,"myRealm");
        return authenticationInfo;
    }

    /**
     * 假設這裏是根據用戶名進行的查詢
     *  MD5:e10adc3949ba59abbe56e057f20f883e
     *  MD5+10次:4a95737b032e98a50c056c41f2fa9ec6
     *  MD5+10次+itsource:831d092d59f6e305ebcfa77e05135eac
     */
    public String getUsers(String username){
        if("admin".equals(username)){
            return "831d092d59f6e305ebcfa77e05135eac";
        }else if("zhang".equals(username)){
            return "123";
        }
        return null;
    }
}

3.2 測試自定義Realm

 @Test
    public void testMyRealm() throws Exception{
        //一.創建一個SecurityManager對象
        // 1.創建realm對象
        MyRealm myRealm = new MyRealm();
        // 2.創建SecurityManager對象
        DefaultSecurityManager securityManager = new DefaultSecurityManager(myRealm);
        //②.相當於把SecurityManager放到了當前上下文
        SecurityUtils.setSecurityManager(securityManager);
        //③.拿到當前用戶
        Subject subject = SecurityUtils.getSubject();

        //Hashed(哈希)Credentials(認證)Matcher(匹配器)
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        //設置哈希算法
        matcher.setHashAlgorithmName("MD5");
        //設置迭代次數
        matcher.setHashIterations(10);
        //把匹配器交給shiro
        myRealm.setCredentialsMatcher(matcher);

        System.out.println("用戶是否登錄:"+subject.isAuthenticated());
        //④.如果沒有登錄,讓他登錄
        if(!subject.isAuthenticated()){
            try {
                UsernamePasswordToken token = new UsernamePasswordToken("admin","123456");
                subject.login(token);
            } catch (UnknownAccountException e) {
                e.printStackTrace();
                System.out.println("用戶名錯誤");
            } catch (IncorrectCredentialsException e) {
                e.printStackTrace();
                System.out.println("密碼錯誤");
            } catch (AuthenticationException e) {
                e.printStackTrace();
                System.out.println("神迷錯誤");
            }
        }
        System.out.println("用戶是否登錄:"+subject.isAuthenticated());

        System.out.println("是否是admin角色:"+subject.hasRole("admin"));
        System.out.println("是否是hr角色:"+subject.hasRole("hr"));

        System.out.println("是否有employee:index權限:"+subject.isPermitted("employee:index"));
        System.out.println("是否有employee:save權限:"+subject.isPermitted("employee:save"));
        System.out.println("是否有department:index權限:"+subject.isPermitted("department:index"));


    }

 

發佈了70 篇原創文章 · 獲贊 3 · 訪問量 5747
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章