1. 程式人生 > >shiro框架學習筆記

shiro框架學習筆記

最近學習了 許可權框架shiro的知識,做一下 學習的筆記

使用ini

這是shiro 最簡單的用法,首先建立一個demo.ini檔案,裡面寫入如下的內容

[users]
xiezihao=123456,admin
[roles]
admin = user.insert,user.update

下面對上面的配置進行一下解釋
[users] 代表的是使用者,之後在程式碼中做登入的時候使用,比如這裡的配置中,的一是就是 ,使用者名稱是xiezihao,密碼是123456,擁有一個admin的角色。
[roles] 代表的是角色的意思,一個使用者可以對應有多個角色,這裡的一是是,admin這個角色擁有,user.insert 和 user.update 兩種操作的許可權

下面將通過示例程式碼演示,如何利用這個ini檔案實現登入,和許可權的認證

 public void t(){
        //設定使用的realm
        IniRealm iniRealm = new IniRealm("classpath:demo.ini");
        //新建一個安全管理器,並將realm設定到這個管理器中去
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        defaultSecurityManager.setRealm
(iniRealm); //將設定好的 安全管理器放入安全工具類中,然後用 安全工具類來獲取subject SecurityUtils.setSecurityManager(defaultSecurityManager); Subject subject = SecurityUtils.getSubject(); //建立登入用的token UsernamePasswordToken token = new UsernamePasswordToken(); token.setUsername("xiezihao"
); token.setPassword("123456".toCharArray()); //登入 subject.login(token); if(subject.isAuthenticated()){ System.out.println("登入成功"); } if(subject.hasRole("admin")){ System.out.println("擁有admin的身份"); } if(subject.isPermitted("user.insert")){ System.out.println("擁有 user.insert 的許可權"); } }

後面的例子其實都和這個比較的相似,都是按照如下順序

  1. 拿到一個realm
  2. 用這個realm 設定出一個安全管理器
  3. 為安全工具類設定安全管理器,然後以此創建出subject
  4. 利用subject做後續的操作

自定義的realm

有的時候框架給出的realm不能滿足我們實際的需求,我們可以自定義realm,新建一個類,讓這個類去繼承 AuthorizingRealm

然後實現裡面的兩個方法:doGetAuthorizationInfo 授權、doGetAuthenticationInfo 登入認證

下面用一個實際的例子 來說明

public class DemoRealm extends AuthorizingRealm {
    //授權的時候執行
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("開始鑑權操作");
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        HashSet<String> roles = new HashSet<>();
        HashSet<String> permissions = new HashSet<>();
        //將使用者的角色和許可權填入set集合  在實際的情況下這一步會從資料庫中獲取
        roles.add("admin");
        permissions.add("user.insert");
        
        //將使用者的角色集合和許可權集合放入到AuthorizationInfo中去
        simpleAuthorizationInfo.setRoles(roles);
        simpleAuthorizationInfo.setStringPermissions(permissions);
        return simpleAuthorizationInfo;
    }

    //登入時候執行
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("開始使用者登入操作");
        //獲取使用者登入的使用者名稱和密碼
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
        String username = usernamePasswordToken.getUsername();
        char[] password = usernamePasswordToken.getPassword();
        //這裡我們模擬一下許可權認證,實際情況上這裡會從資料庫拿資料
        if (username.equals("xiezihao") && new String(password).equals("123456")) {
            /**
             * 如果認證成功返回一個AuthenticationInfo物件,這三個引數分別是
             * 使用者名稱、密碼、和realm名字  realm的名字隨便取好了主要是用來區分的作用
             */
            return new SimpleAuthenticationInfo("xiezihao", "123456", "demoRealm");
        }
        //如果登入失敗就返回null
        return null;
    }
}

下面我們利用一個測試類來測試一下。測試類的寫法可以參考 ini 這個測試的方法

@Test
    public void demoRealmT() {
        //	新建一個 我們自定義的realm
        DemoRealm demoRealm = new DemoRealm();
        //建立安全管理器
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        defaultSecurityManager.setRealm(demoRealm);
        //構建 subject
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken();
        usernamePasswordToken.setUsername("xiezihao");
        usernamePasswordToken.setPassword("123456".toCharArray());
        //登入
        subject.login(usernamePasswordToken);
        if(subject.isAuthenticated()){
            System.out.println("使用者已經成功登入");
        }

        if(subject.hasRole("admin")){
            System.out.println("使用者擁有admin角色");
        }

        if(subject.isPermitted("user.insert")){
            System.out.println("使用者擁有user.insert 操作的許可權");
        }
    }

最後輸出的結果如下

開始使用者登入操作
使用者已經成功登入
開始鑑權操作
使用者擁有admin角色
開始鑑權操作
使用者擁有user.insert 操作的許可權

jdbc realm

前面的操作中我們使用的都是寫死或者本地檔案中寫入使用者配置,但是在實際的過程中我們肯定是從資料庫中讀取的

shiro提供了一個JdbcRealm 的類用來查詢資料庫,但是這個類裡的sql 語句預設都是寫死的,我們可以進入到這個類的原始碼裡去看下

 /**
     * The default query used to retrieve account data for the user.
     */
    protected static final String DEFAULT_AUTHENTICATION_QUERY = "select password from users where username = ?";
    
    /**
     * The default query used to retrieve account data for the user when {@link #saltStyle} is COLUMN.
     */
    protected static final String DEFAULT_SALTED_AUTHENTICATION_QUERY = "select password, password_salt from users where username = ?";

    /**
     * The default query used to retrieve the roles that apply to a user.
     */
    protected static final String DEFAULT_USER_ROLES_QUERY = "select role_name from user_roles where username = ?";

    /**
     * The default query used to retrieve permissions that apply to a particular role.
     */
    protected static final String DEFAULT_PERMISSIONS_QUERY = "select permission from roles_permissions where role_name = ?";

在這個類的開頭定義了 這4個sql 語句,從上到下的用處分別是:

  • 使用者賬號密碼登入
  • 使用者帶salt的賬號密碼登入
  • 查詢使用者名稱下所有的角色
  • 查詢角色下所有的許可權
    所以我們只需要把資料庫的表按照這個sql 語句的形式來建立就可以了,但是這樣子做肯定是不行的,因為實際的業務欄位名可能不是這樣的,所有我們可以重新自定義sql 語句,按照剛才原始碼裡的格式 實際的操作請看下面的程式碼
@Test
    public void test(){
    	//設定一個數據庫連線池,這裡使用了阿里巴巴的druid連線池
        DruidDataSource source = new DruidDataSource();
        source.setUrl("jdbc:mysql://localhost:3306/rili");
        source.setUsername("root");
        source.setPassword("root");
        //建立一個jdbcrealm 並加你個連線池設定到這個realm中去
        JdbcRealm jdbcRealm = new JdbcRealm();
        jdbcRealm.setDataSource(source);
        //自定義登入sql
        String sql = "select password from user where user_name = ?";
        jdbcRealm.setAuthenticationQuery(sql);
        //自定義角色sql
        String sql2 = "select quan from role where name = ?";
        jdbcRealm.setUserRolesQuery(sql2);
        //自定義許可權sql
        String sql3= "select permission from permissions where juese = ?";
        jdbcRealm.setPermissionsQuery(sql3);
        /**
        * 這裡是很重要的一句!必須要設定他為true,原始碼裡的解釋是預設人false
        *  表示只有使用者名稱和角色關聯,如果要角色和許可權關聯必須設定為true
        */
        jdbcRealm.setPermissionsLookupEnabled(true);
       
       //之後的操作步驟就是和之前的一樣就不再做複述了
        DefaultSecurityManager securityManager = new DefaultSecurityManager();
        securityManager.setRealm(jdbcRealm);
        SecurityUtils.setSecurityManager(securityManager);
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken("xiezihao","12345");
        subject.login(token);
        if(subject.isAuthenticated()){
            System.out.println("login ok");
        }
        subject.checkRoles("admin");
        subject.checkPermissions("user.update");

spring 整合shiro

到上面為止,介紹了shiro的基本使用方法,下面講解的是如何將shiro和spring進行整合。

引入依賴

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

建立一個基本的springboot工程,引入必要的web依賴,這裡關於springboot就不再做描述了

編寫自定義realm

public class MyRealm extends AuthorizingRealm {
    //授權
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        //獲取登入的使用者名稱
        String username = (String) principalCollection.getPrimaryPrincipal();
        //授權操作,模擬從資料庫獲取授權資訊
        if (username.equals("admin")) {
            simpleAuthorizationInfo.addRole("admin");
            simpleAuthorizationInfo.addStringPermission("user:insert");
        } else {
            simpleAuthorizationInfo.addRole("default");
        }
        return simpleAuthorizationInfo;
    }

    //登入認證
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //獲取登入的使用者名稱和密碼
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        String username = token.getUsername();
        String password = new String(token.getPassword());
        //認證,這裡應該從資料庫獲取,為了簡便就直接寫了
        if (username.equals("admin") && password.equals("12345")) {
            return new SimpleAuthenticationInfo("admin", "12345", "MyRealm");
        } else {
            return null;
        }
    }
}

編寫shiro 配置類

這裡是重要的一步,定義一個類進行授權的配置