02. Apache Shiro 영역의 실제 전투를 자세히 설명하십시오.

소개: shiro의 기본 영역 및 일반적인 사용 방법 설명

  • 영역 역할: Shiro는 영역에서 보안 데이터를 가져옵니다.

  • 기본적으로 함께 제공되는 영역: idae는 영역 상속 관계를 확인합니다. 기본 구현 및 사용자 정의 상속이 있는 영역이 있습니다.

  • 두 가지 개념

    • principal: 여러 명의 principal이 있을 수 있지만 고유해야 합니다. 일반적인 것에는 사용자 이름, 휴대폰 번호, 이메일 주소 등이 있습니다.
    • 자격 증명: 자격 증명, 일반적으로 암호
    • 그래서 일반적으로 우리는 principal + credential이 account + password라고 말합니다.
  • 개발 중에는 사용자 정의 영역인 경우가 많습니다. 즉, AuthorizingRealm을 통합합니다! ! ! ! ! !

Realm의 상속과 구현 관계

이미지.png

빠른 시작 Shiro 내장 IniRealm 실제 운영 및 권한 확인 API

실제 전투: shiro.ini 파일에서 사용자 잭을 가져온 다음 이 사용자의 관련 권한을 판단합니다.

참고 ⚠️: 이 작성 방식은 데이터베이스에 연결하지 않고 shiro 프레임워크가 수행하는 작업을 이해하여 shiro의 보안 프레임워크 역할을 빠르게 경험할 수 있도록 하기 위한 것입니다. 실제 개발에서는 이 방법을 절대 사용하지 않을 것입니다.

리소스 디렉토리에 shiro.ini 파일 생성

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

테스트 클래스: 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
复制代码

빠른 시작 Shiro 내장 JdbcRealm 실제 작동

이 방법은 개발에 권장됩니다.

먼저 데이터베이스를 생성하고 데이터베이스 이름을 선택할 수 있습니다. 다음은 테이블을 생성하고 일부 기본 데이터를 삽입하는 SQL입니다.

이 테이블은 여러 역할에 해당하는 사용자와 여러 권한에 해당하는 역할입니다.

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

그런 다음 리소스 디렉토리에 jdbcrealm.ini 파일을 생성합니다. 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

复制代码

SpringBoot 프로젝트를 생성할 때 MySQL 드라이버를 원하는대로 작성할 수 있지만 이 파일은 다음 규칙을 엄격히 준수합니다.mysql-connector-java 5에서 사용하는 드라이버 URL은 com.mysql.jdbc입니다. 드라이버 com.mysql.cj.jdbc.Driver는 mysql-connector-java6 이후에 사용됩니다.

JdbcRealm 방법 1: jdbcrealm.ini를 작성하고 jdbcrealm.ini를 사용하여 Shiro가 사용자 로그인 쿼리 데이터베이스를 시뮬레이트하여 권한 정보를 얻도록 돕습니다.

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

데이터 테이블에서 jack 사용자가 role1과 role2를 가지고 있으므로 hasRole 메서드가 false를 반환하므로 Java 코드, shiro.ini 및 MySQL 데이터베이스를 열었습니다. 자신에게 치료를 제공합니다.

이미지.png

이 글을 쓰면서 왜 jdbcrealm.ini 설정 파일과 몇 줄의 코드만 있으면 SQL 문을 전혀 쓰지 않고도 특정 사용자의 정보를 찾을 수 있는지 궁금합니다. 여기서 소스코드를 확인해야 하는데 org.apache.shiro.realm.jdbc.JdbcRealm, 소스코드는 다음과 같습니다.

이미지.png

소스코드를 보다가 문득 JdbcRealm 클래스에 쿼리문이 정의되어 있다는 사실을 깨달았습니다! ! ! ! !

잠깐, 나는 또한 데이터 테이블이 사용자, user_roles, roles_permissions 테이블이라는 것을 알았습니다. 그래서 다른 사람들이 프로젝트를 수행하도록 할 때 우리가 보는 모든 테이블은 마치 동의된 것처럼 같은 이름을 가집니다! ! ! ! ! ! ! 그런 다음 나중에 데이터 테이블을 정의할 때 테이블 이름과 테이블 필드도 이런 식으로 정의해야 합니다.

방법 2: 데이터베이스 연결 풀을 사용하여 shiro는 사용자 로그인을 시뮬레이트하여 데이터베이스 권한 정보를 쿼리합니다.

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

复制代码

요약하다:

IniRealm에서는 사용자 정보의 역할과 권한을 shiro.ini 파일에 넣었습니다.java 코드와 협력하여 시뮬레이션된 사용자 로그인의 정보, 역할 및 권한을 얻었지만 사용자 정보를 누가 저장할 것인지 *.ini 파일에 대해? ? ? ? 사용자는 데이터베이스에 저장됩니다.

그런 다음 처음에 JavaJDBC를 배우는 것과 같은 JdbcRealm 클래스를 배웠습니다.Java 코드를 사용하여 MySQL 데이터베이스를 통과하지만 이 작업은 여전히 ​​편리하지 않습니다. 기업에서 shiro를 사용하여 Realm을 커스터마이징하여 사용자 권한 정보의 커스터마이징을 실현합니다. Shiro는 연재 기사입니다. 내 너겟에 관심을 가져 주셔서 감사합니다.

추천

출처juejin.im/post/7215163152907436088