02. Expliquez en détail le combat réel du royaume Apache Shiro

Introduction : Expliquer le domaine par défaut de shiro et les méthodes d'utilisation courantes

  • Rôle Realm : Shiro obtient les données de sécurité de Realm

  • Le domaine qui l'accompagne par défaut : idae vérifie la relation d'héritage du domaine, il existe des domaines avec une implémentation par défaut et un héritage personnalisé

  • deux notions

    • principal : il peut y avoir plusieurs principaux, mais ils doivent être uniques. Les principaux incluent le nom d'utilisateur, le numéro de téléphone mobile, l'adresse e-mail, etc.
    • informations d'identification : informations d'identification, généralement des mots de passe
    • Donc, généralement, nous disons que principal + identifiant est compte + mot de passe
  • En développement, il s'agit souvent d'un domaine personnalisé, c'est-à-dire intégrez AuthorizingRealm ! ! ! ! ! !

Relation d'héritage et d'implémentation du domaine

image.png

Démarrage rapide Shiro intégré IniRealm fonctionnement pratique et API de vérification des autorisations

Combat réel : obtenez la prise utilisateur à partir du fichier shiro.ini, puis évaluez les autorisations pertinentes de cet utilisateur.

Note ⚠️ : Cette façon d'écrire est de nous faire comprendre ce que fait le framework shiro, sans se connecter à la base de données, pour expérimenter rapidement le rôle du framework de sécurité de shiro. Dans le développement réel, cette méthode ne sera certainement pas utilisée.

Créez un fichier shiro.ini dans le répertoire des ressources

shiro.ino

# 格式 name=password,role1,role2,..roleN

[users]

# user 'root' with password 'secret' and the 'admin' role,

jack = 456, user

# user 'guest' with the password 'guest' and the 'guest' role

lll = 123, root,admin

# 格式 role=permission1,permission2...permissionN 也可以用通配符

# 下面配置user的权限为所有video:find,video:buy,如果需要配置video全部操作crud 则 user = video:*

[roles]
user = video:find,video:buy

# 'admin' role has all permissions, indicated by the wildcard '*'
admin = *
复制代码

Classe de test : QuickStartTest3.java

package com.lzh;

/**
 * @Author:kaiyang.cui
 * @Package:com.lzh
 * @Project:lzh_shiro
 * @name:QuickStartTest
 * @Date:2023/3/27 下午2:05
 * @Filename:QuickStartTest
 * @Description:从shiro.ini 文件中取到用户jack,然后判断这个用户的相关权限
 * @Version:1.0
 */

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.junit.Test;

public class QuickStartTest3 {


    @Test
    public void testAuthentication() {

        // 创建SecurityManager工厂,通过配置文件ini创建
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        SecurityManager securityManager = factory.getInstance();

        // 将securityManager设置到当前运行环境中
        SecurityUtils.setSecurityManager(securityManager);

        Subject subject = SecurityUtils.getSubject();

        // 模拟用户输入的用户名和密码
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("jack", "456");
        subject.login(usernamePasswordToken);

        // 获取验证结果 isAuthenticated() 是否授权,结果是boolen
        System.out.println("认证结果:" + subject.isAuthenticated());

        // 是否有对应的角色
        System.out.println("是否有user角色:" + subject.hasRole("user"));

        System.out.println("getPrincipal认证结果-实际上就是获取登录用户账号" + subject.getPrincipal());

        System.out.println("检查是否有视频删除权限,如果有则不报错,没有就报错。不用担心");
        try {
            subject.checkPermission("video:delete");
        } catch (Exception e) {
            System.err.println("没有video:delete权限");
        }


        //isPermitted() 和 checkPermission() 方法的区别是,前者有返回值,后者没有
        System.out.println("是否有视频购买权限:" + subject.isPermitted("video:buy"));

        // 退出登录
        subject.logout();

        // 检查用户退出登录后的认证结果
        System.out.println("认证结果:" + subject.isAuthenticated());
    }
}
复制代码
认证结果:true
是否有user角色:true
getPrincipal认证结果-实际上就是获取登录用户账号jack
检查是否有视频删除权限,如果有则不报错,没有就报错。不用担心
没有video:delete权限
是否有视频购买权限:true
认证结果:false
复制代码

Démarrage rapide Shiro intégré JdbcRealm fonctionnement pratique

Cette méthode est recommandée pour le développement

Créez d'abord une base de données, vous pouvez choisir le nom de la base de données, ce qui suit est le SQL pour créer une table et insérer des données de base

Cette table est un utilisateur correspondant à plusieurs rôles, et un rôle correspondant à plusieurs autorisations.

/*
 Navicat MySQL Data Transfer

 Source Server         : 阿里云华北root
 Source Server Type    : MySQL
 Source Server Version : 80024
 Source Host           : 39.97.253.89:3306
 Source Schema         : class_shiro

 Target Server Type    : MySQL
 Target Server Version : 80024
 File Encoding         : 65001

 Date: 27/03/2023 16:38:39
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for roles_permissions
-- ----------------------------
DROP TABLE IF EXISTS `roles_permissions`;
CREATE TABLE `roles_permissions` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `role_name` varchar(100) DEFAULT NULL,
  `permission` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_roles_permissions` (`role_name`,`permission`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb3;

-- ----------------------------
-- Records of roles_permissions
-- ----------------------------
BEGIN;
INSERT INTO `roles_permissions` VALUES (4, 'admin', 'video:*');
INSERT INTO `roles_permissions` VALUES (3, 'role1', 'video:buy');
INSERT INTO `roles_permissions` VALUES (2, 'role1', 'video:find');
INSERT INTO `roles_permissions` VALUES (5, 'role2', '*');
INSERT INTO `roles_permissions` VALUES (1, 'root', '*');
COMMIT;

-- ----------------------------
-- Table structure for user_roles
-- ----------------------------
DROP TABLE IF EXISTS `user_roles`;
CREATE TABLE `user_roles` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `username` varchar(100) DEFAULT NULL,
  `role_name` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_user_roles` (`username`,`role_name`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb3;

-- ----------------------------
-- Records of user_roles
-- ----------------------------
BEGIN;
INSERT INTO `user_roles` VALUES (1, 'jack', 'role1');
INSERT INTO `user_roles` VALUES (2, 'jack', 'role2');
INSERT INTO `user_roles` VALUES (4, 'lzh', 'admin');
INSERT INTO `user_roles` VALUES (3, 'lzh', 'root');
COMMIT;

-- ----------------------------
-- Table structure for users
-- ----------------------------
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `username` varchar(100) DEFAULT NULL,
  `password` varchar(100) DEFAULT NULL,
  `password_salt` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_users_username` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb3;

-- ----------------------------
-- Records of users
-- ----------------------------
BEGIN;
INSERT INTO `users` VALUES (1, 'jack', '123', NULL);
INSERT INTO `users` VALUES (2, 'lzh', '456', NULL);
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;
复制代码

Créez ensuite le fichier jdbcrealm.ini dans le répertoire des ressources : jdbcrealm.ini

#注意 文件格式必须为ini,编码为ANSI

#声明Realm,指定realm类型
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm

#配置数据源

#dataSource=com.mchange.v2.c3p0.ComboPooledDataSource

 
dataSource=com.alibaba.druid.pool.DruidDataSource

# mysql-connector-java 5 用的驱动url是com.mysql.jdbc.Driver,mysql-connector-java6以后用的是com.mysql.cj.jdbc.Driver

dataSource.driverClassName=com.mysql.cj.jdbc.Driver

#避免安全警告
dataSource.url=jdbc:mysql://39.97.253.89:3306/class_shiro?characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false

dataSource.username=你的账号

dataSource.password=你的密码

#指定数据源

jdbcRealm.dataSource=$dataSource

#开启查找权限, 默认是false,不会去查找角色对应的权限,坑!!!!!

jdbcRealm.permissionsLookupEnabled=true


#指定SecurityManager的Realms实现,设置realms,可以有多个,用逗号隔开

securityManager.realms=$jdbcRealm

复制代码

Il y a un gouffre ici. Bien que nous puissions écrire le pilote MySQL comme nous le voulons lorsque nous créons le projet SpringBoot, ce fichier respecte strictement les règles suivantes. L'url du pilote utilisé par mysql-connector-java 5 est com.mysql.jdbc. Le pilote, com.mysql.cj.jdbc.Driver est utilisé après mysql-connector-java6

Méthode JdbcRealm 1 : écrivez jdbcrealm.ini et utilisez jdbcrealm.ini pour aider shiro à simuler la base de données de requêtes de connexion des utilisateurs afin d'obtenir les informations d'autorisation

Testez le code pour écrire ce fichier via jdbcrealm.ini :

package com.lzh;

/**
 * @Author:kaiyang.cui
 * @Package:com.lzh
 * @Project:lzh_shiro
 * @name:QuickStartTest
 * @Date:2023/3/27 下午2:05
 * @Filename:QuickStartTest
 * @Description:从shiro.ini 文件中取到用户jack,然后判断这个用户的相关权限
 * @Version:1.0
 */

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.junit.Test;

public class QuickStartTest4 {


    @Test
    public void testAuthentication() {

        // 创建SecurityManager工厂,通过配置文件ini创建
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:jdbcrealm.ini");
        SecurityManager securityManager = factory.getInstance();

        // 将securityManager设置到当前运行环境中
        SecurityUtils.setSecurityManager(securityManager);

        Subject subject = SecurityUtils.getSubject();

        // 模拟用户输入的用户名和密码
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("jack", "123");
        subject.login(usernamePasswordToken);

        // 获取验证结果 isAuthenticated() 是否授权,结果是boolen
        System.out.println("认证结果:" + subject.isAuthenticated());

        // 是否有对应的角色
        System.out.println("是否有user角色:" + subject.hasRole("admin"));

        // 是否有对应的权限
        System.out.println("是否有对应的权限:" + subject.isPermitted("video:delete"));
    }
}
复制代码
认证结果:true
是否有user角色:false
是否有对应的权限:false
复制代码

Dans la table de données, nous avons trouvé que l'utilisateur jack a role1 et role2, donc la méthode hasRole renvoie false, nous avons donc ouvert le code java, shiro.ini et la base de données MySQL. Faites-vous plaisir.

image.png

Au moment où j'écris ceci, je suis curieux de savoir pourquoi un fichier de configuration jdbcrealm.ini plus quelques lignes de code peuvent trouver les informations d'un certain utilisateur sans écrire d'instructions SQL du tout. Ici, vous devez vérifier org.apache.shiro.realm.jdbc.JdbcRealmle code source, le code source est le suivant :

image.png

Après avoir visualisé le code source, j'ai soudainement réalisé que l'instruction de requête avait été définie dans la classe JdbcRealm ! ! ! ! !

Attendez, j'ai aussi découvert que les tables de données sont des tables users, user_roles, roles_permissions, c'est pourquoi, quand on fait faire des projets à d'autres personnes, toutes les tables qu'on voit portent le même nom, comme si elles avaient été convenues ! ! ! ! ! ! ! Ensuite, lorsque je définirai la table de données ultérieurement, je devrai également définir le nom de la table et les champs de la table de cette manière.

Méthode 2 : à l'aide du pool de connexions à la base de données, shiro simule la connexion de l'utilisateur pour interroger les informations d'autorisation de la base de données

package com.lzh;

/**
 * @Author:kaiyang.cui
 * @Package:com.lzh
 * @Project:lzh_shiro
 * @name:QuickStartTest
 * @Date:2023/3/27 下午2:05
 * @Filename:QuickStartTest
 * @Description:方式二使用数据库连接池
 * @Version:1.0
 */

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;

public class QuickStartTest5 {


    @Test
    public void testAuthentication() {
        DefaultSecurityManager securityManager = new DefaultSecurityManager();

        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
        ds.setUrl("jdbc:mysql://39.97.253.89:3306/class_shiro?characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false");
        ds.setUsername("你的账号");
        ds.setPassword("你的密码");

        JdbcRealm jdbcRealm = new JdbcRealm();
        jdbcRealm.setPermissionsLookupEnabled(true);
        jdbcRealm.setDataSource(ds);
        securityManager.setRealm(jdbcRealm);

        // 将securityManager设置到当前运行环境中
        SecurityUtils.setSecurityManager(securityManager);

        Subject subject = SecurityUtils.getSubject();

        // 模拟用户输入的用户名和密码
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("jack", "123");
        subject.login(usernamePasswordToken);

        // 获取验证结果 isAuthenticated() 是否授权,结果是boolen
        System.out.println("认证结果:" + subject.isAuthenticated());

        // 是否有对应的角色
        System.out.println("是否有user角色:" + subject.hasRole("admin"));

        // 是否有对应的权限
        System.out.println("是否有对应的权限:" + subject.isPermitted("video:delete"));
    }
}
复制代码
认证结果:true
是否有user角色:false
是否有对应的权限:false

复制代码

Résumer:

Dans IniRealm, nous mettons les rôles et les autorisations des informations de l'utilisateur dans le fichier shiro.ini. En coopérant avec le code Java, nous avons obtenu les informations, les rôles et les autorisations de la connexion utilisateur simulée, mais qui stockera les informations de l'utilisateur dans le fichier *.ini ? ? ? ? Les utilisateurs sont stockés dans la base de données.

Ensuite, nous avons appris la classe JdbcRealm, qui revient à apprendre JavaJDBC au début. Nous utiliserons du code java pour parcourir la base de données MySQL, mais cette opération n'est toujours pas très pratique. Nous utilisons shiro dans l'entreprise pour réaliser la personnalisation des informations d'autorisation des utilisateurs en personnalisant Realm. Shiro est un article en série, bienvenue pour faire attention à mes pépites.

Je suppose que tu aimes

Origine juejin.im/post/7215163152907436088
conseillé
Classement