【笔记】Spring Cloud Gateway 实现 gRPC 代理

Spring Cloud Gateway 在 3.1.x 版本中增加了针对 gRPC 的网关代理功能支持,本片文章描述一下如何实现相关支持.本文主要基于 Spring Cloud Gateway 的 官方文档 进行一个实践练习。有兴趣的可以翻看官方文档。

在这里插入图片描述
由于 Grpc 是基于 HTTP2 协议进行传输的,因此 Srping Cloud Gateway 在支持了 HTTP2 的基础上天然支持对 Grpc 服务器的代理,只需要在现有代理基础上针对 grpc 协议进行一些处理即可。

以下为实现步骤,这里提供了示例代码,可以按需取用.

生成服务器证书

由于 Grpc 协议使用了 Http2 作为通信协议, Http2 在正常情况下是基于 TLS 层上进行通信的,这就要求我们需要配置服务器证书,这里为了测试,使用脚本生成了一套 CA 证书:

#!/bin/bash
# 指定生成证书的目录
dir=$(dirname "$0")/../resources/x509
[ -d "$dir" ] && find "$dir" -type f -exec rm -rf {} \;
mkdir -p "$dir"
pushd "$dir" || exit
# 生成.key  私钥文件 和 csr 证书签名请求文件
openssl req -new -nodes -sha256 -newkey rsa:2048 -keyout ca.key -out ca.csr \
-subj "/C=CN/ST=Zhejiang/L=Hangzhou/O=Ghimi Technology/OU=Ghimi Cloud/CN=ghimi.top"
# 生成自签名 .crt 证书文件
openssl x509 -req -in ca.csr -key ca.key -out ca.crt -days 3650
# 生成服务器私钥文件 和 csr 证书请求文件(私钥签名文件)
openssl req -new -nodes -sha256 -newkey rsa:2048 -keyout server.key -out server.csr \
-subj "/C=CN/ST=Zhejiang/L=Hangzhou/O=Ghimi Technology/OU=Ghimi Blog/CN=blog.ghimi.top"
# 3. 生成 server 证书,由 ca证书颁发
openssl x509 -req -in server.csr -out server.crt -CA ca.crt -CAkey ca.key -CAcreateserial -days 3650 -extensions SAN \
-extfile <(cat /etc/ssl/openssl.cnf <(printf "\n[SAN]\nsubjectAltName=DNS:dns.ghimi.top,IP:127.0.0.1,IP:::1"))
# 将 crt 证书转换为 pkcs12 格式,生成 server.p12 文件,密码 123456
openssl pkcs12 -export -in server.crt -inkey server.key -CAfile ca.crt \
-password pass:123456 -name server -out server.p12
# 导出服务器证书和证书私钥为 java keystore 格式 server.jks 为最终的导出结果 密码 123456
keytool -importkeystore -srckeystore server.p12 -destkeystore server.jks \
-srcstoretype pkcs12 -deststoretype jks -srcalias server -destalias server \
-deststorepass 123456 -srcstorepass 123456
# 将 ca 证书导入到 server.jks 中
keytool -importcert -keystore server.jks -file ca.crt -alias ca -storepass 123456 -noprompt
popd || exit

构建 Grpc 服务

首先我们需要创建一个 Maven 工程,并编写 gRPC 相关的服务器代码:

添加 gRPC 所需要的相关依赖:

<!-- grpc 关键依赖-->
io.grpc:grpc-netty-shaded:jar:1.64.0:runtime -- module io.grpc.netty.shaded [auto]
io.grpc:grpc-protobuf:jar:1.64.0:compile -- module io.grpc.protobuf [auto]
io.grpc:grpc-stub:jar:1.64.0:compile -- module io.grpc.stub [auto]
io.grpc:grpc-netty:jar:1.64.0:compile -- module io.grpc.netty [auto]

用 protobuf 生成一个 Java gRPC模板:

syntax = "proto3";
option java_multiple_files = true;
option java_package = "service";

message HelloReq {
  string name = 1;
}
message HelloResp {
  string greeting = 1;
}
service HelloService {
  rpc hello(HelloReq) returns (HelloResp);
}

然后在 pom.xml 添加 prptobuf 生成插件:

<!-- project.build.plugins -->
<plugin>
	<groupId>org.xolstice.maven.plugins</groupId>
	<artifactId>protobuf-maven-plugin</artifactId>
	<version>0.6.1</version>
	<configuration>
		<protocArtifact>com.google.protobuf:protoc:3.25.1:exe:${os.detected.classifier}</protocArtifact>
		<pluginId>grpc-java</pluginId>
		<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.64.0:exe:${os.detected.classifier}</pluginArtifact>
		<!--设置grpc生成代码到指定路径-->
        <!--<outputDirectory>${project.build.sourceDirectory}</outputDirectory>-->
		<!--生成代码前是否清空目录-->
		<clearOutputDirectory>true</clearOutputDirectory>
	</configuration>
	<executions>
		<execution>
			<goals>
				<goal>compile</goal>
				<goal>compile-custom</goal>
			</goals>
		</execution>
	</executions>
</plugin>

注意在指定 protoc 的版本时要和上面 grpc 依赖的 protobuf 版本保持一致,否则可能会出现类找不到的报错。
在这里插入图片描述
然后执行执行 Maven 命令生成 Protobuf 对应的 Java 代码:

mvn protobuf:compile protobuf:compile-custom

之后就可以基于生成的 Protobuf Java 代码编写一个 gRPC Server 了 :

public static void main(String[] args) throws IOException, InterruptedException {
    TlsServerCredentials.Builder tlsBuilder = TlsServerCredentials.newBuilder();
    File serverCert = new ClassPathResource("/x509/server.crt").getFile();
    File serverKey = new ClassPathResource("/x509/server.key").getFile();
    File caCert = new ClassPathResource("/x509/ca.crt").getFile();
    ServerCredentials credentials  = tlsBuilder.trustManager(caCert).keyManager(serverCert, serverKey).build();
    // ServerCredentials credentials = InsecureServerCredentials.create(); // 不建议使用,非常坑
    Server server = Grpc.newServerBuilderForPort(443, credentials).addService(new HelloImpl()).build();
    server.start().awaitTermination();
}

static class HelloImpl extends HelloServiceGrpc.HelloServiceImplBase {
    @Override
    public void hello(HelloReq request, StreamObserver<HelloResp> responseObserver) {
        String msg = "hello " + request.getName() + " from server";
        System.out.println("server received a req,reply: " + msg);
        HelloResp res = HelloResp.newBuilder().setGreeting(msg).build();
        responseObserver.onNext(res);
        responseObserver.onCompleted();
    }
}

尝试启动 GrpcServer ,检查端口是否已被监听,当前端口绑定在 443 上,这里 GrpcServer 的服务器证书一定要配置.

编写 GrpcClient 代码:

public static void main(String[] args) throws InterruptedException, IOException {
    // 当服务器配置了证书时需要指定 ca 证书
    TlsChannelCredentials.Builder tlsBuilder = TlsChannelCredentials.newBuilder();
    File caCert = new ClassPathResource("/x509/ca.crt").getFile();
    ChannelCredentials credentials = tlsBuilder.trustManager(caCert).build();
    // 不做服务器证书验证时使用这个
    // ChannelCredentials credentials = InsecureChannelCredentials.create();
    ManagedChannelBuilder<?> builder = Grpc.newChannelBuilder("127.0.0.1:7443", credentials);
    ManagedChannel channel = builder.build();
    HelloServiceGrpc.HelloServiceBlockingStub stub = HelloServiceGrpc.newBlockingStub(channel);
    service.HelloReq.Builder reqBuilder = service.HelloReq.newBuilder();
    HelloResp resp = stub.hello(reqBuilder.setName("ghimi").build());
    System.out.printf("success greeting from server: %s", resp.getGreeting());
    channel.shutdown().awaitTermination(5, TimeUnit.MINUTES);
}

执行 GrpcClient,调用 443 端口的 GrpcServer 查看执行效果:
在这里插入图片描述
现在我们就可以开发 Spring Cloud Gateway 了,首先添加依赖,我这里添加了 spring-cloud-starter-gateway:3.1.9 版本(为了适配 Java8,已经升级 Java11 的可以提升至更高版本)。

org.springframework.cloud:spring-cloud-starter-gateway:3.1.9

编写 GrpcGateway 启动类:

@SpringBootApplication
public class GrpcGateway {
    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(GrpcGateway.class, args);
    }
}

先不做配置尝试运行一下,看下是否能够正常运行:
在这里插入图片描述
可以看到成功监听到了 8080 端口,这是 Spring Cloud Gateway 的默认监听端口,现在我们在 /src/main/resources/ 目录下添加 application.yml 配置,配置代理 grpc 端口:

server:
  port: 7443 #端口号
  http2:
    enabled: true
  ssl:
    enabled: true
    key-store: classpath:x509/server.p12
    key-store-password: 123456
    key-store-type: pkcs12
    key-alias: server
spring:
  application:
    name: scg_grpc
  cloud:
    gateway: #网关路由配置
      httpclient:
        ssl:
          use-insecure-trust-manager: true
#          trustedX509Certificates:
#            - classpath:x509/ca.crt
      routes:
        - id: user-grpc #路由 id,没有固定规则,但唯一,建议与服务名对应
          uri: https://[::1]:443 #匹配后提供服务的路由地址
          predicates:
            #以下是断言条件,必选全部符合条件
            - Path=/**               #断言,路径匹配 注意:Path 中 P 为大写
            - Header=Content-Type,application/grpc
          filters:
            - AddResponseHeader=X-Request-header, header-value

添加 application.yml 后,重启 Spring Cloud Gateway 尝试用 GrpcClient 调用 7443 代理端口,可以看到请求成功:
在这里插入图片描述

报错分析

GrpcServer 和 GrpcClient 如果都配置了 InsecureServerCredentials 的情况下, GrpcClient 可以直接调用 GrpcServer 成功:

GrpcServer

TlsServerCredentials.Builder tlsBuilder = TlsServerCredentials.newBuilder();
ServerCredentials credentials = InsecureServerCredentials.create(); // 配置通过 h2c(http2 clear text) 协议访问
Server server = Grpc.newServerBuilderForPort(443, credentials).addService(new HelloImpl()).build();
server.start().awaitTermination();

GrpcClient

ChannelCredentials credentials = InsecureChannelCredentials.create(); // 通过 h2c 协议访问 GrpcServer
tlsBuilder.requireFakeFeature();
ManagedChannelBuilder<?> builder = Grpc.newChannelBuilder("127.0.0.1:443", credentials);
ManagedChannel channel = builder.build();
HelloServiceGrpc.HelloServiceBlockingStub stub = HelloServiceGrpc.newBlockingStub(channel);
service.HelloReq.Builder reqBuilder = service.HelloReq.newBuilder();
HelloResp resp = stub.hello(reqBuilder.setName("ghimi").build());
System.out.printf("success greeting from server: %s\n", resp.getGreeting());
channel.shutdown().awaitTermination(5, TimeUnit.MINUTES);

此时使用 GrpcClient 调用 GrpcServer ,可以调用成功:
在这里插入图片描述

但是,如果中间添加了 Spring Cloud Gateway 的话, Grpc Server 和 Grpc Client 就都不能使用 InsecureCredentials 了, Spring Cloud Gateway 在这种场景下无论与 client 还是和 server 通信都会由于不识别的协议格式而报错:
在这里插入图片描述

如果 GrpcServer 没有配置服务器证书而是使用了 InsecureServerCredentials.create() ,GrpcClient 虽然不使用证书访问能够直接验证成功,但是如果中间通过 GrpcGateway 的话这种机制就有可能出现问题,因为 GrpcGateway 与 GrpcServer 之间的通信是基于 Http2 的,而非 Grpc 特定的协议,在 GrpcServer 没有配置服务器证书的情况下处理的包可能会导致 GrpcGateway 无法识别,但是如果 GrpcServer 配置了证书后 GrpcGateway 就能够正常验证了。

GrpcServer 在没有配置证书的情况下通过 Srping Cloud Gateway 的方式进行代理,并且 Spring Cloud Gateway 的 spring.cloud.gateway.http-client.ssl.use-insecure-trust-manager=true 的场景下 GrpcClient 访问 Spring Cloud Gateway 会报错:

GrpcClient 报错信息:

Exception in thread "main" io.grpc.StatusRuntimeException: UNKNOWN: HTTP status code 500
invalid content-type: application/json
headers: Metadata(:status=500,x-request-header=header-value,content-type=application/json,content-length=146)
DATA-----------------------------
{"timestamp":"2024-06-28T13:18:03.455+00:00","path":"/HelloService/hello","status":500,"error":"Internal Server Error","requestId":"31f8f577/1-8"}
	at io.grpc.stub.ClientCalls.toStatusRuntimeException(ClientCalls.java:268)
	at io.grpc.stub.ClientCalls.getUnchecked(ClientCalls.java:249)
	at io.grpc.stub.ClientCalls.blockingUnaryCall(ClientCalls.java:167)
	at service.HelloServiceGrpc$HelloServiceBlockingStub.hello(HelloServiceGrpc.java:160)
	at com.example.GrpcClient.main(GrpcClient.java:30)

报错信息解析,GrpcClient 报错结果来自于 Spring Cloud Gateway ,返回结果为不识别的返回内容 invalid content-type: application/json 这是由于 Spring Cloud Gateway 返回了的报错信息是 application/json 格式的,但是 GrpcClient 通过 grpc 协议通信,因此会将错误格式错误直接返回而非正确解析错误信息.

GrpcGateway 报错信息:

io.netty.handler.ssl.NotSslRecordException: not an SSL/TLS record: 00001204000000000000037fffffff000400100000000600002000000004080000000000000f0001
	at io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1313) ~[netty-handler-4.1.100.Final.jar:4.1.100.Final]
	Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
	*__checkpoint ⇢ org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter [DefaultWebFilterChain]
	*__checkpoint ⇢ HTTP POST "/HelloService/hello" [ExceptionHandlingWebHandler]
Original Stack Trace:
		at io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1313) ~[netty-handler-4.1.100.Final.jar:4.1.100.Final]
		at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1383) ~[netty-handler-4.1.100.Final.jar:4.1.100.Final]

这里就是的报错信息是 GrpcGateway 无法正确解析来自 GrpcServer 的 http2 的包信息而产生的报错.这是由于 GrpcGateway 与 GrpcServer 在 h2c(Http2 Clean Text) 协议上的通信格式存在差异,从而引发报错.

最后是来自 GrpcServer 的报错:

628, 2024 9:18:03 下午 io.grpc.netty.shaded.io.grpc.netty.NettyServerTransport notifyTerminated
信息: Transport failed
io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2Exception: HTTP/2 client preface string missing or corrupt. Hex dump for received bytes: 16030302650100026103036f6977c824c322105c600bd1db
	at io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2Exception.connectionError(Http2Exception.java:109)
	at io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2ConnectionHandler$PrefaceDecoder.readClientPrefaceString(Http2ConnectionHandler.java:321)
	at io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2ConnectionHandler$PrefaceDecoder.decode(Http2ConnectionHandler.java:247)
	at io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2ConnectionHandler.decode(Http2ConnectionHandler.java:453)

这里就是 GrpcServer 与 GrpcGateway 通信过程由于协议包无法识别导致通信终止,从而引发报错.

报错场景2

在 Spring Cloud Gateway 的 application.yml 中同时配置了 use-insecure-trust-manager: truetrustedX509Certificates 导致的报错:

spring:
  cloud:
    gateway: #网关路由配置
      httpclient:
        ssl:
          use-insecure-trust-manager: true
          trustedX509Certificates:
            - classpath:x509/ca.crt

use-insecure-trust-manager: true 表示在于 GrpcServer 通信的过程中不会验证服务器证书,这样如果证书存在什么问题的情况下就不会引发报错了,但是在同时配置了 use-insecure-trust-manager: truetrustedX509Certificates 的情况下 use-insecure-trust-manager: true 选项是不生效的,Spring Cloud Gateway 会还是尝试通过配置的 ca 证书去验证服务器证书,从而引发报错,因此不要同时配置 use-insecure-trust-manager: truetrustedX509Certificates这两个选项。

# 同时配置了 `use-insecure-trust-manager: true` 和 `trustedX509Certificates` 后服务证书校验失败报错
javax.net.ssl.SSLHandshakeException: No subject alternative names matching IP address 0:0:0:0:0:0:0:1 found
	at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:130) ~[na:na]
	Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
	*__checkpoint ⇢ org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter [DefaultWebFilterChain]
	*__checkpoint ⇢ HTTP POST "/HelloService/hello" [ExceptionHandlingWebHandler]
Original Stack Trace:

客户端通常情况下只需要配置 ca 证书,用于验证服务器证书,但是验证服务器证书这一步是可以跳过的,在一些场景下服务器证书的校验比较严格的时候容易出问题,此时可以选择不进行服务器证书校验,在 Spring Cloud Gateway 代理访问 GrpcServer 时,可以为 Spring Cloud Gateway 配置 use-insecure-trust-manager: true 来取消对 GrpcServer 的强验证。

No subject alternative names matching IP address 0:0:0:0:0:0:0:1 found

这个问题就是在校验服务器证书时,由于服务器证书校验失败导致的报错了,通常情况下, client 会校验服务器的FQDN域名信息是否与请求的连接一致:

# 请求服务器证书
openssl req -new -nodes -sha256 -newkey rsa:2048 -keyout server.key -out server.csr \
-subj "/C=CN/ST=Zhejiang/L=Hangzhou/O=Ghimi Technology/OU=Ghimi Blog/CN=blog.ghimi.top"

上面是在使用命令生成服务器证书时配置的信息,其中 CN=blog.ghimi.top 就是我配置的域名信息,这就要求我的 GrpcServer 的 ip 地址绑定了这个域名,然后 GrpcClient 通过这个域名访问:

ManagedChannelBuilder<?> builder = Grpc.newChannelBuilder("blog.ghimi.top:7443", credentials);

在这种情况下 GrpcClient 会拿服务器返回的证书与当前连接信息进行比较,如果一致则服务器验证成功,否则验证失败并抛出异常.

在 GrpcServer 只有 ip 地址没有域名的情况下,基于域名的验证就不生效了,此时去做证书验证就一定会报错:

# 同时配置了 `use-insecure-trust-manager: true` 和 `trustedX509Certificates` 后服务证书校验失败报错
javax.net.ssl.SSLHandshakeException: No subject alternative names matching IP address 0:0:0:0:0:0:0:1 found
	at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:130) ~[na:na]
	Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
	*__checkpoint ⇢ org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter [DefaultWebFilterChain]
	*__checkpoint ⇢ HTTP POST "/HelloService/hello" [ExceptionHandlingWebHandler]
Original Stack Trace:

报错信息中提到的 subject alternative names 就是在域名失效后的另外一种验证手段,他要求ca在签发服务器证书时向服务器证书中添加一段附加信息,这个信息中可以添加证书的可信 ip 地址:

# 通过 ca 证书颁发服务器证书
openssl x509 -req -in server.csr -out server.crt -CA ca.crt -CAkey ca.key -CAcreateserial -days 3650 -extensions SAN \
-extfile <(cat /etc/ssl/openssl.cnf <(printf "\n[SAN]\nsubjectAltName=DNS:dns.ghimi.top,IP:127.0.0.1"))

上面脚本中的 IP:127.0.0.1 就是添加的可信地址,我们可以同时添加多个服务器地址,以上面的报错为例,我们只需要在生成服务器证书的时候添加 ::1 的本地 ipv6 地址即可修复该错误:

openssl x509 -req -in server.csr -out server.crt -CA ca.crt -CAkey ca.key -CAcreateserial -days 3650 -extensions SAN \
-extfile <(cat /etc/ssl/openssl.cnf <(printf "\n[SAN]\nsubjectAltName=DNS:dns.ghimi.top,IP:127.0.0.1,IP:::1"))

报错场景4 使用 pkcs12 配置了多张自签名 ca 证书识别失效问题

解决方案,改为使用 Java Keystore 格式的证书即可修复.

参考资料

  • Spring Cloud Gateway and gRPC
  • spring-cloud-gateway-grpc
  • gRPC-Spring-Boot-Starter 文档
  • rx-java
  • Working with Certificates and SSL
  • 介绍一下 X.509 数字证书中的扩展项 subjectAltName

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

zabbix监控进阶:如何分时段设置不同告警阈值(多阈值告警)

作者 乐维社区&#xff08;forum.lwops.cn&#xff09;乐乐 在生产环境中&#xff0c;企业的业务系统状态并不是一成不变的。在业务高峰时段&#xff0c;如节假日、促销活动或特定时间段&#xff0c;系统负载和用户访问量会大幅增加&#xff0c;此时可能需要设置更高的告警阈值…

vscode 使用正则将/deep/ 替换成 :deep()

在VSCODE编辑器的SEARCH中按上图书写即可&#xff0c;正则表达式如下&#xff1a;(\/deep\/)(.*?)(?\{) 替换操作如下&#xff1a;:deep($2) 如果有用,号隔开的用&#xff1a;(\/deep\/)(.*?)(?,)替换操作如下&#xff1a;:deep($2) 即可实现快速替换所有/deep/写法; 同理…

Cyuyanzhong的内存函数

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、memcpy函数的使用与模拟实现二、memmove函数的使用和模拟实现三、memset函数与memcmp函数的使用&#xff08;一&#xff09;、memset函数&#xff08;内存块…

一文速览Google的Gemma:从gemma1到gemma2

前言 如此文《七月论文审稿GPT第3.2版和第3.5版&#xff1a;通过paper-review数据集分别微调Mistral、gemma》所讲 Google作为曾经的AI老大&#xff0c;我司自然紧密关注&#xff0c;所以当Google总算开源了一个gemma 7b&#xff0c;作为有技术追求、技术信仰的我司&#xff0…

大模型ReAct:思考与工具协同完成复杂任务推理

ReAct: Synergizing Reasoning and Acting in Language Models Github&#xff1a;https://github.com/ysymyth/ReAct 一、动机 人类的认知通常具备一定的自我调节&#xff08;self-regulation&#xff09;和策略制定&#xff08;strategization&#xff09;的能力&#xff0…

福昕阅读器再打开PDF文件时,总是单页显示,如何设置打开后就自动显示单页连续的模式呢

希望默认进入连续模式 设置方法 参考链接 如何设置使福昕阅读器每次启动时不是阅读模式 每次启动后都要退出阅读模式 麻烦_百度知道 (baidu.com)https://zhidao.baidu.com/question/346796551.html#:~:text%E5%9C%A8%E3%80%90%E5%B7%A5%E5%85%B7%E3%80%91%E9%87%8C%E6%9C%89%E…

Springboot下使用Redis管道(pipeline)进行批量操作

之前有业务场景需要批量插入数据到Redis中&#xff0c;做的过程中也有一些感悟&#xff0c;因此记录下来&#xff0c;以防忘记。下面的内容会涉及到 分别使用for、管道处理批量操作&#xff0c;比较其所花费时间。 分别使用RedisCallback、SessionCallback进行Redis pipeline …

从零开始学Spring Boot系列-集成Spring Security实现用户认证与授权

在Web应用程序中&#xff0c;安全性是一个至关重要的方面。Spring Security是Spring框架的一个子项目&#xff0c;用于提供安全访问控制的功能。通过集成Spring Security&#xff0c;我们可以轻松实现用户认证、授权、加密、会话管理等安全功能。本篇文章将指导大家从零开始&am…

昇思25天学习打卡营第11天|基于MindSpore通过GPT实现情感分类

学AI还能赢奖品&#xff1f;每天30分钟&#xff0c;25天打通AI任督二脉 (qq.com) 基于MindSpore通过GPT实现情感分类 %%capture captured_output # 实验环境已经预装了mindspore2.2.14&#xff0c;如需更换mindspore版本&#xff0c;可更改下面mindspore的版本号 !pip uninsta…

Mysql常用SQL:日期转换成周_DAYOFWEEK(date)

有时候需要将查询出来的日期转换成周几&#xff0c;Mysql本身语法就是支持这种转换的&#xff0c;就是DAYOFWEEK()函数 语法格式&#xff1a;DAYOFWEEK(date) &#xff08;date&#xff1a;可以是指定的具体日期&#xff08; 如2024-06-29 &#xff09;&#xff0c;也可以是日期…

一个项目学习IOS开发---创建一个IOS开发项目

前提&#xff1a; 由于IOS开发只能在MacOS上开发&#xff0c;所以黑苹果或者购买一台MacBook Pro是每个IOS开发者必备的技能或者工具之一 Swift开发工具一般使用MacOS提供的Xcode开发工具 首先Mac Store下载Xcode工具 安装之后打开会提醒你安装IOS的SDK&#xff0c;安装好之…

媒体宣发套餐的概述及推广方法-华媒舍

在今天的数字化时代&#xff0c;对于产品和服务的宣传已经变得不可或缺。媒体宣发套餐作为一种高效的宣传方式&#xff0c;在帮助企业塑造品牌形象、扩大影响力方面扮演着重要角色。本文将揭秘媒体宣发套餐&#xff0c;为您呈现一条通往成功的路。 1. 媒体宣发套餐的概述 媒体…

使用Tailwindcss之后,vxe-table表头排序箭头高亮消失的问题解决

环境 vue2.7.8 vxe-table3.5.9 tailwindcss/postcss7-compat2.2.17 postcss7.0.39 autoprefixer9.8.8 问题 vxe-table 表格表头 th 的排序箭头在开启正序或逆序排序时&#xff0c;会显示蓝色高亮来提示用户表格数据处在排序情况下。在项目开启运行了tailwindcss之后&#xff0…

Kafka入门-基础概念及参数

一、Kafka术语 Kafka属于分布式的消息引擎系统&#xff0c;它的主要功能是提供一套完备的消息发布与订阅解决方案。可以为每个业务、每个应用甚至是每类数据都创建专属的主题。 Kafka的服务器端由被称为Broker的服务进程构成&#xff0c;即一个Kafka集群由多个Broker组成&#…

dledger原理源码分析系列(二)-心跳

简介 dledger是openmessaging的一个组件&#xff0c; raft算法实现&#xff0c;用于分布式日志&#xff0c;本系列分析dledger如何实现raft概念&#xff0c;以及dledger在rocketmq的应用 本系列使用dledger v0.40 本文分析dledger的心跳 关键词 Raft Openmessaging 心跳/…

Android14之RRO资源文件替换策略(二百二十一)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒体系统工程师系列【原创干货持续更新中……】🚀 优质视频课程:AAOS车载系统+AOSP…

.NET 一款利用内核驱动关闭AV/EDR的工具

01阅读须知 此文所提供的信息只为网络安全人员对自己所负责的网站、服务器等&#xff08;包括但不限于&#xff09;进行检测或维护参考&#xff0c;未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作。利用此文所提供的信息而造成的直接或间接后果和损失&#xf…

微服务 | Springboot整合GateWay+Nacos实现动态路由

1、简介 路由转发 执行过滤器链。 ​ 网关&#xff0c;旨在为微服务架构提供一种简单有效的统一的API路由管理方式。同时&#xff0c;基于Filter链的方式提供了网关的基本功能&#xff0c;比如&#xff1a;鉴权、流量控制、熔断、路径重写、黑白名单、日志监控等。 基本功能…

搜维尔科技:「研讨会」惯性动捕技术在工效学领域应用研讨会

Movella将于7月2日&#xff08;周二&#xff09;下午2点举行主题为惯性动捕技术在工效学领域应用的研讨会。来自Movella的伙伴赋能经理Jeffrey Muller作为嘉宾出席&#xff0c;届时主讲人将为大家带来Xsens惯性动捕技术在工效学领域的应用分享。同时&#xff0c;研讨会还邀请多…

最近写javaweb出现的一个小bug---前端利用 form 表单传多项数据,后端 Servlet 取出的各项数据均为空

目录&#xff1a; 一. 问题引入二 解决问题 一. 问题引入 近在写一个 java web 项目时&#xff0c;遇到一个让我头疼了晚上的问题&#xff1a;前端通过 post 提交的 form 表单数据可以传到后端&#xff0c;但当我从 Servlet 中通过 request.getParameter(“name”) 拿取各项数…