轻量级 Java 权限认证框架——Sa-Token
文章目录
- Sa-Token 介绍
- SpringBoot 集成 Sa-Token
- Sa-Token 功能
- 登录认证
- 会话查询
- Token 查询
- 权限认证
- 权限校验
- 角色校验
- 注解鉴权
- 注册 Sa-Token 拦截器
- 关闭注解校验
- 路由拦截鉴权
- 注册 Sa-Token 路由拦截器
- [记住我] 模式
- 密码加密
- Sa-Token 集成 Redis
- 方式1、使用 jdk 默认序列化方式
- 方式2、使用 jackson 序列化方式
- 集成 Redis 注意
- 集成 Thymeleaf
- 1、引入依赖
- 2、注册标签方言对象
- 3、使用标签方言
- 登录判断
- 3.2、角色判断
- 权限判断
- 调用 Sa-Token 相关API
- Sa-Token使用问题汇总
- 访问静态资源
Sa-Token 介绍
Sa-Token 是一个轻量级 Java 权限认证框架,主要解决:登录认证、权限认证、单点登录、OAuth2.0、分布式Session会话、微服务网关鉴权 等一系列权限相关问题。
Sa-Token最新开发文档地址:https://sa-token.cc
Sa-Token功能结构图:

SpringBoot 集成 Sa-Token
-
创建 SpringBoot 项目
-
再 Pom.xml 文件中添加 Sa-Token 依赖
<!-- Sa-Token 权限认证,在线文档:https://sa-token.cc --> <dependency><groupId>cn.dev33</groupId><artifactId>sa-token-spring-boot-starter</artifactId><version>1.34.0</version> </dependency>注:如果你使用的
SpringBoot 3.x,只需要将sa-token-spring-boot-starter修改为sa-token-spring-boot3-starter即可。 -
设置配置文件
支持零配置启动项目 ,但同时也可以在
application.yml中增加如下配置。server:# 端口port: 8081############## Sa-Token 配置 (文档: https://sa-token.cc) ############## sa-token: # token名称 (同时也是cookie名称)token-name: satoken# token有效期,单位s 默认30天, -1代表永不过期 timeout: 2592000# token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒activity-timeout: -1# 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录) is-concurrent: true# 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token) is-share: true# token风格token-style: uuid# 是否输出操作日志 is-log: false
Sa-Token 功能
登录认证
-
会话登录
// 会话登录:参数填写要登录的账号id,建议的数据类型:long | int | String, 不可以传入复杂类型,如:User、Admin 等等 StpUtil.login(Object id);StpUtil.login()函数让 Sa-Token 为这个账号创建了一个Token凭证,且通过 Cookie 上下文返回给了前端,除此之外,Sa-Token 在背后做了大量的工作,包括但不限于:- 检查此账号是否之前已有登录
- 为账号生成
Token凭证与Session会话 - 通知全局侦听器,xx 账号登录成功
- 将
Token注入到请求上下文 - 等等其它工作……
在 Cookie 功能的加持下,我们可以仅靠
StpUtil.login(id)一句代码就完成登录认证。- Cookie 可以从后端控制往浏览器中写入 Token 值。
- Cookie 会在前端每次发起请求时自动提交 Token 值。
-
注销登录
// 当前会话注销登录 StpUtil.logout(); -
是否登录
// 获取当前会话是否已经登录,返回true=已登录,false=未登录 StpUtil.isLogin(); -
检查登录
// 检验当前会话是否已经登录, 如果未登录,则抛出异常:`NotLoginException`,代表当前会话暂未登录,可能的原因有很多 StpUtil.checkLogin();
会话查询
// 获取当前会话账号id, 如果未登录,则抛出异常:`NotLoginException`
StpUtil.getLoginId();// 类似查询API还有:
StpUtil.getLoginIdAsString(); // 获取当前会话账号id, 并转化为`String`类型
StpUtil.getLoginIdAsInt(); // 获取当前会话账号id, 并转化为`int`类型
StpUtil.getLoginIdAsLong(); // 获取当前会话账号id, 并转化为`long`类型// ---------- 指定未登录情形下返回的默认值 ----------// 获取当前会话账号id, 如果未登录,则返回null
StpUtil.getLoginIdDefaultNull();// 获取当前会话账号id, 如果未登录,则返回默认值 (`defaultValue`可以为任意类型)
StpUtil.getLoginId(T defaultValue);
Token 查询
// 获取当前会话的token值
StpUtil.getTokenValue();// 获取当前`StpLogic`的token名称
StpUtil.getTokenName();// 获取指定token对应的账号id,如果未登录,则返回 null
StpUtil.getLoginIdByToken(String tokenValue);// 获取当前会话剩余有效期(单位:s,返回-1代表永久有效)
StpUtil.getTokenTimeout();// 获取当前会话的token信息参数
StpUtil.getTokenInfo();
权限认证
因为每个项目的需求不同,其权限设计也千变万化, 所以 Sa-Token 将 [ 获取当前账号权限码集合 ] 操作以接口的方式暴露给你,以方便你根据自己的业务逻辑进行重写。
新建一个类,实现 StpInterface 接口,例如以下代码:
/*** 自定义权限验证接口扩展*/
@Component // 保证此类被SpringBoot扫描,完成Sa-Token的自定义权限验证扩展
public class StpInterfaceImpl implements StpInterface {/*** 返回一个账号所拥有的权限码集合 */@Overridepublic List<String> getPermissionList(Object loginId, String loginType) {// 本list仅做模拟,实际项目中要根据具体业务逻辑来查询权限List<String> list = new ArrayList<String>(); list.add("user.add");list.add("user.update");list.add("user.get");// list.add("user.delete");list.add("art.*");return list;}/*** 返回一个账号所拥有的角色标识集合 (权限与角色可分开校验)*/@Overridepublic List<String> getRoleList(Object loginId, String loginType) {// 本list仅做模拟,实际项目中要根据具体业务逻辑来查询角色List<String> list = new ArrayList<String>(); list.add("admin");list.add("super-admin");return list;}}
参数解释:
- loginId:账号id,即你在调用
StpUtil.login(id)时写入的标识值。 - loginType:账号体系标识,此处可以暂时忽略,在 [ 多账户认证 ] 章节下会对这个概念做详细的解释。
权限校验
// 获取:当前账号所拥有的权限集合
StpUtil.getPermissionList();// 判断:当前账号是否含有指定权限, 返回 true 或 false
StpUtil.hasPermission("user.add"); // 校验:当前账号是否含有指定权限, 如果验证未通过,则抛出异常: NotPermissionException
StpUtil.checkPermission("user.add"); // 校验:当前账号是否含有指定权限 [指定多个,必须全部验证通过]
StpUtil.checkPermissionAnd("user.add", "user.delete", "user.get"); // 校验:当前账号是否含有指定权限 [指定多个,只要其一验证通过即可]
StpUtil.checkPermissionOr("user.add", "user.delete", "user.get");
角色校验
// 获取:当前账号所拥有的角色集合
StpUtil.getRoleList();// 判断:当前账号是否拥有指定角色, 返回 true 或 false
StpUtil.hasRole("super-admin"); // 校验:当前账号是否含有指定角色标识, 如果验证未通过,则抛出异常: NotRoleException
StpUtil.checkRole("super-admin"); // 校验:当前账号是否含有指定角色标识 [指定多个,必须全部验证通过]
StpUtil.checkRoleAnd("super-admin", "shop-admin"); // 校验:当前账号是否含有指定角色标识 [指定多个,只要其一验证通过即可]
StpUtil.checkRoleOr("super-admin", "shop-admin");
权限通配符(“*”):Sa-Token允许你根据通配符指定泛权限,例如当一个账号拥有
art.*的权限时,art.add、art.delete、art.update都将匹配通过
注解鉴权
@SaCheckLogin: 登录校验 —— 只有登录之后才能进入该方法。@SaCheckRole("admin"): 角色校验 —— 必须具有指定角色标识才能进入该方法。@SaCheckPermission("user:add"): 权限校验 —— 必须具有指定权限才能进入该方法。@SaCheckSafe: 二级认证校验 —— 必须二级认证之后才能进入该方法。@SaCheckBasic: HttpBasic校验 —— 只有通过 Basic 认证后才能进入该方法。@SaIgnore:忽略校验 —— 表示被修饰的方法或类无需进行注解鉴权和路由拦截器鉴权。@SaCheckDisable("comment"):账号服务封禁校验 —— 校验当前账号指定服务是否被封禁。
Sa-Token 使用全局拦截器完成注解鉴权功能,为了不为项目带来不必要的性能负担,拦截器默认处于关闭状态
因此,为了使用注解鉴权,你必须手动将 Sa-Token 的全局拦截器注册到你项目中
注册 Sa-Token 拦截器
以SpringBoot2.0为例,新建配置类SaTokenConfigure.java
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {// 注册 Sa-Token 拦截器,打开注解式鉴权功能 @Overridepublic void addInterceptors(InterceptorRegistry registry) {// 注册 Sa-Token 拦截器,打开注解式鉴权功能 registry.addInterceptor(new SaInterceptor()).addPathPatterns("/**"); }
}
保证此类被springboot启动类扫描到即可
关闭注解校验
SaInterceptor 只要注册到项目中,默认就会打开注解校验,如果要关闭此能力,需要:
@Override
public void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new SaInterceptor(handle -> {SaRouter.match("/**").check(r -> StpUtil.checkLogin());}).isAnnotation(false) // 指定关闭掉注解鉴权能力,这样框架就只会做路由拦截校验了 ).addPathPatterns("/**");
}
路由拦截鉴权
假设我们有如下需求:
项目中所有接口均需要登录认证,只有 “登录接口” 本身对外开放
那么给每个接口加上鉴权注解?手写全局拦截器?似乎都不是非常方便。
注册 Sa-Token 路由拦截器
以SpringBoot2.0为例, 新建配置类SaTokenConfigure.java
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {// 注册拦截器@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 注册 Sa-Token 拦截器,校验规则为 StpUtil.checkLogin() 登录校验。registry.addInterceptor(new SaInterceptor(handle -> StpUtil.checkLogin())).addPathPatterns("/**").excludePathPatterns("/user/doLogin"); }
}
以上代码,我们注册了一个基于 StpUtil.checkLogin() 的登录校验拦截器,并且排除了/user/doLogin接口用来开放登录(除了/user/doLogin以外的所有接口都需要登录才能访问)。
[记住我] 模式
Sa-Token的登录授权,默认就是[记住我]模式,为了实现[非记住我]模式,你需要在登录时如下设置 StpUtil.login() 函数的第二个参数:
// 设置登录账号id为10001,第二个参数指定是否为[记住我],当此值为false后,关闭浏览器后再次打开需要重新登录
StpUtil.login(10001, false);
- 勾选 [记住我] 按钮时:调用
StpUtil.login(10001, true),此时用户即使重启浏览器 Token 依然有效。 - 不勾选 [记住我] 按钮时:调用
StpUtil.login(10001, false),此时用户在重启浏览器后 Token 便会消失,导致会话失效。
密码加密
Sa-Token 支持摘要加密、对称加密、非对称加密、Base64加密等方式。
// md5加密
SaSecureUtil.md5("123456");// sha1加密
SaSecureUtil.sha1("123456");// sha256加密
SaSecureUtil.sha256("123456");
Sa-Token 集成 Redis
方式1、使用 jdk 默认序列化方式
<!-- Sa-Token 整合 Redis (使用 jdk 默认序列化方式) -->
<dependency><groupId>cn.dev33</groupId><artifactId>sa-token-dao-redis</artifactId><version>1.34.0</version>
</dependency>
方式2、使用 jackson 序列化方式
<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
<dependency><groupId>cn.dev33</groupId><artifactId>sa-token-dao-redis-jackson</artifactId><version>1.34.0</version>
</dependency>
集成 Redis 注意
1. 无论使用哪种序列化方式,你都必须为项目提供一个 Redis 实例化方案,例如:
<!-- 提供Redis连接池 -->
<dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId>
</dependency>
2. 引入了依赖,我还需要为 Redis 配置连接信息吗?
需要!只有项目初始化了正确的 Redis 实例,Sa-Token才可以使用 Redis 进行数据持久化,参考以下yml配置:
spring: # redis配置 redis:# Redis数据库索引(默认为0)database: 1# Redis服务器地址host: 127.0.0.1# Redis服务器连接端口port: 6379# Redis服务器连接密码(默认为空)# password: # 连接超时时间timeout: 10slettuce:pool:# 连接池最大连接数max-active: 200# 连接池最大阻塞等待时间(使用负值表示没有限制)max-wait: -1ms# 连接池中的最大空闲连接max-idle: 10# 连接池中的最小空闲连接min-idle: 0
3. 集成 Redis 后,是我额外手动保存数据,还是框架自动保存?
框架自动保存。集成 Redis 只需要引入对应的 pom依赖 即可,框架所有上层 API 保持不变。
4. 集成包版本问题
Sa-Token-Redis 集成包的版本尽量与 Sa-Token-Starter 集成包的版本一致,否则可能出现兼容性问题。
集成 Thymeleaf
可以在 Thymeleaf 页面中使用 Sa-Token 相关API,俗称 —— 标签方言。
1、引入依赖
首先我们确保项目已经引入 Thymeleaf 依赖,然后在此基础上继续添加:
<!-- 在 thymeleaf 标签中使用 Sa-Token -->
<dependency><groupId>cn.dev33</groupId><artifactId>sa-token-dialect-thymeleaf</artifactId><version>1.34.0</version>
</dependency>
2、注册标签方言对象
在 SaTokenConfigure 配置类中注册 Bean
@Configuration
public class SaTokenConfigure {// Sa-Token 标签方言 (Thymeleaf版)@Beanpublic SaTokenDialect getSaTokenDialect() {return new SaTokenDialect();}
}
3、使用标签方言
然后我们就可以愉快的使用在 Thymeleaf 页面中使用标签方言了
登录判断
<h2>标签方言测试页面</h2>
<p>登录之后才能显示:<span sa:login>value</span>
</p>
<p>不登录才能显示:<span sa:notLogin>value</span>
</p>
3.2、角色判断
<p>具有角色 admin 才能显示:<span sa:hasRole="admin">value</span>
</p>
<p>同时具备多个角色才能显示:<span sa:hasRoleAnd="admin, ceo, cto">value</span>
</p>
<p>只要具有其中一个角色就能显示:<span sa:hasRoleOr="admin, ceo, cto">value</span>
</p>
<p>不具有角色 admin 才能显示:<span sa:lackRole="admin">value</span>
</p>
权限判断
<p>具有权限 user-add 才能显示:<span sa:hasPermission="user-add">value</span>
</p>
<p>同时具备多个权限才能显示:<span sa:hasPermissionAnd="user-add, user-delete, user-get">value</span>
</p>
<p>只要具有其中一个权限就能显示:<span sa:hasPermissionOr="user-add, user-delete, user-get">value</span>
</p>
<p>不具有权限 user-add 才能显示:<span sa:lackPermission="user-add">value</span>
</p>
调用 Sa-Token 相关API
以上的标签方言,可以满足我们大多数场景下的权限判断,然后有时候我们依然需要更加灵活的在页面中调用 Sa-Token 框架API
首先在 SaTokenConfigure 配置类中为 Thymeleaf 配置全局对象:
@Configuration
public class SaTokenConfigure {@Autowiredprivate void configureThymeleafStaticVars(ThymeleafViewResolver viewResolver) {viewResolver.addStaticVariable("stp", StpUtil.stpLogic);}}
然后就可以在页面上调用 StpLogic 的 API 了,例如:
<p>调用 StpLogic 方法调用测试</p>
<p th:if="${stp.isLogin()}">从SaSession中取值:<span th:text="${stp.getSession().get('name')}"></span>
</p>
Sa-Token使用问题汇总
访问静态资源
项目引入Sa-Token后,需要对访问的静态资源路径放行,否则就会被拦截,那么需要在用户自创建的WebMvcConfigurer实现类中,配置本地资源映射路径,并添加Sa-Token拦截器的路由拦截规则,对访问静态资源的路径进行放行。
//配置本地资源映射路径 @Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");}// 注册Sa-token拦截器// 注册拦截器@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 注册 Sa-Token 拦截器,定义详细认证规则registry.addInterceptor(new SaInterceptor(handler -> {// 指定一条 match 规则SaRouter.match("/**") // 拦截.notMatch("/static/**") // 放行 /static/** 路径.check(r -> StpUtil.checkLogin()); // 执行校验登录})).addPathPatterns("/**");}
相关文章:
轻量级 Java 权限认证框架——Sa-Token
文章目录Sa-Token 介绍SpringBoot 集成 Sa-TokenSa-Token 功能登录认证会话查询Token 查询权限认证权限校验角色校验注解鉴权注册 Sa-Token 拦截器关闭注解校验路由拦截鉴权注册 Sa-Token 路由拦截器[记住我] 模式密码加密Sa-Token 集成 Redis方式1、使用 jdk 默认序列化方式方…...
算法复习(四、五、六)
动态规划 动态规划算法的有效性依赖于问题本身所具有的两个重要性质:最优子结构、重叠子问题 关于动态规划算法和备忘录方法的适用条件: 要求: 用分治法和动态规划法分别解决最大子段和问题(第四步求最优解不需要掌握ÿ…...
SORT与DeepSORT简介
一、MOT( mutil-object tracking)步骤 在《DEEP LEARNING IN VIDEO MUTIL-OBJECT TEACKING: A SURVEY》这篇基于深度学习多目标跟踪综述中,描绘了MOT问题的四个主要步骤 1.跟定视频原始帧 2.使用目标检测器如Faster-rcnn, YOLO, SSD等进行检测,获取目标…...
TCP/IP网络编程——多播与广播
完整版文章请参考: TCP/IP网络编程完整版文章 文章目录第 14 章 多播与广播14.1 多播14.1.1 多播的数据传输方式以及流量方面的优点14.1.2 路由(Routing)和 TTL(Time to Live,生存时间),以及加入组的办法14…...
K8S DNS解析过程和延迟问题
一、Linux DNS查询解析原理(对于调用glibc库函数gethostbyname的程序)我们在浏览器访问www.baidu.com这个域名,dns怎么查询到这台主机呢? 1、在浏览器中输入www.baidu.com域名,操作系统会先查找本地DNS解析器缓存&a…...
【JavaScript】js实现深拷贝的方法
前言 在js中我们想要实现深拷贝,首先要了解深浅拷贝的区别。 浅拷贝:只是拷贝数据的内存地址,而不是在内存中重新创建一个一模一样的对象(数组) 深拷贝:在内存中开辟一个新的存储空间,完完全全…...
RK3288 GPIO记录
1、引脚对应的GPIO 编号第一种 使用/sys/kernel/debug/gpio查询所有gpio引脚的基数第二种 cat /sys/class/gpio/gpiochip248/label对应的label就是GPIO引脚,例如下图GPIO8对应的基数就是2482、计算编号编号 基数 PIN脚如GPIO8的基数是248, GPIO8_A6的编…...
MongoDB介绍及使用教程
文章目录一、MongoDB介绍1. 什么是MongoDB2. 为什么要用MongoDB3. MongoDB的应用场景4. MongoDB基本概念二、MongoDB使用教程1.下载安装(Windows)2.MongoDB Conpass简单使用(选学)3.使用navicat连接MongoDB4.JAVA项目中使用MongoD…...
51单片机开发环境搭建 - VS Code 从编写到烧录
我安装并测试成功的环境: 操作系统:Windows 10 (22H2)单片机:STC89C52RCPython version: 3.7.6 在这之前,给51单片机写程序是用 Keil 5(编写编译)、STC-ISP(烧录),由于…...
python datetime、字符串和时间戳之间的相互转换12小时制和24小时制时间相互转化
文章目录1.字符串转datetime格式2.datetime转字符串3.时间戳转datetime格式4.datetime格式转时间戳5.应用:将12小时制的字符串转换为时间戳1.字符串转datetime格式 把字符串转换为datetime的格式 项目字符串的样子‘%m/%d/%Y %H:%M:%S’2/3/2023 15:30:20‘%m-%d-…...
百度百科词条怎么做?百度百科词条创建攻略分享
只要是想要将自己宣传出去的企业或是个人,都建议创建属于自己的百度百科词条,因为百度百科词条流量大、权重高、排名靠前,创建百度百科词条可以提高企业或是个人的知名度和口碑。 百度百科词条怎么做?每天都有用户在百度上搜索这…...
基于Hive的河北新冠确诊人数分析系统的设计与实现
项目描述 临近学期结束,还是毕业设计,你还在做java程序网络编程,期末作业,老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据疫情当下,你想解决的问…...
k8s二进制部署
目录 一、环境准备 常见的k8s部署方式 关闭防火墙 关闭selinux 关闭swap 根据规划设置主机名 在master添加hosts 将桥接的IPv4流量传递到iptables的链 时间同步 二、部署etcd集群 1、master节点部署 #查看证书的信息 上传etcd-cert.sh 和etcd.sh 到/opt/k8s/ 目录…...
Windows出现0xc00d36e5错误怎么办?
当我们使用Windows Media Player来播放视频文件时,可能会出现无法播放,并显示0xc00d36e5错误代码。该错误可能是因为Windows Media Player不支持视频格式、注册表项损坏、系统配置问题、第三方应用程序冲突等。下面将开始介绍0xc00d36e5错误的解决方法&a…...
Idea搭建Spring5.3.x源码阅读环境
1. 概述 Spring是一个轻量级Java开源框架,在Java项目开发过程中已经离不开Spring全家桶了,包括Spring、SpringBoot、SpringCloud等,学习好Spring基础源码也有助于更好在项目中使用Spring相关组件,在学习源码前需要搭建好源码学习…...
2.20jdbc
一.数据库编程的必备条件编程语言:java c c Python数据库 Oracle,MySQL,SQL Server数据库驱动包:不同的数据库,对应不同的编程语言提供了不同的数据库驱动包:MySQL提供了Java的驱动包mysqlconnector-java,需要就Java操作MySQL需要该驱动包二.Java的数据库编程JDBC,即…...
【代码随想录训练营】【Day19休息】【Day20】第六章|二叉树|654.最大二叉树|617.合并二叉树|700.二叉搜索树中的搜索|98.验证二叉搜索树
最大二叉树 题目详细:LeetCode.654 这道题在题目几乎就说明了解题的思路了: 创建一个根节点,其值为 nums 中的最大值;递归地在最大值左边的子数组上构建左子树;递归地在最大值右边的子数组上构建右子树;…...
华为云计算之容灾技术
容灾是物理上的容错技术,不是逻辑上的容错同步远程复制:主备距离≤200km,只有在主备设备上都写成功,才会告诉主机写成功,不会丢失数据异步远程复制:主备距离>200km,只要主设备上写成…...
React系列之Redux
1 Redux概述 Redux 是 JavaScript 状态容器,提供可预测化的状态管理。Redux中文文档 Redux 和react没有必然关系,redux可以应用于各种框架,包括jquery,甚至js都可以使用redux,只不过redux和react更加搭配。redux也推…...
最简单得方法解决TCP分包粘包问题
如何用最简单的方法解决TCP传输中的分包粘包问题? 首先需要说明一点,分包粘包等等一系列的问题并不是协议本身存在的问题,而是程序员在写代码的时候,没有搞清楚数据的边界导致的。 看个简单的例子,TCP客户端不断的向服…...
【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...
visual studio 2022更改主题为深色
visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中,选择 环境 -> 常规 ,将其中的颜色主题改成深色 点击确定,更改完成...
C++ 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
3403. 从盒子中找出字典序最大的字符串 I
3403. 从盒子中找出字典序最大的字符串 I 题目链接:3403. 从盒子中找出字典序最大的字符串 I 代码如下: class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...
ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...
全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...
蓝桥杯3498 01串的熵
问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798, 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...
Netty从入门到进阶(二)
二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架,用于…...
为什么要创建 Vue 实例
核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...
给网站添加live2d看板娘
给网站添加live2d看板娘 参考文献: stevenjoezhang/live2d-widget: 把萌萌哒的看板娘抱回家 (ノ≧∇≦)ノ | Live2D widget for web platformEikanya/Live2d-model: Live2d model collectionzenghongtu/live2d-model-assets 前言 网站环境如下,文章也主…...
