Security

Authentication Policy(0.8)

Istio 身份认证策略使运维为一个服务(或者服务们)指定身份认证要求。Istio 身份认证策略由两部分组成:

  • Peer:验证进行连接的一方,即直接客户端。常见的认证机制是相互TLS认证( mutual TLS)。
  • Origin:验证发出请求的一方(例如最终用户,设备等),原始客户端。JWT是目前唯一受支持的原始认证机制。

Istio 配置服务端执行身份验证,但是,它不强制客户端的策略。对于相互TLS身份认证,用户可以使用 destination rules 来配置客户端遵守预期的协议。对于其他情况,应用程序负责获取并附加凭证(如JWT)到请求。

如果适用,来自两个认证部分的身份会输出到下一层(如授权,Mixer)。为了简化授权规则,该策略还可以指定应将哪个身份(peer或origin)用作“委托人”。默认情况下,它将被设置为peer的身份。

Architecture

认证策略被保存在Istio的config仓库中(在0.7时,使用K8s CRD实现仓库),并且通过控制面(control plane)分配。根据网格的大小,配置传播可能需要几秒到几分钟的时间。在转换过程中,你可能会预计流量丢失或者身份验证结果不一致。
这里写图片描述

策略的作用范围是命名空间,其中(可选)目标选择器规则可缩小应用策略的服务集(与策略相同的命名空间内)。这与基于K8s RBAC的ACL模型一致。更具体地说,只有命名空间地管理员才能为该命名空间中的服务设置策略。

认证由Istio sidecars实施。例如,使用Envoy sidecar,它是SSL设置和HTTP过滤器的组合。如果认证失败,请求将被拒绝(使用SSL握手错误代码或http 401,具体取决于身份验证机制的类型)。如果身份认证成功,将会生成如下认证属性:

  • source.principal: peer主体。如果peer认证未被使用,这个属性不会设置。

  • request.auth.principal: 取决于政策主体的约束力,这可以是peer主体(如果是USE_PEER)或origin主体(如果是USE_ORIGIN)。

  • request.auth.audiences: 反映原始JWT(JWT用于原始认证)的听众(aud)声明。
  • request.auth.presenter: 同样,反映了原始JWT的授权演示者 (azp)声明。
  • request.auth.claims: 来自原始JWT的所有未加工字符串声明。

Origin主体(原始认证主体)未明确输出。一般来说,它总是可以通过“/”分隔符连接 (iss) 和主体 (sub) 声明来重建(例如,如果isssub 声明分别是 “googleapis.com”和“123456”,则原始主体是“googleapis.com/123456”)。另一方面,如果主体绑定是USE_ORIGIN,那么 request.auth.principal 将携带与原始主体相同的值。

Anatomy of a policy

Target selectors

定义规则以查找应该应用策略的服务。如果未提供规则,则策略将与策略的相同命名空间中的所有服务匹配,即所谓的命名空间级策略(与具有非空选择器规则的服务级策略相反)。Istio使用服务级策略(如果可用),否则将回退到命名空间级策略。如果两者都未定义,则使用基于服务网格配置和/或服务注释的默认策略,该策略只能设置互相TLS认证(这些是在Istio 0.7之前为Istio服务网格配置相互TLS的机制)。可以看 testing Istio mutual TLS.

从0.8开始,认证策略是启用/禁用每个服务的互相TLS的推荐方式。未来版本中将删除使用服务注释的选项。

运维负责避免冲突,例如创建与同一服务匹配的多个服务级策略(或同一命名空间上的多个命名空间级策略)。

示例:规则选择product-page服务(在任何端口上),reviews:9000.

 targets:
 - name: product-page
 - name: reviews
   ports:
   - number: 9000
Peer authentication

定义对peer验证的验证方法(和相关参数)。它可以列出多种方法;只有其中一个满足认证才能通过。然而,从0.7版本开始,只支持相互TLS。如果不需要peer认证,请忽略此选项。

使用互相TLS的peer认证的示例:

  peers:
  - mtls:

从Istio 0.7开始,mtls 设置不需要任何参数(因此 -mtls: {}, - mtls: or - mtls: null 声明就足够了)。未来,它可能携带参数来提供不同的相互TLS实现。

Origin authentication

定义对origin认证的验证方法(和相关参数)。只有JWT支持此功能,但是,该政策可以由不同的发行人列出多个JWT。类似于peer验证,只需满足其中一个列出的方法即可通过身份验证。

origins:
- jwt:
    issuer: "https://accounts.google.com"
    jwksUri: "https://www.googleapis.com/oauth2/v3/certs"
Principal binding

定义认证的主体是什么。默认情况下,它将是peer的主体(如果没有应用peer认证,它将保持不变)。策略编写者可以选择使用USE_ORIGIN覆盖它。未来,我们将同样支持conditional-binding。(如 当peer是X为USE_PEER,否则为USE_ORIGIN

Istio Role-Based Access Control(RBAC)

Overview

Istio基于角色访问控制(RBAC)为服务网格中的服务提供命名空间级、服务级、方法级的访问控制。其特点:
1. 基于角色的语义,便于使用。
2. 服务到服务,终端用户到服务的授权。
3. 通过在角色和角色绑定中支持自定义属性实现灵活性。

Architecture

下图是Istio RBAC架构。管理员指定Istio RBAC 策略。策略被保存在Istio Config Store。
这里写图片描述
Istio RBAC引擎做了两件事:
1. 取得RBAC策略。 Istio RBAC引擎观测RBAC 策略的变化。如果发现任何变化,它会取得更新后的策略。
2. 批准请求。 在运行期,当一个请求发送过来,请求上下文经过Istio RBAC引擎。RBAC引擎比对RBAC策略来评估请求上下文,并返回审批结果(ALLOW或DENY)。

Request Context

当前版本,Istio RBAC引擎被实现为一个Mixer 适配器。请求上下文是由一个审批模板的实例提供的。请求上下文包含了一个审批模块需要知道环境及请求的所有信息。实际上,它由两部分:
1. subject 包含识别调用者标识的属性列表,包括 "user" name/ID,subject所属的"groups", 或任何关于subject的附加信息,如命名空间,服务名。
2. action 指定“如何访问一个服务”。它包含正在执行的action的 "namespace", "service", "path", "method" ,及任何关于action的附加属性。
下面是一个请求上下文的例子。

apiVersion: "config.istio.io/v1alpha2"
   kind: authorization
   metadata:
     name: requestcontext
     namespace: istio-system
   spec:
     subject:
       user: source.user | ""
       groups: ""
       properties:
         service: source.service | ""
         namespace: source.namespace | ""
     action:
       namespace: destination.namespace | ""
       service: destination.service | ""
       method: request.method | ""
       path: request.path | ""
       properties:
         version: request.headers["version"] | ""

Istio RBAC Policy

Istio RBAC引入服务角色和服务角色绑定,它们在k8s中被定义为CustomResourceDefinition (CRD) 对象。
1. ServiceRole 定义了网格中访问服务的角色。
2. ServiceRoleBinding 授予主体(如一个用户,一个组,一个服务)一个角色。

ServiceRole

一个服务角色说明包含一组规则。每个规则都有以下标准字段:
1. services: 一组服务名,匹配请求上下文中的action.service字段。
2. methods: 一组方法名,匹配请求上下文的 action.method 字段。请求上下文中有HTTP或gRPC方法。注意gRPC的方法格式为 “packageName.serviceName/methodName” (区分大小写)
3. paths: 一组HTTP路径,匹配请求上下文的action.path 字段。在gRPC中该地段被忽略。

一个服务角色说明仅适用在 "metadata" 中指定的命名空间。 “services” 和“methods”是“rules”中的必要字段。“path”是可选的。如果没有指定或设置为“*”,它适用“any”实例。

下面是一个简单的 “service-admin”角色,在 “default” 命名空间中有对所有服务的访问权限。

 apiVersion: "config.istio.io/v1alpha2"
   kind: ServiceRole
   metadata:
     name: service-admin
     namespace: default
   spec:
     rules:
     - services: ["*"]
       methods: ["*"]

另一个 “products-viewer”角色,对“default”命名空间中的“products.default.svc.cluster.local”服务有读权限(“GET” and “HEAD”)。

  apiVersion: "config.istio.io/v1alpha2"
   kind: ServiceRole
   metadata:
     name: products-viewer
     namespace: default
   spec:
     rules:
     - services: ["products.default.svc.cluster.local"]
       methods: ["GET", "HEAD"]

此外,在rule中对所有字段支持前后缀匹配。例如,你可以定义一个 “tester”角色,在“default” 命名空间中有如下权限:
1. 对所有前缀为“test-”服务拥有所有权限(如“test-bookstore”, “test-performance”, “test-api.default.svc.cluster.local”)。
2. 对“bookstore.default.svc.cluster.local”服务中所有路径后缀为“/reviews”拥有读权限 (“GET”)。(如“/books/reviews”, “/events/booksale/reviews”, “/reviews”)

apiVersion: "config.istio.io/v1alpha2"
   kind: ServiceRole
   metadata:
     name: tester
     namespace: default
   spec:
     rules:
     - services: ["test-*"]
       methods: ["*"]
     - services: ["bookstore.default.svc.cluster.local"]
       paths: ["*/reviews"]
       methods: ["GET"]

在服务角色中,“namespace”+”services”+”paths”+”methods” 的结合定义了“一个服务被允许怎样被访问”。在一些场景中,你可能需要指定一个rule适用的附加约束。如,一个rule可能仅适用一个服务的某一“version”,或仅适用标签为“foo”的服务。你可以轻易的适用自定义字段指定这些约束。
例如,下面的ServiceRole定义扩展了之前的 “products-viewer”角色,新增了服务版本属于“v1” or “v2”的约束。注意“version” 属性有请求上下文的 "action.properties.version" 提供。

apiVersion: "config.istio.io/v1alpha2"
   kind: ServiceRole
   metadata:
     name: products-viewer-version
     namespace: default
   spec:
     rules:
     - services: ["products.default.svc.cluster.local"]
       methods: ["GET", "HEAD"]
       constraints:
       - key: "version"
         values: ["v1", "v2"]

ServiceRoleBinding

一个ServiceRoleBinding说明包含两部分:
1. roleRef 指在同一命名空间中的ServiceRole对象。
2. 一组对比角色的subjects。

一个subject可能是一个 “user”,一个“group”或表示一组“properties”。每个条目(“user” or “group” or an entry in “properties”)必须匹配请求上下文实例中的“subject”部分的某一字段(“user” or “groups” or an entry in “properties”)。

下面是一个“test-binding-products”服务角色绑定对象,绑定“product-viewer”服务角色的两个subjects:
1. 用户“alice@yahoo.com”。
2. 在命名空间“abc”中的“reviews.abc.svc.cluster.local”服务。

 apiVersion: "config.istio.io/v1alpha2"
   kind: ServiceRoleBinding
   metadata:
     name: test-binding-products
     namespace: default
   spec:
     subjects:
     - user: "alice@yahoo.com"
     - properties:
         service: "reviews.abc.svc.cluster.local"
         namespace: "abc"
     roleRef:
       kind: ServiceRole
       name: "products-viewer"

如果你想在上述案例中使一个服务可以被公开访问,你可以设置 user: “*” 。它会为所有用户/服务指派一个ServiceRole。

apiVersion: "config.istio.io/v1alpha2"
   kind: ServiceRoleBinding
   metadata:
     name: binding-products-allusers
     namespace: default
   spec:
     subjects:
     - user: "*"
     roleRef:
       kind: ServiceRole
       name: "products-viewer"

Enabling Istio RBAC

Istio RBAC可以添加如下Mixer适配器规则。该规则包含两部分。第一部分定义一个RBAC handler。它有两个参数,"config_store_url"and "cache_duration".
1. "config_store_url" 参数指定RBAC引擎在何处获取RBAC策略。默认值是"k8s://",表示k8s API server。相反,如果你在本地测试RBAC策略,你需要设置本地目录,如"fs:///tmp/testdata/configroot".
2. "cache_duration" 参数指定审批结果在Mixer client(i.e., Istio proxy)中缓存的持续时间。默认值1分钟。

第二部分定义一个规则,指定RBAC handler应该被上面定义的请求上下文调用。
下面的例子,Istio RBAC在命名空间 “default”中起效,缓存时间30s。

apiVersion: "config.istio.io/v1alpha2"
   kind: rbac
   metadata:
     name: handler
     namespace: istio-system
   spec:
     config_store_url: "k8s://"
     cache_duration: "30s"

   ---
   apiVersion: "config.istio.io/v1alpha2"
   kind: rule
   metadata:
     name: rbaccheck
     namespace: istio-system
   spec:
     match: destination.namespace == "default"
     actions:
     # handler and instance names default to the rule's namespace.
     - handler: handler.rbac
       instances:
       - requestcontext.authorization
   ---


Mutual TLS Authentication

Overview

Istio Auth的目的是增强微服务间通信的安全性,且不需修改服务代码。它负责:
1. 为每个服务提供一个强大的标识,保证它能在集群和云中互通。
2. 确保服务到服务、终端用户到服务的通信。
3. 提供密钥管理系统以自动化密钥和证书的生成、分发、轮换和撤销。

Architecture

下图是Istio Auth的架构,包含三个主要组件: identity, key management, and communication security。该图描述了Istio Auth如何确保‘frontend’ 服务作为‘frontend-team’ 服务账户与‘backend’服务作为 ‘backend-team’服务账户之间的服务到服务通信。Istio支持运行在k8s容器和VM/裸机中的服务。
这里写图片描述
如图,Istio Auth利用秘密卷从Istio CA向k8s容器交付密钥/证书。对于运行在VM/裸机中的服务,我们引入节点代理,它是一个运行在每个VM/裸机中的进程。它在本地生成私钥和CSR(证书签名请求),发送CSR到Istio CA去签名,然后和私钥一同生成证书并分发到Envoy。

Components

Identity

Istio Auth 使用k8s服务账户去确认谁运行服务:
1. 在Istio中的服务账户形式为: “spiffe://<domain>/ns/<namespace>/sa/<serviceaccount>”
1). domain是当前的cluster.local。我们在未来会支持自定义domain。
2). namespace是k8s服务账户的命名空间。
3). serviceaccount 是k8s服务账户名。
2. 一个服务账户是一个工作负载运行的身份(或角色),代表了工作负载的权限。对于需要强安全性的系统,一个工作负载的权限总数不应用一个随机字符 (i.e., service name, label, etc),或是被部署的二进制文件标识。
1). 如,假设我们有一个从多租户数据库中拉取数据的工作负载。如果Alice可以运行这个工作负载,她拉取的数据就和Bob运行这个工作负载时拉取的数据有所不同。
3. 服务账户可以通过识别一个机器,一个用户,一个工作负载或一组工作负载(不同工作负载可以作为相同服务账户运行)提供的灵活性来实现强安全策略。
4. 工作负载运行的服务账户在工作负载的生命周期内不会更改。
5. 服务账户唯一性可通过域名限制来确保。

Communication security

服务到服务通信客户端Envoy和服务端Envoy的通道实现。端到端安全通信通过如下确保:
1. 服务和Envoy间的本地TCP连接。
2. 代理间的相互TLS连接。
3. 安全命名:在握手过程中,客户端Envoy检查运行在目标服务上的服务端证书提供的服务账户。

Key management

Istio v2.0 支持服务运行在k8s pods和VM/裸机上。我们针对每种情况提供了不同的密钥配置机制。
对于运行在k8s pods上的服务,每个集群Istio CA自动化密钥&证书管理流程。主要表现为下面四个关键操作:
1. 为每个服务账户生成一个 SPIFFE key和证书对。
2. 根据服务账户将密钥和证书对分发到每个pod。
3. 定期轮换密钥和证书。
4. 必要时撤销特定密钥和证书对。
对于运行在VM/裸机上的服务,上面四个操作由节点代理和Istio CA共同完成。

Workflow

Istio Auth 工作流由两阶段组成,部署和运行期。部署阶段,我们分别讨论两种情况(k8s和VM/裸机)。一旦密钥和证书部署完成,两种场景的运行期阶段就是相同的。本节我们简要介绍工作流。

Deployment phase(Kubernetes Scenario)
  1. Istio CA观测k8s API Server,为每个已存在和新建的服务账户创建SPIFFE密钥和证书对,并将它们发送到API Server。
  2. 当一个pod被创建时,API Server 根据使用Kubernetes secrets的服务账户装载密钥和证书对。
  3. Pilot使用适当的密钥和证书以及安全的命名信息生成配置,定义了哪些服务账户可以运行某种服务,并将其传递给Envoy。
Deployment phase(VM/bare-metal Machines Scenario)
  1. Istio CA创建一个gRPC服务去采取CSR请求。
  2. 节点代理创建私钥和CSR,发送CSR到Istio CA签名。
  3. Istio CA确认CSR携带的认证信息,签署CSR并生成证书。
  4. 节点代理把从CA获取的证书和私钥推送给Envoy。
  5. 上述CSR过程定期重复进行轮换。
Runtime phase
  1. 来自客户服务的出站流量重新路由到本地Envoy。
  2. 客户端Envoy和服务端Envou相互TLS握手。握手期间,同时进行安全的命名检查,以确保服务端证书提供的服务账户可以运行服务端服务。
  3. mTLS连接建立后,流量将转发到服务端Envoy,随后通过本地TCP连接转发到服务端服务。

Best practices

这节,我们提供一些部署指南,然后讨论一个真实世界的场景。

Deployment guidelines
  1. 如果在一个集群中有多种服务运维(又名 SREs)部署不同服务,我们推荐为每个SRE组创建一个隔离的命名空间来隔离它们的权限。例如,你可能为team1创建命名空间 “team1-ns” ,为team2创建“team2-ns” ,这两组都不能访问另一组的服务。
  2. 如果Istio CA受到威胁,集群中所有托管的密钥和证书都可能被暴露。我们强烈建议在专有命名空间(如istio-ca-ns)上运行Istio CA,只有集群管理员才能访问该命名空间。
Example

我们看一个包含三个服务的三层应用:photo-frontend, photo-backend, and datastore。Photo-frontend and photo-backend 服务被photo SRE team管理,而datastore 服务被datastore SRE team管理。Photo-frontend可以访问photo-backend, photo-backend可以访问datastore。但是,photo-frontend不能访问datastore。
在这个场景中,一个集群管理员创建3个命名空间: istio-ca-ns, photo-ns, and datastore-ns。管理员有所有命名空间的权限。每个team都有自己命名空间的权限。 photo SRE team 在命名空间 photo-ns中分别创建两个服务账户去运行photo-frontend 和photo-backend 。datastore SRE team 在命名空间datastore-ns中创建一个服务账户运行datastore 。并且,我们需要在Istio Mixer强制执行服务访问控制,使photo-frontend 不能访问datastore。
在此设置中,Istio CA能够为所有命名空间提供密钥和证书管理,并将微服务部署彼此隔离。

Future work

  1. 集群间 服务到服务认证。
  2. 强大的审批机制:ABAC,RBAC等
  3. 每项服务允许授权的支持
  4. 安全的Istio组件(Mixer, Pilot)
  5. 使用JWT/OAuth2/OpenID_Connect 实现终端用户到服务认证。
  6. 支持GCP服务账户
  7. 用于服务和Envoy间本地通信的Unix域套接字。
  8. 支持中间代理
  9. 可插入密钥管理组件
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值