Shrio02 Realm做用、自定義簡潔Realm、Realm實現類使用

1 Realm簡介

1.1 Realm做用

shiro最終是經過Realm獲取安全數據的(如用戶、角色、權限),也就是說認證或者受權都會經過Realm進行數據操做java

1.2 Realm接口

1.2.1 源代碼
mysql

1.2.2 方法說明
》getName:返回一個惟一的 Realm 名字 
》supports:判斷此 Realm 是否支持此 Token 
》getAuthenticationInfo:根據 Token 獲取認證信息,該方法就是用來實現認證邏輯的(從Realm的實現類org.apache.shiro.realm.AuthenticatingRealm#getAuthenticationInfo中能夠看出)sql

1.3 AuthenticationToken

》層級關係
數據庫

》關係圖
apache

》開發時通常將用戶名和密碼封裝成一個UsernamePasswordToken對象api

1.4 注意

》supports須要對Token類型進行判斷,判斷實參類型是否知足條件;這裏指定的是AuthenticationToken類型(任何Token類型均可以傳入,由於AuthenticationToken是一個父接口),因此在實現類中只須要判斷實參是不是指定的Token類型便可(實際開發時傳入的實參通常都是UsernamePasswordToken類型,因此在supports方法中只須要斷定實參是不是這個類型便可)。
》實現認證的邏輯就是寫在org.apache.shiro.realm.Realm#getAuthenticationInfo這個方法中的(從Realm的實現類org.apache.shiro.realm.AuthenticatingRealm#getAuthenticationInfo中能夠看出)緩存

1.5 簡易自定義Realm

1.5.1 思路
》實現Realm接口
》getName返回一個惟一的Realm名稱便可
》supports中斷定實參類型是不是UsernamePasswordToken類型
》getAuthenticationInfo中實現認證邏輯
1.5.2 代碼實現安全

package com.xunyji.demo03.shirotest.realm;

import org.apache.shiro.authc.*;
import org.apache.shiro.realm.Realm;

/**
 * @author AltEnter
 * @create 2019-01-20 20:11
 * @desc 自定義簡易Realm
 **/
public class MySimpleRealm implements Realm {

    public String getName() {
        return "mySimpleRealm";
    }

    public boolean supports(AuthenticationToken token) {
        if (token instanceof UsernamePasswordToken) {
            return true;
        }
        return false;
    }

    public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username = (String) token.getPrincipal();
        String password = new String((char[])token.getCredentials());
        System.out.println(String.format("用戶名爲:%s, 用戶密碼爲:%s", username, password));
        if (!"fury".equals(username)) {
            System.out.println("用戶名錯誤");
            return null;
        }
        if (!"111111".equals(password)) {
            System.out.println("密碼錯誤");
            return null;
        }

        return new SimpleAuthenticationInfo(username, password, getName());
    }
}

1.5.3 單元測試類maven

package com.xunyji.demo03.shirotest.realm;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.subject.Subject;
import org.junit.Test;

import static org.junit.Assert.*;

public class MySimpleRealmTest {


    
    @Test
    public void test01() {
        MySimpleRealm mySimpleRealm = new MySimpleRealm();
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        defaultSecurityManager.setRealm(mySimpleRealm);
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken("fury", "111111");
        subject.login(token);
        System.out.println(String.format("認證結果:%s", subject.isAuthenticated()));

    }

}

2 Realm源碼分析

Realm接口主要是認證的主接口,可是它的一些實現類既能夠進行認證也能夠進行受權邏輯ide

2.1 Realm相關類層次圖

todo: 貼圖

2.2 Realm相關類說明

》CachingRealm:帶有緩存實現的Realm,至關於Realm的擴展
》AuthenticatingRealm:專門作認證的Realm,它繼承自CachingRealm
》AuthorizingRealm:專門作受權的Realm,由於它集成自AuthenticatingRealm,因此它也能夠實現認證邏輯;自定義的Realm通常都是繼承該類,而後重寫裏面的認證方法和受權方法便可。
》IniRealm:用ini文件存儲用戶信息時使用,[users]部分指定用戶名/密碼及其角色; [roles]部分指 定角色即權限信息; 
》PropertiesRealm:用properties文件存儲用戶信息時使用,user.username=password,role1,role2 指定用戶 名/密碼及其角色;role.role1=permission1,permission2 指定角色及權限信息; 
》JdbcRealm:用數據庫存儲用戶信息時使用,經過 sql 查詢相應的信息,如「select password from users where username = ?」獲取用戶密碼,「select password, password_salt from users where username = ?」獲取用戶密碼及鹽;「select role_name from user_roles where username = ?」 獲取用戶角色;「select permission from roles_permissions where role_name = ?」獲取角色對 應的權限信息;也能夠調用相應的 api 進行自定義 sql;

2.3 AuthenticatingRealm詳解

》getAuthenticationInfo:該方法是Realm中getAuthenticationInfo的實現,該方法是實現認證邏輯的;該方法是一個final方法,因此AuthenticatingRealm的子類不能重寫該方法;
》doGetAuthenticationInfo:getAuthenticationInfo方法調用doGetAuthenticationInfo實現認證邏輯,該方法是一個protected方法,專門暴露給子類進行重寫的,並且是子類實現認證邏輯必須重寫的方法。

2.4 Realm主要實現類

2.4.1 IniRealm
》用戶相關信息存儲在一個ini文件中
2.4.2 PropertiesRealm
》用戶相關信息存儲在一個properties文件中
2.4.3 JdbcRealm
》用戶信息存儲在數據庫中

2.5 IniRealm使用教程

2.5.1 繼承關係圖

2.5.2 建立一個maven工程並引入shiro、junit相關依賴

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.4.0</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

2.5.3 在resources目錄下建立ini文件用於存放用戶信息

》項目目錄結構

ini配置文件詳解

https://blog.csdn.net/u011781521/article/details/74892074

》ini文件內容

[users]
fury=111111,role1,role2
zeus=222222,role1
[roles]
role1=user:delete,user:update,user:create,user:read
role2=car:create

》測試代碼

package com.xunyji.demo03.shirotest.realm;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;

/**
 * @author AltEnter
 * @create 2019-01-17 22:07
 * @desc IniRealm測試類
 **/
public class IniRealmDemo {

    @Test
    public void iniRealmTest() {
        //        01 建立Realm
        IniRealm iniRealm = new IniRealm("classpath:shiro.ini");
//        02 建立SecurityManager並將Realm設置到SecurityManager中
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        defaultSecurityManager.setRealm(iniRealm);

//        03 將SecurityManager設置到SecurityUtils中
        SecurityUtils.setSecurityManager(defaultSecurityManager);
//        04 從SecurityUtils中獲取Subject
        Subject subject = SecurityUtils.getSubject();
//        05 將用戶名和用戶密碼封裝成一個Token
        UsernamePasswordToken token = new UsernamePasswordToken("zeus", "222222");
//        06 經過Subject進行登陸認證
        try {
            subject.login(token);
        } catch (Exception e) {
            e.printStackTrace();
        }
//        07 經過Subject判斷登陸認證結果
        System.out.println(String.format("認證結果爲:%s", subject.isAuthenticated()));

        ArrayList<String> roleList = new ArrayList<String>();
        roleList.add("role1");
        System.out.println("是否有role1角色:" + Arrays.toString(subject.hasRoles(roleList)));
        System.out.println("是否有role1角色:" + subject.hasRole("role1"));
        subject.checkPermission("user:create");
    }

}

2.6 PropertiesRealm使用教程

參見2.5 + 百度 

2.7 JdbcRealm使用教程

》繼承關係圖

》引入shiro、junit、mysql、druid依賴

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.4.0</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.45</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.6</version>
        </dependency>

》根據JdbcRealm源碼建立相關表的SQL

/*
Navicat MySQL Data Transfer

Source Server         : mysql5.4
Source Server Version : 50540
Source Host           : localhost:3306
Source Database       : shiro

Target Server Type    : MYSQL
Target Server Version : 50540
File Encoding         : 65001

Date: 2019-01-17 21:07:56
*/

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for `roles_permissions`
-- ----------------------------
DROP TABLE IF EXISTS `roles_permissions`;
CREATE TABLE `roles_permissions` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `role_name` varchar(255) NOT NULL,
  `permission` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- Records of roles_permissions
-- ----------------------------
INSERT INTO `roles_permissions` VALUES ('1', 'admin', 'user:update');

-- ----------------------------
-- Table structure for `user_roles`
-- ----------------------------
DROP TABLE IF EXISTS `user_roles`;
CREATE TABLE `user_roles` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) NOT NULL,
  `role_name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- Records of user_roles
-- ----------------------------
INSERT INTO `user_roles` VALUES ('1', 'fury', 'admin');
INSERT INTO `user_roles` VALUES ('2', 'fury', 'user');

-- ----------------------------
-- Table structure for `users`
-- ----------------------------
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) NOT NULL,
  `password` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- Records of users
-- ----------------------------
INSERT INTO `users` VALUES ('1', 'fury', '1111');
View Code

》測試類

package com.xunyji.demo03.shirotest.realm;

import com.alibaba.druid.pool.DruidDataSource;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.jdbc.JdbcRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Test;

/**
 * @author AltEnter
 * @create 2019-01-20 21:07
 * @desc JdbcRealm使用Demo
 **/
public class JdbcRealmDemo {
    DruidDataSource data =new DruidDataSource();
    {
        data.setUrl("jdbc:mysql://localhost:3306/shiro?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC");
        data.setUsername("root");
        data.setPassword("182838");
    }
    @Test
    public void testAuthentication(){

        JdbcRealm jdbcRealm = new JdbcRealm();
        jdbcRealm.setDataSource(data);
        jdbcRealm.setPermissionsLookupEnabled(true); // 開啓權限查詢功能
        String sql="select password from users where username= ?";
        jdbcRealm.setAuthenticationQuery(sql);

        String roleSql ="select role_name from user_roles where username = ?";
        jdbcRealm.setUserRolesQuery(roleSql);

        //1.構建securtymanager
        DefaultSecurityManager manager = new DefaultSecurityManager();
        manager.setRealm(jdbcRealm);

        //2.主體提交認證請求
        SecurityUtils.setSecurityManager(manager);
        Subject subject = SecurityUtils.getSubject();

        // UsernamePasswordToken token =new UsernamePasswordToken("Mark","123456");
        UsernamePasswordToken token =new UsernamePasswordToken("fury","111111");
        subject.login(token);

        //是否定證的一個方法
        boolean authenticated = subject.isAuthenticated();
        System.out.println("authenticated==============="+authenticated);
        subject.checkRole("user");
        subject.checkRole("admin");

        subject.checkPermission("user:update");


    }
}

2.8 SimpleAccountRealm使用教程

》層次關係圖

》說明

這種類型是經過硬編碼來存儲用戶數據的

》測試代碼

package com.xunyji.demo03.shirotest.realm;

import com.sun.org.apache.bcel.internal.generic.NEW;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.SimpleAccountRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Before;
import org.junit.Test;

/**
 * @author AltEnter
 * @create 2019-01-20 21:15
 * @desc SimpleAccountRealm使用Demo類
 **/
public class SimpleAccountRealmDemo {

    private SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm();

    @Before
    public void addUser() {
        simpleAccountRealm.addAccount("fury", "111111");
    }

    @Test
    public void test01() {
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        defaultSecurityManager.setRealm(simpleAccountRealm);

        SecurityUtils.setSecurityManager(defaultSecurityManager);
        Subject subject = SecurityUtils.getSubject();

        UsernamePasswordToken token = new UsernamePasswordToken("fury", "111111");

        subject.login(token);

        System.out.println(String.format("認證結果爲:%s", subject.isAuthenticated()));

        subject.logout();

        System.out.println(String.format("認證結果爲:%s", subject.isAuthenticated()));


    }
}

 

相關文章
相關標籤/搜索