02. Explique em detalhes o combate real do reino Apache Shiro

Introdução: Explique o reino padrão de shiro e os métodos de uso comuns

  • Função do Realm: Shiro obtém dados de segurança do Realm

  • O reino que vem com ele por padrão: idae verifica o relacionamento de herança do reino, existem reinos com implementação padrão e herança personalizada

  • dois conceitos

    • principal: pode haver vários principais, mas eles precisam ser únicos. Os mais comuns incluem nome de usuário, número de celular, endereço de e-mail, etc.
    • credencial: Credenciais, geralmente senhas
    • Então geralmente dizemos principal + credencial é conta + senha
  • No desenvolvimento, muitas vezes é um domínio personalizado, ou seja, integrar o AuthorizingRealm! ! ! ! ! !

Relação de herança e implementação do reino

imagem.png

Início rápido Shiro built-in IniRealm operação prática e API de verificação de permissão

Combate real: obtenha o usuário jack do arquivo shiro.ini e, em seguida, julgue as permissões relevantes desse usuário

Nota ⚠️: Esta forma de escrever é para nos deixar entender o que o framework shiro faz, sem se conectar ao banco de dados, para experimentar rapidamente o papel do framework de segurança do shiro. No desenvolvimento real, esse método definitivamente não será usado.

Crie um arquivo shiro.ini no diretório 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 = *
复制代码

Classe de teste: 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
复制代码

Início rápido Shiro built-in JdbcRealm operação prática

Este método é recomendado para o desenvolvimento

Primeiro crie um banco de dados, você pode escolher o nome do banco de dados, o seguinte é o SQL para criar uma tabela e inserir alguns dados básicos

Esta tabela é um usuário correspondente a várias funções e uma função correspondente a várias permissões.

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

Em seguida, crie o arquivo jdbcrealm.ini no diretório resouces: 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

复制代码

Há um problema aqui. Embora possamos escrever o driver MySQL como quisermos quando criamos o projeto SpringBoot, este arquivo segue estritamente as seguintes regras: O url do driver usado pelo mysql-connector-java 5 é com.mysql.jdbc. Driver, com.mysql.cj.jdbc.Driver é usado após mysql-connector-java6

Método JdbcRealm 1: Escreva jdbcrealm.ini e use jdbcrealm.ini para ajudar Shiro a simular o banco de dados de consulta de login do usuário para obter informações de permissão

Teste o código para escrever este arquivo através do 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
复制代码

Na tabela de dados, descobrimos que o usuário jack tem role1 e role2, então o método hasRole retorna false, então abrimos o código java, shiro.ini e o banco de dados MySQL. Dê a si mesmo um mimo.

imagem.png

Enquanto escrevo isso, estou curioso para saber por que um arquivo de configuração jdbcrealm.ini mais algumas linhas de código podem localizar as informações de um determinado usuário sem escrever nenhuma instrução SQL. Aqui você tem que verificar org.apache.shiro.realm.jdbc.JdbcRealmo código-fonte, o código-fonte é o seguinte:

imagem.png

Depois de ver o código-fonte, de repente percebi que a instrução de consulta foi definida na classe JdbcRealm! ! ! ! !

Peraí, também descobri que as tabelas de dados são tabelas users, user_roles, role_permissions, e é por isso que, quando colocamos outras pessoas para fazer projetos, todas as tabelas que vemos têm o mesmo nome, como se tivessem sido combinadas! ! ! ! ! ! ! Então, quando eu definir a tabela de dados posteriormente, também preciso definir o nome da tabela e os campos da tabela dessa maneira.

Método 2: usando o pool de conexão do banco de dados, shiro simula o login do usuário para consultar as informações de permissão do banco de dados

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:

No IniRealm, colocamos as funções e permissões das informações do usuário no arquivo shiro.ini. Cooperando com o código java, obtivemos as informações, funções e permissões do login do usuário simulado, mas quem armazenará as informações do usuário. sobre no arquivo *.ini? ? ? ? Os usuários são armazenados no banco de dados.

Em seguida, aprendemos a classe JdbcRealm, que é como aprender JavaJDBC no início. Usaremos o código java para acessar o banco de dados MySQL, mas essa operação ainda não é muito conveniente. Usamos o shiro na empresa para realizar a personalização das informações de permissão do usuário, personalizando o Realm. Shiro é um artigo em série, bem-vindo para prestar atenção às minhas pepitas.

Guess you like

Origin juejin.im/post/7215163152907436088