8.15日学习打卡
目录:
- 8.15日学习打卡
- 为什么需要服务网关
- Higress是什么
- 安装DockerCompose
- 部署Higress
- 创建网关微服务模块
- Higress路由配置
- Higress策略配置-跨域配置
- Higress解决如何允许跨域
- Higress策略配置之什么是HTTP认证
- Higress策略配置-Basic 认证
- 什么是JWT认证
- JWT方式
- JWT原理
- 载荷 / Payload
- 创建认证中心微服务
- 编写JWT工具类生成jwt
- 编写JWT工具类解密Token
- 认证中心微服务颁发Token令牌
- JWT配置
- Key 认证
- 请求屏蔽
为什么需要服务网关
传统的单体架构中只需要开放一个服务给客户端调用,但是微服务架构中是将一个系统拆分成多个微服务,如果没有网关,客户端只能在本地记录每个微服务的调用地址,当需要调用的微服务数量很多时,它需要了解每个服务的接口,这个工作量很大。
服务网关的基本功能
微服务网关的作用:
- 提供了统一访问入口,降低了服务受攻击面
- 提供了统一跨域解决方案
- 提供了统一日志记录操作,可以进行统一监控
- 提供了统一权限认证支持
- 提供了微服务限流功能,可以保护微服务,防止雪崩效应发生
主流网关的对比与选型
- Kong 网关:Kong 的性能非常好,非常适合做流量网关,但是对于复杂系统不建议业务网关用 Kong,主要是工程性方面的考虑
- Zuul1.x 网关:Zuul 1.0 的落地经验丰富,但是性能差、基于同步阻塞IO,适合中小架构,不适合并发流量高的场景,因为容易产生线程耗尽,导致请求被拒绝的情况
- gateway 网关:功能强大丰富,性能好,官方基准测试 RPS (每秒请求数)是Zuul的1.6倍,能与 SpringCloud 生态很好兼容,单从流式编程+支持异步上也足以让开发者选择它了。
- Higress:一个遵循开源Ingress/Gateway API标准,提供流量调度、服务治理、安全防护三合一的高集成、易使用、易扩展、热更新的下一代云原生网关。
Higress是什么
Higress是什么
Higress是基于阿里内部的Envoy Gateway实践沉淀、以开源Istio + Envoy为核心构建的下一代云原生网关,实现了流量网关 + 微服务网关 + 安全网关三合一的高集成能力,深度集成Dubbo、Nacos、Sentinel等微服务技术栈,能够帮助用户极大的降低网关的部署及运维成本且能力不打折。
传统网关分类
行业中通常把网关分为两个大类:流量网关与业务网关,流量网关主要提供全局性的、与后端业务无关的策略配置,例如阿里内部的的统一接入网关Tengine就是典型的流量网关;业务网关顾名思义主要提供独立业务域级别的、与后端业务紧耦合策略配置,随着应用架构模式从单体演进到现在的分布式微服务,业务网关也有了新的叫法 - 微服务网关(图示说明如下)。在目前容器技术与K8s主导的云原生时代,下一代网关模式依然是这样吗?
Higress定位
在虚拟化时期的微服务架构下,业务通常采用流量网关 + 微服务网关的两层架构,流量网关负责南北向流量调度和安全防护,微服务网关负责东西向流量调度和服务治理,而在容器和 K8s 主导的云原生时代,Ingress 成为 K8s 生态的网关标准,赋予了网关新的使命,使得流量网关 + 微服务网关合二为一成为可能。
安装DockerCompose
关闭防火墙
systemctl stop firewalld
设置安装仓库
#安装yum的工具包
yum install -y yum-utils \
device-mapper-persistent-data \
lvm2 --skip-broken
#更新本地镜像源
# 设置docker镜像源
yum-config-manager \
--add-repo \
https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
sed -i 's/download.docker.com/mirrors.aliyun.com\/docker-ce/g' /etc/yum.repos.d/docker-ce.repo
#将软件包信息提前在本地索引缓存,用来提高搜索安装软件的速度,建议执行这个命令可以提升yum安装的速度。
yum makecache fast
安装docker引擎
sudo yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y
启动docker
systemctl start docker
设置docker自启动
systemctl enable docker
测试docker是否安装成功
docker run hello-world
部署Higress
搭建Higress
在安装之前要保证Docker安装成功并且成功安装Docker Copmpose插件。
curl -fsSL https://higress.io/standalone/get-higress.sh | bash -s -- -c nacos://192.168.47.100:8848 --nacos-username=nacos --nacos-password=nacos -p <你的密码>
启动成功后,本机端口占用情况如下:
80端口:Higress 暴露,用于 HTTP 协议代理
443端口:Higress 暴露,用于 HTTPS 协议代理
15020端口:Higress 暴露,用于暴露 Prometheus 指标
8080端口:Higress 控制台 暴露,(admin/123456)
Higress命令
命令解释:
startup.sh : 启动Higress
shutdown.sh : 停止Higress
configure.sh : 配置nacos地址
访问Higress控制台
在浏览器中输入http://127.0.0.1:8080,使用用户名 admin 和安装时设置的密码登录 Higress 控制台。
创建网关微服务模块
创建子模块 jjy-order-higress
引入依赖包
<!-- springboot依赖包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- nacos依赖包 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
编写配置文件
在resources文件夹下面创建application.yml文件。
spring:
application:
# 应用名字
name: order-service
cloud:
nacos:
discovery:
# Nacos注册中心的地址
server-addr: 192.168.47.100:8848
server:
port: 8006
编写主启动类
在com.jjy文件夹下面创建OrderHigressAppcation主启动类。
/**
* 主启动类
*/
@Slf4j
@EnableDiscoveryClient
@SpringBootApplication
public class OrderAppcation
{
public static void main( String[] args )
{
SpringApplication.run(OrderAppcation.class,args);
log.info("****************** 订单微服务启动成功 ***********");
}
}
编写测试控制器
package com.jjy.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class IndexController {
@GetMapping("/index")
public String index(){
return "hello higress";
}
}
Higress路由配置
什么是路由
在微服务架构中,路由是一种用于管理和定向请求流量的重要机制。微服务架构将一个应用程序拆分成一组小型、独立的服务,每个服务专注于执行特定的业务功能。路由在这样的环境中起到了指导请求流向的作用。
安装Switchhosts
SwitchHosts是一款便捷且免费的软件,体积为8M左右,不会占用电脑过多的内存,并且默认就是绿色软件,带有简体中文界面,在windows7、xp与vista等系统中能够运行。
该软件主要带有两个功能:
切换hosts与编辑hosts。
设置域名
创建路由
配置支付服务路由规则。
设置路由策略
重写地址
请求验证
执行以下命令,验证测试路由可以正常工作:
# should output a JSON object containing request data
curl http://www.it.com/payment/index
Higress策略配置-跨域配置
为什么会出现跨域问题
出于浏览器的同源策略限制。同源策略是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。
什么是跨域
当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域
当前页面url | 被请求页面url | 是否跨域 | 原因 |
---|---|---|---|
http://www.test.com/ | http://www.test.com/index.html | 否 | 同源(协议、域名、端口号相同) |
http://www.test.com/ | https://www.test.com/index.html | 跨域 | 协议不同(http/https) |
http://www.test.com/ | http://www.baidu.com/ | 跨域 | 主域名不同(test/baidu) |
http://www.test.com/ | http://blog.test.com/ | 跨域 | 子域名不同(www/blog) |
http://www.test.com:8080/ | http://www.test.com:7001/ | 跨域 | 端口号不同(8080/7001) |
跨域问题演示
在resources中创建templates文件夹,在编写index页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
</body>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<script>
$.get("http://www.it.com/order/index",function(data,status){
alert("Data: " + data );
});
</script>
</html>
Higress解决如何允许跨域
CORS
- 如何允许跨域,一种解决方法就是目的域告诉请求者允许什么来源域来请求,那么浏览器就会知道B域是否允许A域发起请求。
- CORS(“跨域资源共享”(Cross-origin resource sharing))就是这样一种解决手段。
CORS使得浏览器在向目的域发起请求之前先发起一个OPTIONS方式的请求到目的域获取目的域的信息,比如获取目的域允许什么域来请求的信息。
Higress策略配置之什么是HTTP认证
概述
HTTP认证是一种用于保护Web应用程序的一种身份验证机制。它通过在HTTP请求的头部添加认证信息,来验证用户的身份和权限。HTTP认证可以用于保护敏感信息,限制访问某些资源,或者在访问某些操作之前要求用户提供凭据。
HTTP 基本认证
常见的验证方案包括
1、Basic Authentication(基本认证)
Basic认证是最常见的HTTP认证方式之一。在Basic认证中,客户端发送请求时,会在请求头中包含一个"Authorization"字段,该字段包含了经过Base64编码的用户名和密码。
2、Digest Authentication(摘要认证)
Digest认证是一种更安全的认证方式。在Digest认证中,服务器会向客户端发送一个随机数(称为"nonce"),客户端根据该随机数和用户密码计算一个摘要,并将其发送给服务器。服务器收到摘要后,会验证其有效性。Digest认证相对于Basic认证而言,更难以被中间人攻击截获密码。
3、Bearer Token Authentication(令牌认证)
Bearer认证是一种使用令牌(Token)进行身份验证的方式。在Bearer认证中,客户端在请求头中添加一个"Authorization"字段,该字段包含了一个令牌信息。服务器在接收到请求后,会验证令牌的有效性,并根据令牌来识别用户身份。
4、OAuth(开放授权)
OAuth认证是一种开放标准的身份验证协议,用于授权第三方应用程序访问用户资源。在OAuth认证中,用户可以通过授权服务器授权第三方应用程序访问自己的资源。这种方式可以避免用户将密码直接提供给第三方应用程序。
Higress策略配置-Basic 认证
Basic 概述
Basic 认证是HTTP 中非常简单的认证方式,因为简单,所以不是很安全,不过仍然非常常用。当一个客户端向一个需要认证的HTTP服务器进行数据请求时,,HTTP服务器会返回401状态码,要求客户端输入用户名和密码。用户输入用户名和密码后,用户名和密码会经过BASE64加密附加到请求信息中再次请求HTTP服务器,HTTP服务器会根据请求头携带的认证信息,决定是否认证成功及做出相应的响应。
功能说明
basic-auth插件实现了基于 HTTP Basic Auth 标准进行认证鉴权的功能
配置字段
全局配置
名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
---|---|---|---|---|
consumers | array of object | 必填 | - | 配置服务的调用者,用于对请求进行认证 |
global_auth | bool | 选填 | - | 若配置为true,则全局生效认证机制; 若配置为false,则只对做了配置的域名和路由生效认证机制; 若不配置则仅当没有域名和路由配置时全局生效(兼容机制) |
consumers中每一项的配置字段说明如下:
名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
---|---|---|---|---|
credential | string | 必填 | - | 配置该consumer的访问凭证 |
name | string | 必填 | - | 配置该consumer的名称 |
域名和路由级配置
名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
---|---|---|---|---|
allow | array of string | 必填 | - | 对于符合匹配条件的请求,配置允许访问的consumer名称 |
注意:
- 对于通过认证鉴权的请求,请求的header会被添加一个X-Mse-Consumer字段,用以标识调用者的名称。
配置示例
1、对特定路由或域名开启认证和鉴权
以下配置将对网关特定路由或域名开启 Basic Auth 认证和鉴权,注意凭证信息中的用户名和密码之间使用":"分隔,credential字段不能重复。
全局配置
consumers:
- credential: 'admin:123456'
name: consumer1
- credential: 'guest:abc'
name: consumer2
global_auth: false
路由级配置
对 route-a 和 route-b 这两个路由做如下配置:
allow:
- consumer1
若是在控制台进行配置,此例指定的 route-a
和 route-b
即在控制台创建路由时填写的路由名称,当匹配到这两个路由时,将允许name
为consumer1
的调用者访问,其他调用者不允许访问;此例指定的 *.example.com
和 test.com
用于匹配请求的域名,当发现域名匹配时,将允许name
为consumer2
的调用者访问,其他调用者不允许访问。
测试配置
相关错误码
HTTP 状态码 | 出错信息 | 原因说明 |
---|---|---|
401 | Request denied by Basic Auth check. No Basic Authentication information found. | 请求未提供凭证 |
401 | Request denied by Basic Auth check. Invalid username and/or password | 请求凭证无效 |
403 | Request denied by Basic Auth check. Unauthorized consumer | 请求的调用方无访问权限 |
什么是JWT认证
JWT (全称:Json Web Token)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为 JSON 对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。官方网址https://jwt.io/
传统的session认证
Session 的认证流程通常会像这样:
缺点:
安全性:CSRF攻击因为基于cookie来进行用户识别, cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。
扩展性:对于分布式应用,需要实现 session 数据共享
性能:每一个用户经过后端应用认证之后,后端应用都要在服务端做一次记录,以方便用户下次请求的鉴别,通常而言session都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大,与REST风格不匹配。因为它在一个无状态协议里注入了状态。
JWT方式
优点:
- 无状态
- 适合移动端应用
- 单点登录友好
JWT原理
JWT 的原理是,服务器认证以后,生成一个 JSON 对象,发回给用户,就像下面这样
{
"姓名": "张三",
"角色": "管理员",
"到期时间": "2030年7月1日0点0分"
}
注意:
用户与服务端通信的时候,都要发回这个 JSON
对象。服务器完全只靠这个对象认定用户身份。为了防止用户篡改数据,服务器在生成这个对象的时候会加上签名,服务器就不保存任何 session
数据了,也就是说,服务器变成无状态了,从而比较容易实现扩展。
JWT 结构
一个 JWT 实际上就是一个字符串,它由三部分组成:头部、载荷与签名。中间用点 . 分隔成三个部分。注意 JWT 内部是没有换行的。
头部 / header
JSON对象,描述 JWT 的元数据。其中 alg 属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256);typ 属性表示这个令牌(token)的类型(type),统一写为 JWT。
{
"alg": "HS256",
"typ": "JWT"
}
注意:
上面代码中,alg属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256);typ属性表示这个令牌(token)的类型(type),JWT 令牌统一写为JWT然后将头部进行Base64编码构成了第一部分,Base64是一种用64个字符来表示任意二进制数据的方法,Base64是一种任意二进制到文本字符串的编码方法,常用于在URL、Cookie、网页中传输少量二进制数据。
载荷 / Payload
Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。JWT 指定七个默认字段供选择。除了默认字段之外,你完全可以添加自己想要的任何字段,一般用户登录成功后,就将用户信息存放在这里
iss:发行人
exp:到期时间
sub:主题
aud:用户
nbf:在此之前不可用
iat:发布时间
jti:JWT ID用于标识该JWT
{
"iss": "xxxxxxx",
"sub": "xxxxxxx",
"aud": "xxxxxxx",
"user": {
'username': 'itbaizhan',
'userId': 1
}
}
签名 / Signature
- 签名部分是对上面的 头部、载荷 两部分数据进行的数据签名
- 为了保证数据不被篡改,则需要指定一个密钥,而这个密钥一般只有你知道,并且存放在服务端
- 生成签名的代码一般如下:
// 其中secret 是密钥
String signature = HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
JWT 的使用方式
流程:
客户端收到服务器返回的 JWT,可以储存在 Cookie 里面,也可以储存在
localStorage。此后,客户端每次与服务器通信,都要带上这个 JWT。你可以把它放在 Cookie
里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求的头信息Authorization
字段里面。
创建认证中心微服务
认证微服务模块
引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.bitbucket.b_c</groupId>
<artifactId>jose4j</artifactId>
<version>0.7.0</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.18</version>
</dependency>
</dependencies>
创建配置文件
在resources文件夹下面创建application.yml文件。
spring:
application:
# 应用名字
name: auth-service
cloud:
nacos:
discovery:
# Nacos注册中心的地址
server-addr: 192.168.47.100:8848
server:
port: 8989
创建主启动类
@SpringBootApplication
@Slf4j
public class AuthApplication
{
public static void main( String[] args ) throws JoseException {
SpringApplication.run(AuthApplication.class,args);
log.info("***************** 认证授权中心启动成功 **************");
}
}
编写JWT工具类生成jwt
引入依赖
<dependency>
<groupId>org.bitbucket.b_c</groupId>
<artifactId>jose4j</artifactId>
<version>0.7.0</version>
</dependency>
生成公钥和私钥
RsaJsonWebKey rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
final String publicKeyString = rsaJsonWebKey.toJson(JsonWebKey.OutputControlLevel.PUBLIC_ONLY);
final String privateKeyString = rsaJsonWebKey.toJson(JsonWebKey.OutputControlLevel.INCLUDE_PRIVATE);
System.out.println(publicKeyString);
System.out.println(privateKeyString);
生成token
// 创建claims,这将是JWT的内容 B部分
JwtClaims claims = new JwtClaims();
// 谁创建了令牌并签署了它
claims.setIssuer("abcd");
// 令牌将被发送给谁
claims.setAudience("Audience");
// 令牌失效的时间长(从现在开始10分钟)
claims.setExpirationTimeMinutesInTheFuture(10);
// 令牌的唯一标识符
claims.setGeneratedJwtId();
// 当令牌被发布/创建时(现在)
claims.setIssuedAtToNow();
// 在此之前,令牌无效(2分钟前)
claims.setNotBeforeMinutesInThePast(2);
// 主题 ,是令牌的对象
claims.setSubject("subject");
// 可以添加关于主题的附加 声明/属性
claims.setClaim("userId", userId);
claims.setClaim("username", username);
// JWT是一个JWS和/或一个带有JSON声明的JWE作为有效负载。
// 在这个例子中,它是一个JWS,所以我们创建一个JsonWebSignature对象。
JsonWebSignature jws = new JsonWebSignature();
// JWS的有效负载是JWT声明的JSON内容
jws.setPayload(claims.toJson());
System.out.println(claims.toJson());
PrivateKey privateKey = new RsaJsonWebKey(JsonUtil.parseJson(privatekey)).getPrivateKey();
// JWT使用私钥签署
jws.setKey(privateKey);
/*
* 设置关键ID(kid)头,因为这是一种礼貌的做法。 在这个例子中,我们只有一个键但是使用键ID可以帮助
* 促进平稳的关键滚动过程
*/
jws.setKeyIdHeaderValue("keyId");
// 在jw/jws上设置签名算法,该算法将完整性保护声明
jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
/*
* 签署JWS并生成紧凑的序列化或完整的jw/JWS 表示,它是由三个点('.')分隔的字符串
* 在表单头.payload.签名中使用base64url编码的部件 如果你想对它进行加密,你可以简单地将这个jwt设置为有效负载
* 在JsonWebEncryption对象中,并将cty(内容类型)头设置为“jwt”。
*/
String jwt = jws.getCompactSerialization();
// 现在你可以用JWT做点什么了。比如把它寄给其他的派对
// 越过云层,穿过网络。
System.out.println("JWT: " + jwt);
return jwt;
编写JWT工具类解密Token
public static void checkJwt(String jwt) throws MalformedClaimException, JoseException {
/*
* 使用JwtConsumer builder构建适当的JwtConsumer,它将 用于验证和处理JWT。 JWT的具体验证需求是上下文相关的, 然而,
* 通常建议需要一个(合理的)过期时间,一个受信任的时间 发行人, 以及将你的系统定义为预期接收者的受众。
* 如果JWT也被加密,您只需要提供一个解密密钥对构建器进行解密密钥解析器。
*/
PublicKey publicKey1 = new RsaJsonWebKey(JsonUtil.parseJson(publicKey)).getRsaPublicKey();
JwtConsumer jwtConsumer = new JwtConsumerBuilder().setRequireExpirationTime()
.setAllowedClockSkewInSeconds(30) // 允许在验证基于时间的令牌时留有一定的余地,以计算时钟偏差。单位/秒
.setRequireSubject() // 主题声明
.setExpectedIssuer("Issuer") // JWT需要由谁来发布,用来验证 发布人
.setExpectedAudience("Audience") // JWT的目的是给谁, 用来验证观众
.setVerificationKey(publicKey1) // 用公钥验证签名 ,验证秘钥
.setJwsAlgorithmConstraints( // 只允许在给定上下文中预期的签名算法,使用指定的算法验证
new AlgorithmConstraints(ConstraintType.WHITELIST, // 白名单
AlgorithmIdentifiers.RSA_USING_SHA256))
.build(); // 创建JwtConsumer实例
try {
// 验证JWT并将其处理为jwtClaims
JwtClaims jwtClaims = jwtConsumer.processToClaims(jwt);
// 如果JWT失败的处理或验证,将会抛出InvalidJwtException。
// 希望能有一些有意义的解释(s)关于哪里出了问题。
System.out.println("JWT validation succeeded! " + jwtClaims);
} catch (InvalidJwtException e) {
System.out.println("Invalid JWT! " + e);
// 对JWT无效的(某些)特定原因的编程访问也是可能的
// 在某些情况下,您是否需要不同的错误处理行为。
// JWT是否已经过期是无效的一个常见原因
if (e.hasExpired()) {
System.out.println("JWT expired at " + e.getJwtContext().getJwtClaims().getExpirationTime());
}
// 或者观众是无效的
if (e.hasErrorCode(ErrorCodes.AUDIENCE_INVALID)) {
System.out.println("JWT had wrong audience: " + e.getJwtContext().getJwtClaims().getAudience());
}
}
}
认证中心微服务颁发Token令牌
编写认证中心控制器
创建controller包,在controller里面新建AuthController接口。
@RestController
public class AuthController {
@Autowired
SysLoginService sysLoginService;
@PostMapping("login")
public R<?> login(@RequestBody LoginBody form) throws MalformedClaimException, JoseException {
return sysLoginService.login(form.getUsername(), form.getPassword());
}
}
编写统一结果返回集
package com.jjy.domian;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
/**
* 统一结果返回集
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class R {
// 状态码
private int code;
// 返回信息
private String meg;
// 数据
private Object data;
public static R fail(String meg){
R r = new R();
r.setCode(500);
r.setMeg(meg);
return r;
}
public static R ok(Object data){
R r = new R();
r.setCode(200);
r.setMeg("sucess");
r.setData(data);
return r;
}
}
登录业务层
package com.jjy.service;
import com.alibaba.nacos.common.utils.StringUtils;
import com.itbaizhan.domian.LoginBodyDTO;
import com.itbaizhan.domian.R;
import com.itbaizhan.utils.JwtUtils;
import org.jose4j.lang.JoseException;
import org.springframework.stereotype.Service;
@Service
public class SysLoginService {
/**
* 登录
* @param loginBodyDTO
* @return
*/
public R login(LoginBodyDTO loginBodyDTO) throws JoseException {
// 1、用户名或者密码校验
if (StringUtils.isEmpty(loginBodyDTO.getUsername()) || StringUtils.isEmpty(loginBodyDTO.getPassword()) ){
return R.fail("用户名或者密码为空");
}
// 2、判断用户名和密码是否正确
//TODO 数据库操作
if (loginBodyDTO.getUsername().equals("admin")&& loginBodyDTO.getPassword().equals("123456")){
// 颁发登录token
String token = JwtUtils.sign(1001L, "admin");
return R.ok(token);
}else {
return R.fail("用户名或者密码不对");
}
}
}
JWT配置
基于Token的认证
很多对外开放的API需要识别请求者的身份,并据此判断所请求的资源是否可以返回给请求者。token就是一种用于身份验证的机制,基于这种机制,应用不需要在服务端保留用户的认证信息或者会话信息,可实现无状态、分布式的Web应用授权,为应用的扩展提供了便利。
业务流程:
1.客户端向API网关发起认证请求,请求中一般会携带终端用户的用户名和密码;
2.网关将请求直接转发给后端服务;
3.后端服务读取请求中的验证信息(比如用户名、密码)进行验证,验证通过后使用私钥生成标准的token,返回给网关;
4.网关将携带token的应答返回给客户端,客户端需要将这个token缓存到本地;
5.客户端向API网关发送业务请求,请求中携带token;
6.网关使用用户设定的公钥对请求中的token进行验证,验证通过后,将请求透传给后端服务;
7.后端服务进行业务处理后应答;
8.网关将业务应答返回给客户端。
功能说明
jwt-auth插件实现了基于JWT(JSON Web Tokens)进行认证鉴权的功能,支持从HTTP请求的URL参数、请求头、Cookie字段解析JWT,同时验证该Token是否有权限访问。
配置字段
全局配置
名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
---|---|---|---|---|
consumers | array of object | 必填 | - | 配置服务的调用者,用于对请求进行认证 |
global_auth | bool | 选填 | - | 若配置为true,则全局生效认证机制; 若配置为false,则只对做了配置的域名和路由生效认证机制; 若不配置则仅当没有域名和路由配置时全局生效(兼容机制) |
consumers中每一项的配置字段说明如下:
名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
---|---|---|---|---|
name | string | 必填 | - | 配置该consumer的名称 |
jwks | string | 必填 | - | https://www.rfc-editor.org/rfc/rfc7517 指定的json格式字符串,是由验证JWT中签名的公钥(或对称密钥)组成的Json Web Key Set |
issuer | string | 必填 | - | JWT的签发者,需要和payload中的iss字段保持一致 |
claims_to_headers | array of object | 选填 | - | 抽取JWT的payload中指定字段,设置到指定的请求头中转发给后端 |
from_headers | array of object | 选填 | [{“name”:“Authorization”,“value_prefix”:"Bearer "}] | 从指定的请求头中抽取JWT |
from_params | array of string | 选填 | access_token | 从指定的URL参数中抽取JWT |
from_cookies | array of string | 选填 | - | 从指定的cookie中抽取JWT |
clock_skew_seconds | number | 选填 | 60 | 校验JWT的exp和iat字段时允许的时钟偏移量,单位为秒 |
keep_token | bool | 选填 | true | 转发给后端时是否保留JWT |
注意:
只有当from_headers,from_params,from_cookies均未配置时,才会使用默认值
from_headers 中每一项的配置字段说明如下:
名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
---|---|---|---|---|
name | string | 必填 | - | 抽取JWT的请求header |
value_prefix | string | 必填 | - | 对请求header的value去除此前缀,剩余部分作为JWT |
claims_to_headers 中每一项的配置字段说明如下:
名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
---|---|---|---|---|
claim | string | 必填 | - | JWT |
header | string | 必填 | - | 从payload取出字段的值设置到这个请求头中,转发给后端 |
override | bool | 选填 | true | true时,存在同名请求头会进行覆盖;false时,追加同名请求头 |
域名和路由级配置
名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
---|---|---|---|---|
allow | array of string | 必填 | - | 对于符合匹配条件的请求,配置允许访问的consumer名称 |
注意:
对于通过认证鉴权的请求,请求的header会被添加一个X-Mse-Consumer字段,用以标识调用者的名称。
配置示例
1、配置全局策略
在插件市场中找到JWT Auth进行策略配置。
consumers:
- issuer: "abcd"
jwks: |
{
"keys": [
{
"kty": "RSA",
"n": "u8SyxKf2kLkmOKOU-mcbXQmacQDCPtxfMGKzYx6HWaGcCFbIrFDubGIWhe3GRP5uQqXekqwDtiqurdGfUzOLSSLfe7bmCmEgntNbF9bgk8lZUhzszmb4sGk6VK4YiOiTWGYWn_7jyKyF_OXEpXY4C3WWWsZwQfLPNUYfVZE76o1MXT9F3622RhLSPOFVqJYL6RlzllvNc2PdfzVEBnFU4wKszT0n9J8ZrNAnlNUxOXi7Y78fLqQks60ERznZwytwB8krydQGkjH9y9pf70QFJW228mUxXHnPhG87Gi2eE62TardkBCvQcm4TJlEB5dnmhFYFhRkAR6IznUAjtkFZIw",
"e": "AQAB",
"use": "sig",
"kid": "keyId",
"alg": "RS256"
}
]
}
name: "consumer1"
global_auth: false
2、配置路由级别
开启order-service微服务权限认证。
allow:
- "consumer1"
测试认证中心
1、生成用户登录token令牌
2、测试token令牌
Key 认证
功能说明
key-auth
插件实现了基于 API Key 进行认证鉴权的功能,支持从 HTTP 请求的 URL 参数或者请求头解析 API Key,同时验证该 API Key 是否有权限访问。
配置字段
全局配置
名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
---|---|---|---|---|
consumers | array of object | 必填 | - | 配置服务的调用者,用于对请求进行认证 |
keys | array of string | 必填 | - | API Key 的来源字段名称,可以是 URL 参数或者 HTTP 请求头名称 |
in_query | bool | in_query 和 in_header 至少有一个为 true | true | 配置 true 时,网关会尝试从 URL 参数中解析 API Key |
in_header | bool | in_query 和 in_header 至少有一个为 true | true | 配置 true 时,网关会尝试从 HTTP 请求头中解析 API Key |
global_auth | bool | 选填 | - | 若配置为true,则全局生效认证机制; 若配置为false,则只对做了配置的域名和路由生效认证机制; 若不配置则仅当没有域名和路由配置时全局生效(兼容机制) |
consumers中每一项的配置字段说明如下:
名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
---|---|---|---|---|
credential | string | 必填 | - | 配置该consumer的访问凭证 |
name | string | 必填 | - | 配置该consumer的名称 |
域名和路由级配置
名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
---|---|---|---|---|
allow | array of string | 必填 | - | 对于符合匹配条件的请求,配置允许访问的consumer名 |
注意:
对于通过认证鉴权的请求,请求的header会被添加一个X-Mse-Consumer字段,用以标识调用者的名称。
配置示例
1、对特定路由或域名开启
以下配置将对网关特定路由或域名开启 Key Auth 认证和鉴权,注意credential字段不能重复
全局配置
consumers:
- credential: 2bda943c-ba2b-11ec-ba07-00163e1250b5
name: consumer1
- credential: c8c8e9ca-558e-4a2d-bb62-e700dcc40e35
name: consumer2
keys:
- apikey
in_query: true
global_auth: false
路由级配置
对 route-a 和 route-b 这两个路由做如下配置:
allow:
- consumer1
- ```
对 *.exmaple.com 和 test.com 在这两个域名做如下配置:
```yml
allow:
- consumer2
请求未提供 API Key,返回401
curl http://xxx.hello.com/test
相关错误码
HTTP 状态码 | 出错信息 | 原因说明 |
---|---|---|
401 | No API key found in request | 请求未提供 API Key |
401 | Request denied by Key Auth check. Invalid API key | 不允许当前 API Key 访问 |
403 | Request denied by Basic Auth check. Unauthorized consumer | 请求的调用方无访问权限 |
请求屏蔽
功能说明
request-block
插件实现了基于 URL、请求头等特征屏蔽 HTTP 请求,可以用于防护部分站点资源不对外部暴露。
配置字段
名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
---|---|---|---|---|
block_urls | array of string | 选填,block_urls,block_headers,block_bodies 中至少必填一项 | - | 配置用于匹配需要屏蔽 URL 的字符串 |
block_headers | array of string | 选填,block_urls,block_headers,block_bodies 中至少必填一项 | - | 配置用于匹配需要屏蔽请求 Header 的字符串 |
block_bodies | array of string | 选填,block_urls,block_headers,block_bodies 中至少必填一项 | - | 配置用于匹配需要屏蔽请求 Body 的字符串 |
blocked_code | number | 选填 | 403 | 配置请求被屏蔽时返回的 HTTP 状态码 |
blocked_message | string | 选填 | - | 配置请求被屏蔽时返回的 HTTP 应答 Body |
case_sensitive | bool | 选填 | true | 配置匹配时是否区分大小写,默认区分 |
配置示例
1、屏蔽请求 url 路径
block_urls:
- swagger.html
- foo=bar
case_sensitive: false
根据该配置,下列请求将被禁止访问:
curl http://example.com?foo=Bar
curl http://exmaple.com/Swagger.html
2、屏蔽请求 header
block_headers:
- example-key
- example-value
根据该配置,下列请求将被禁止访问:
curl http://example.com -H 'example-key: 123'
curl http://exmaple.com -H 'my-header: example-value'
3、屏蔽请求 body
block_bodies:
- "hello world"
case_sensitive: false
根据该配置,下列请求将被禁止访问:
curl http://example.com -d 'Hello World'
curl http://exmaple.com -d 'hello world'
如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,大家的支持就是我坚持下去的动力