02. Explica en detalle el combate real del reino de Apache Shiro

Introducción: explicar el reino predeterminado de shiro y los métodos de uso común

  • Rol de Realm: Shiro obtiene datos de seguridad de Realm

  • El reino que viene con él por defecto: idae verifica la relación de herencia del reino, hay reinos con implementación predeterminada y herencia personalizada

  • dos conceptos

    • principal: puede haber varios principales, pero deben ser únicos. Los más comunes incluyen el nombre de usuario, el número de teléfono móvil, la dirección de correo electrónico, etc.
    • credencial: Credenciales, generalmente contraseñas
    • Por lo general, decimos principal + credencial es cuenta + contraseña
  • En desarrollo, a menudo es un dominio personalizado, es decir, ¡integra AuthorizingRealm! ! ! ! ! !

Relación de herencia e implementación del reino

imagen.png

Inicio rápido Shiro incorporado IniRealm operación práctica y verificación de permisos api

Combate real: obtenga el conector de usuario del archivo shiro.ini y luego juzgue los permisos relevantes de este usuario

Nota ⚠️: esta forma de escribir es para permitirnos entender lo que hace el marco de shiro, sin conectarse a la base de datos, para experimentar rápidamente el rol del marco de seguridad de shiro. En el desarrollo real, este método definitivamente no se utilizará.

Cree un archivo shiro.ini en el directorio de recursos

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 = *
复制代码

Clase de prueba: 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
复制代码

Inicio rápido Shiro incorporado JdbcRealm operación práctica

Este método se recomienda para el desarrollo

Primero cree una base de datos, puede elegir el nombre de la base de datos, el siguiente es el SQL para crear una tabla e insertar algunos datos básicos

Esta tabla es un usuario correspondiente a varios roles y un rol correspondiente a varios permisos.

/*
 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;
复制代码

Luego cree el archivo jdbrealm.ini en el directorio de recursos: jdbrealm.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

复制代码

Hay un hoyo aquí. Aunque podemos escribir el controlador MySQL lo que queramos cuando creamos el proyecto SpringBoot, este archivo cumple estrictamente con las siguientes reglas. La URL del controlador utilizada por mysql-connector-java 5 es com.mysql.jdbc. Controlador, com.mysql.cj.jdbc.Driver se usa después de mysql-connector-java6

Método 1 de JdbcRealm: escriba jdbcrealm.ini y use jdbcrealm.ini para ayudar a shiro a simular la base de datos de consulta de inicio de sesión del usuario para obtener información de permisos

Pruebe el código para escribir este archivo a través de jdbrealm.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
复制代码

En la tabla de datos, encontramos que el usuario jack tiene rol1 y rol2, por lo que el método hasRole devuelve falso, por lo que abrimos el código Java, shiro.ini y la base de datos MySQL. Date un capricho.

imagen.png

Mientras escribo esto, tengo curiosidad por saber por qué un archivo de configuración jdbrealm.ini más unas pocas líneas de código pueden encontrar la información de un determinado usuario sin escribir sentencias SQL. Aquí tienes que comprobar org.apache.shiro.realm.jdbc.JdbcRealmel código fuente, el código fuente es el siguiente:

imagen.png

Después de ver el código fuente, de repente me di cuenta de que la declaración de consulta se había definido en la clase JdbcRealm. ! ! ! !

Espera, también descubrí que las tablas de datos son tablas de usuarios, roles_usuarios, roles_permisos, por lo que, cuando hacemos que otras personas hagan proyectos, todas las tablas que vemos tienen el mismo nombre, ¡como si hubieran sido acordadas! ! ! ! ! ! ! Luego, cuando defina la tabla de datos más tarde, también necesito definir el nombre de la tabla y los campos de la tabla de esta manera.

Método 2: utilizando el grupo de conexiones de la base de datos, shiro simula el inicio de sesión del usuario para consultar la información de permisos de la base de datos

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

复制代码

Resumir:

En IniRealm, colocamos los roles y permisos de la información del usuario en el archivo shiro.ini. Al cooperar con el código Java, hemos obtenido la información, los roles y los permisos del inicio de sesión del usuario simulado, pero quién almacenará la información del usuario. sobre en el archivo *.ini? ? ? ? Los usuarios se almacenan en la base de datos.

Luego aprendimos la clase JdbcRealm, que es como aprender JavaJDBC al principio.Usaremos código Java para pasar por la base de datos MySQL, pero esta operación aún no es muy conveniente. Usamos shiro en la empresa para realizar la personalización de la información de permisos del usuario mediante la personalización de Realm. Shiro es un artículo en serie, bienvenido a prestar atención a mis pepitas.

Supongo que te gusta

Origin juejin.im/post/7215163152907436088
Recomendado
Clasificación