Springboot集成shiro框架

news2024/9/24 11:30:48

前言

以前的项目代码,整理记录一下。

一、什么是shiro

官方:Shiro是一个功能强大且易于使用的Java安全框架,可以运行在JavaSE和JavaEE项目中,可执行身份验证、授权、加密和会话管理。

二、Shiro核心组件

1、UsernamePasswordToken,shiro用来封装用户登录信息,使用用户登录信息来创建令牌Token
2、SecurityManager,Shiro的核心部分,负责安全认证和授权
3、Subject,Shiro的一个抽象概念,代表一个用户实体或单独的个体对象
4、Realm,开发者自定义模块,根据业务逻辑进行认证和授权的逻辑设计
5、AuthenticationInfo,用户的角色信息集合,认证时使用
6、AuthorzationInfo,角色的权限信息集合,授权时使用
7、DefaultWebSecurityManager,安全管理器,开发者自定义的Realm模块需要注入到DefaultWebSecurityManager进行管理才能生效
8、ShiroFilterFactoryBean,过滤器工厂,Shiro的基本运行机制是开发者定制规则,Shiro去执行,具体的执行操作就是ShiroFilterFactoryBean创建的一个个filter对象来完成

三、Springboot集成Shiro

  • 引入Shiro依赖

 完整pom.xml依赖文件,参考最后的项目地址

<dependency>
	<groupId>org.apache.shiro</groupId>
	<artifactId>shiro-spring</artifactId>
	<version>2.0.0-alpha-3</version>
</dependency>
  • 创建一个ShiroConfig配置类

将自定义Realm注入 securityManager安全管理器;
过滤器工厂拦截请求;
开启对shior注解的支持

package com.rocky.springbootshiro.config;

import com.rocky.springbootshiro.shiro.CustomRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {

    //将自己的验证方式加入容器,开发者自定义模块
    @Bean
    public CustomRealm myShiroRealm() {
        CustomRealm customRealm = new CustomRealm();
        customRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return customRealm;
    }

    /**
     * 将自定义Realm注入 securityManager安全管理器
     */
    @Bean(value = "securityManager")
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 设置自定义 realm.
        securityManager.setRealm(myShiroRealm());

        return securityManager;
    }

    /**
     * 过滤器工厂拦截请求
     * 使用Qualifier根据方法名获取实例
     * @param securityManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean factory(@Qualifier("securityManager") SecurityManager securityManager) {
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        factoryBean.setSecurityManager(securityManager);

        //必须使用LinkedHashMap有序集合,shiro根据配置的规则进行拦截认证时,是根据容器中的存储顺序决定的
        Map<String, String> filterRuleMap = new LinkedHashMap<>();
        filterRuleMap.put("/loginValidateCode","anon");
        filterRuleMap.put("/loginValidateCode*","anon");
        filterRuleMap.put("/listUsers","anon");
        filterRuleMap.put("/user/logout","anon");
        filterRuleMap.put("/login","anon");// 所有请求通过我们自己的JWT Filter
        filterRuleMap.put("/unauthorized/**", "anon");// 访问 /unauthorized/** 不通过JWTFilter
        filterRuleMap.put("/**", "authc");//其余接口一律拦截,主要这行代码必须放在所有权限设置的最后,不然会导致所有 url 都被拦截

        factoryBean.setFilterChainDefinitionMap(filterRuleMap);
        return factoryBean;
    }

    /**
     * 开启对shior注解的支持
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher(){
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        //设置加密算法
        hashedCredentialsMatcher.setHashAlgorithmName("SHA1");
        //设置加密的次数
//        hashedCredentialsMatcher.setHashIterations(1);
        //true加密采用hex编码,false加密采用base64编码
//        hashedCredentialsMatcher.setStoredCredentialsHexEncoded(false);
        return hashedCredentialsMatcher;
    }


}
  • 编写自定义的Realm类

编写自己的Realm类继承AuthorizingRealm,实现doGetAuthenticationInfo(认证)和doGetAuthorizationInfo(授权)方法

package com.rocky.springbootshiro.shiro;

import com.rocky.springbootshiro.bean.Permissions;
import com.rocky.springbootshiro.bean.Role;
import com.rocky.springbootshiro.bean.User;
import com.rocky.springbootshiro.common.SignUtil;
import com.rocky.springbootshiro.pojo.Users;
import com.rocky.springbootshiro.service.LoginService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.lang.util.ByteSource;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class CustomRealm extends AuthorizingRealm {

    @Autowired
    private LoginService loginService;


    /**
     * 进行认证
     * @param authenticationToken
     * @return
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken){
        System.out.println("======认证=====");
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;
        System.out.println(new String(usernamePasswordToken.getPassword()));
        Users u = loginService.selectUserByUserName(usernamePasswordToken.getUsername());
        if (u!=null){
            //加盐值
            return new SimpleAuthenticationInfo(u,u.getPassword(),ByteSource.Util.bytes(SignUtil.SALT_STR),getName());
        }
        return null;
    }


    /**
     * @MethodName doGetAuthorizationInfo
     * @Description 授权配置
     * 只有当需要检测用户权限的时候才会调用此方法,例如checkRole,checkPermission,@RequiresPermissions,@RequiresRoles之类的,有几个校验方法就调用几次
     */
    @Override
    public AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("========授权处理=======");
        //获取登录用户名
        Subject subject = SecurityUtils.getSubject();
        Users u = (Users) subject.getPrincipal();

        //添加角色和权限
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();

        //超级管理员
        if("admin".equals(u.getUsername())) {
            simpleAuthorizationInfo.addRole("admin");
            simpleAuthorizationInfo.addStringPermission("*:*:*");

            return simpleAuthorizationInfo;
        }
        //授权用户角色和权限
        User user = loginService.getUserByName(u.getUsername());
        for (Role role : user.getRoles()) {
            //添加角色
            simpleAuthorizationInfo.addRole(role.getRoleCode());
            //添加权限
            for (Permissions permissions : role.getPermissions()) {
                simpleAuthorizationInfo.addStringPermission(permissions.getPermissinsCode());
            }
        }
        return simpleAuthorizationInfo;
    }


}
  • 授权逻辑

这里涉及4张表:用户表,角色表,角色权限关联表,权限表。一个用户可能多个角色,一个角色可能绑定多个权限,都是一对多的关系,User类实体包含一个Role实体Set集合,Role类包含Permissions实体Set集合,授权时通过主外键关联查询出对应的角色和权限

 ==用户表==

DROP TABLE IF EXISTS `users`;
CREATE TABLE `users`  (
  `lid` int NOT NULL AUTO_INCREMENT,
  `username` varchar(255)  NOT NULL,
  `rolelid` varchar(500)  NULL DEFAULT NULL,
  `name` varchar(255)  NOT NULL,
  `password` varchar(64)  NOT NULL,
  `mobilephone` varchar(15)  NULL DEFAULT NULL,
  `email` varchar(60)  NULL DEFAULT NULL,
  `isuse` int NOT NULL DEFAULT 1,
  `isdelete` int NOT NULL DEFAULT 0,
  `operatorid` int NULL DEFAULT NULL,
  `address` varchar(255)  NULL DEFAULT NULL,
  `operatorname` varchar(20)  NULL DEFAULT NULL,
  `operatortime` varchar(20)  NULL DEFAULT NULL,
  `sex` int NULL DEFAULT NULL,
  `rolename` varchar(500)  NULL DEFAULT NULL,
  `telephone` varchar(15)  NULL DEFAULT NULL,
  `createname` varchar(60)  NULL DEFAULT NULL,
  `createtime` varchar(20)  NULL DEFAULT NULL,
  `age` int NULL DEFAULT NULL,
  PRIMARY KEY (`lid`) USING BTREE,
  UNIQUE INDEX `unique_user_username`(`username` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 30 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = DYNAMIC;

==角色权限关联表==

DROP TABLE IF EXISTS `sys_role_permission`;
CREATE TABLE `sys_role_permission`  (
  `id` int NOT NULL AUTO_INCREMENT,
  `role_id` int NULL DEFAULT NULL COMMENT '角色id',
  `permission_id` int NULL DEFAULT NULL COMMENT '权限id',
  `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `delete_status` varchar(1)  NULL DEFAULT '1' COMMENT '是否有效 1有效     2无效',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 20 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '角色-权限关联表' ROW_FORMAT = DYNAMIC;

==角色表==

DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role`  (
  `id` int NOT NULL AUTO_INCREMENT,
  `role_name` varchar(20)  NULL DEFAULT NULL COMMENT '角色名',
  `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `delete_status` varchar(1)  NULL DEFAULT '1' COMMENT '是否有效  1有效  2无效',
  `role_code` varchar(255)  NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '后台角色表' ROW_FORMAT = COMPACT;

==权限表==

DROP TABLE IF EXISTS `sys_permission`;
CREATE TABLE `sys_permission`  (
  `id` int NOT NULL DEFAULT 0 COMMENT '自定id,主要供前端展示权限列表分类排序使用.',
  `menu_code` varchar(255)  NULL DEFAULT '' COMMENT '归属菜单,前端判断并展示菜单使用,',
  `menu_name` varchar(255)  NULL DEFAULT '' COMMENT '菜单的中文释义',
  `permission_code` varchar(255)  NULL DEFAULT '' COMMENT '权限的代码/通配符,对应代码中@RequiresPermissions 的value',
  `permission_name` varchar(255)  NULL DEFAULT '' COMMENT '本权限的中文释义',
  `required_permission` tinyint(1) NULL DEFAULT 2 COMMENT '是否本菜单必选权限, 1.必选 2非必选 通常是\"列表\"权限是必选',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '后台权限表' ROW_FORMAT = COMPACT;
  • 登录校验

使用Subject对象调用login方法进行登录校验

@PostMapping("/login")
public ResultMap login(HttpServletRequest request, HttpServletResponse resp, User user) {
	ResultMap resultMap = new ResultMap();
	if (StringUtils.isEmpty(user.getUserName()) || StringUtils.isEmpty(user.getPassWord())) {
		return resultMap.fail().code(500).message("请输入用户名和密码!");
	}

	//用户认证信息
	Subject subject = SecurityUtils.getSubject();
	UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(
			user.getUserName(),
			user.getPassWord()
	);

	//进行登录验证,shiro默认登录过期时间是30分钟
	subject.login(usernamePasswordToken);

	Users u = loginService.selectUserByUserName(user.getUserName());
	return resultMap.success().code(200).ObjData("data", JSONObject.toJSON(u)).message("");
}
  • 方法权限绑定

 这里使用注解式来控制权限,也可以使用编程式来判断权限,获取Subject subject = SecurityUtils.getSubject(),
里面有hasRole()  hasRoles() checkPermission()等方法来检查权限,没有权限会报错,可使用一个全局的异常类来捕获Shiro的报错,然后自定义返回异常信息给到前端

/**
 * 拥有 user或admin 角色的用户可以访问下面的页面
 */
@RequiresRoles(logical = Logical.OR, value = {"user", "admin"})
@GetMapping("/getMessage")
public ResultMap getMessage() {
	Users u = (Users) SecurityUtils.getSubject().getPrincipal();
	System.out.println(u.getUsername());
	return resultMap.success().code(200).message("admin角色 和 user:list权限!");
}

/**
 * 拥有vip权限且,是user或admin角色
 */
@GetMapping("/getVipMessage")
@RequiresRoles(logical = Logical.OR, value = {"user","admin"})
@RequiresPermissions("vip")
public ResultMap getVipMessage() {
	return resultMap.success().code(200).message("user角色或者admin角色,和vip权限!");
}
  • 其它权限注解 

/**
 * @RequiresAuthentication
 * 已经通过subject登录认证
 *
 * @RequiresUser
 * subject已经身份验证或者通过记住我登录
 *
 * @RequiresRoles(logical = Logical.OR, value = {"user","admin"})
 * 拥有user角色或者admin角色
 *
 * @RequiresPermissions("user:list")
 * 拥有user:list权限
 *
 *@GetMapping("/getAuthMessage")
 * 已经通过subject登录认证
 */

  • 定义全局异常类ExceptionController 

@RestControllerAdvice注解将作用在所有注解了@RequestMapping的控制器的方法上

package com.rocky.springbootshiro.controller;

import com.rocky.springbootshiro.bean.ResultMap;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authz.AuthorizationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;

/**
 * @Author Mr.HPC
 * @Description 定义全局的异常处理类
 * @Date 23:46 2021/4/7
 */
@RestControllerAdvice
public class ExceptionController {
    private final ResultMap resultMap;

    @Autowired
    public ExceptionController(ResultMap resultMap) {
        this.resultMap = resultMap;
    }

    //没有权限
    @ExceptionHandler(AuthorizationException.class)
    public ResultMap handle401_1(Throwable ex) {
//        ex.printStackTrace();
        return resultMap.fail().code(401).message("ExceptionController:您没有权限访问!");
    }
    //账号密码错误
    @ExceptionHandler(AuthenticationException.class)
    public ResultMap handle401_2(Throwable ex){
        return resultMap.fail().code(401).message("ExceptionController:用户名或密码错误!");
    }
    //账号不存在
    @ExceptionHandler(UnknownAccountException.class)
    public ResultMap handle401_3(Throwable ex){
        return resultMap.fail().code(401).message("ExceptionController:账号不存在!");
    }



    // 捕捉其他所有异常
    @ExceptionHandler(Exception.class)
    public ResultMap globalException(HttpServletRequest request, Throwable ex) {
//        ex.printStackTrace();
        return resultMap.fail()
                .code(getStatus(request).value())
                .message("ExceptionController:访问出错,无法访问: " + ex.getMessage());
    }

    private HttpStatus getStatus(HttpServletRequest request) {
        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
        if (statusCode == null) {
            return HttpStatus.INTERNAL_SERVER_ERROR;
        }
        return HttpStatus.valueOf(statusCode);
    }
}

四、Postman测试

登录成功

有权限

没权限 

登录失败 

五、实例项目代码地址 

码云:https://gitee.com/nevertouch/springboot-shiro.git

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1534253.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

面试真经(运维工程师)

1.熟悉的排序算法有哪些&#xff0c;它们的时间空间复杂度如何? 排序算法主要分为内部排序和外部排序。内部排序指的是数据记录在内存中进行排序&#xff0c;而外部排序则适用于排序的数据量很大&#xff0c;一次不能容纳全部排序记录的情况&#xff0c;需要在排序过程中访问…

【爬虫】专栏文章索引

为了方便 快速定位 和 便于文章间的相互引用等 作为一个快速准确的导航工具 爬虫 目录&#xff1a; &#xff08;一&#xff09;web自动化和接口自动化 &#xff08;二&#xff09;实战-爬取Boss直聘信息数据

为什么物联网网关需要边缘计算能力?边缘计算应用场景有哪些?

【前言】本篇为物联网硬件系列学习笔记&#xff0c;分享学习&#xff0c;欢迎评论区交流~ 什么是边缘计算&#xff1f; 边缘计算&#xff08;Edge Computing&#xff09;是一种分布式计算范式&#xff0c;旨在将计算和数据存储功能放置在接近数据源或终端设备的边缘位置&#…

一代大神跌落神坛——Java炸了!

曾经它是只手遮天的一大计算机语言.......可现如今&#xff0c;腹背受敌、大势已去&#xff0c;一代神话跌落神坛&#xff01; Java薪水20k降至15k难掩颓势&#xff0c;事业编3k升至3500尽显嫡道风范&#xff01;嫡嫡道道、嫡嫡道道~ 没错&#xff0c;就是它&#xff01;Java…

【C语言】模拟实现 atoi

文章目录 atoi()函数模拟实现思路分析代码呈现 atoi()函数 通过上述cplusplus和MSDN对atoi函数的介绍我们可以得出以下几个关键点 库函数&#xff1a; <stdlib.h>形参&#xff1a;const char * str返回值&#xff1a; int作用&#xff1a;atoi函数是将一个字符串转化成一…

运维篇SHELL脚本实战案例

统计出每个IP的访问量有多少&#xff1f; 检查是否提供了日志文件的路径作为参数。使用awk从日志文件的每行中提取第一个字段&#xff08;假设这是IP地址&#xff09;。使用sort对提取的IP地址进行排序。使用uniq -c统计每个唯一IP地址的出现次数。最后&#xff0c;使用sort -…

Python模块-基础知识

Python模块-基础知识 1.模块分类&#xff1a; &#xff08;1&#xff09;自定义模块&#xff1a; 如果你自己写一个py文件&#xff0c;在文件内写入一堆函数&#xff0c;则它被称为自定义模块&#xff0c;即使用python编写的.py文件 &#xff08;2&#xff09;第三方模块&…

从0到1:Java构建高并发、高可用分布式系统的实战经验分享

文章目录 引言基础架构选择与设计微服务架构分布式储存与计算 高并发处理策略异步处理与消息队列并发控制与资源隔离 高可用性设计与故障恢复冗余与集群化容错与自我修复监控与运维自动化 引言 随着互联网业务的快速发展和技术迭代升级&#xff0c;作为Java架构师&#xff0c;…

springboot企业级抽奖项目业务一(登录模块)

开发流程 该业务基于rouyi生成好了mapper和service的代码&#xff0c;现在需要在controller层写接口 实际操作流程&#xff1a; 看接口文档一>controller里定义函数一>看给出的工具类一>补全controller里的函数一>运行测试 接口文档 在登录模块有登录和登出方…

虚拟内存页表和内存保护

前言 大家好我是jiantaoyab&#xff0c;这是我所总结作为学习的笔记第21篇&#xff0c;在这里分享给大家&#xff0c;这篇文章讲虚拟内存和内存之间的页表和内存安全问题。 虚拟内存 前面的文章提到过&#xff0c;程序装载到内存的过程。可以知道&#xff0c;程序并不直接访…

爬虫实战-Python爬取百度当天热搜内容

爬虫实战-Python爬取百度当天热搜内容 学习建议学习目标预期内容目标分解热搜地址热搜标题热搜简介热搜指数小总结 代码实现总结 学习建议 本文仅用于学习使用&#xff0c;不做他用&#xff1b;本文仅获取页面的内容&#xff0c;作为学习和对Python知识的了解&#xff0c;不会…

如何使用Net2FTP+cpolar搭建专属文件共享站点并实现无公网IP远程访问——“cpolar内网穿透”

文章目录 1.前言2. Net2FTP网站搭建2.1. Net2FTP下载和安装2.2. Net2FTP网页测试 3. cpolar内网穿透3.1.Cpolar云端设置3.2.Cpolar本地设置 4.公网访问测试5.结语 1.前言 文件传输可以说是互联网最主要的应用之一&#xff0c;特别是智能设备的大面积使用&#xff0c;无论是个人…

MySQL高级学习笔记

1、MySQL架构组成 1.1 高级MySQL介绍 什么是DBA&#xff1f; 数据库管理员&#xff0c;英文是Database Administrator&#xff0c;简称DBA&#xff1b; 百度百科介绍 数据库管理员&#xff08;简称DBA&#xff09;&#xff0c;是从事管理和维护数据库管理系统&#xff08;D…

ISIS骨干网连续性简述

默认情况下&#xff0c; 一、L1路由器是ISIS 普通区域内部路由器&#xff0c;只能与L1和L1-2路由器建立邻接关系&#xff0c;不能与L2路由器建立邻接关系。 二、L2路由器是骨干区域的路由器&#xff0c;L2路由器只能与其他 L2路由器同处一个区域&#xff0c;可与本区域的L2路由…

分布式系统的基本特性

一般&#xff0c;分布式系统需要支持以下特性&#xff1a; 资源共享 开放性 并发性 可伸缩性 容错性 透明性 下面分别讨论。 容易理解的 资源共享 一旦授权&#xff0c;可以访问环境中的任何资源。 资源&#xff1a;包括硬件(e.g. printer, scanner, camera)、软件&a…

MYSQL 同步到ES 如何设计架构保持一致性

简单使用某个组件很容易&#xff0c;但是一旦要搬到生产上就要考虑各种各样的异常&#xff0c;保证你方案的可靠性&#xff0c;可恢复性就是我们需要思考的问题。今天来聊聊我们部门在 MYSQL 同步到ES的方案设计。 在面对复杂条件查询时&#xff0c;MYSQL往往显得力不从心&…

机器视觉学习(六)—— 图像的颜色识别

目录 一、色彩空间 1.1 RGB色彩空间 1.2 HSV色彩空间 1.3 灰度 1.4 CMYK色彩空间 1.5 Lab色彩空间 二、色彩空间转换 三、识别颜色 3.1 识别一种特定的颜色 3.2 识别多种颜色 一、色彩空间 计算机视觉中常用的色彩空间有RGB色彩空间、HSV色彩空间、CMYK色彩空间、La…

windows DCMTK编译使用(qt) 医学图像

由于项目需要生成DICOM格式的图片&#xff0c;需要使用到第三方开源库DCMTK&#xff0c;于是研究了一番&#xff0c;该库是C编写的&#xff0c;DICOM主要用于医疗体系中&#xff0c;除了可以保存图片信息外&#xff0c;还可以储存患者信息&#xff0c;病例信息&#xff0c;医疗…

如何修复WordPress网站媒体库上传文件失败的问题

公司最近推出了一系列新产品&#xff0c;为了更新网站的视频和图片&#xff0c;我们需要将它们上传至网站媒体库。然而&#xff0c;在上传视频时&#xff0c;我们却遇到了一些问题。系统提示说&#xff0c;我们尝试上传的视频文件大小超出了站点的最大上传限制。尽管我们的视频…

vue3中使用cesium

vue3中使用cesium Cesium是一个开源的JavaScript库&#xff0c;专门用于创建3D地球和2D地图的Web应用程序。它提供了丰富的功能和工具&#xff0c;使得开发人员能够轻松地构建出高质量的地理空间可视化应用。 1. 安装cesium包 npm install cesium2. 复制node_modules中的Ces…