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

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生成公钥和私钥。私钥保存在授权中心&#xff0c;公钥保存在网关(gateway)和各个信任微服务中。 2.用户请求登录。 3.授权中心进行校验&#xff0c;通过后使用私钥对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&#xff0c;ArrayList的缺陷 2&#xff0c;链表 2.1 链表的概念及结构 2.2 链表的实现 2.2.1 无头单向非循环链表实现 3&#xff0c;LinkedList的模拟实现 3.1 无头双向链表实现 4&#xff0c;LinkedList的使用 4.1 什么是LinkedList 4.2 LinkedList的使用 5…...

为数据安全护航,袋鼠云在数据分类分级上的探索实践

在大数据时代&#xff0c;数据具有多源异构的特性&#xff0c;且价值各异&#xff0c;企业需依据数据的重要性、价值指数等予以区分&#xff0c;以利采取不同的数据保护举措&#xff0c;避免数据泄露。故而&#xff0c;数据分类分级管理属于数据安全保护中极为重要的环节之一。…...

Spring 循环依赖详解

Spring 循环依赖详解 1. 引言 在Spring框架中&#xff0c;依赖注入&#xff08;Dependency Injection, DI&#xff09;是其核心功能之一&#xff0c;它通过配置来管理对象的创建和它们之间的依赖关系。然而&#xff0c;在复杂的应用程序中&#xff0c;开发人员有时会遇到循环…...

项目经理真的不能太“拧巴”

前期的项目经理经常是“拧巴”的&#xff0c;就是心里纠结、思路混乱、行动迟缓。对于每天需要面对各种挑战、协调各方资源、确保项目顺利进行的项目经理来说&#xff0c;这种“拧巴”不仅会让自己陷入内耗中&#xff0c;还会让项目出大问题。 项目计划总是改来改去&#xff0…...

企业如何选择合适的CRM工具?除Salesforce之外的10大主流选择

对比salesforce&#xff0c;其他10款优秀CRM&#xff1a;纷享销客CRM、Zoho CRM、腾讯企点、销售易、企业微信 (WeCom)、Odoo CR、OroCRM、金蝶、用友CRM、EspoCRM 虽然Salesforce以其全面的功能和强大的市场占有率在海外收获了许多客户&#xff0c;但Salesforce在国内市场的接…...

每年1-1.2万人毕业,男女比例约3:1,测绘工程的就业率如何

测绘工程&#xff0c;一个让人闻风丧胆的理科专业&#xff0c;虎扑评分4.2&#xff1a; 干过测绘的&#xff0c;苦不苦只有大家心里知道&#xff0c;带大家来感受一下&#xff0c;兄弟们的精神状态都十分美妙&#xff1a; 测绘专业到底是什么情况&#xff1f; PS.测绘分为本科…...

JimuReport 积木报表 v1.7.6 版本发布,免费的低代码报表

项目介绍 一款免费的数据可视化报表工具&#xff0c;含报表和大屏设计&#xff0c;像搭建积木一样在线设计报表&#xff01;功能涵盖&#xff0c;数据报表、打印设计、图表报表、大屏设计等&#xff01; Web 版报表设计器&#xff0c;类似于excel操作风格&#xff0c;通过拖拽完…...

“灵活就业者“超两亿人 游戏开发者如何破局?

随着“灵活就业”者数量突破两亿&#xff0c;我相信“寒气”已经传递到每一位普通人&#xff01;对于游戏行业的“灵活就业”者&#xff0c;应当如何破局&#xff1f; 首先应该恭喜大家&#xff0c;选择了一个相对“稳健”的行业&#xff0c;无论大环境如何&#xff0c;游戏/软…...

MySQL事务与存储引擎

一、事务的概念 是一种机制、一个操作序列&#xff0c;包含了一组数据库操作命令&#xff0c;并且把所有的命令作为一个整体一起向系统提交或撤销操作请求&#xff0c;即这一组数据库命令要么都执行&#xff0c;要么都不执行是一个不可分割的工作逻辑单元&#xff0c;在数据库…...

总是给数据库表字段设置默认值的好处

1、NOT NULL DEFAULT 的好处 在设计数据库表结构时&#xff0c;将字段设置为不能为空并设置默认值有以下几种好处&#xff1a; 1.1、数据完整性 通过设置字段不能为空&#xff0c;可以确保每条记录都包含必要的数据&#xff0c;从而保证了数据的完整性。例如&#xff0c;在用…...

11.2 Go 常用包介绍

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…...

Sqlite3数据库基本使用

一、基本概念 数据&#xff1a;能够输入计算机并能被计算机程序识别和处理的信息集合 数据库&#xff1a;长期存储在计算机内、有组织的、可共享的大量数据的集合 DBMS&#xff1a;位于用户与操作系统之间的一层数据管理软件&#xff0c;用于操纵和管理数据库 二、安装 在线…...

实现贪吃蛇小游戏【简单版】

1. 贪吃蛇游戏设计与分析 1.1 地图 我们最终的贪吃蛇大纲要是这个样子&#xff0c;那我们的地图如何布置呢&#xff1f; 这里不得不讲⼀下控制台窗口的⼀些知识&#xff0c;如果想在控制台的窗口中指定位置输出信息&#xff0c;我们得知道该位置的坐标&#xff0c;所以首先介…...

uniapp实现内嵌其他网页的功能

一、用到的知识点 页面跳转页面间跳转&#xff0c;参数传递web-view使用 二、使用navigator 页面跳转。 navigator 组件类似HTML中的<a>组件&#xff0c;但只能跳转本地页面。目标页面必须在pages.json中注册。所以这么写是不行的&#xff1a; <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 矢量信号模拟/分析软件 苏州新利通仪器仪表 在现代无线通信中&#xff0c;IQ调制属于标准配置&#xff0c;经常应用于通信系统的信号调制和解调环节。IQ调制的应用简化了通信设备的硬件结构&#xff0c;同时提高了频谱资源的利用效率&#xff0c;提…...

C++使用GDAL库完成tiff图像的合并

全色图 完整代码&#xff1a; #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的核心组成部分 状态&#xff08;State&#xff09;同步队列&#xff08;Sync Queue&#xff09;条件队列&#xff08;Condition Queue&#xff09; AQS的内部实现 节点&#xff08;Node&#xff09;锁的获取与释放 独占锁共享锁 条件变量 AQS的应…...

集合进阶:List集合

一.List集合的特有方法 1.Collection的方法List都继承了 2.List集合因为有索引,所以多了很多索引操作的方法。 3.add // 1.创建一个集合List<String> list new ArrayList<>(); // 2.添加元素list.add("aaa");list.add("bbb"…...

el-table表头修改文字或者背景颜色,通过header-row-style设置样式

方式一 <el-table :header-cell-style"{text-align: center}" />方式二 <template><el-table :header-cell-style"tableHeaderColor" /> </template> <script> export default {methods: {tableHeaderColor ({row, column…...

web前端-CSS

CSS CSS概述: CSS是Cascading Style Sheets&#xff08;级联样式表&#xff09;,是一种样式表语言,用于控制网页布局,外观(比如背景图片,图片高度,文本颜色,文本字体,高级定位等等) 可将页面的内容与样式分离开,样式放于单独的.css文件或者HTML某处 CSS是网页样式,HTML是网页…...

u8g2 使用IIC驱动uc1617 lcd 字符显示只显示上半部分,不显示下半部

使用u8g2 使用硬件iic驱动某些page为4个字节 带灰度的lcd显示屏幕的时候有时候只显示上半部,下半部不显示,例如uc1617等。 原因: 以uc1617为例,链接https://github.com/olikraus/u8g2/blob/master/csrc/u8x8_d_uc1617.c 在u8x8_d_uc1617_common方法中的case U8X8_MSG_DI…...

单片机第五季-第八课:STM32CubeMx和FreeRTOS

1&#xff0c;FreeRTOS背景介绍 RTOS简介&#xff1a; 实时操作系统&#xff0c;本用于追求实时性的嵌入式系统&#xff0c;典型&#xff1a;ucos/uclinux/vxworks&#xff1b; 特点&#xff1a;中断响应快、一般可嵌套中断、使用实地址、多任务&#xff1b; &#xff08;实…...

【Linux】进程控制1——进程创建和进程终止

1.进程创建 1.1.再谈fork 在linux中fork函数时非常重要的函数&#xff0c;它从已存在进程中创建一个新进程。新进程为子进程&#xff0c;而原进程为父进程。 #include <unistd.h> pid_t fork(void);//pid_t为整形 返回值&#xff1a;子进程中的fork()返回0&#xff…...

计算机图形学入门16:曲线

1.曲线 曲线&#xff08;Curves&#xff09;在图形学中应用非常广泛&#xff0c;比如&#xff1a;相机的拍摄路径、物体的移动路径、动画曲线、矢量字体等。如下图所示&#xff0c;是使用曲线到矢量字体的应用&#xff0c;通过移动一些控制点来改变字体。 2.贝塞尔曲线 2.1 贝…...

【Ruby基础01】windows和termux中搭建Ruby开发环境

windows下环境搭建 railsinstaller官方git地址 按照文档安装git、nodejs、yarn&#xff0c;安装教程百度一下。railsinstall可以从release页面下载最新版本4.1.0。 安装完成如下 安装RubyMine 下载RubyMine RubyMine下载地址 安装激活 下载文件&#xff0c;按照里面的流程…...

2406c++,iguana动态反射

原文 iguana是一个基于编译期反射的序化库,支持从结构序化到不同数据格式. iguana可序化一个C结构到json,xml,yaml和protobuf格式.这都是通过编译期反射实现的. 现在iguana也支持了动态反射,主要特征: 1,通过对象名创建对象实例 2,取对象所有字段名 3,根据字段名取字段值 4,根…...

干货分享——AB站帮你解决独立站收款难题

目前&#xff0c;国内已经有抖音、拼多多和淘宝平台推出“仅退款”售后服务&#xff0c;无疑是加剧了原本就在疯狂打价格战的国内电商的严峻现状&#xff0c;使得商家不得不担心被顾客“薅羊毛”。在国内电商环境严重“内卷”的情况下&#xff0c;拓宽海外市场不失为一大妙计。…...

政府网站什么时候建设的/现在做网络推广都有什么方式

2019独角兽企业重金招聘Python工程师标准>>> 虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流(即字节码)”这个动作放到Java虚拟机外部去实现&#xff0c;以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块称…...

陕西大型网站建设/苏州百度

如何防止C语言头文件被重复包含&#xff1f; 一般情况下&#xff0c;我们都是把函数声明、类定义、模板定义等写到一个头文件里&#xff0c;需要时将相应的头文件用#include包含到源文件&#xff08;*.c文件&#xff09;里来。但头文件中又允许包含其它的头文件&#xff0c;这…...

网站建设多少钱信息/智能建站网站模板

微信小程序中的空格怎么打 文章目录微信小程序中的空格怎么打怎么打呢&#xff1f;解决怎么打呢&#xff1f; 微信小程序中的多个空格怎么打&#xff1f; &nbsp不行。 解决 复 制 吧 前面每个字之间都有一个空白字符&#xff0c;&#x1f42e;space...

近两年成功的网络营销案例及分析/厦门站长优化工具

牛客网 编程初学者入门训练 BC69 空心正方形图案 思路&#xff1a; 大体思路&#xff1a;我们需要引用一个while循环&#xff0c;两个for循环&#xff0c;以及用if,else语句进行判断输出。具体思路&#xff1a;由于题目中出现了关键字眼——多组输入&#xff0c;所以一般都是…...

那个做网站好/搜索广告是什么

Arcgis中如何为Shapefile属性表添加字段属性描述了要素的相关特性&#xff0c;并存储于表中。在创建新的属性表或是向已有的属性表中增加字段的时候&#xff0c;必须指明数据类型和字段属性&#xff0c;比如精度(Precision)或长度(Length)。数据类型的选择和相关设置会影响存储…...

做纸巾定制的网站/制作一个网站需要多少费用

题目均来自牛客网 1、 有以下代码片段&#xff1a; String str1"hello"; String str2"he" new String("llo"); System.out.println(str1str2); 请问输出的结果是&#xff1a;false - 解 类似问题连接&#xff1a; String is immutable. W…...