基于Java的shiro框架介绍和使用

Shiro框架概述
Apache Shiro是一个强大且易用的Java安全框架,提供了身份验证、授权、密码学和会话管理等功能。它被广泛用于保护各种类型的应用程序,包括Web应用、RESTful服务、移动应用和大型企业级应用。使用Shiro,你可以将安全性集成到应用程序中而不必担心复杂的实现细节。

Shiro的核心概念
在深入了解Shiro的原理之前,我们先来了解一下Shiro的一些核心概念:

Subject(主体):代表当前用户,可以是一个人、设备或者其他与应用交互的实体。Subject封装了与安全性相关的操作,如身份验证和授权。

SecurityManager(安全管理器):负责管理所有Subject,是Shiro的核心。它协调各种安全组件的工作,确保安全性的全面性。

Realm(域):负责验证Subject的身份,并提供与授权数据交互。可以将Realm看作是安全数据源。

Authentication(身份验证):验证Subject的身份是否合法。通常包括用户名密码验证、多因素认证等。

Authorization(授权):确定Subject是否有权限执行特定的操作。授权是安全框架的另一个关键方面。

Session Management(会话管理):处理用户的会话,确保安全地管理用户的状态信息。

Shiro的优势
Shiro的优势在于其简单性和灵活性。相较于其他安全框架,Shiro采用了非常直观的API和清晰的架构,使得开发者能够更轻松地理解和使用。此外,Shiro的可扩展性也是其强大之处,你可以根据自己的需求轻松地定制和扩展功能。

shiro能解决什么问题?

认证:验证用户的身份

授权:对用户执行访问控制:判断用户是否被允许做某事

会话管理:在任何环境下使用 Session API,即使没有 Web 或EJB 容器。

加密:以更简洁易用的方式使用加密功能,保护或隐藏数据防止被偷窥

Realms:聚集一个或多个用户安全数据的数据源

Shiro 的四大核心部分

Authentication(身份验证):简称为“登录”,即证明用户是谁。

Authorization(授权):访问控制的过程,即决定是否有权限去访问受保护的资源。

Session Management(会话管理):管理用户特定的会话,即使在非 Web 或 EJB 应用程序。

Cryptography(加密):通过使用加密算法保持数据安全

shiro的三个核心组件:

Subject :正与系统进行交互的人,或某一个第三方服务。所有 Subject 实例都被绑定到(且这是必须的)一个SecurityManager 上。

SecurityManager:Shiro 架构的心脏,用来协调内部各安全组件,管理内部组件实例,并通过它来提供安全管理的各种服务。当 Shiro 与一个 Subject 进行交互时,实质上是幕后的 SecurityManager 处理所有繁重的 Subject 安全操作。

Realms :本质上是一个特定安全的 DAO。当配置 Shiro 时,必须指定至少一个 Realm 用来进行身份验证和/或授权。Shiro 提供了多种可用的 Realms 来获取安全相关的数据。如关系数据库(JDBC),INI 及属性文件等。可以定义自己 Realm 实现来代表自定义的数据源

Shiro的安装与配置
现在,让我们一起来了解如何在项目中引入Shiro,并进行基本的配置。在这里,我以一个基于Spring Boot的Web应用为例进行演示。

步骤1:引入Shiro依赖
首先,在你的项目中引入Shiro的依赖。如果你使用Maven,可以在pom.xml中添加以下依赖:

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring-boot-starter</artifactId>
    <version>1.8.0</version> <!-- 请替换为最新版本 -->
</dependency>

步骤2:配置Shiro
在Spring Boot项目中,Shiro的配置通常是通过ShiroConfig类来完成的。创建一个ShiroConfig类,并配置相关的Bean:

@Configuration
public class ShiroConfig {

    @Bean
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 设置Realm
        securityManager.setRealm(myRealm());
        return securityManager;
    }

    @Bean
    public MyRealm myRealm() {
        return new MyRealm();
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(securityManager);
        // 配置拦截规则等
        // ...
        return shiroFilter;
    }
}


在这个例子中,我们配置了一个DefaultWebSecurityManager,并设置了自定义的MyRealm作为Realm。同时,还配置了ShiroFilterFactoryBean,用于定义拦截规则。

步骤3:编写Realm
创建一个继承AuthorizingRealm的自定义Realm类,用于处理身份验证和授权逻辑:

public class MyRealm extends AuthorizingRealm {

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        // 处理身份验证逻辑,例如从数据库中查询用户信息
        // ...
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        // 处理授权逻辑,例如从数据库中查询用户权限信息
        // ...
    }
}

在doGetAuthenticationInfo方法中,你可以根据传入的AuthenticationToken进行用户身份验证;而在doGetAuthorizationInfo方法中,你可以根据PrincipalCollection获取用户的权限信息。

Shiro的身份验证
Shiro的身份验证是整个安全框架的核心。下面,让我们通过一个简单的示例来演示如何在Shiro中进行用户身份验证。

// 获取当前用户
Subject currentUser = SecurityUtils.getSubject();

// 创建一个身份验证令牌
UsernamePasswordToken token = new UsernamePasswordToken("username", "password");

try {
    // 调用登录方法进行身份验证
    currentUser.login(token);
    // 身份验证成功,执行其他逻辑
} catch (AuthenticationException e) {
    // 身份验证失败,处理异常
}

在这个例子中,我们首先获取当前用户的Subject,然后创建一个UsernamePasswordToken,设置用户名和密码。接着,调用currentUser.login(token)方法进行身份验证,如果身份验证失败,将会抛出AuthenticationException异常,你可以在catch块中处理相应的异常信息。

为了更好地了解Shiro的身份验证过程,让我们详细讨论一下MyRealm中的doGetAuthenticationInfo方法。这个方法是Shiro用来处理身份验证逻辑的地方。

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    // 将AuthenticationToken转换为UsernamePasswordToken
    UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
    
    // 从token中获取用户名
    String username = token.getUsername();
    
    // 在实际项目中,这里通常是从数据库中根据用户名查询用户信息
    // 这里为了演示,我们假设存在一个用户
    if (!"username".equals(username)) {
        throw new UnknownAccountException("用户不存在");
    }
    
    // 假设数据库中的密码是经过加密的,这里为了演示使用明文密码
    String password = "password";
    
    // 返回认证信息,包括用户名和密码
    return new SimpleAuthenticationInfo(username, password, getName());
}


在这个简单的身份验证逻辑中,我们通过UsernamePasswordToken获取到用户输入的用户名,然后假设在数据库中查询到了对应的用户信息。如果用户名不存在,抛出UnknownAccountException异常表示用户未知。如果存在用户,将明文密码返回给Shiro框架,Shiro会将用户输入的密码与数据库中的密码进行匹配。

需要注意的是,在实际项目中,密码存储应该是经过安全加密的,而不是明文存储。Shiro提供了一些常见的加密算法,你可以根据项目需求选择适当的算法。

Shiro的授权
Shiro的授权功能使我们能够精确地定义用户对应用程序中资源的访问权限。通过授权,我们可以防止未经授权的用户访问敏感数据或执行危险操作。

授权的基本概念
在Shiro中,授权通常分为两个步骤:角色授权和权限授权。

角色授权:将用户分配给一个或多个角色,每个角色代表一组相关的权限。用户通过角色间接获得权限。

权限授权:直接将权限赋予用户,允许用户执行具体的操作。权限是对应用程序中资源的访问控制。

示例:角色授权
让我们通过一个简单的例子来演示如何在Shiro中进行角色授权。

首先,在MyRealm中重写doGetAuthorizationInfo方法:

@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
    
    // 获取当前用户的用户名
    String username = (String) principalCollection.getPrimaryPrincipal();
    
    // 假设在数据库中查询到该用户的角色信息
    Set<String> roles = new HashSet<>();
    roles.add("admin"); // 用户拥有admin角色
    
    // 将角色信息设置到AuthorizationInfo中
    authorizationInfo.setRoles(roles);
    
    return authorizationInfo;
}


在这个例子中,我们假设用户拥有admin角色。在实际项目中,你需要从数据库中查询用户的角色信息。

然后,在应用程序中,你可以通过以下方式检查用户是否拥有特定角色:

// 获取当前用户
Subject currentUser = SecurityUtils.getSubject();

// 检查用户是否拥有admin角色
if (currentUser.hasRole("admin")) {
    // 用户拥有admin角色,执行相应逻辑
} else {
    // 用户没有admin角色,执行其他逻辑
}

示例:权限授权
现在,让我们看一个授权中的权限授权的例子。

在MyRealm中继续完善doGetAuthorizationInfo方法:

@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
    
    // 获取当前用户的用户名
    String username = (String) principalCollection.getPrimaryPrincipal();
    
    // 假设在数据库中查询到该用户的权限信息
    Set<String> permissions = new HashSet<>();
    permissions.add("user:read"); // 用户拥有读取用户信息的权限
    
    // 将权限信息设置到AuthorizationInfo中
    authorizationInfo.setStringPermissions(permissions);
    
    return authorizationInfo;
}


在这个例子中,我们假设用户拥有user:read权限。同样,你需要从数据库中查询用户的权限信息。

在应用程序中,你可以通过以下方式检查用户是否拥有特定权限:

// 获取当前用户
Subject currentUser = SecurityUtils.getSubject();

// 检查用户是否拥有user:read权限
if (currentUser.isPermitted("user:read")) {
    // 用户拥有user:read权限,执行相应逻辑
} else {
    // 用户没有user:read权限,执行其他逻辑
}

Shiro的会话管理
Shiro提供了灵活且强大的会话管理功能,用于管理用户的会话状态。会话是指用户在系统中的交互期间保持的状态,通常用于存储用户的登录信息、权限信息以及其他相关数据。

会话管理的基本概念
在Shiro中,会话管理主要涉及以下几个方面:

会话创建和销毁:Shiro会自动管理会话的创建和销毁,你可以配置会话的超时时间。

会话存储:会话中存储用户的身份信息、权限信息等,以便于在用户请求之间共享数据。

会话监听:可以通过会话监听器来监听会话的创建、销毁、过期等事件,以执行一些自定义的逻辑。

示例:会话管理
让我们通过一个简单的例子来演示如何在Shiro中进行会话管理。首先,我们需要配置Shiro的会话管理器和会话DAO。

在ShiroConfig中添加以下配置:

@Configuration
public class ShiroConfig {

    @Bean
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 设置Realm
        securityManager.setRealm(myRealm());
        // 设置会话管理器
        securityManager.setSessionManager(sessionManager());
        // 设置会话DAO
        securityManager.setSessionDAO(sessionDAO());
        return securityManager;
    }

    @Bean
    public MyRealm myRealm() {
        return new MyRealm();
    }

    @Bean
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        // 设置全局会话超时时间(毫秒)
        sessionManager.setGlobalSessionTimeout(1800000); // 30分钟
        // 是否在会话过期后自动删除
        sessionManager.setDeleteInvalidSessions(true);
        // 是否开启定时调度器检测过期会话
        sessionManager.setSessionValidationSchedulerEnabled(true);
        return sessionManager;
    }

    @Bean
    public EnterpriseCacheSessionDAO sessionDAO() {
        // 使用企业级缓存SessionDAO,可以替换为其他实现
        EnterpriseCacheSessionDAO sessionDAO = new EnterpriseCacheSessionDAO();
        // 设置缓存名称,根据实际情况配置
        sessionDAO.setActiveSessionsCacheName("shiro-activeSessionCache");
        return sessionDAO;
    }

    // ...其他配置
}


在这个配置中,我们配置了一个DefaultWebSessionManager作为会话管理器,设置了全局会话超时时间为30分钟。同时,我们使用了EnterpriseCacheSessionDAO作为会话DAO,用于存储会话数据。你可以根据项目需求选择不同的会话DAO实现。

接下来,在MyRealm中,我们可以通过重写doGetAuthenticationInfo方法将用户的身份信息存储到会话中:

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    // 将AuthenticationToken转换为UsernamePasswordToken
    UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;

    // 从token中获取用户名
    String username = token.getUsername();

    // 在实际项目中,这里通常是从数据库中根据用户名查询用户信息
    // 这里为了演示,我们假设存在一个用户
    if (!"username".equals(username)) {
        throw new UnknownAccountException("用户不存在");
    }

    // 假设数据库中的密码是经过加密的,这里为了演示使用明文密码
    String password = "password";

    // 返回认证信息,包括用户名和密码
    SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username, password, getName());

    // 获取当前Subject的会话,存储用户身份信息
    Session session = SecurityUtils.getSubject().getSession();
    session.setAttribute("currentUsername", username);

    return authenticationInfo;
}


在这个例子中,我们使用SecurityUtils.getSubject().getSession()获取当前Subject的会话对象,然后将用户名存储到会话的currentUsername属性中。这样,在整个用户会话期间,我们都可以通过SecurityUtils.getSubject().getSession().getAttribute("currentUsername")获取到当前用户的用户名。

Shiro的其他特性
除了上述介绍的核心功能之外,Shiro还提供了许多其他有用的特性,例如密码加密、RememberMe功能、单点登录等。在这里,简单介绍一下其中的一些特性。

密码加密
在真实项目中,用户密码通常不会以明文形式存储在数据库中,而是经过加密处理。Shiro提供了方便的密码加密工具,可以轻松地对密码进行加密和验证。

首先,在MyRealm中,修改doGetAuthenticationInfo方法,将明文密码加密后返回:

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    // 将AuthenticationToken转换为UsernamePasswordToken
    UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;

    // 从token中获取用户名
    String username = token.getUsername();

    // 在实际项目中,这里通常是从数据库中根据用户名查询用户信息
    // 这里为了演示,我们假设存在一个用户
    if (!"username".equals(username)) {
        throw new UnknownAccountException("用户不存在");
    }

    // 假设数据库中的密码是经过加密的
    String encryptedPassword = encryptPassword("password");

    // 返回认证信息,包括用户名和加密后的密码
    return new SimpleAuthenticationInfo(username, encryptedPassword, getName());
}

private String encryptPassword(String password) {
    // 使用Shiro提供的密码加密工具
    return new Md5Hash(password).toHex();
}


在这个例子中,我们使用Md5Hash对密码进行MD5加密。你可以根据实际项目需求选择其他加密算法。

RememberMe功能
Shiro的RememberMe功能允许用户在关闭浏览器后仍然保持登录状态。通过简单的配置,我们可以启用RememberMe功能。

在ShiroConfig中添加RememberMe的配置:

@Bean
public CookieRememberMeManager rememberMeManager() {
    CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();
    SimpleCookie rememberMeCookie = new SimpleCookie("rememberMe");
    rememberMeCookie.setMaxAge(2592000); // 设置Cookie的有效期,单位秒,这里为30天
    rememberMeManager.setCookie(rememberMeCookie);
    return rememberMeManager;
}

@Bean
public DefaultWebSecurityManager securityManager() {
    DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    // 设置Realm
    securityManager.setRealm(myRealm());
    // 设置会话管理器
    securityManager.setSessionManager(sessionManager());
    // 设置会话DAO
    securityManager.setSessionDAO(sessionDAO());
    // 设置RememberMe管理器
    securityManager.setRememberMeManager(rememberMeManager());
    return securityManager;
}


在这个配置中,我们创建了一个CookieRememberMeManager,并设置了RememberMe的Cookie名称和有效期。然后将其添加到DefaultWebSecurityManager中。

单点登录
Shiro还支持单点登录(SSO),使用户能够在多个关联的应用程序中使用同一套凭据进行登录。Shiro的单点登录功能可以通过集成其他身份验证和授权提供程序来实现,其中包括OAuth、CAS等。在这里,我们简单介绍一下使用OAuth 2.0的单点登录配置。

首先,在ShiroConfig中添加OAuth的配置:

@Bean
public OAuth2Realm oAuth2Realm() {
    return new OAuth2Realm();
}

@Bean
public DefaultWebSecurityManager securityManager() {
    DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    // 设置Realm
    securityManager.setRealm(oAuth2Realm());
    // 设置会话管理器
    securityManager.setSessionManager(sessionManager());
    // 设置会话DAO
    securityManager.setSessionDAO(sessionDAO());
    // 设置RememberMe管理器
    securityManager.setRememberMeManager(rememberMeManager());
    return securityManager;
}


在这个配置中,我们创建了一个OAuth2Realm并将其设置为主Realm。OAuth2Realm是一个自定义的Realm,用于处理OAuth 2.0的身份验证和授权。

接下来,实现OAuth2Realm:

public class OAuth2Realm extends AuthorizingRealm {

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        // 处理OAuth 2.0的授权逻辑
        // ...
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        // 处理OAuth 2.0的身份验证逻辑
        // ...
    }
}

在doGetAuthenticationInfo和doGetAuthorizationInfo方法中,你需要实现OAuth 2.0的身份验证和授权逻辑,具体实现方式取决于你使用的OAuth提供商。

这只是Shiro单点登录的一个简单示例,实际上,单点登录可能涉及到更复杂的协议和配置,具体实现方式取决于你的项目需求。

                                                 感谢大家的阅读,觉得有帮助的朋友点点赞!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于FPGA的数字信号处理(5)--Signed的本质和作用

前言 Verilog中的signed是一个很多人用不好&#xff0c;或者说不太愿意用的一个语法。因为不熟悉它的机制&#xff0c;所以经常会导致运算结果莫名奇妙地出错。其实了解了signed以后&#xff0c;很多时候用起来还是挺方便的。 signed的使用方法主要有两种&#xff0c;其中一种…

Android View事件分发面试问题及回答

问题 1: 请简述Android中View的事件分发机制是如何工作的&#xff1f; 答案: 在Android中&#xff0c;事件分发机制主要涉及到三个主要方法&#xff1a;dispatchTouchEvent(), onInterceptTouchEvent(), 和 onTouchEvent(). 当一个触摸事件发生时&#xff0c;首先被Activity的…

配置 Trunk,实现相同VLAN的跨交换机通信

1.实验环境 公司的员工人数已达到 100 人&#xff0c;其网络设备如图所示。现在的网络环境导致广播较多网速慢&#xff0c;并且也不安全。公司希望按照部门划分网络&#xff0c;并且能够保证一定的网络安全性。 其网络规划如下。 PC1和 PC3为财务部&#xff0c;属于VLAN 2&…

邦注科技 温控箱对企业的重要性

注塑加工是将加热的熔融塑料注入模具中形成所需产品的工艺过程。良好的注塑加工工艺需要控制好许多参数&#xff0c;其中最重要的因素之一就是模具的温度。模具温度的不稳定会导致产品尺寸大小、表面缺陷等方面的问题&#xff0c;甚至会导致生产不良品&#xff0c;加大生产成本…

Educational Codeforces Round 165 (Rated for Div. 2 ABCDE 题)视频讲解

A. Two Friends Problem Statement Monocarp wants to throw a party. He has n n n friends, and he wants to have at least 2 2 2 of them at his party. The i i i-th friend’s best friend is p i p_i pi​. All p i p_i pi​ are distinct, and for every i ∈…

通义灵码实战系列:一个新项目如何快速启动,如何维护遗留系统代码库?

作者&#xff1a;别象 进入 2024 年&#xff0c;AI 热度持续上升&#xff0c;翻阅科技区的文章&#xff0c;AI 可谓是军书十二卷&#xff0c;卷卷有爷名。而麦肯锡最近的研究报告显示&#xff0c;软件工程是 AI 影响最大的领域之一&#xff0c;AI 已经成为了软件工程的必选项&…

FLUKE万用表17B+的电压档最大内阻

项目中遇到一个测量兆欧级别电阻两端电压的问题&#xff0c;发现按照上图中的电路搭建出来的电路测得的电压为8.25V左右&#xff0c;按理说应为9V才对&#xff0c;后来想到万用表测量电压档不同的档位会有不同内阻&#xff0c;测量的电阻应远小于万用表电压档内阻才有效。本次测…

顶尖页面性能优化跃升之道:uniapp首屏加载性能极致优化策略权威指南(白屏现象终结攻略)

页面加载性能优化至关重要&#xff0c;直接影响用户体验满意度及网站流量转化。优化加载性能可以减少用户等待时间&#xff0c;提升交互响应&#xff0c;有效减少出现白屏的情况&#xff0c;增加用户留存&#xff0c;同时有利于搜索引擎排名&#xff0c;对网站流量、品牌形象及…

【常规】解决win11的Edge浏览器掉线问题

文章目录 【问题】【解决】step1 右键点击wifi--【网络和Internet设置】step2 点击打开后&#xff0c;打开【高级网络设置】后边的箭头step3 进入下一级以后&#xff0c;点击【WLAN】右侧的箭头step4 【更多适配选项】--【编辑】step5 取消Internet协议版本6&#xff08;TCP/IP…

php反序列化字符串逃逸

字符串逃逸 字符串逃逸是通过改变序列化字符串的长度造成的php反序列化漏洞 一般是因为替换函数使得字符串长度发生变化&#xff0c;不论变长还是变短&#xff0c;原理都大致相同 在学习之前&#xff0c;要先了解序列化字符串的结构&#xff0c;在了解结构的基础上才能更好理解…

Qt Creator导入第三方so库和jar包——Qt For Android

前言 之前了解了在Android Studio下导入so库和jar包&#xff0c;现在实现如何在Qt上导入so库和jar包。 实现 下面是我安卓开发&#xff08;需调用安卓接口的代码&#xff09;的目录&#xff08;图1&#xff09;&#xff0c;此目录结构和原生态环境&#xff08;Android Studi…

PS证件照

证件照尺寸 小一寸&#xff1a;2.2cm*3.3cm 一寸&#xff1a;2.5cm*3.5cm 像素413*295 &#xff08;分辨率为300像素/英寸&#xff09; 比例5&#xff1a;7 二寸&#xff1a;3.5cm*4.9cm 二寸照相比例是4&#xff1a;3&#xff0c;像素是626*413 蓝底&#xff1a;R&a…

python学习之词云图片生成

代码实现 import jieba import wordcloudf open("D:/Pythonstudy/data/平凡的世界.txt", "r", encoding"utf-8") t f.read() print(t) f.close() ls jieba.lcut(t) txt " ".join(ls)w wordcloud.WordCloud(font_path"D:/cc…

【Unity动画系统】详解Root Motion动画在Unity中的应用(二)

Root Motion遇到Blend Tree 如果Root Motion动画片段的速度是1.8&#xff0c;那么阈值就要设置为1.8&#xff0c;那么在代码中的参数就可以直接反映出Root Motion的最终移动速度。 Compute Thresholds&#xff1a;根据Root Motion中某些数值自动计算这里的阈值。 Velocity X/…

使用 Python 和 OpenCV 进行实时目标检测的详解

使用到的模型文件我已经上传了&#xff0c;但是不知道能否通过审核&#xff0c;无法通过审核的话&#xff0c;就只能 靠大家自己发挥实力了&#xff0c;^_^ 目录 简介 代码介绍 代码拆解讲解 1.首先&#xff0c;让我们导入需要用到的库&#xff1a; 2.然后&#xff0c;设…

《QT实用小工具·四十三》历史编辑器(支持历史搜索 关键字匹配)

1、概述 源码放在文章末尾 该项目实现了在输入框中输入部分信息能全部展现之前的历史输入信息&#xff0c;支持历史搜索和关键词匹配&#xff0c;项目demo演示如下所示&#xff1a; 项目部分代码如下所示&#xff1a; #include "historymodel.h" #include <QM…

Java发送请求-http+https的

第一步&#xff1a;建议ssl连接对象&#xff0c;信任所有证书 第二步&#xff1a;代码同时支持httphttps 引入源码类 是一个注册器 引入这个类&#xff0c;和它的方法create 注册器&#xff0c;所以对http和https都进行注册&#xff0c;参数为id和item&#xff0c;其中http的…

【已解决】pandas读excel中长数字变成科学计数法的问题

pandas 读excel中的长数字时&#xff0c;即使excel中已经设置为文本&#xff0c;读进df后也会自动变成科学计数法。 在日常的数据分析和处理工作中&#xff0c;Excel和pandas是数据分析师们不可或缺的得力助手。然而&#xff0c;在使用pandas读取Excel文件时&#xff0c;我们有…

CSAPP | Floating Point

CSAPP | Floating Point b i b_i bi​ b i − 1 b_{i-1} bi−1​ … b 2 b_2 b2​ b 1 b_1 b1​ b 0 b_0 b0​ b − 1 b_{-1} b−1​ b − 2 b_{-2} b−2​ b − 3 b_{-3} b−3​ … b − j b_{-j} b−j​ S ∑ k − j i b k 2 k S\sum_{k-j}^{i}b_k\times2^k S∑k…

如何批量复制多个文件到多个目录中(批量复制文件,多对多文件高效操作的方法)

首先&#xff0c;需要用到的这个工具&#xff1a; 度娘网盘 提取码&#xff1a;qwu2 蓝奏云 提取码&#xff1a;2r1z 现在开始说具体操作 1、首先&#xff0c;我准备了3个文件夹和两个可爱的图片&#xff1a; 当然&#xff0c;在实际使用的时候肯定不止这些&#xff0c;我这…