单点登录平台Casdoor搭建与使用,集成gitlab同步创建删除账号

news2024/12/21 15:49:11

一,简介

一般来说,公司有很多系统使用,为了实现统一的用户名管理和登录所有系统(如 GitLab、Harbor 等),并在员工离职时只需删除一个主账号即可实现权限清除,可以采用 单点登录 (SSO) 和 集中式身份认证 系统。以下工具都可使用。

Keycloak(开源,功能强大且易于部署)
Okta/Auth0(商业化解决方案,支持更多高级功能)
LDAP(轻量级目录访问协议,可搭配 FreeIPA)
Casdoor (轻量级的身份认证和授权平台)

上面试用了keycloak和casdoor,keycloak配置复杂,而且界面不是很友好,所以选择了Casdoor。
casdoor用他们的云需要付费,自己服务器搭建则免费,所以这里自建就行了。

Casdoor是一款轻量级的身份认证和授权平台,支持多种协议(OIDC、OAuth2、SAML、CAS 等),并提供了简单易用的界面和丰富的功能。主要有以下特点:

1,,多协议支持

支持 OIDC、OAuth2、SAML、CAS 等协议,可以适配不同的应用系统。
支持单点登录(SSO),登录一次即可访问多个系统。

2,集中用户管理

提供用户增删改查功能,支持直接管理用户账户。
支持与 LDAP 集成,实现统一的用户信息管理。

3,权限控制

支持角色权限管理,可以为用户分配不同的权限和角色。
删除用户后,其在其他系统中的访问权限也会被移除。

4,易集成

提供 SDK 和 API,可以轻松集成到 GitLab、Jenkins、Harbor 等支持标准协议的系统中。

5,支持多种登录方式

支持密码登录、短信/邮件验证码登录,以及社交登录(如 Google、GitHub 等)。
满足企业和用户对灵活认证方式的需求。

如果公司有现成的LDAP使用,或者比较老的系统,可以使用Casdoor+LDAP方式,可以同步至casdoor集中管理。

二,Casdoor搭建

casdoor支持多语言,界面语言支持都非常友好,看文档也不费力,这也是选择它的一个重要原因。
在这里插入图片描述
这里测试,用最简单的docker搭建即可。生成环境建议其他正式安装方式。

创建文件夹,将配置文件挂载到此文件夹,便于配置。
官方配置文件下载:https://github.com/casdoor/casdoor/blob/master/conf/app.conf

mkdir /casdoor
cd /casdoor
vim app.conf

最后启动

docker run -d -p 8000:8000 -v /root/casdoor:/conf casbin/casdoor-all-in-one

由于与gitlab集成,必须需要SSL证书,所以用nginx反代绑定域名操作,这里示例casdoor.ywbj.cc
登录网站,默认账号密码admin 123
在这里插入图片描述

三,Casdoor基本使用

1,组织用户管理

用户管理,只有一个默认系统组织 built-in,这里新建一个公司组织company,便于管理。
company下,创建一个开发群组develop。
群组下有一个用户zhangsan(组织下有应用时才可以创建)
在这里插入图片描述

在这里插入图片描述

2,应用管理

在‘身份认证’的‘应用’,里面,默认有一个应用app-built-in,主要功能是登录casdoor自己系统的。
目前此应用默认只有 默认组织 built-in 可以登录。其他不在此组织的用户登录都无法登录。
在这里插入图片描述
如果创建新组织新用户,需要登录casdoor,就还需要修改一下配置。
编辑默认应用,在组织选择模式,选择 “选择”或者“输入”,都可以。
在这里插入图片描述
再次打开页面,可以选择组织,如这里测试的zhangsan只有选择company后,才能登录。
在这里插入图片描述

四,创建应用集成gitlab

1,casdoor创建应用

官网有集成说明文档,也可以参考:https://casdoor.org/zh/docs/integration/ruby/gitlab

在应用里面创建一个新的应用,用于gitlab集成。我这里gitlab测试网址为:gitlab.ywbj.cc

填写主要的信息就2个,组织选择company
重定向url,根据自己gitlab网址来写,这里示例为:

https://gitlab.ywbj.cc/users/auth/openid_connect/callback

记下ID和密钥,在gitlab配置需要用到。
在这里插入图片描述

2,gitlab配置

在gitlab服务器,修改配置文件

vi /etc/gitlab/gitlab.rb

在OmniA添加配置,当然在可以任何地方都可以添加,我添加这里便于管理。
将casdoor的ID和密钥,url写入即可。

gitlab_rails['gitlab_username_changing_enabled'] = false
gitlab_rails['omniauth_auto_link_user'] = true                              
gitlab_rails['omniauth_allow_single_sign_on'] = ['openid_connect']          
gitlab_rails['omniauth_block_auto_created_users'] = false                                              
                                                                            
gitlab_rails['omniauth_providers'] = [                                      
    {                                                                       
        name: "openid_connect",                                             
        label: "Casdoor", # 可选的登录按钮标签,默认为 "Openid Connect"     
        args: {                                                                     
            name: "openid_connect",                                                 
            scope: ["openid", "profile", "email"],                                  
            response_type: "code",                                                  
            issuer:  "https://casdoor.ywbj.cc",                                     
            client_auth_method: "query",                                            
            discovery: true,                                                        
            verify_ssl: false,                                                      
            uid_field: "preferred_username",                                        
            client_options: {                                                       
                identifier: "8f14334a3bd68bf336a9",                                 
                secret: "9dd1b27f0e1a30226d01385c73b5aabdfd4a91bd",                 
                redirect_uri: "https://gitlab.ywbj.cc/users/auth/openid_connect/callback"
            }                                                                            
        }                                                                                
    }                                                                                    
] 

配置文件说明,官方说明没有以下配置,需要加上去,否则登录失败报错:Signing in using your Casdoor account without a pre-existing account in gitlab.example.com is not allowed.

原因GitLab 不允许通过 Casdoor 登录的用户自动创建 GitLab 账户。这是 GitLab 的默认行为,因为 OpenID Connect 登录要求用户账户在 GitLab 中事先存在。

#为了账号名称统一管理,禁止用户更改登录用户名。默认用户可以更改自己的用户名。
gitlab_rails['gitlab_username_changing_enabled'] = false

#omniauth_auto_link_user: 自动链接现有 GitLab 账户(如果 email 匹配)。
gitlab_rails['omniauth_auto_link_user'] = true

#omniauth_allow_single_sign_on: 允许通过 OpenID Connect 登录自动创建账户。
gitlab_rails['omniauth_allow_single_sign_on'] = ['openid_connect']

#omniauth_block_auto_created_users: 设置为 false,允许自动创建的账户默认启用。
gitlab_rails['omniauth_block_auto_created_users'] = false

配置完成后,重新加载配置

gitlab-ctl reconfigure

3,登录测试

在casdoor 的用户管理,创建一个用户,组织company,应用选择刚才创建的gitlab。
然后登录,可以看到gitlab应用。
在这里插入图片描述
点击应用跳转,或者直接打开gitlab.ywbj.cc 登录界面,可以直接使用casdoor用户登录。
在这里插入图片描述
点击casdoor,可以直接点击登录,或者输入casdoor账号密码登录即可。
在这里插入图片描述
登录成功后,gitlab会自动创建同样的名称的账号,账号集成完成。
在这里插入图片描述

五,webhook自动删除锁定用户

上面集成了成功创建了gitlab账号,但是在casdoor删除用户时,gitlab是不会单独删除的,gitlab有自己的用户数据库。

所以这时需要搭建一个中间服务,用于检测casdoor操作并传送到gitlab执行。

casdoor支持webhook,可以检测并发送到其他api的执行。
这里检测两个动作,一个封锁或解封用户,一个删除用户。

1,创建中间服务

要实现真正的全自动化并且灵活、安全、可扩展,最佳选择仍然是使用一个中间服务器。

中间服务器的实现方式,可以参考以下实现方案:

技术选型:
语言:Python(Flask/FastAPI)、Node.js(Express)、Go 等都可以。
部署:可以运行在 Docker 容器中,方便与现有系统集成。

这里选用python的flask搭建。

首先在gitlab使用管理员创建一个访问令牌token,权限为api和sudo权限。
python安装flask,编辑中间服务。

pip install flask
vim wehook.py

最后完整代码如下:

from flask import Flask, request, jsonify
import requests
import json
import logging

app = Flask(__name__)

# 设置日志
logging.basicConfig(
    filename='webhook.log',
    level=logging.INFO,
    format='%(asctime)s %(levelname)s: %(message)s'
)

GITLAB_API_URL = "https://gitlab.ywbj.cc/api/v4"
GITLAB_ACCESS_TOKEN = "glpat-CQk9oDfm-Dcz3f9NHojw"

@app.route('/casdoor-webhook', methods=['POST'])
def handle_webhook():
    # 记录请求内容
    logging.info("Headers: %s", request.headers)

    # Step 1: 解析 Webhook 数据
    data = request.data.decode('utf-8')
    try:
        json_data = json.loads(data)
    except json.JSONDecodeError:
        logging.error("Invalid JSON payload")
        return jsonify({"error": "Invalid JSON payload"}), 400

    #logging.info("Parsed JSON: %s", json_data)

    if json_data.get("action") == "update-user":
        object_data = json.loads(json_data["object"])
        name = object_data.get('name')
        email = object_data.get("email")
        is_forbidden = object_data.get("isForbidden", False)
        logging.info('Processing update-user for name: %s, email: %s, isForbidden: %s', name, email, is_forbidden)

        # Step 3: 在 GitLab 中查找用户 ID
        response = requests.get(
            f"{GITLAB_API_URL}/users?search={name}",
            headers={"Authorization": f"Bearer {GITLAB_ACCESS_TOKEN}"}
        )

        users = response.json()
        if not users or len(users) == 0:
            logging.warning("User not found in GitLab: %s", name)
            return jsonify({"error": "User not found"}), 404

        user_id = users[0]["id"]
        
        # Step 4: 根据 isForbidden 状态封锁或解封用户
        if is_forbidden:
            block_response = requests.post(
                f"{GITLAB_API_URL}/users/{user_id}/block",
                headers={"Authorization": f"Bearer {GITLAB_ACCESS_TOKEN}"}
            )
            logger.info("Block user response: %s", block_response.status_code)
            if block_response.status_code == 201:
                logger.info("User blocked successfully: %s", name)
                return jsonify({"message": "User blocked successfully"}), 200
            else:
                logger.error("Failed to block user: %s", name)
                return jsonify({"error": "Failed to block user"}), 500
        else:
            unblock_response = requests.post(
                f"{GITLAB_API_URL}/users/{user_id}/unblock",
                headers={"Authorization": f"Bearer {GITLAB_ACCESS_TOKEN}"}
            )
            logger.info("Unblock user response: %s", unblock_response.status_code)
            if unblock_response.status_code == 201:
                logger.info("User unblocked successfully: %s", name)
                return jsonify({"message": "User unblocked successfully"}), 200
            else:
                logger.error("Failed to unblock user: %s", name)
                return jsonify({"error": "Failed to unblock user"}), 500

    if json_data.get("action") == "delete-user":
        object_data = json.loads(json_data["object"])
        name = object_data.get('name')
        email = object_data.get("email")
        logging.info('Processing delete-user for name: %s, email: %s', name, email)

        # Step 3: 在 GitLab 中查找用户 ID
        response = requests.get(
            f"{GITLAB_API_URL}/users?search={name}",
            headers={"Authorization": f"Bearer {GITLAB_ACCESS_TOKEN}"}
        )
        #logging.info("GitLab search response: %s", response.json())

        users = response.json()
        if not users or len(users) == 0:
            logging.warning("User not found in GitLab: %s", name)
            return jsonify({"error": "User not found"}), 404

        user_id = users[0]["id"]

        # Step 4: 删除用户
        delete_response = requests.delete(
            f"{GITLAB_API_URL}/users/{user_id}",
            headers={"Authorization": f"Bearer {GITLAB_ACCESS_TOKEN}"}
        )
        if delete_response.status_code == 204:
            logging.info("User deleted successfully: %s", name)
            return jsonify({"message": "User deleted successfully"}), 200
        else:
            logging.error("Failed to delete user: %s", name)
            return jsonify({"error": "Failed to delete user"}), 500

    logging.warning("Invalid action: %s", json_data.get("action"))
    return jsonify({"error": "Invalid action"}), 400

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

最后运行即可测试。

python wehook.py

2,创建webhook

组织同样使用company。

链接:需要传给中间服务api的链接。
我这里用了nginx反代监听5000端口,使用域名https://webhook.ywbj.cc/casdoor-webhook作为api链接。

事件:检测update-user,delete-user两个动作,用于检测封锁还是删除用户。
在这里插入图片描述

3,casdoor封锁删除用户测试

在casdoor用户管理,编辑,打开被禁用,保存退出。
在这里插入图片描述
在gitlab管理员查看用户
在这里插入图片描述
同样,直接删除casdoor的用户,gitlab也直接删除。

在日志文件查看日志,也可以看到相关信息。

root@ht2024061430711:~/webhook# cat webhook.log 

2024-12-19 12:59:27,133 INFO: Processing update-user for name: zhangsan, email: 1i88kf@example.com, isForbidden: True
2024-12-19 12:59:28,059 INFO: Block user response: 201
2024-12-19 12:59:28,060 INFO: User blocked successfully: zhangsan
2024-12-19 12:59:28,062 INFO: 127.0.0.1 - - [19/Dec/2024 12:59:28] "POST /casdoor-webhook HTTP/1.0" 200 -
2024-12-19 13:01:42,837 INFO: Headers: Host: webhook.ywbj.cc

2024-12-19 13:01:42,839 INFO: Processing delete-user for name: zhangsan, email: 1i88kf@example.com
2024-12-19 13:01:43,328 INFO: User deleted successfully: zhangsan
2024-12-19 13:01:43,330 INFO: 127.0.0.1 - - [19/Dec/2024 13:01:43] "POST /casdoor-webhook HTTP/1.0" 200 -

六,构建docker持续运行(扩展)

以上已经实现基本功能,测试也可以直接用nohup 命令一直在后台运行。

但是便于稳定与管理,这里使用打包成docker镜像运行。

在运行flask应用时,经常看到警告

INFO: WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. 

表明 Flask 当前正在使用其内置开发服务器,这种服务器仅适合开发和调试环境,不推荐在生产环境中使用。要优化并适用于生产环境,可以采用 WSGI 服务器,如 Gunicorn 或 uWSGI。

所以生产环境,这里采用Gunicorn。

1,在文件夹中创建必要的文件

创建Dockerfile构建文件

# 使用官方 Python 基础镜像
FROM python:3.9-slim

# 设置工作目录
WORKDIR /app

# 复制当前目录到容器中
COPY . .

# 安装依赖
RUN pip install --no-cache-dir -r requirements.txt

# 暴露 Flask 默认端口
EXPOSE 5000

# 使用 Gunicorn 启动 Flask 应用
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "webhook:app"]

Gunicorn 是一种高性能的 WSGI HTTP 服务器,适合生产环境,性能更稳定,支持多线程和多进程。

如果需要更高的性能,还可以调整 Gunicorn 参数,例如指定工作进程数和线程数:

CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "3", "--threads", "2", "webhook:app"]

创建一个 requirements.txt 文件,用于存放 Python 依赖。内容如下:

flask
requests
gunicorn

创建一个 .dockerignore 文件,避免将不必要的文件复制到镜像中:

__pycache__/
*.pyc
*.log
*.txt~
*.git

2,重新配置日志持久化

修改代码,确保日志写入到特定路径,例如 ./logs/webhook.log:
用于挂载日志文件到宿主机。

# 确保日志路径
log_file_path = "./logs/webhook.log"

# 创建日志记录器
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
logger.propagate = False

# 添加文件日志
file_handler = logging.FileHandler(log_file_path)
file_handler.setLevel(logging.INFO)
file_formatter = logging.Formatter("%(asctime)s %(levelname)s: %(message)s")
file_handler.setFormatter(file_formatter)

# 添加控制台日志
stream_handler = logging.StreamHandler()
stream_handler.setLevel(logging.INFO)
stream_formatter = logging.Formatter("%(asctime)s %(levelname)s: %(message)s")
stream_handler.setFormatter(stream_formatter)

# 将日志处理器添加到记录器
logger.addHandler(file_handler)
logger.addHandler(stream_handler)

logger.info("Webhook server started. Logging initialized.")

后面loggin.info全部修改为logger.info即可

3,构建和运行 Docker 容器

#构建镜像
docker build -t flask-webhook .
#运行服务
docker run -d --restart unless-stopped --name flask-webhook -p 5000:5000 -v ./logs:/app/logs flask-webhook

查看服务并测试

root@ht2024061430711:~/webhook# docker ps
CONTAINER ID   IMAGE                       COMMAND                  CREATED         STATUS         PORTS                                       NAMES
0ffe0b07f6f7   flask-webhook               "gunicorn --bind 0.0…"   3 minutes ago   Up 3 minutes   0.0.0.0:5000->5000/tcp, :::5000->5000/tcp   flask-webhook

运行后查看日志

root@ht2024061430711:~/webhook# cat logs/webhook.log 
2024-12-19 15:36:38,991 INFO: Webhook server started. Logging initialized.
2024-12-19 15:36:38,991 INFO: Webhook server started. Logging initialized.
2024-12-19 07:53:26,415 INFO: Processing update-user for name: laowang, email: 1ul3ev@example.com, isForbidden: False
2024-12-19 07:53:27,171 INFO: Unblock user response: 201
2024-12-19 07:53:27,171 INFO: User unblocked successfully: laowang
2024-12-19 07:53:48,163 INFO: Headers: Host: webhook.ywbj.cc

2024-12-19 07:53:48,166 INFO: Processing delete-user for name: laowang, email: 1ul3ev@example.com
2024-12-19 07:53:48,583 INFO: User deleted successfully: laowang

运行正常。

下一步集成其他系统账号,有时间在更新。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OCR:文字识别

使用场景: 远程身份认证 自动识别录入用户身份/企业资质信息,应用于金融、政务、保险、电商、直播等场景,对用户、商家、主播进行实名身份认证,有效降低用户输入成本,控制业务风险 文档电子化 识别提取各类办公文档、合同文件、企…

亚信安全春节14天双倍假期通告

亚信安全14天双倍假期来袭 “网安福利王”再次实至名归 2024年 8773小时,31582680秒 亚信安全一直驰骋于云网安世界 奋战在“安全 数智化”的壮阔征途上 如今,新春的脚步渐近 长达14天的春节长假 能让我们暂且放下忙碌的工作 去除班味&#xff0c…

使用Python打开资源管理器并选择文件

from PySide6.QtWidgets import QFileDialogdef openSelectFile(Path):filename, _ QFileDialog.getOpenFileName(Path, "打开文件", "", "所有文件 (*)")if filename:print(f"选择的文件: {filename}")return filename 代码解释 &a…

uniapp blob格式转换为video .mp4文件使用ffmpeg工具

前言 介绍一下这三种对象使用场景 您前端一旦涉及到文件或图片上传Q到服务器,就势必离不了 Blob/File /base64 三种主流的类型它们之间 互转 也成了常态 Blob - FileBlob -Base64Base64 - BlobFile-Base64Base64 _ File uniapp 上传文件 现在已获取到了blob格式的…

五、windows上vscode构建c/c++环境

1、安装vscode 官网下载界面:https://code.visualstudio.com/Download 请根据电脑系统安装所需版本点击下载链接(一般情况下点击windows按钮即可)鼠标左键双击,即可运行安装程序,点击【确认】;选择安装路径…

ElasticSearch中的模糊搜索:为什么输入错误还能搜索出来?

引言 在日常搜索中,用户经常会因为拼写错误或输入笔误导致搜索结果不准确。然而,ElasticSearch 提供了一种非常智能的模糊搜索(Fuzzy Search)功能,使得即使关键词输入错误,依然能够返回准确或接近的结果。…

prober.php探针

raw.githubusercontent.com/kmvan/x-prober/master/dist/prober.php

DIY-ESP8266移动PM2.5传感器-带屏幕-APP

本教程将指导您制作一台专业级的空气质量检测仪。这个项目使用经济实惠的ESP8266和PMS5003传感器,配合OLED显示屏,不仅能实时显示PM2.5数值,还能通过手机APP随时查看数据。总成本70元,相比几百的用的便宜,用的心理踏实…

怎么将pdf中的某一个提取出来?介绍几种提取PDF中页面的方法

怎么将pdf中的某一个提取出来?传统上,我们可能通过手动截取屏幕或使用PDF阅读器的复制功能来提取信息,但这种方法往往不够精确,且无法保留原文档的排版和格式。此外,很多时候我们需要提取的内容可能涉及多个页面、多个…

2024微博用户消费趋势报告:七成城市用户更爱用微博

文 | 魏力 发布 | 大力财经 站在岁末回首这一年,在信息浪潮的汹涌翻涌之下,社交媒体平台犹如社会经济的晴雨表,精准地折射出大众生活与消费的万千景象。近日,大力财经看到一份报告,微博发布了《2024微博用户消费趋势…

#渗透测试#漏洞挖掘#红蓝攻防#护网#sql注入介绍06-基于子查询的SQL注入(Subquery-Based SQL Injection)

免责声明 本教程仅为合法的教学目的而准备,严禁用于任何形式的违法犯罪活动及其他商业行为,在使用本教程前,您应确保该行为符合当地的法律法规,继续阅读即表示您需自行承担所有操作的后果,如有异议,请立即停…

【数据安全】如何保证其安全

数据安全风险 数字经济时代,数据已成为重要的生产要素。智慧城市、智慧政务的建设,正以数据为核心,推动城市管理的智能化和公共服务的优化。然而,公共数据开放共享与隐私保护之间的矛盾日益凸显,如何在确保数据安全的…

武汉市电子信息与通信工程职称公示了

2024年武汉市电子信息与通信工程专业职称公示了,本次公示通过人员有109人。 基本这已经是今年武汉市工程相关职称最后公示了,等待出证即可。 为什么有人好奇,一样的资料,都是业绩、论文等,有的人可以过,有的…

勤研低代码平台:高效数据集成助力企业数字化转型

在数字化转型的浪潮中,企业对高效开发工具的需求日益增长。勤研低代码平台强大的开发能力和灵活的数据集成方案,是企业提升效率、降低成本的理想选择。数据集成作为勤研低代码平台的核心功能之一,为企业提供了高效整合和利用数据的能力&#…

【毕业设计】A079-基于Java的影院订票系统的设计与实现

🙊作者简介:在校研究生,拥有计算机专业的研究生开发团队,分享技术代码帮助学生学习,独立完成自己的网站项目。 代码可以查看项目链接获取⬇️,记得注明来意哦~🌹 赠送计算机毕业设计600个选题ex…

大腾智能受邀出席南京工业软件云工程应用创新中心工业软件生态应用推广大会并领奖

12月18日,南京工业软件云工程应用创新中心工业软件生态应用推广大会在南京江北新区圆满召开。本次大会由南京江北新区管委会主办,南京工业软件云工程应用创新中心、南京江北新区智能制造产业发展管理办公室联合承办,华为云计算技术有限公司支…

EasyPlayer.js播放器Web播放H.265要兼顾哪些方面?

在数字化时代,流媒体技术已经成为信息传播和娱乐消费的重要方式。随着互联网技术的飞速发展和移动设备的普及,流媒体服务正在重塑我们的生活和工作方式。从视频点播、在线直播到音乐流媒体,流媒体技术的广泛应用不仅改变了内容的分发和消费模…

fabric.js

目录 一、在canvas上画简单的图形 二、在canvas上用路径(Path)画不规则图形 三、在canvas上插入图片并设置旋转属性(angle) 四、让元素动起来(animate) 五、图像过滤器(filters)让图片多姿多彩 六、颜色模式(Color)和相互转换(toRgb、toHex) 七、对图形的渐变填充(Gradi…

白话AI大模型(LLM)原理

大模型(例如 GPT-4或类似的深度学习模型)是基于神经网络的系统,用于理解、生成文本、图像或其他数据类型。其工作原理可以分为以下几个核心步骤,我将通过易于理解的例子逐一解释。 1. 神经网络的基本概念 大模型背后有一个非常庞…

基于海思soc的智能产品开发(巧用mcu芯片)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 对于开发车规级嵌入式软件的同学来说,socmcu这样的组合,他们并不陌生。但是传统的工业领域,比如发动机、医疗或…