JWT整合Gateway实现鉴权(RSA与公私密钥工具类)
一.业务流程

1.使用RSA生成公钥和私钥。私钥保存在授权中心,公钥保存在网关(gateway)和各个信任微服务中。
2.用户请求登录。
3.授权中心进行校验,通过后使用私钥对JWT进行签名加密。并将JWT返回给用户
4.用户携带JWT访问
5.gateway直接通过公钥解密JWT进行验证
二.RSA测试Demo
JWT包含三部分数据
header头部分-Payload载荷(包含用户信息身份)-Signature签名
一.工具类
用户基本信息
package entity;/*** 载荷对象*/
public class UserInfo {private Long id;private String username;public UserInfo() {}public UserInfo(Long id, String username) {this.id = id;this.username = username;}public Long getId() {return this.id;}public void setId(Long id) {this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}
}
公钥私钥生成读取类
package utils;import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;public class RsaUtils {/*** 从文件中读取公钥** @param filename 公钥保存路径,相对于classpath* @return 公钥对象* @throws Exception*/public static PublicKey getPublicKey(String filename) throws Exception {byte[] bytes = readFile(filename);return getPublicKey(bytes);}/*** 从文件中读取密钥** @param filename 私钥保存路径,相对于classpath* @return 私钥对象* @throws Exception*/public static PrivateKey getPrivateKey(String filename) throws Exception {byte[] bytes = readFile(filename);return getPrivateKey(bytes);}/*** 获取公钥** @param bytes 公钥的字节形式* @return* @throws Exception*/public static PublicKey getPublicKey(byte[] bytes) throws Exception {X509EncodedKeySpec spec = new X509EncodedKeySpec(bytes);KeyFactory factory = KeyFactory.getInstance("RSA");return factory.generatePublic(spec);}/*** 获取密钥** @param bytes 私钥的字节形式* @return* @throws Exception*/public static PrivateKey getPrivateKey(byte[] bytes) throws Exception {PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);KeyFactory factory = KeyFactory.getInstance("RSA");return factory.generatePrivate(spec);}/*** 根据密文,生存rsa公钥和私钥,并写入指定文件** @param publicKeyFilename 公钥文件路径* @param privateKeyFilename 私钥文件路径* @param secret 生成密钥的密文* @throws IOException* @throws NoSuchAlgorithmException*/public static void generateKey(String publicKeyFilename, String privateKeyFilename, String secret) throws Exception {KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");SecureRandom secureRandom = new SecureRandom(secret.getBytes());keyPairGenerator.initialize(1024, secureRandom);KeyPair keyPair = keyPairGenerator.genKeyPair();// 获取公钥并写出byte[] publicKeyBytes = keyPair.getPublic().getEncoded();writeFile(publicKeyFilename, publicKeyBytes);// 获取私钥并写出byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();writeFile(privateKeyFilename, privateKeyBytes);}private static byte[] readFile(String fileName) throws Exception {return Files.readAllBytes(new File(fileName).toPath());}private static void writeFile(String destPath, byte[] bytes) throws IOException {File dest = new File(destPath);if (!dest.exists()) {dest.createNewFile();}Files.write(dest.toPath(), bytes);}
}
公钥私钥加解密类
package utils;import entity.UserInfo;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.joda.time.DateTime;import java.security.PrivateKey;
import java.security.PublicKey;public class JwtUtils {/*** 私钥加密token** @param userInfo 载荷中的数据* @param privateKey 私钥* @param expireMinutes 过期时间,单位秒* @return* @throws Exception*/public static String generateToken(UserInfo userInfo, PrivateKey privateKey, int expireMinutes) throws Exception {return Jwts.builder().claim(JwtConstans.JWT_KEY_ID, userInfo.getId()).claim(JwtConstans.JWT_KEY_USER_NAME, userInfo.getUsername()).setExpiration(DateTime.now().plusMinutes(expireMinutes).toDate()).signWith(SignatureAlgorithm.RS256, privateKey).compact();}/*** 私钥加密token** @param userInfo 载荷中的数据* @param privateKey 私钥字节数组* @param expireMinutes 过期时间,单位秒* @return* @throws Exception*/public static String generateToken(UserInfo userInfo, byte[] privateKey, int expireMinutes) throws Exception {return Jwts.builder().claim(JwtConstans.JWT_KEY_ID, userInfo.getId()).claim(JwtConstans.JWT_KEY_USER_NAME, userInfo.getUsername()).setExpiration(DateTime.now().plusMinutes(expireMinutes).toDate()).signWith(SignatureAlgorithm.RS256, RsaUtils.getPrivateKey(privateKey)).compact();}/*** 公钥解析token** @param token 用户请求中的token* @param publicKey 公钥* @return* @throws Exception*/private static Jws<Claims> parserToken(String token, PublicKey publicKey) {return Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token);}/*** 公钥解析token** @param token 用户请求中的token* @param publicKey 公钥字节数组* @return* @throws Exception*/private static Jws<Claims> parserToken(String token, byte[] publicKey) throws Exception {return Jwts.parser().setSigningKey(RsaUtils.getPublicKey(publicKey)).parseClaimsJws(token);}/*** 获取token中的用户信息** @param token 用户请求中的令牌* @param publicKey 公钥* @return 用户信息* @throws Exception*/public static UserInfo getInfoFromToken(String token, PublicKey publicKey) throws Exception {Jws<Claims> claimsJws = parserToken(token, publicKey);Claims body = claimsJws.getBody();return new UserInfo(ObjectUtils.toLong(body.get(JwtConstans.JWT_KEY_ID)),ObjectUtils.toString(body.get(JwtConstans.JWT_KEY_USER_NAME)));}/*** 获取token中的用户信息** @param token 用户请求中的令牌* @param publicKey 公钥* @return 用户信息* @throws Exception*/public static UserInfo getInfoFromToken(String token, byte[] publicKey) throws Exception {Jws<Claims> claimsJws = parserToken(token, publicKey);Claims body = claimsJws.getBody();return new UserInfo(ObjectUtils.toLong(body.get(JwtConstans.JWT_KEY_ID)),ObjectUtils.toString(body.get(JwtConstans.JWT_KEY_USER_NAME)));}
}
辅助类:
package utils;import org.apache.commons.lang3.StringUtils;/*** 从jwt解析得到的数据是Object类型,转换为具体类型可能出现空指针,* 这个工具类进行了一些转换*/
public class ObjectUtils {public static String toString(Object obj) {if (obj == null) {return null;}return obj.toString();}public static Long toLong(Object obj) {if (obj == null) {return 0L;}if (obj instanceof Double || obj instanceof Float) {return Long.valueOf(StringUtils.substringBefore(obj.toString(), "."));}if (obj instanceof Number) {return Long.valueOf(obj.toString());}if (obj instanceof String) {return Long.valueOf(obj.toString());} else {return 0L;}}public static Integer toInt(Object obj) {return toLong(obj).intValue();}
}
package utils;public abstract class JwtConstans {public static final String JWT_KEY_ID = "id";public static final String JWT_KEY_USER_NAME = "username";
}
二.测试代码
import entity.UserInfo;
import org.junit.Before;
import org.junit.Test;
import utils.JwtUtils;
import utils.RsaUtils;import java.security.PrivateKey;
import java.security.PublicKey;public class JwtTest {private static final String pubKeyPath = "D:\\tmp\\rsa\\rsa.pub";private static final String priKeyPath = "D:\\tmp\\rsa\\rsa.pri";private PublicKey publicKey;private PrivateKey privateKey;@Testpublic void testRsa() throws Exception {RsaUtils.generateKey(pubKeyPath, priKeyPath, "234");}//在运行生成token之前,获取公钥和私钥对象@Beforepublic void testGetRsa() throws Exception {this.publicKey = RsaUtils.getPublicKey(pubKeyPath);this.privateKey = RsaUtils.getPrivateKey(priKeyPath);}//通过私钥加密@Testpublic void testGenerateToken() throws Exception {// 生成tokenString token = JwtUtils.generateToken(new UserInfo(20L, "jack"), privateKey, 5);System.out.println("token = " + token);}//通过公钥解析token@Testpublic void testParseToken() throws Exception {String token = "eyJhbGciOiJSUzI1NiJ9.eyJpZCI6MjsInVzZXJuYW1lIjoiamFjayIsImV4cCI6MTY3MDAzOTI0M30.Y6MstaAuWwgsNenMRYQSBeG-zx9kHmh75PJuGrJuyPPuetebwqS6Xl2NQGmMYx1mQII-Tq6M-vGGMvQJ8q2Dd8GXL1u-RMC9-e7SKkAgVFP0YzwY3YJRgw9q62snWZqZllmNIgp_jFu14HHHktCS49V-bd1HR9W2PMQoWOeWquI";// 解析tokenUserInfo user = JwtUtils.getInfoFromToken(token, publicKey);System.out.println("id: " + user.getId());System.out.println("userName: " + user.getUsername());}
}
三.测试流程

指定路径一定包括rsa


三.Spring Cloud+Gateway+RSA
一.gateway模块:
yml文件
定义白名单(不需要鉴权的路径),公钥地址(解密鉴权),以及cookieName(获取加密的token)

配置类:
获取公钥
package com.yigou.gw.config;import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import utils.RsaUtils;import javax.annotation.PostConstruct;
import java.security.PublicKey;@ConfigurationProperties(prefix = "yigou.jwt")
@Data
@Slf4j
public class JwtProperties {private String pubKeyPath;// 公钥private PublicKey publicKey; // 公钥private String cookieName;//@PostConstruct注解的方法将会在依赖注入完成后被自动调用。//执行顺序:Constructor >> @Autowired >> @PostConstruct@PostConstructpublic void init(){try {// 获取公钥和私钥this.publicKey = RsaUtils.getPublicKey(pubKeyPath);} catch (Exception e) {log.error("初始化公钥失败!", e);throw new RuntimeException();}}
}
获取定义的白名单对象
package com.yigou.gw.config;import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;import java.util.List;@ConfigurationProperties(prefix = "yigou.filter")
@Data
public class FilterProperties {private List<String> allowPaths;
}
自定义拦截:
package com.yigou.gw.filter;import com.yigou.gw.config.FilterProperties;
import com.yigou.gw.config.JwtProperties;
import entity.UserInfo;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.filter.OrderedFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import utils.JwtUtils;import java.util.List;@Component
@EnableConfigurationProperties({JwtProperties.class, FilterProperties.class})
@Slf4j
public class LoginFilter implements GlobalFilter, Ordered {@Autowiredprivate JwtProperties prop;@Autowiredprivate FilterProperties fprop;//核心过滤器方法@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {//设置白名单,白名单里面的请求路径,直接放行String path = exchange.getRequest().getPath().toString();List<String> allowPaths = fprop.getAllowPaths();for (String allowPath : allowPaths) {//判断path是否以allowPath开头if (path.startsWith(allowPath)){//放行return chain.filter(exchange);}}//1.获取请求中的tokenHttpCookie cookie = exchange.getRequest().getCookies().getFirst(prop.getCookieName());String token=null;if (cookie!=null){token=cookie.getValue();}log.info("token:",token);try {//2.解析tokenUserInfo userInfo = JwtUtils.getInfoFromToken(token, prop.getPublicKey());//3.放行if (userInfo!=null){return chain.filter(exchange);}} catch (Exception e) {e.printStackTrace();//如果出现异常,设置异常状态exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);}return exchange.getResponse().setComplete();}//过滤器执行顺序@Overridepublic int getOrder() {return 0;}
}
功能总结:
1.使用yml配置获取公钥存储路径以及白名单路径。
2.通过路径得到公钥,自定义拦截器先去对比是否为白名单路径,若为白名单路径直接放行。
3.若不为白名单 通过cookieName得到存储的token,并使用公钥解析是否可以获得userinfo对象来判断是否放行。
二.鉴权中心模板:
yml文件:
server:port: 8087
spring:application:name: auth-servicecloud:nacos:discovery:server-addr: http://192.168.147.129:8848yigou:jwt:secret: yigou@Login(Auth}*^31)&javaman% # 登录校验的密钥pubKeyPath: D:\\tmp\\rsa\\rsa.pub # 公钥地址priKeyPath: D:\\tmp\\rsa\\rsa.pri # 私钥地址expire: 1800 # 过期时间,单位秒cookieName: YG_TOKENcookieMaxAge: 1800
配置文件:
package com.yigou.auth.config;import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import utils.RsaUtils;import javax.annotation.PostConstruct;
import java.io.File;
import java.security.PrivateKey;
import java.security.PublicKey;@ConfigurationProperties(prefix = "yigou.jwt")
@Data
@Slf4j
public class JwtProperties {private String secret; // 密钥private String pubKeyPath;// 公钥private String priKeyPath;// 私钥private int expire;// token过期时间private PublicKey publicKey; // 公钥private PrivateKey privateKey; // 私钥private String cookieName;private Integer cookieMaxAge;/*** @PostContruct:在构造方法执行之后执行该方法*/@PostConstructpublic void init(){try {File pubKey = new File(pubKeyPath);File priKey = new File(priKeyPath);if (!pubKey.exists() || !priKey.exists()) {// 生成公钥和私钥RsaUtils.generateKey(pubKeyPath, priKeyPath, secret);}// 获取公钥和私钥this.publicKey = RsaUtils.getPublicKey(pubKeyPath);this.privateKey = RsaUtils.getPrivateKey(priKeyPath);} catch (Exception e) {log.error("初始化公钥和私钥失败!", e);throw new RuntimeException();}}// getter setter ...
}
controller
package com.yigou.auth.controller;import com.yigou.auth.config.JwtProperties;
import com.yigou.auth.service.AuthService;
import com.yigou.common.enums.ExceptionEnum;
import com.yigou.common.exception.YgException;
import com.yigou.common.utils.CookieUtils;
import com.yigou.user.entity.TbUser;
import entity.UserInfo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import utils.JwtUtils;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;@RestController
@EnableConfigurationProperties(JwtProperties.class)
public class AuthController {@Autowiredprivate JwtProperties prop;@Autowiredprivate AuthService authService;//接收用户名和密码,通过密钥加密生成JWT,写入到cookie中保存@PostMapping("/accredit")public ResponseEntity<Void> authentication(@RequestParam("username") String username,@RequestParam("password") String password,HttpServletResponse response,HttpServletRequest request){//1.验证之后,生成JWT-tokenString token=authService.authentication(username,password);//2.如果token是空的,验证失败if(StringUtils.isEmpty(token)){return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();}//3.如果不为空,说明不为空,返回token存放到cookie中CookieUtils.setCookie(request,response,prop.getCookieName(),token,prop.getCookieMaxAge(),true);return ResponseEntity.ok().build();}//验证用户信息,如果用户信息验证成功,返回用户信息@GetMapping("verify")public ResponseEntity<UserInfo> verify(@CookieValue("YG_TOKEN") String token,HttpServletRequest request,HttpServletResponse response){//1.根据token解析获取到的cookie中的tokentry {UserInfo userInfo = JwtUtils.getInfoFromToken(token, prop.getPublicKey());//重新生成tokenString newToken = JwtUtils.generateToken(userInfo, prop.getPrivateKey(), prop.getExpire());//重新把Token设置到cookie中,覆盖原来的cookieCookieUtils.setCookie(request,response,prop.getCookieName(),newToken, prop.getCookieMaxAge());return ResponseEntity.ok(userInfo);} catch (Exception e) {throw new YgException(ExceptionEnum.USER_TOKEN_EXISTS_FALL);}}
}
service:
package com.yigou.auth.service.impI;import com.yigou.auth.client.UserClient;
import com.yigou.auth.config.JwtProperties;
import com.yigou.auth.service.AuthService;
import com.yigou.common.enums.ExceptionEnum;
import com.yigou.common.exception.YgException;
import com.yigou.user.entity.TbUser;
import entity.UserInfo;
import lombok.ToString;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import utils.JwtUtils;@Service
public class AuthServiceImpl implements AuthService {@Autowiredprivate UserClient userClient;@Autowiredprivate JwtProperties prop;//验证用户名和密码之后,生成jwt-token@Overridepublic String authentication(String username, String password) {//1.通过用户名,密码验证是否存在TbUser user = userClient.getUserInfo(username, password);if (user==null){return null;}//2.生成tokenUserInfo userInfo = new UserInfo();userInfo.setId(user.getId());userInfo.setUsername(user.getUsername());try {String token = JwtUtils.generateToken(userInfo, prop.getPrivateKey(), prop.getExpire());return token;} catch (Exception e) {throw new YgException(ExceptionEnum.JWT_TOKEN_CREATE_fALL);}}
}
调用接口
package com.yigou.auth.client;import com.yigou.user.api.UserApi;
import org.springframework.cloud.openfeign.FeignClient;@FeignClient("user-service")
public interface UserClient extends UserApi {
}
三.注意点:
1.使用Feign调用其他模块方法:
将提供模块的对外接口包以及httpclient依赖到调用模块

<!--httpclient支持--><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId></dependency>
调用模块的接口类继承提供模块对外开放的接口类
注释@FeignClient的参数名和提供模块在注册中心的名相同。

2.提供模块建立对外接口包
对外接口类 所提供方法 要与controller方法行映射
3.调用模块主函数开启FeignClients
package com.yigou.auth;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableDiscoveryClient
@EnableFeignClients
public class YgAuthServiceApplication {public static void main(String[] args) {SpringApplication.run(YgAuthServiceApplication.class, args);}}
四.总结
1.登录请求通过网关(登录为白名单 因此不会拦截校验),网关转发到授权中心模块
2.授权中心模块通过前端发送的用户信息feign调用用户模块查询用户信息。根据是否有该用户来判断是否使用该用户信息以及拿到yml定义的私钥路径去生成token存入cookie中。


3.若登陆成功 那么随后每次请求都会携带cookie中的token 在网关进行拦截以及公钥解密。每次鉴权都会在鉴权模块将token重新刷新一遍。


相关文章:
JWT整合Gateway实现鉴权(RSA与公私密钥工具类)
一.业务流程 1.使用RSA生成公钥和私钥。私钥保存在授权中心,公钥保存在网关(gateway)和各个信任微服务中。 2.用户请求登录。 3.授权中心进行校验,通过后使用私钥对JWT进行签名加密。并将JWT返回给用户 4.用户携带JWT访问 5.gateway直接通过公钥解密JWT进…...
vue实现全屏screenfull-封装组件
1. 安装依赖 npm install --save screenfull 2. 引用 import screenfull from "screenfull" 3.封装fullScreen/index: <template><div><el-tooltip v-if"!content" effect"dark" :content"fullscreenTips" placement&…...
【LinkedList与链表】
目录 1,ArrayList的缺陷 2,链表 2.1 链表的概念及结构 2.2 链表的实现 2.2.1 无头单向非循环链表实现 3,LinkedList的模拟实现 3.1 无头双向链表实现 4,LinkedList的使用 4.1 什么是LinkedList 4.2 LinkedList的使用 5…...
为数据安全护航,袋鼠云在数据分类分级上的探索实践
在大数据时代,数据具有多源异构的特性,且价值各异,企业需依据数据的重要性、价值指数等予以区分,以利采取不同的数据保护举措,避免数据泄露。故而,数据分类分级管理属于数据安全保护中极为重要的环节之一。…...
Spring 循环依赖详解
Spring 循环依赖详解 1. 引言 在Spring框架中,依赖注入(Dependency Injection, DI)是其核心功能之一,它通过配置来管理对象的创建和它们之间的依赖关系。然而,在复杂的应用程序中,开发人员有时会遇到循环…...
项目经理真的不能太“拧巴”
前期的项目经理经常是“拧巴”的,就是心里纠结、思路混乱、行动迟缓。对于每天需要面对各种挑战、协调各方资源、确保项目顺利进行的项目经理来说,这种“拧巴”不仅会让自己陷入内耗中,还会让项目出大问题。 项目计划总是改来改去࿰…...
企业如何选择合适的CRM工具?除Salesforce之外的10大主流选择
对比salesforce,其他10款优秀CRM:纷享销客CRM、Zoho CRM、腾讯企点、销售易、企业微信 (WeCom)、Odoo CR、OroCRM、金蝶、用友CRM、EspoCRM 虽然Salesforce以其全面的功能和强大的市场占有率在海外收获了许多客户,但Salesforce在国内市场的接…...
每年1-1.2万人毕业,男女比例约3:1,测绘工程的就业率如何
测绘工程,一个让人闻风丧胆的理科专业,虎扑评分4.2: 干过测绘的,苦不苦只有大家心里知道,带大家来感受一下,兄弟们的精神状态都十分美妙: 测绘专业到底是什么情况? PS.测绘分为本科…...
JimuReport 积木报表 v1.7.6 版本发布,免费的低代码报表
项目介绍 一款免费的数据可视化报表工具,含报表和大屏设计,像搭建积木一样在线设计报表!功能涵盖,数据报表、打印设计、图表报表、大屏设计等! Web 版报表设计器,类似于excel操作风格,通过拖拽完…...
“灵活就业者“超两亿人 游戏开发者如何破局?
随着“灵活就业”者数量突破两亿,我相信“寒气”已经传递到每一位普通人!对于游戏行业的“灵活就业”者,应当如何破局? 首先应该恭喜大家,选择了一个相对“稳健”的行业,无论大环境如何,游戏/软…...
MySQL事务与存储引擎
一、事务的概念 是一种机制、一个操作序列,包含了一组数据库操作命令,并且把所有的命令作为一个整体一起向系统提交或撤销操作请求,即这一组数据库命令要么都执行,要么都不执行是一个不可分割的工作逻辑单元,在数据库…...
总是给数据库表字段设置默认值的好处
1、NOT NULL DEFAULT 的好处 在设计数据库表结构时,将字段设置为不能为空并设置默认值有以下几种好处: 1.1、数据完整性 通过设置字段不能为空,可以确保每条记录都包含必要的数据,从而保证了数据的完整性。例如,在用…...
11.2 Go 常用包介绍
💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…...
Sqlite3数据库基本使用
一、基本概念 数据:能够输入计算机并能被计算机程序识别和处理的信息集合 数据库:长期存储在计算机内、有组织的、可共享的大量数据的集合 DBMS:位于用户与操作系统之间的一层数据管理软件,用于操纵和管理数据库 二、安装 在线…...
实现贪吃蛇小游戏【简单版】
1. 贪吃蛇游戏设计与分析 1.1 地图 我们最终的贪吃蛇大纲要是这个样子,那我们的地图如何布置呢? 这里不得不讲⼀下控制台窗口的⼀些知识,如果想在控制台的窗口中指定位置输出信息,我们得知道该位置的坐标,所以首先介…...
uniapp实现内嵌其他网页的功能
一、用到的知识点 页面跳转页面间跳转,参数传递web-view使用 二、使用navigator 页面跳转。 navigator 组件类似HTML中的<a>组件,但只能跳转本地页面。目标页面必须在pages.json中注册。所以这么写是不行的: <navigator url&quo…...
【Ruby简单脚本01】查看wifi密码
脚本 # 使用io库 def get_cmd_result(cmd) IO.popen(cmd,:external_encoding>GBK).read.encode("utf-8") end def list_wifi wifi_pwds Hash.new # 获取所有wifi文件 o1 get_cmd_result("netsh wlan show profiles") # 获取所有匹配结果 …...
VSG/VSA 矢量信号模拟/分析软件
_Ceyear思仪 _ VSG/VSA 矢量信号模拟/分析软件 苏州新利通仪器仪表 在现代无线通信中,IQ调制属于标准配置,经常应用于通信系统的信号调制和解调环节。IQ调制的应用简化了通信设备的硬件结构,同时提高了频谱资源的利用效率,提…...
C++使用GDAL库完成tiff图像的合并
全色图 完整代码: #include "gdal_priv.h" #include "cpl_string.h" #include <vector> #include <algorithm> #include <iostream> #include <filesystem>using namespace std; namespace fs std::filesystem; vec…...
深入理解AQS:Java并发编程中的核心组件
目录 AQS简介AQS的设计思路AQS的核心组成部分 状态(State)同步队列(Sync Queue)条件队列(Condition Queue) AQS的内部实现 节点(Node)锁的获取与释放 独占锁共享锁 条件变量 AQS的应…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...
Docker 运行 Kafka 带 SASL 认证教程
Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...
关于 WASM:1. WASM 基础原理
一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...
蓝桥杯3498 01串的熵
问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798, 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...
STM32HAL库USART源代码解析及应用
STM32HAL库USART源代码解析 前言STM32CubeIDE配置串口USART和UART的选择使用模式参数设置GPIO配置DMA配置中断配置硬件流控制使能生成代码解析和使用方法串口初始化__UART_HandleTypeDef结构体浅析HAL库代码实际使用方法使用轮询方式发送使用轮询方式接收使用中断方式发送使用中…...
Python Einops库:深度学习中的张量操作革命
Einops(爱因斯坦操作库)就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库,用类似自然语言的表达式替代了晦涩的API调用,彻底改变了深度学习工程…...
