当前位置: 首页 > news >正文

【Spring Security】打造安全无忧的Web应用--使用篇

🥳🥳Welcome Huihui's Code World ! !🥳🥳

接下来看看由辉辉所写的关于Spring Security的相关操作吧 

目录

🥳🥳Welcome Huihui's Code World ! !🥳🥳

一.Spring Security中的授权是什么

二. 基于单体项目的授权

1.修改User配置角色和权限

2.修改SpringSecurity配置类

3.测试

三.基于前后端分离项目的授权 

JsonResponseBody

JsonResponseStatus

修改SpringSecurity配置类


在正式讲解今天的内容之前,我们还需要了解一个东西--RBAC数据模型,RBAC(Role-Based Access Control,基于角色的访问控制)数据模型是一种访问控制模型,它使用角色作为访问控制的基本单元。在这个模型中,用户被分配到不同的角色,每个角色代表着一组操作或任务。系统管理员将访问权限授予角色,而不是直接授予给用户。当用户需要执行某个操作时,系统可以检查该用户是否拥有执行此操作所需的角色,并根据角色的权限来授权或拒绝访问。RBAC 数据模型可以有效地管理复杂的访问控制策略,提高系统安全性和管理效率。

遇到复杂的权限管理,都是配置在数据库中的,一般由五张表组成,即RBAC。

  1)RBAC数据模型的五张表

          用户表,存储用户信息,由业务人员维护;

          角色表,存储角色信息,由业务人员维护 ;

          资源表,存储资源信息(菜单、按钮及其URL),由开发人员维护;

          用户-角色关系表,存储用户和角色的对应关系,多对多,由业务人员维护;

          角色-资源关系表,存储角色和资源的对应关系 ,多对多,由业务人员维护;

一.Spring Security中的授权是什么

        在Spring Security中,授权是指系统根据用户的角色和权限决定用户是否有权访问特定的资源或执行特定的操作。

        Spring Security提供了多种授权机制,包括基于角色的授权和基于权限的授权

                基于角色的授权是指系统将用户分配到不同的角色,每个角色具有不同的权限,然后通过判断用户是否具有特定的角色来进行授权。例如,系统可以定义"ADMIN"角色和"USER"角色,管理员拥有更高的权限,可以访问和操作更多的资源,而普通用户只能访问受限资源。

                基于权限的授权是指系统将具体的权限授予用户,用户可以直接拥有某个权限,而不需要通过角色间接获得。例如,系统可以定义"READ"、"WRITE"、"DELETE"等权限,根据用户所拥有的权限判断其是否有权对资源进行读取、写入或删除操作。

        Spring Security还支持细粒度的授权控制,可以通过注解、表达式或配置文件等方式来定义授权规则。可以使用@PreAuthorize@PostAuthorize@Secured等注解来标注方法或类,以声明访问资源的权限要求。

        通过授权机制,Spring Security可以保护应用程序免受未经授权的访问,确保只有具备合适权限的用户才能访问敏感资源,提高应用程序的安全性

二. 基于单体项目的授权

1.修改User配置角色和权限

这里也可以使用多表联查的方式去做,不过考虑到在数据量过于庞大的情况下,我们常常会进行分库分表,所以我们这里采用的是流的形式来编写的

package com.wh.security.config;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.wh.security.model.*;
import com.wh.security.service.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;/*** @author是辉辉啦* @create 2023-12-23-14:21*/
@Component
public class MyUserDetailsService implements UserDetailsService {@Autowiredprivate IUserService userService;@Autowiredprivate IUserRoleService userRoleService;@Autowiredprivate IRoleService roleService;@Autowiredprivate IRoleModuleService roleModuleService;@Autowiredprivate IModuleService moduleService;/*** 实现Spring Security内置的UserDetailService接口,重写loadUserByUsername方法实现数据库的身份校验* @param username* @return* @throws UsernameNotFoundException*/@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//根据用户名查询数据库中用户信息User user = userService.getOne(new QueryWrapper<User>().eq("username", username));//判断用户是否存在if(Objects.isNull(user)) {throw new UsernameNotFoundException("该用户不存在!!!");}//先拿到用户的身份idList<Integer> roleIds = userRoleService.list(new QueryWrapper<UserRole>().eq("user_id", user.getId())).stream().map(UserRole::getRoleId).collect(Collectors.toList());//根据身份id再拿到身份所对应的角色名称roleNameList<String> roleName = roleService.list(new QueryWrapper<Role>().in("role_id", roleIds)).stream().map(Role::getRoleName).collect(Collectors.toList());//然后通过身份id再拿到身份和权限的中间表的idList<Integer> moduleIds = roleModuleService.list(new QueryWrapper<RoleModule>().in("role_id", roleIds)).stream().map(RoleModule::getModuleId).collect(Collectors.toList());//再通过这个中间表的id拿到对应的可访问的模块的urlList<String> moduleUrls = moduleService.list(new QueryWrapper<Module>().in("id", moduleIds)).stream().map(Module::getUrl).filter(Objects::nonNull).collect(Collectors.toList());//把roleName和moduleUrls整合在一起roleName.addAll(moduleUrls);//把拿到的角色的名称以及角色可访问到的url赋值给user中的哪个管理角色权限的字段// roles [管理员,普通用户,book:manager:add,book:manager:list]List<SimpleGrantedAuthority> authorities = roleName.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());user.setAuthorities(authorities);return user;}
}

2.修改SpringSecurity配置类

加一个注解

@EnableGlobalMethodSecurity(prePostEnabled = true)

@EnableGlobalMethodSecurity是Spring Security提供的一个注解,用于启用方法级别的安全性。它可以在任何@Configuration类上使用,以启用Spring Security的方法级别的安全性功能。它接受一个或多个参数,用于指定要使用的安全注解类型和其他选项。以下是一些常用的参数:

  • prePostEnabled:如果设置为true,则启用@PreAuthorize@PostAuthorize注解。默认值为false

  • securedEnabled:如果设置为true,则启用@Secured注解。默认值为false

  • jsr250Enabled:如果设置为true,则启用@RolesAllowed注解。默认值为false

  • proxyTargetClass:如果设置为true,则使用CGLIB代理而不是标准的JDK动态代理。默认值为false

使用@EnableGlobalMethodSecurity注解后,可以在应用程序中使用Spring Security提供的各种注解来保护方法,例如@Secured@PreAuthorize@PostAuthorize@RolesAllowed。这些注解允许您在方法级别上定义安全规则,以控制哪些用户可以访问哪些方法。

注解介绍:

注解说明
@PreAuthorize用于在方法执行之前对访问进行权限验证
@PostAuthorize用于在方法执行之后对返回结果进行权限验证
@Secured用于在方法执行之前对访问进行权限验证
@RolesAllowed是Java标准的注解之一,用于在方法执行之前对访问进行权限验证

3.测试

使用管理员角色登录

使用普通角色登录

如果说没有权限或者是退出登录等其他操作,都会是跳到指定的页面

三.基于前后端分离项目的授权 

关于前面的配置都是和上面所说的一样,这里就不过多阐述了,但是前后端分离的项目就是不能够直接跳转页面了,而是要返回响应的内容。所以我们需要写一个专门的响应类

JsonResponseBody

package com.wh.security.resp;import lombok.Data;@Data
public class JsonResponseBody<T> {private Integer code;private String msg;private T data;private Long total;private JsonResponseBody(JsonResponseStatus jsonResponseStatus, T data) {this.code = jsonResponseStatus.getCode();this.msg = jsonResponseStatus.getMsg();this.data = data;}private JsonResponseBody(JsonResponseStatus jsonResponseStatus, T data, Long total) {this.code = jsonResponseStatus.getCode();this.msg = jsonResponseStatus.getMsg();this.data = data;this.total = total;}public static <T> JsonResponseBody<T> success() {return new JsonResponseBody<T>(JsonResponseStatus.OK, null);}public static <T> JsonResponseBody<T> success(T data) {return new JsonResponseBody<T>(JsonResponseStatus.OK, data);}public static <T> JsonResponseBody<T> success(T data, Long total) {return new JsonResponseBody<T>(JsonResponseStatus.OK, data, total);}public static <T> JsonResponseBody<T> unknown() {return new JsonResponseBody<T>(JsonResponseStatus.UN_KNOWN, null);}public static <T> JsonResponseBody<T> other(JsonResponseStatus jsonResponseStatus) {return new JsonResponseBody<T>(jsonResponseStatus, null);}}

JsonResponseStatus

package com.wh.security.resp;import lombok.Getter;@Getter
public enum JsonResponseStatus {OK(200, "OK"),UN_KNOWN(500, "未知错误"),RESULT_EMPTY(1000, "查询结果为空"),NO_ACCESS(3001, "没有权限"),NO_LOGIN(4001, "没有登录"),LOGIN_FAILURE(5001, "登录失败"),;private final Integer code;private final String msg;JsonResponseStatus(Integer code, String msg) {this.code = code;this.msg = msg;}}

并且需要在配置类中将登录成功或者权限认证失败等这些需要跳转页面的操作全部都修改掉

修改SpringSecurity配置类

package com.wh.security.config;import com.fasterxml.jackson.databind.ObjectMapper;
import com.wh.security.resp.JsonResponseBody;
import com.wh.security.resp.JsonResponseStatus;
import com.wh.security.service.impl.UserServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;import javax.annotation.Resource;
import javax.sql.DataSource;@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig {// 自动装配自定义的用户服务实现类@Autowiredprivate MyUserDetailsService myUserDetailsService;// 自动装配数据源@Resourcepublic DataSource dataSource;// 自动装配对象映射器@Autowiredprivate ObjectMapper objectMapper;// 自动装配自定义的登录失败处理器@Autowiredprivate MyAuthenticationFailureHandler myAuthenticationFailureHandler;// 创建密码编码器实例@Beanpublic PasswordEncoder bcryptPasswordEncoder() {return new BCryptPasswordEncoder();}// 创建模拟用户数据的服务实现类// @Autowired// private UserDetailsService userDetailsService;// 获取认证管理器(认证管理器),登录时认证使用(基于数据库方式)@Beanpublic AuthenticationManager authenticationManager() throws Exception {// 创建DaoAuthenticationProvider实例DaoAuthenticationProvider provider=new DaoAuthenticationProvider();// 设置userDetailsService,基于数据库方式进行身份认证provider.setUserDetailsService(myUserDetailsService);// 配置密码编码器provider.setPasswordEncoder(bcryptPasswordEncoder());return new ProviderManager(provider);}// 配置持久化Token方式,注意tokenRepository.setCreateTableOnStartup()配置@Beanpublic PersistentTokenRepository persistentTokenRepository(){JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();tokenRepository.setDataSource(dataSource);// 设置为true要保障数据库该表不存在,不然会报异常哦// 所以第二次打开服务器应用程序的时候得把它设为falsetokenRepository.setCreateTableOnStartup(false);return tokenRepository;}// 配置安全过滤器链(SecurityFilterChain)@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/", "/noAccess", "/toLogin").permitAll()//所有请求全部需要登录.anyRequest().authenticated().and().formLogin().loginPage("/toLogin")//设置处理登录请求的接口.loginProcessingUrl("/userLogin").usernameParameter("username").passwordParameter("password")//登录成功.successHandler((req, resp, auth) -> {Object user = auth.getPrincipal();objectMapper.writeValue(resp.getOutputStream(), JsonResponseBody.success(user));})//登录失败.failureHandler(myAuthenticationFailureHandler).and().exceptionHandling()//权限不足.accessDeniedHandler((req, resp, ex) -> {objectMapper.writeValue(resp.getOutputStream(), JsonResponseBody.other(JsonResponseStatus.NO_ACCESS));})//没有认证.authenticationEntryPoint((req, resp, ex) -> {objectMapper.writeValue(resp.getOutputStream(), JsonResponseBody.other(JsonResponseStatus.NO_LOGIN));}).and().logout().logoutUrl("/logout").logoutSuccessUrl("/");http.csrf().disable(); //禁用CSRF保护return http.build();}
}

这样就不会跳页面了,而是返回出对应的数据

好啦,今天的分享就到这了,希望能够帮到你呢!😊😊   

相关文章:

【Spring Security】打造安全无忧的Web应用--使用篇

&#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于Spring Security的相关操作吧 目录 &#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 一.Spring Security中的授权是…...

体验一下 CodeGPT 插件

体验一下 CodeGPT 插件 0. 背景1. CodeGPT 插件安装2. CodeGPT 插件基本配置3. (可选)CodeGPT 插件预制提示词原始配置(英文)4. CodeGPT 插件预制提示词配置(中文)5. 简单验证一下 0. 背景 看到B站Up主 “wwwzhouhui” 一个关于 CodeGPT 的视频&#xff0c;感觉挺有意思&#…...

深度学习 | 基础卷积神经网络

卷积神经网络是人脸识别、自动驾驶汽车等大多数计算机视觉应用的支柱。可以认为是一种特殊的神经网络架构&#xff0c;其中基本的矩阵乘法运算被卷积运算取代&#xff0c;专门处理具有网格状拓扑结构的数据。 1、全连接层的问题 1.1、全连接层的问题 “全连接层”的特点是每个…...

[字符编码]windwos下使用libiconv转换编码格式(二)

在http://t.csdnimg.cn/PLUuz笔记中实现了常用编码格式转换的功能,但这还是一个demo。因为代码中向libiconv库函数传递的字符串是存放在堆空间中的(我也是从网上找例子测试,是否一定要开辟堆空间存放还有待考证),如果一次性转换的字节数很巨大的话,就会导致内存空间不足,进而引…...

textile 语法

1、文字修饰 修饰行内文字 字体样式textile 语法对应的 XHTML 语法实际显示效果加强*strong*<strong>strong</strong>strong强调_emphasis_<em>emphasis</em>emphasis加粗**bold**<b>bold</b>bold斜体__italics__<i>italics</i…...

【快速开发】使用SvelteKit

自我介绍 做一个简单介绍&#xff0c;酒架年近48 &#xff0c;有20多年IT工作经历&#xff0c;目前在一家500强做企业架构&#xff0e;因为工作需要&#xff0c;另外也因为兴趣涉猎比较广&#xff0c;为了自己学习建立了三个博客&#xff0c;分别是【全球IT瞭望】&#xff0c;【…...

【docker笔记】docker常用命令

1、帮助启动类命令 1.1 启动、重启、查询当前状态、停止 systemctl start docker systemctl stop docker systemctl restart docker systemctl status docker1.2 设置开机启动 systemctl enable docker1.3 查看docker概要信息 docker info1.4 查看docker帮助文档 docker -…...

API 接口怎样设计才安全?

设计安全的API接口是确保应用程序和数据安全的重要方面之一。下面是一些设计安全的API接口的常见实践&#xff1a; 1. 身份验证和授权&#xff1a; 使用适当的身份验证机制&#xff0c;如OAuth、JWT或基本身份验证&#xff0c;以确保只有经过身份验证的用户可以访问API。实施…...

网站被CC攻击了怎么办?CC攻击有什么危害

网络爆炸性地发展&#xff0c;网络环境也日益复杂和开放&#xff0c;同时各种各样的恶意威胁和攻击日益增多&#xff0c;其中网站被CC也是常见的情况。 CC攻击有什么危害呢&#xff1f; 被CC会导致&#xff1a; 1.访问速度变慢&#xff1a;网站遭受CC攻击后&#xff0c;由于…...

Docker - 镜像 | 容器 日常开发常用指令 + 演示(一文通关)

目录 Docker 开发常用指令汇总 辅助命令 docker version docker info docker --help 镜像命令 查看镜像信息 下载镜像 搜索镜像 删除镜像 容器命令 查看运行中的容器 运行容器 停止、启动、重启、暂停、恢复容器 杀死容器 删除容器 查看容器日志 进入容器内部…...

要参加微软官方 Copilot 智能编程训练营了

GitHub Copilot 是由 GitHub、OpenAI 和 Microsoft 联合开发的生成式 AI 模型驱动的。 GitHub Copilot 分析用户正在编辑的文件及相关文件的上下文&#xff0c;并在编写代码时提供自动补全式的建议。 刚好下周要参加微软官方组织的 GitHub Copilot 工作坊-智能编程训练营&…...

Python入门学习篇(五)——列表字典

1 列表 1.1 定义 ①有序可重复的元素集合 ②可以存放不同类型的数据 ③个人理解:类似于java中的数组1.2 相关方法 1.2.1 获取列表长度 a 语法 len(列表名)b 示例代码 list2 [1, 2, "hello", 4] print(len(list2))c 运行结果 1.2.2 获取列表值 a 语法 列表名…...

React尝鲜

组件 React的组件就是一个js函数&#xff0c;函数内部return一个由jsx语法创建的html代码片段。 //MyComp.js export default function MyComp(){return (<h1>我是新组件MyComp</h1>) } 在需要引入组件的地方import导入组件&#xff0c;并放在相应位置 //App.js…...

锯齿云服务器租赁使用教程

首先登陆锯齿云账号 网盘上传数据集与代码 随后我们需要做的是将所需要的数据集与代码上传到网盘&#xff08;也可以直接在租用服务器后将数据集与代码传到服务器的硬盘上&#xff0c;但这样做会消耗大量时间&#xff0c;造成资源浪费&#xff09; 点击工作空间&#xff1a;…...

HarmonyOS和OpenHarmony的区别

1.概要 众所周知&#xff0c;鸿蒙是华为开发的一款分布式操作系统。因为开发系统&#xff0c;最重要的是集思广益&#xff0c;大家共同维护。为了在IOS和Android之间生存&#xff0c;鸿蒙的茁壮成长一定是需要开源&#xff0c;各方助力才能实现。   在这种思想上&#xff0c;…...

Redis Stream消息队列之基本语法与使用方式

前言 本文的主角是Redis Stream&#xff0c;它是Redis5.0版本新增加的数据结构&#xff0c;主要用于消息队列&#xff0c;提供了消息的持久化和主备复制功能&#xff0c;可以让任何客户端访问任何时刻的数据&#xff0c;并且能记住每一个客户端的访问位置&#xff0c;还能保证…...

制造行业定制软件解决方案——工业信息采集平台

摘要&#xff1a;针对目前企业在线检测数据信号种类繁多&#xff0c;缺乏统一监控人员和及时处置措施等问题。蓝鹏测控开发针对企业工业生产的在线数据的集中采集分析平台&#xff0c;通过该工业信息采集平台可将企业日常各种仪表设备能够得到数据进行集中分析处理存储&#xf…...

[python]用python实现对arxml文件的操作

目录 关键词平台说明一、背景二、方法2.1 库2.2 code 关键词 python、excel、DBC、openpyxl 平台说明 项目Valuepython版本3.6 一、背景 有时候需要批量处理arxml文件(ARXML 文件符合 AUTOSAR 4.0 标准)&#xff0c;但是工作量太大&#xff0c;阔以考虑用python。 二、方…...

pdf 在线编辑

https://smallpdf.com/edit-pdf#rapp 参考 https://zh.wikihow.com/%E5%B0%86%E5%9B%BE%E5%83%8F%E6%8F%92%E5%85%A5PDF...

自然语言处理(NLP):理解语言,赋能未来

目录 前言1 什么是NLP2 NLP的用途3 发展历史4 NLP的基本任务4.1 词性标注&#xff08;Part-of-Speech Tagging&#xff09;4.2 命名实体识别&#xff08;Named Entity Recognition&#xff09;4.3 共指消解&#xff08;Co-reference Resolution&#xff09;4.4 依存关系分析&am…...

FastAPI使用loguru时,出现重复日志打印的解决方案

首先看图&#xff0c;发现每个日志都被打印了3条。其实这个和uvicorn日志打印的设计有关&#xff0c;在uvicorn中有多个logger&#xff0c;分别是uvicorn、uvicorn.error、uvicorn.access 而LOGGING默认有一个属性propagate&#xff0c;这个属性为True时&#xff0c;子日志记录…...

构建每个聚类的profile和deletion_mean特征

通过summarize_clusters函数构建每个聚类的protein[cluster_profile]和protein[cluster_deletion_mean]特征。目的是把extra_msa信息反映到msa中。 集成函数数据处理流程&#xff1a; sample_msa ->make_masked_msa -> nearest_neighbor_clusters -> summarize_clu…...

Milvus数据一致性介绍及选择方法

1、Milvus 时钟机制 Milvus 通过时间戳水印来保障读链路的一致性&#xff0c;如下图所示&#xff0c;在往消息队列插入数据时&#xff0c; Milvus 不光会为这些插入记录打上时间戳&#xff0c;还会不间断地插入同步时间戳&#xff0c;以图中同步时间戳 syncTs1 为例&#xff0…...

异常处理和单元测试python

一、实验题目 异常处理和单元测试 二、实验目的 了解异常的基本概念和常用异常类。掌握异常处理的格式、处理方法。掌握断言语句的作用和使用方法。了解单元测试的基本概念和作用。掌握在Python中使用测试模块进行单元测试的方法和步骤。 三、实验内容 编程实现如下功能&a…...

蓝牙物联网在汽车领域的应用

I、蓝牙的技术特点 ​ 1998 年 5 月&#xff0c;瑞典爱立信、芬兰诺基亚、日本东芝、美国IBM 和英特尔公司五家著名厂商&#xff0c;在联合拓展短离线通信技术的标准化活动时提出了蓝牙技术的概念。蓝牙工作在无需许可的 2.4GHz 工业频段 (SIM)之上(我国的频段范围为2400.0~248…...

用23种设计模式打造一个cocos creator的游戏框架----(二十二)原型模式

1、模式标准 模式名称&#xff1a;原型模式 模式分类&#xff1a;创建型 模式意图&#xff1a;用原型实例指定创建对象的种类&#xff0c;并且通过复制这些原型创建新的对象 结构图&#xff1a; 适用于&#xff1a; 1、当一个系统应该独立于它的产品创建、构成和表示时 2、…...

paddle 55 使用Paddle Inference部署嵌入nms的PPYoloe模型(端到端fps达到52.63)

Paddle Inference 是飞桨的原生推理库,提供服务器端的高性能推理能力。由于 Paddle Inference 能力直接基于飞桨的训练算子,因此它支持飞桨训练出的所有模型的推理。paddle平台训练出的模型转换为静态图时可以选用Paddle Inference的框架进行推理,博主以前都是将静态图转换为…...

自动化测试工具-Selenium:WebDriver的API/方法使用全解

我们上一篇文章介绍了Selenium的三大组件&#xff0c;其中介绍了WebDriver是最重要的组件。在这里&#xff0c;我们将看到WebDriver常用的API/方法&#xff08;注&#xff1a;这里使用Python语言来进行演示&#xff09;。 1. WebDriver创建 打开VSCode&#xff0c;我们首先引…...

如何通过蓝牙串口启动智能物联网?

1、低功耗蓝牙(BLE)介绍 BLE 技术是一种低成本、短距离、可互操作的鲁棒性无线技术&#xff0c;工作在免许可的 2,4 GHZ 工业、科学、医学(Industrial Scientific Medical&#xff0c;ISM)频段。BLE在设计之初便被定位为一种超低功耗(Ultra Low Power&#xff0c;ULP)无线技术&…...

Linux---基础操作命令

内容导航 类别内容导航机器学习机器学习算法应用场景与评价指标机器学习算法—分类机器学习算法—回归机器学习算法—聚类机器学习算法—异常检测机器学习算法—时间序列数据可视化数据可视化—折线图数据可视化—箱线图数据可视化—柱状图数据可视化—饼图、环形图、雷达图统…...