Shiro安全框架(上)

news2024/9/21 2:35:27

目录

第一章 权限概述

1、什么是权限

2、身份认证概念-Authentication

【1】什么是认证

【2】对象

2、用户授权概念-Authorization

【1】什么是授权

【2】授权流程

第二章 Shiro概述

1、Shiro简介

【1】什么是Shiro?

【2】Shiro 的特点

2、核心组件

第三章 Shiro入门

1、身份认证

【1】基本流程

【2】案例演示

【2.1】需求

【2.2】实现

【2.2.1】新建项目

【2.2.3】编写shiro.ini

【2.2.4】测试类验证测试

【2.3】小结

2、Realm(重要)

【1】Realm接口

【2】自定义Realm

【2.1】需求

【2.2】实现

【2.2.1】创建项目

【2.2.2】定义SecurityService

【2.2.3】定义MyRealm

【2.2.4】编辑shiro.ini

3、散列算法-SimpleHash

4、Realm使用散列算法

【1】新建项目

【2】创建密文密码

【3】修改SecurityService

【4】指定密码匹配方式

5、用户授权

【1】基本流程

【2】案例演示

【2.1】需求

【2.2】实现

【2.2.1】创建项目

【2.2.2】编写SecurityService(测试功能使用的是假数据)

【2.2.3】编写MyRealm

【2.2.4】编写App

【3】小结


第一章 权限概述

1、什么是权限

权限管理,一般指根据系统设置的安全策略或者安全规则,用户可以访问而且只能访问自己被授权的资源,不多不少。权限管理几乎出现在任何系统里面,只要有用户和密码的系统。

权限管理在系统中一般分为:

  • 访问权限

    一般表示你能做什么样的操作,或者能够访问那些资源。例如:给张三赋予“人事主管”角色,“人事主管”具有“查询员工”、“添加员工”、“修改员工”和“删除员工”权限。此时张三能够进入系统,则可以进行这些操作
  • 数据权限

    一般表示某些数据你是否属于你,或者属于你可以操作范围。例如:李四是"项目经理"角色,他可以看他的员工所有的任务分解,他的员工只能看自己负责的任务分解

2、身份认证概念-Authentication

【1】什么是认证

身份认证,就是判断一个用户是否为合法用户的处理过程。最常用的简单身份认证方式是系统通过核对用户输入的用户名和密码,看其是否与系统中存储的该用户的用户名和密码一致,来判断用户身份是否正确。例如:密码登录,手机短信验证、三方授权等

【2】对象

Subject:主体:访问系统的用户,主体可以是用户、程序等,进行认证的都称为主体,简单理解就是当前用户

Principal:身份信息是主体(subject)进行身份认证的标识,标识必须具有唯一性,如用户名、手机号、邮箱地址等,一个主体可以有多个身份,但是必须有一个主身份(Primary Principal),简单理解就是当前账号

credential:凭证信息:是只有主体自己知道的安全信息,如密码、证书等,简单理解就是密码

2、用户授权概念-Authorization

【1】什么是授权

授权,即访问控制,控制谁能访问哪些资源。主体进行身份认证后,系统会为其分配对应的权限,当访问资

源时,会校验其是否有访问此资源的权限。

这里首先理解4个对象。

用户对象subject:当前操作的用户、程序,例如:zhangSan,liSi,admin1。

资源对象resource:当前被访问的对象,如系统菜单、页面、按钮、方法、系统商品信息等

访问类型:商品菜单,订单菜单、分销商菜单

数据类型:我的商品,我的订单,我的评价

角色对象role :一组 "权限操作许可权" 的集合。

权限对象permission:权限操作许可权

我的商品(资源)===>访问我的商品(权限许可)

分销商菜单(资源)===》访问分销商列表(权限许可)

【2】授权流程

第二章 Shiro概述

1、Shiro简介

【1】什么是Shiro?

Shiro是apache旗下一个开源框架,它将软件系统的安全认证相关的功能抽取出来,实现用户身份认证,权限授权、加密、会话管理等功能,组成了一个通用的安全认证框架。

【2】Shiro 的特点

Shiro 是一个强大而灵活的开源安全框架,能够非常清晰的处理认证、授权、管理会话以及密码加密。如下是它所具有的特点:

· 易于理解的 Java Security API;

· 简单的身份认证(登录),支持多种数据源(LDAP,JDBC 等);

· 对角色的简单的签权(访问控制),也支持细粒度的鉴权;

· 支持一级缓存,以提升应用程序的性能;

· 内置的基于 POJO 企业会话管理,适用于 Web 以及非 Web 的环境;

· 异构客户端会话访问;

· 非常简单的加密 API;

· 不跟任何的框架或者容器捆绑,可以独立运行。

2、核心组件

  • Shiro架构图

  • Subject

Subject主体,外部应用与subject进行交互,subject将用户作为当前操作的主体,这个主体:可以是一个通过浏览器请求的用户,也可能是一个运行的程序。Subject在shiro中是一个接口,接口中定义了很多认证授相关的方法,外部程序通过subject进行认证授,而subject是通过SecurityManager安全管理器进行认证授权
  • SecurityManager

SecurityManager权限管理器,它是shiro的核心,负责对所有的subject进行安全管理。通过SecurityManager可以完成subject的认证、授权等,SecurityManager是通过Authenticator进行认证,通过Authorizer进行授权,通过SessionManager进行会话管理等。SecurityManager是一个接口,继承了Authenticator, Authorizer, SessionManager这三个接口
  • Authenticator

Authenticator即认证器,对用户登录时进行身份认证
  • Authorizer

Authorizer授权器,用户通过认证器认证通过,在访问功能时需要通过授权器判断用户是否有此功能的操作权限。
  • Realm(数据库读取+认证功能+授权功能实现)

Realm领域,相当于datasource数据源,securityManager进行安全认证需要通过Realm获取用户权限数据
比如:
    如果用户身份数据在数据库那么realm就需要从数据库获取用户身份信息。
注意:
    不要把realm理解成只是从数据源取数据,在realm中还有认证授权校验的相关的代码。 
  • SessionManager

SessionManager会话管理,shiro框架定义了一套会话管理,它不依赖web容器的session,所以shiro可以使用在非web应用上,也可以将分布式应用的会话集中在一点管理,此特性可使它实现单点登录。
  • SessionDAO

SessionDAO即会话dao,是对session会话操作的一套接口
比如:
    可以通过jdbc将会话存储到数据库
    也可以把session存储到缓存服务器
  • CacheManager

CacheManager缓存管理,将用户权限数据存储在缓存,这样可以提高性能
  • Cryptography

Cryptography密码管理,shiro提供了一套加密/解密的组件,方便开发。比如提供常用的散列、加/解密等功能

第三章 Shiro入门

1、身份认证

【1】基本流程

流程如下:

1、Shiro把用户的数据封装成标识token,token一般封装着用户名,密码等信息

2、使用Subject门面获取到封装着用户的数据的标识token

3、Subject把标识token交给SecurityManager,在SecurityManager安全中心中,SecurityManager把标识token委托给认证器Authenticator进行身份验证。认证器的作用一般是用来指定如何验证,它规定本次认证用到哪些Realm

4、认证器Authenticator将传入的标识token,与数据源Realm对比,验证token是否合法

【2】案例演示
【2.1】需求
1、使用shiro完成一个用户的登录
【2.2】实现
 <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-core</artifactId>
      <version>1.3.2</version>
    </dependency>
    <dependency>
      <groupId>commons-logging</groupId>
      <artifactId>commons-logging</artifactId>
      <version>1.2</version>
    </dependency>
【2.2.1】新建项目

shiro

【2.2.3】编写shiro.ini
[users]
zhanSan=123
liSi=456
【2.2.4】测试类验证测试
@Test
    void contextLoads() {
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);
        Subject subject = SecurityUtils.getSubject();
        log.info("是否登录成功"+String.valueOf(subject.isAuthenticated()));
        subject.login(new UsernamePasswordToken("zhangSan","admin"));
        log.info("是否登录成功"+String.valueOf(subject.isAuthenticated()));
    }

【2.2.4】测试

当账号密码正确的时候代码可以继续执行
当错误的时候,会抛出异常
UnknownAccountException 账号不存在异常
IncorrectCredentialsException 认证失败异常
​
​
tips:
Incorrect 不正确
Credentials 认证
【2.3】小结
1、权限定义:ini文件
2、加载过程:
    导入权限ini文件构建权限工厂
    工厂构建安全管理器
    使用SecurityUtils工具生效安全管理器
    使用SecurityUtils工具获得主体
    使构建账号token用SecurityUtils工具获得主体
    构建账号token
    登录操作----------抛出异常

2、Realm(重要)

【1】Realm接口

所以,一般在真实的项目中,我们不会直接实现Realm接口,我们一般的情况就是直接继承AuthorizingRealm,能够继承到认证与授权功能。它需要强制重写两个方法

【2】自定义Realm
【2.1】需求
1、自定义Realm,取得密码用于比较
【2.2】实现
【2.2.1】创建项目

shiro01

【2.2.2】定义SecurityService

SecurityService 

package com.itqq.service;
​
public interface SecurityService {
        String findPasswordByLoginName(String loginName);
}

SecurityServiceImpl

package com.itqq.shiro.service.impl;
​
import com.itheima.shiro.service.SecurityService;
​
/**
 * @Description:权限服务层
 */
public class SecurityServiceImpl implements SecurityService {
​
    @Override
    public String findPasswordByLoginName(String loginName) {
        return "123";
    }
}
【2.2.3】定义MyRealm
package com.itqq.realm;
​
import com.haogu.service.SecurityService;
import com.haogu.service.impl.SecurityServiceImpl;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
​
public class MyRealm extends AuthorizingRealm {
    // 授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }
​
    // 认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username = (String) token.getPrincipal();
        SecurityService securityService = new SecurityServiceImpl();
        String password = securityService.findPasswordByUsername(username);
        if("".equals(password) || password == null){
            throw new UnknownAccountException("账号不存在");
        }
        return new SimpleAuthenticationInfo(username,password,getName());
    }
}
​
【2.2.4】编辑shiro.ini
#声明自定义的realm,且为安全管理器指定realms
[main]
myRealm=com.itqq.realm.MyRealm
securityManager.realms=$myRealm
​
##[users]
##zhangSan=123

3、散列算法-SimpleHash

散列算法一般用于生成数据的摘要信息,是一种不可逆的算法,一般适合存储密码之类的数据,常见的散列算法如MD5、SHA等。一般进行散列时最好提供一个salt(盐),比如加密密码“hello123”,产生的散列值是“be3ee20eac72da4ef6b233814bb61e67afab7af1”,可以到一些md5解密网站很容易的通过散列值得到密码“admin”,即如果直接对密码进行散列相对来说破解更容易,此时我们可以加一些只有系统知道的干扰数据,如salt(即盐);这样散列的对象是“密码+salt”,这样生成的散列值相对来说更难破解。

shiro支持的散列算法:

Md2Hash、Md5Hash、Sha1Hash、Sha256Hash、Sha384Hash、Sha512Hash

  @Test
    public void m1() {
        // algorithmName 加密方式:md5,md2,sha-1等
        // source 加密内容
        // salt 盐
        // hashIterations 加密次数
        String password = new SimpleHash("md5","hello123","dvfdhj",512).toHex();
        System.out.println(password);
    }

4、Realm使用散列算法

上面我们了解散列算法,那么在realm中怎么使用?在realm02中我们使用的密码是明文的校验方式,也就是SecurityServiceImpl中findPasswordByUsername返回的是明文123的密码

【1】新建项目

shiro01/MD503

【2】创建密文密码
password:c8bb39206310191b78d22680243afc42
salt:dvfdhj
原密码:hello123
【3】修改SecurityService

SecurityService修改成返回salt和password的map

package com.itqq.service.impl;
​
import com.itqq.service.SecurityService;
​
public class SecurityServiceImpl implements SecurityService {
    @Override
    public String findPasswordByUsername(String username) {
        return "c8bb39206310191b78d22680243afc42";
    }
}
【4】指定密码匹配方式

为MyRealm类添加构造方法如下:

public MyRealm(){
        //指定密码匹配方式为md5
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher("md5");
        //指定密码迭代次数
        matcher.setHashIterations(512);
        //使用父亲方法使匹配方式生效
        setCredentialsMatcher(matcher);
    }

修改MyRealm类的认证doGetAuthenticationInfo方法如下

 // 认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username = (String) token.getPrincipal();
        SecurityService securityService = new SecurityServiceImpl();
        String password = securityService.findPasswordByUsername(username);
        if("".equals(password) || password == null){
            throw new UnknownAccountException("账号不存在");
        }
        // 第1个参数:账号
        // 第2个参数:密码
        // 第3个参数:盐
        // 第4个参数:real的名字
        return new SimpleAuthenticationInfo(username,password, ByteSource.Util.bytes("dvfdhj"),getName());
    }
//    认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken token1 = (UsernamePasswordToken) token;
        String username = token1.getUsername();
        if(!StringUtils.hasText(username)){
            throw new AuthenticationException("用户名不能为空");
        }
        String password = new String(token1.getPassword());
        if(!StringUtils.hasText(password)){
            throw new AuthenticationException("密码不能为空");
        }
        User user = securityService.findByUsername(username);
        if (user == null){
            return null;
        }
        // 密码判断:shiro框架自行判断--与用户输入的账号和密码进行对比,验证token是否合法
        // user.getId(),是shiro框架提供的可以改变的值,一般是id
        //        原本写是username/phone --------> id
        return new SimpleAuthenticationInfo(user.getId(),user.getPassword(), ByteSource.Util.bytes("adagag"),getName());
    }

5、用户授权

【1】基本流程

1、首先调用Subject.isPermitted/hasRole接口,其会委托给SecurityManager。

2、SecurityManager接着会委托给内部组件Authorizer;

3、Authorizer再将其请求委托给我们的Realm去做;Realm才是真正干活的

4、Realm将用户请求的参数封装成权限对象。再从我们重写的doGetAuthorizationInfo方法中获取从数据库中查询到的权限集合。

5、Realm将用户传入的权限对象,与从数据库中查出来的权限对象,进行一一对比。如果用户传入的权限对象在从数据库中查出来的权限对象中,则返回true,否则返回false。

进行授权操作的前提:用户必须通过认证。

在真实的项目中,角色与权限都存放在数据库中。

好了,接下来,我们要重写我们本小节的核心方法了。在MyRealm中找到下列方法:

//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    return null;
}

此方法的传入的参数PrincipalCollection principals,是一个包装对象,它表示"用户认证凭证信息"。包装的是谁呢?没错,就是认证doGetAuthenticationInfo()方法的返回值的第一个参数username。你可以通过这个包装对象的getPrimaryPrincipal()方法拿到此值,然后再从数据库中拿到对应的角色和资源,构建SimpleAuthorizationInfo。

/**
     * @Description 授权方法
     */
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    //拿到用户认证凭证信息
    String loginName = (String) principals.getPrimaryPrincipal();
    //从数据库中查询对应的角色和资源
    SecurityService securityService = new SecurityServiceImpl();
    List<String> roles = securityService.findRoleByloginName(loginName);
    List<String> permissions = securityService.findPermissionByloginName(loginName);
    //构建资源校验
    SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
    authorizationInfo.addRoles(roles);
    authorizationInfo.addStringPermissions(permissions);
    return authorizationInfo;
}
【2】案例演示
【2.1】需求
1、实现doGetAuthorizationInfo方法实现鉴权
2、使用subject类实现权限的校验
【2.2】实现
【2.2.1】创建项目

shiro01/authentication-realm04

【2.2.2】编写SecurityService(测试功能使用的是假数据)

在SecurityService中添加

package com.itqq.service;
​
import java.util.List;
​
public interface UserService {
    String findPasswordByUsername(String username);
    List<String> findRolesByUsername(String username);
    List<String> findPermissionByUsername(String username);
}
​

SecurityServiceImpl添加实现

package com.itqq.service.impl;
​
import com.itqq.service.UserService;
​
import java.util.ArrayList;
import java.util.List;
​
public class UserServiceImpl implements UserService {
​
​
​
    @Override
    public String findPasswordByUsername(String username) {
        if ("admin".equals(username)){
            return "4d5233a24fd625a06437778c4ed76969"; // 123
        }else if("zhangSan".equals(username)){
            return "a7b19b47e46ce6ca9860c326db041859"; // 1234
        }else if("liSi".equals(username)){
            return "a7b19b47e46ce6ca9860c326db041859"; // 1234
        }
        return null;
    }
​
    @Override
    public List<String> findRolesByUsername(String username) {
        List<String> roles = new ArrayList<>();
        roles.add("普通用户");
        if("admin".equals(username)){
            roles.add("管理员");
            roles.add("VIP用户");
        }else if("liSi".equals(username)){
            roles.add("VIP用户");
        }
        return roles;
    }
​
    @Override
    public List<String> findPermissionByUsername(String username) {
        List<String> permissions = new ArrayList<>();
        permissions.add("comment:select"); // 评论表的查询权限
        permissions.add("comment:insert"); // 评论表的添加权限
        if("admin".equals(username)){
//            permissions.add("user:select");
//            permissions.add("user:insert");
//            permissions.add("user:update");
//            permissions.add("user:delete");
            permissions.add("*:*"); // 所有表的所有权限
        }
        if("liSi".equals(username) ){
//            permissions.add("comment:update");
//            permissions.add("comment:delete");
            permissions.add("comment:*"); // 评论表的所有权限
        }
        return permissions;
    }
}
【2.2.3】编写MyRealm

在MyRealm中修改doGetAuthorizationInfo方法如下

package com.itqq.realm;
​
import com.itqq.service.SecurityService;
import com.itqq.service.impl.SecurityServiceImpl;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
​
import java.util.List;
​
public class MyRealm extends AuthorizingRealm {
​
​
    public MyRealm(){
        //指定密码匹配方式为md5
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher("md5");
        //指定密码迭代次数
        matcher.setHashIterations(512);
        //使用父亲方法使匹配方式生效
        setCredentialsMatcher(matcher);
    }
​
    // 授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        //拿到账号
        String username = (String) principals.getPrimaryPrincipal();
        // SecurityServiceImpl()不在ioc容器中所以要new一个对象,不能注入
        SecurityService securityService = new SecurityServiceImpl();
        // 获取角色
        List<String> roles = securityService.findRoleByUsername(username);
        // 获取权限
        List<String> permissions = securityService.findPermissionByUsername(username);
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.addRoles(roles);
        authorizationInfo.addStringPermissions(permissions);
        return authorizationInfo;
    }
​
    // 认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username = (String) token.getPrincipal();
        SecurityService securityService = new SecurityServiceImpl();
        String password = securityService.findPasswordByUsername(username);
        if("".equals(password) || password == null){
            throw new UnknownAccountException("账号不存在");
        }
        return new SimpleAuthenticationInfo(username,password, ByteSource.Util.bytes("dvfdhj"),getName());
    }
}
【2.2.4】编写App
package com.itqq;
​
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
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;
​
​
public class App 
{
    public static void main( String[] args ) {
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);
        Subject subject = SecurityUtils.getSubject();
        try {
            subject.login(new UsernamePasswordToken("zhangSan","1234"));
        }catch (IncorrectCredentialsException e){
            System.out.println("密码错误");
        }catch (UnknownAccountException e){
            System.out.println("账号不存在");
        }
        System.out.println(subject.isAuthenticated());
        System.out.println("管理员:"+ subject.hasRole("管理员"));
        System.out.println("普通用户:"+ subject.hasRole("普通用户"));
        System.out.println("VIP用户:"+ subject.hasRole("VIP用户"));
        // has是返回boolean值,check抛出异常
//        subject.checkRole("管理员"); // UnauthenticatedException
        System.out.println("用户表添加:"+subject.isPermitted("user:insert"));
        System.out.println("评论表的查询:"+subject.isPermitted("comment:select"));
        // UnauthorizedException: Subject does not have permission [comment:update]
        subject.checkPermission("comment:update");
    }
}
​
【3】小结

1、鉴权需要实现doGetAuthorizationInfo方法
2、鉴权使用门面subject中方法进行鉴权
    以check开头的会抛出异常
    以is和has开头会返回布尔值

总结:

Shiro框架的几个核心组件:

  1. Subject - 这是Shiro的安全主体,代表了应用程序用户。它封装了所有与安全操作相关的方法,如登录、登出、权限检查等。

  2. Realms - Realm是Shiro与应用数据源(如数据库)交互的模块,用于获取用户信息和角色权限。你可以定义多个Realm来从不同的数据源获取信息。

  3. SecurityManager - 它是Shiro的核心,负责管理Subject、Realms和其他安全相关的组件。它是Shiro的“大脑”,协调所有的安全操作。

  4. Session Manager - 用于会话管理和状态跟踪,可以控制用户的会话生命周期,并提供跨域会话管理能力。

  5. Caching - Shiro提供了缓存机制,可以提高安全性操作的性能,如缓存认证和授权结果。

  6. Cryptography - 提供加密和散列功能,用于保护敏感数据。

Shiro的设计使得它可以轻松集成到任何Java应用中,无论是Web应用还是独立的Java应用。它的灵活性和可扩展性使得它成为企业级应用安全解决方案的首选。

 


总结结束!!! 

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

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

相关文章

【CSharp】VisualStudio2019进行Windows窗体编程时关于AnyCPU的说明

【CSharp】VisualStudio2019进行Windows窗体编程时关于AnyCPU的说明 1.背景2.说明3.修改1.背景 IDE:Visual Studio 2019 通过VS2019编写windows窗体应用程序时,在VS2019界面可以看到Any CPU,如下: 2.说明 在Visual Studio 2019中进行Windows窗体编程时, AnyCPU是一个平…

wefwefwe

c语言中的小小白-CSDN博客c语言中的小小白关注算法,c,c语言,贪心算法,链表,mysql,动态规划,后端,线性回归,数据结构,排序算法领域.https://blog.csdn.net/bhbcdxb123?spm1001.2014.3001.5343 给大家分享一句我很喜欢我话&#xff1a; 知不足而奋进&#xff0c;望远山而前行&am…

微前端--qiankun

qiankun qiankun分为accpication和parcel模式。 aplication模式基于路由工作&#xff0c;将应用分为两类&#xff0c;基座应用和子应用&#xff0c;基座应用维护路由注册表&#xff0c;根据路由的变化来切换子应用。子应用是一个独立的应用&#xff0c;需要提供生命周期方法供…

AI智能名片小程序在内容营销中的创新应用:以“48小时够你玩”系列为例

摘要&#xff1a;在数字化时代&#xff0c;内容营销已成为企业连接消费者、塑造品牌形象、推动销售增长的关键策略。AI智能名片小程序&#xff0c;作为新兴技术的集大成者&#xff0c;以其智能化、个性化、便捷化的特点&#xff0c;为内容营销注入了新的活力。本文深入探讨了AI…

[C++][STL源码剖析] 详解AVL树的实现

1.概念 二叉搜索树虽可以缩短查找的效率&#xff0c;但如果数据有序或接近有序二叉搜索树将退化为单支树&#xff0c;查找元素相当于在顺序表中搜索元素&#xff0c;效率低下。 因此&#xff0c;两位俄罗斯的数学家G.M.Adelson-Velskii 和E.M.Landis在1962年 发明了一种解决上…

医疗信息化之PACS系统源码,C#医学影像系统源码,成熟在用稳定运中

C#语言开发的一套PACS系统源码&#xff0c;C/S架构&#xff0c;成熟稳定&#xff0c;多家大型综合医院应用案例。自主版权&#xff0c;支持二次开发&#xff0c;授权后可商用。 医学影像存储与传输系统是针对数据库存储、传输服务、图像处理进行了优化,存储更安全、传输更稳定、…

《云原生安全攻防》-- 容器攻击案例:镜像投毒与Fork炸弹

在本节课程中&#xff0c;我们将介绍两个比较有意思的容器攻击案例&#xff0c;镜像投毒与Fork炸弹。 在这个课程中&#xff0c;我们将学习以下内容&#xff1a; 镜像投毒&#xff1a;构建恶意镜像&#xff0c;诱导用户拉取镜像创建容器。 Fork炸弹&#xff1a;Fork炸弹的攻击…

Axure设计之轮播图(动态面板+中继器)

轮播图&#xff08;Carousel&#xff09;是一种网页或应用界面中常见的组件&#xff0c;用于展示一系列的图片或内容&#xff0c;通常通过自动播放或用户交互&#xff08;如点击箭头按钮&#xff09;来切换展示不同的内容。轮播图能够吸引用户的注意力&#xff0c;有效展示重要…

无人机10公里WiFi图传摄像模组,飞睿智能超清远距离无线监控,智能安防新潮流

在这个科技日新月异的时代&#xff0c;我们对影像的捕捉和传播有了更高的要求。从传统的有线传输到无线WiFi图传&#xff0c;每一次技术的飞跃都为我们带来了全新的视觉体验。今天&#xff0c;我们要探讨的&#xff0c;正是一款具有划时代意义的科技产品——飞睿智能10公里WiFi…

PySide的style().unpolish()与style().unpolish()

在 Qt 框架中&#xff0c;polish() 方法用于重新应用样式表或更新控件的外观。它的作用是强制控件重新应用样式表&#xff0c;以确保外观的更新能反映在控件上。这个方法通常在控件的样式表更改或控件的属性发生变化后使用&#xff08;发生变化的属性与外观的改变有相关&#x…

国外镜像学术导航与国内专利资源中心查询网址神器!!!快收藏!

目录 ​编辑 中国专利公布公告 思谋学术&#xff1a;思谋学术_谷歌学术搜索和文献资源 谷歌学术 学术资源工具 学术文献检索 文献下载 科研快讯 中国专利公布公告 http://epub.cnipa.gov.cn/ 简介&#xff1a;中国国家知识产权局&#xff08;CNIPA&#xff09;的电…

Vivado生成网表文件并创建自定义IP

平台&#xff1a;vivado2018.3 应用场景&#xff0c;在设计的过程中&#xff0c;我们一些特定的模块需要交付给别人&#xff0c;但是又不想让他们看到其中的源码。就可以将源码封装成网表和IP文件。 vivado生成网表文件 设置综合。设置-flatten_hierarchy* 为full。 这里可…

一文了解图像处理、图像分析和图像理解

图像处理&#xff1a;利⽤计算机对图像进⾏去除噪声、增强、复原、分割、特征提取、识别、等处理的理论、⽅法和技术。狭义的图像处理主要是对图像进⾏各种加⼯&#xff0c;以改变图像的视觉效果并为⾃动识别奠定基础&#xff0c;或对图像进⾏压缩编码以减少所需存储空间。 图…

802.11无线网络权威指南(二):无线帧结构

802.11无线网络权威指南&#xff08;二&#xff09;&#xff1a;无线帧结构 无线协议桢的三种类型无线网络帧结构完整帧格式control frameDuration/IDAddressSequence ControlQoS ControlHT Control 字段Frame Body 帧体FCS 校验域 帧细节管理帧控制帧RTS 帧CTS 帧ACK 帧格式PS…

使用BenchmarkDotNet对C#代码进行基准测试

一、基准测试 基准测试&#xff08;benchmarking&#xff09;是一种测量和评估软件性能指标的活动。你可以在某个时候通过基准测试建立一个已知的性能水平&#xff08;称为基准线&#xff09;&#xff0c;当系统的软硬件环境发生变化之后再进行一次基准测试以确定那些变化对性…

手持气象站:掌中的天气预报员

在科技日新月异的今天&#xff0c;手持气象站作为一种便携、智能的气象监测设备&#xff0c;逐渐成为了户外探险、农业生产、环境监测等领域的得力助手。它以其小巧的体积、丰富的功能和精准的数据&#xff0c;赢得了广大用户的青睐。 一、手持气象站的概述 手持气象站是一种集…

Springboot 多数据源事务

起因 在一个service方法上使用的事务,其中有方法是调用的多数据源orderDB 但是多数据源没有生效,而是使用的primaryDB 原因 spring 事务实现的方式 以 Transactional 注解为例 (也可以看 TransactionTemplate&#xff0c; 这个流程更简单一点)。 入口&#xff1a;ProxyTransa…

算法通关:006_1二分查找

查找一个数组里面是否存在num import java.util.Arrays;/*** Author: ggdpzhk* CreateTime: 2024-07-23*/ public class _006 {//二分查找//保证数组有序才能用二分查找&#xff0c;不然无意义public static boolean exist(int[] arr,int num){if(arr null || arr.length 0)…

VUE3学习第二篇:报错记录

1、在我整理好前端代码框架后&#xff0c;而且也启动好了对应的后台服务&#xff0c;访问页面&#xff0c;正常。 2、报错ReferenceError: defineModel is not defined 学到这里报错了 在vue网站的演练场&#xff0c;使用没问题 但是在我自己的代码里就出问题了 3、watchEffec…

2024-07-23 Unity插件 Odin Inspector11 —— 使用 Odin 自定义编辑窗口

文章目录 1 OdinEditorWindow1.1 运作方式1.2 使用特性绘制 OdinEditorWindow1.3 在 OdinEditorWindow 中渲染对象 2 OdinMenuEditorWindow2.1 添加菜单导航栏2.2 添加导航栏示例 ​ Odin Window 可以完整地访问 Odin 绘图系统&#xff0c;不再需要操心 Window 的绘制 方式&am…