Spring Security- 授权码模式的实战配置与实现

👋 大家好,欢迎来到我的技术博客!
📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用简单的方式讲清楚复杂的问题。
🎯 本文将围绕Spring Security这个话题展开,希望能为你带来一些启发或实用的参考。
🌱 无论你是刚入门的新手,还是正在进阶的开发者,希望你都能有所收获!
文章目录
- 🌿 Spring Security - 授权码模式的实战配置与实现
🌿 Spring Security - 授权码模式的实战配置与实现
在当今微服务架构盛行的时代,系统的安全性和用户身份验证变得尤为重要。OAuth2 作为一种开放授权协议,已经成为现代 Web 应用和 API 安全通信的标准之一。其中,授权码模式(Authorization Code Grant) 是 OAuth2 中最常用、最安全的授权方式,特别适用于拥有后端服务器的应用。
本文将深入探讨如何使用 Spring Security 实现 OAuth2 的授权码模式,从理论基础到实际代码配置,一步步带你构建一个完整的认证流程。我们将涵盖客户端注册、资源服务器保护、授权服务器搭建、令牌管理等核心内容,并通过清晰的代码示例帮助你掌握这一关键技术。
🎯 本文适合有一定 Spring Boot 和 Spring Security 基础的开发者阅读,目标是让你能够独立完成基于 Spring Security 的 OAuth2 授权码模式集成。
🔐 什么是 OAuth2?为什么选择授权码模式?
OAuth2 是一种用于委托授权的开放标准,它允许第三方应用在不获取用户密码的前提下,获得对用户资源的有限访问权限。比如你在某 App 登录时看到“使用微信登录”或“使用 Google 账号登录”,背后就是 OAuth2 在起作用。
OAuth2 定义了四种主要的授权模式:
- 授权码模式(Authorization Code)
- 隐式模式(Implicit)
- 密码模式(Password)
- 客户端凭证模式(Client Credentials)
其中,授权码模式是最推荐用于 Web 应用的安全模式,因为它具备以下优点:
- ✅ 所有敏感信息(如访问令牌)都在后端传输,不会暴露给浏览器。
- ✅ 支持刷新令牌(Refresh Token),可延长会话有效期。
- ✅ 可以进行客户端身份验证,防止伪造请求。
- ✅ 适用于有后端服务的应用程序(如 Spring Boot 后端 + Vue/React 前端)。
📚 更多关于 OAuth2 的官方说明可以参考:OAuth.net 官方文档
🧱 架构概览:授权码模式的工作流程
我们先来看一下授权码模式的整体流程,理解其交互过程有助于后续代码实现。
📌 关键点说明:
- 授权码
code是一次性的,只能使用一次。 access_token由客户端后台向授权服务器请求获得,不会经过前端。- 整个过程中用户的密码始终只发送给授权服务器,客户端无法获取。
🛠️ 技术选型与项目结构
我们将使用 Spring Boot 3.x 搭配 Spring Security 6+ 来实现整个流程。Spring Security 自 5.1 起全面支持 OAuth2,而从 5.7 开始进一步简化了配置方式,推荐使用 Java Config 替代 XML。
项目模块划分
本示例采用单体结构模拟完整流程,实际生产中建议拆分为多个微服务:
oauth2-demo/
├── auth-server/ → 授权服务器(颁发 token)
├── resource-server/ → 资源服务器(提供 API 数据)
└── client-app/ → 客户端应用(处理登录跳转 & 获取 token)
但为了便于演示,我们将在一个 Spring Boot 工程中整合 授权服务器 和 资源服务器 功能(即自包含模式),并通过不同端点区分职责。
⚠️ 注意:在正式环境中,授权服务器应独立部署以保证安全性。
🏗️ 初始化 Spring Boot 项目
首先创建一个标准的 Spring Boot 项目,引入必要的依赖项。
Maven 依赖(pom.xml)
<dependencies>
<!-- Web 支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Security 核心 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- OAuth2 Authorization Server 支持 -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-authorization-server</artifactId>
<version>1.2.0</version>
</dependency>
<!-- Thymeleaf 模板引擎(用于展示页面) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- 数据库支持(可选,用于持久化 client 信息) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
📌 特别注意:spring-security-oauth2-authorization-server 是 Spring 团队推出的官方授权服务器模块,取代了旧的 Spring Security OAuth 项目。
🔐 搭建授权服务器(Authorization Server)
授权服务器负责处理 /oauth2/authorize 和 /oauth2/token 请求,验证用户身份并发放令牌。
1. 启用授权服务器功能
我们需要通过配置类启用 OAuth2 授权服务器支持。
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
@Order(1)
public SecurityFilterChain authorizationServerSecurityFilterChain(
HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
return http.build();
}
@Bean
@Order(2)
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http)
throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/login", "/error").permitAll()
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login")
.permitAll());
return http.build();
}
@Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("123456")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
@Bean
public RegisteredClientRepository registeredClientRepository() {
RegisteredClient registeredClient = RegisteredClient.withId("1")
.clientId("demo-client")
.clientSecret("{noop}demo-secret") // 不加密明文密码
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
.redirectUri("http://127.0.0.1:8080/login/oauth2/code/demo-client") // 回调地址
.scope("read")
.scope("write")
.build();
return new InMemoryRegisteredClientRepository(registeredClient);
}
@Bean
public JWKSource<SecurityContext> jwkSource() throws NoSuchAlgorithmException {
RSAKey rsaKey = generateRsaKey();
JWKSet jwkSet = new JWKSet(rsaKey);
return (jwkSelector, context) -> jwkSelector.select(jwkSet);
}
private static RSAKey generateRsaKey() throws NoSuchAlgorithmException {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
return new RSAKey.Builder(publicKey)
.privateKey(privateKey)
.keyID(UUID.randomUUID().toString())
.build();
}
@Bean
public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
}
}
🔍 解析上述关键组件:
| 组件 | 说明 |
|---|---|
@EnableWebSecurity |
启用 Spring Security |
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity() |
自动配置 /oauth2/authorize 和 /token 端点 |
RegisteredClientRepository |
存储客户端信息(client_id, secret, scope 等) |
UserDetailsService |
提供用户账号体系支持 |
JWKSource |
用于 JWT 签名和验证的密钥源 |
🔐 安全提示:
生产环境不应使用 {noop} 加密方式,应改用 BCrypt 或 SCrypt。例如:
.password("$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG") // BCrypt encoded "password"
你可以使用在线工具生成:https://bcrypt-generator.com/
2. 配置授权端点路径与登录页
接下来我们需要定义 /login 页面以及相关控制器来支持用户登录。
创建 LoginController
@Controller
public class LoginController {
@GetMapping("/login")
public String login() {
return "login";
}
@GetMapping("/")
public String home(Model model) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
model.addAttribute("username", authentication.getName());
return "home";
}
}
创建 Thymeleaf 模板文件
在 src/main/resources/templates/login.html 中添加登录表单:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>登录</title>
<meta charset="UTF-8"/>
<style>
body { font-family: Arial; text-align: center; margin-top: 100px; }
input { padding: 10px; margin: 10px; width: 200px; }
button { padding: 10px 20px; background: #007bff; color: white; border: none; cursor: pointer; }
</style>
</head>
<body>
<h2>🔑 用户登录</h2>
<form th:action="@{/login}" method="post">
<div>
<input type="text" name="username" placeholder="用户名" required/>
</div>
<div>
<input type="password" name="password" placeholder="密码" required/>
</div>
<button type="submit">登录</button>
</form>
<p style="color: red;" th:if="${param.error}">
登录失败,请检查用户名或密码!
</p>
</body>
</html>
主页模板 home.html:
<!DOCTYPE html>
<html>
<head><title>首页</title></head>
<body style="text-align:center; padding:100px;">
<h1>🎉 欢迎回来,<span th:text="${username}"></span>!</h1>
<p>你现在已成功通过身份验证。</p>
<a href="/logout">登出</a>
</body>
</html>
3. 添加退出登录支持
Spring Security 默认支持 /logout,但我们也可以自定义行为。
@Bean
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/login", "/error", "/logout-success").permitAll()
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login")
.failureUrl("/login?error")
.permitAll()
)
.logout(logout -> logout
.logoutSuccessUrl("/login?logout")
.permitAll()
);
return http.build();
}
现在访问 /logout 即可安全退出当前会话。
💻 客户端应用接入授权码流程
虽然我们目前是在同一个工程中运行授权服务器,但在逻辑上我们可以将其视为两个角色:客户端应用 和 授权服务器。
假设我们的客户端运行在 http://127.0.0.1:8080,当用户尝试访问某个需要登录的功能时,会被重定向到授权服务器进行认证。
1. 注册客户端回调地址
回顾之前的 RegisteredClient 配置:
.redirectUri("http://127.0.0.1:8080/login/oauth2/code/demo-client")
这个 URL 是 Spring Security 内置的 OAuth2 登录处理器,默认路径为:
/login/oauth2/code/{registrationId}
其中 {registrationId} 对应客户端名称,在这里是 demo-client。
2. 触发授权跳转
我们可以通过一个简单的链接触发授权流程:
<!-- 在任意页面添加 -->
<a href="/oauth2/authorize?response_type=code&client_id=demo-client&scope=read&redirect_uri=http://127.0.0.1:8080/login/oauth2/code/demo-client&state=xyzabc">
使用 OAuth2 登录
</a>
不过更常见的方式是使用 Spring Security 提供的自动跳转机制。
🔄 Spring Security 自动化登录流程
实际上,我们不需要手动拼接 URL。Spring Security 提供了 OAuth2LoginAuthenticationFilter 来自动处理整个流程。
只需在配置中启用 OAuth2 登录即可。
修改 SecurityConfig 中的 FilterChain
@Bean
@Order(2)
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/login", "/error", "/logout-success").permitAll()
.anyRequest().authenticated()
)
.oauth2Login(oauth2 -> oauth2
.redirectionEndpoint(redir -> redir
.baseUri("/login/oauth2/code/*")
)
)
.formLogin(form -> form
.loginPage("/login")
.failureUrl("/login?error")
.permitAll()
)
.logout(logout -> logout
.logoutSuccessUrl("/login?logout")
.permitAll()
);
return http.build();
}
此时当你访问任何受保护资源(如 /),系统会自动拦截并跳转到 /oauth2/authorize。
🧩 自定义授权确认页面(Consent Page)
默认情况下,用户登录后会直接返回授权码,没有“是否同意授权”的确认页。但在正式系统中,我们应该让用户明确授权范围。
创建 Consent Controller
@Controller
public class ConsentController {
@GetMapping("/oauth2/consent")
public String consent(
@RequestParam("client_id") String clientId,
@RequestParam("scope") String scope,
Model model) {
// 查询客户端信息
RegisteredClient client = registeredClientRepository().findById(clientId);
model.addAttribute("clientName", client.getClientName());
model.addAttribute("scopes", scope.split(" "));
return "consent";
}
}
⚠️ 注意:上面代码中 registeredClientRepository() 需要从容器中注入,不能直接调用。我们稍后会改为依赖注入。
创建 consent.html 模板
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>授权确认</title>
<style>body{font-family:Arial;text-align:center;margin:100px;}</style>
</head>
<body>
<h2>🛡️ 授权请求</h2>
<p><strong th:text="${clientName}"></strong> 想要访问你的账户:</p>
<ul style="list-style:none;padding:0;">
<li th:each="s : ${scopes}">
🔹 权限:<span th:text="${s}"></span>
</li>
</ul>
<form method="post" action="/oauth2/authorize">
<input type="hidden" name="client_id" value="demo-client"/>
<input type="hidden" name="scope" th:value="${#arrays.toString(scopes)}"/>
<input type="hidden" name="response_type" value="code"/>
<input type="hidden" name="redirect_uri" value="http://127.0.0.1:8080/login/oauth2/code/demo-client"/>
<button type="submit" name="consent" value="allow"
style="padding:10px 20px;background:green;color:white;border:none;margin:5px;">
允许
</button>
<button type="submit" name="consent" value="deny"
style="padding:10px 20px;background:red;color:white;border:none;margin:5px;">
拒绝
</button>
</form>
</body>
</html>
配置授权服务器使用自定义确认页
@Bean
@Order(1)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
// 自定义授权确认页面
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
.authorizationEndpoint(authorizationEndpoint ->
authorizationEndpoint.consentPage("/oauth2/consent")
);
return http.build();
}
这样,用户在登录后就会看到授权确认页,增强用户体验和安全性。
🔑 使用数据库存储客户端信息(持久化)
目前我们使用的是内存中的 InMemoryRegisteredClientRepository,适合测试但不适合生产。
下面我们演示如何将客户端信息保存到 H2 数据库中。
1. 创建 Client 表结构
Spring Authorization Server 提供了默认的 schema,位于:
我们在 schema.sql 中创建必要表格:
DROP TABLE IF EXISTS oauth2_registered_client;
CREATE TABLE oauth2_registered_client (
id varchar(100) NOT NULL,
client_id varchar(100) NOT NULL,
client_id_issued_at timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL,
client_secret varchar(200) DEFAULT NULL,
client_secret_expires_at timestamp DEFAULT NULL,
client_name varchar(200) NOT NULL,
client_authentication_methods varchar(1000) NOT NULL,
authorization_grant_types varchar(1000) NOT NULL,
redirect_uris varchar(1000) DEFAULT NULL,
post_logout_redirect_uris varchar(1000) DEFAULT NULL,
scopes varchar(1000) NOT NULL,
client_settings varchar(2000) NOT NULL,
token_settings varchar(2000) NOT NULL,
PRIMARY KEY (id)
);
2. 插入初始数据(data.sql)
INSERT INTO oauth2_registered_client (
id, client_id, client_name, client_secret,
client_authentication_methods, authorization_grant_types, redirect_uris, scopes,
client_settings, token_settings
) VALUES (
'1', 'demo-client', 'Demo Client', '{noop}demo-secret',
'client_secret_basic', 'authorization_code,refresh_token', 'http://127.0.0.1:8080/login/oauth2/code/demo-client',
'read write',
'{"@class":"java.util.HashMap","settings.client.require-proof-key":false,"settings.client.require-authorization-consent":true}',
'{"@class":"java.util.HashMap","settings.token.reuse-refresh-tokens":true,"settings.token.id-token-signature-algorithm":["org.springframework.security.oauth2.jose.jws.SignatureAlgorithm","RS256"],"settings.token.access-token-time-to-live":["java.time.Duration",900.000000],"settings.token.refresh-token-time-to-live":["java.time.Duration",3600.000000]}'
);
3. 替换为 JDBC 实现
@Bean
public RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate) {
return new JdbcRegisteredClientRepository(jdbcTemplate);
}
✅ 此时客户端信息将从数据库加载,支持动态注册和更新。
🛡️ 资源服务器保护 API
除了授权服务器,我们还需要一个资源服务器来提供受保护的数据接口。
1. 启用资源服务器支持
@Configuration
public class ResourceServerConfig {
@Bean
@Order(3)
public SecurityFilterChain resourceServerSecurityFilterChain(HttpSecurity http)
throws Exception {
http
.securityMatcher("/api/**")
.authorizeHttpRequests(auth -> auth
.anyRequest().hasAuthority("SCOPE_read")
)
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> jwt.decoder(jwtDecoder()))
);
return http.build();
}
@Bean
public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
}
}
2. 创建受保护的 API 接口
@RestController
public class ApiController {
@GetMapping("/api/user")
public Map<String, Object> getUser(Authentication authentication) {
Map<String, Object> data = new HashMap<>();
data.put("name", authentication.getName());
data.put("authorities", authentication.getAuthorities());
data.put("timestamp", System.currentTimeMillis());
return data;
}
}
现在访问 http://localhost:8080/api/user 将需要有效的 Bearer Token。
🧪 测试授权码流程
启动应用后,按以下步骤测试:
- 浏览器访问
http://localhost:8080 - 被重定向到
/login - 输入用户名
user,密码123456 - 登录成功后进入授权确认页(Consent)
- 点击“允许”,跳转回客户端
- 成功登录并显示欢迎页面
- 访问
/api/user查看 JSON 数据
你也可以使用 Postman 或 curl 手动测试授权流程:
# 第一步:获取授权码
open "http://localhost:8080/oauth2/authorize?response_type=code&client_id=demo-client&scope=read&redirect_uri=http://127.0.0.1:8080/login/oauth2/code/demo-client&state=abc123"
# 第二步:用 code 换 token(需 base64 编码 client credentials)
curl -X POST http://demo-client:demo-secret@localhost:8080/oauth2/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code&code=YOUR_CODE_HERE&redirect_uri=http://127.0.0.1:8080/login/oauth2/code/demo-client"
返回结果类似:
{
"access_token": "eyJraWQiOiI...",
"token_type": "Bearer",
"expires_in": 900,
"scope": "read",
"refresh_token": "eyJraWQiOiI..."
}
然后用该 token 请求资源:
curl -H "Authorization: Bearer eyJraWQiOiI..." http://localhost:8080/api/user
🧰 常见问题与最佳实践
❓ 为什么我的 redirect_uri 不匹配?
确保 RegisteredClient 中注册的 redirect_uri 与实际请求完全一致(包括协议、端口、路径)。
错误示例:
- 注册的是
http://localhost:8080/callback - 实际请求
http://127.0.0.1:8080/callback→ ❌ 失败!
✅ 解决方案:统一使用 127.0.0.1 或 localhost,避免混用。
❓ 如何支持 HTTPS?
开发阶段可用 http,但生产环境必须使用 https。若需本地测试 HTTPS,可通过以下方式开启:
# application.yml
server:
port: 8443
ssl:
key-store: classpath:keystore.p12
key-store-password: changeit
key-store-type: PKCS12
key-alias: tomcat
生成证书命令:
keytool -genkeypair -alias tomcat -keyalg RSA -keysize 2048 -storetype PKCS12 \
-keystore keystore.p12 -validity 3650
❓ 如何自定义 Token 内容?
可以通过 OAuth2TokenCustomizer 扩展 JWT 内容:
@Bean
public OAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer() {
return context -> {
if (context.getTokenType().getValue().equals("id_token")) {
context.getClaims().claim("custom_claim", "custom_value");
}
};
}
❓ 如何实现单点登录(SSO)?
结合 Spring Session + Redis 可实现跨应用共享登录状态:
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
配置 Redis 存储 session,多个服务连接同一实例即可实现 SSO。
📚 参考文档:Spring Session 官方介绍
🧱 总结:授权码模式的核心要点
| 要点 | 说明 |
|---|---|
| ✅ 安全性高 | 授权码作为中间凭证,access_token 不暴露给浏览器 |
| ✅ 支持刷新令牌 | 可长期维持用户登录状态 |
| ✅ 客户端需保密 | client_secret 必须安全存储,不可泄露 |
| ✅ 必须有后端 | 适用于 Web 应用,不适用于纯静态站点 |
| ✅ 推荐使用 HTTPS | 所有通信应加密传输 |
🌐 扩展阅读与参考资料
- 📘 OAuth 2.0 官方规范 RFC 6749 —— 最权威的技术文档
- 📘 OpenID Connect Core 规范 —— 基于 OAuth2 的身份层协议
- 🎓 Okta Developer Blog - OAuth2 Guide —— 实战案例丰富
- 🛠️ OAuth Debugger Tool —— 在线调试 OAuth2 请求参数
🎯 结语
通过本文的详细讲解与实战编码,相信你已经掌握了如何使用 Spring Security 实现 OAuth2 的 授权码模式。我们从零开始搭建了一个完整的授权服务器,实现了用户登录、授权确认、令牌发放、资源访问等全流程功能,并介绍了数据库持久化、自定义 Token、安全配置等进阶技巧。
OAuth2 并非银弹,但它确实是现代分布式系统中保障安全与解耦的关键技术之一。掌握授权码模式,是你迈向企业级应用开发的重要一步。
🚀 下一步你可以尝试:
- 将授权服务器独立部署为微服务
- 集成第三方登录(如 GitHub、Google)
- 实现动态客户端注册(Dynamic Client Registration)
- 添加审计日志记录每次授权行为
愿你在构建安全系统的道路上越走越稳,写出既强大又可靠的代码!💪
🌈 “安全不是功能,而是责任。” —— 每一位开发者都应铭记于心。
🙌 感谢你读到这里!
🔍 技术之路没有捷径,但每一次阅读、思考和实践,都在悄悄拉近你与目标的距离。
💡 如果本文对你有帮助,不妨 👍 点赞、📌 收藏、📤 分享 给更多需要的朋友!
💬 欢迎在评论区留下你的想法、疑问或建议,我会一一回复,我们一起交流、共同成长 🌿
🔔 关注我,不错过下一篇干货!我们下期再见!✨
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐
所有评论(0)