Spring Security OAuth2实战:手把手搭建认证服务器与资源服务器(JWT+密码模式)
引言在现代微服务架构中安全认证与授权是绕不开的话题。OAuth2 作为业界标准的授权协议能够帮助我们实现第三方应用授权、单点登录以及资源保护。Spring Security 提供了对 OAuth2 的一流支持使得开发者可以快速构建符合标准的认证与资源服务器。本文将聚焦于密码模式Password Grant使用 JWT 令牌手把手带你完成一个从零开始的 OAuth2 实战项目。你将学到OAuth2 的核心角色与流程如何配置 Spring Security OAuth2 授权服务器如何配置资源服务器保护 REST APIJWT 令牌的生成与校验常见问题与注意事项本文示例基于Spring Boot 2.7 Spring Security OAuth2 2.5 JWT所有代码均可直接复制运行。如果你使用的是 Spring Boot 3.x官方推荐使用新的Spring Authorization Server但传统方案在大量存量项目中依然广泛存在且理解它是学习新方案的重要基础。一、OAuth2 核心概念速览在动手之前我们先快速回顾那些容易混淆的角色和概念。1.1 四大角色资源所有者Resource Owner通常就是用户拥有受保护资源。客户端Client想要访问用户资源的第三方应用如手机 App、Web 前段。授权服务器Authorization Server负责认证用户并颁发令牌。资源服务器Resource Server托管受保护资源根据令牌决定是否放行。1.2 四种授权模式授权码模式Authorization Code最安全、最完整适合前后端分离的第三方应用。简化模式Implicit不安全已不推荐。密码模式Resource Owner Password Credentials用户将用户名密码直接交给客户端客户端换取令牌。仅适用于高度信任的应用如官方 App。客户端模式Client Credentials无用户参与客户端以自己的身份访问资源。本文实战选用密码模式因为它最直观也最能体现认证流程的每一步。1.3 JWT 令牌JWTJSON Web Token是一种自包含的令牌格式包含头部、载荷和签名。相比于默认的内存令牌JWT 具有无状态、可扩展、跨域验证等优点。在 OAuth2 中JWT 既可以作为访问令牌Access Token也可以通过非对称加密实现资源服务器直接验证令牌无需频繁访问授权服务器。二、项目环境准备2.1 依赖配置创建一个 Spring Boot 2.7.x 项目pom.xml关键依赖如下parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.14/version /parent dependencies !-- Spring Security OAuth2 自动配置 -- dependency groupIdorg.springframework.security.oauth.boot/groupId artifactIdspring-security-oauth2-autoconfigure/artifactId version2.6.8/version /dependency !-- JWT 支持 -- dependency groupIdorg.springframework.security/groupId artifactIdspring-security-jwt/artifactId version1.1.1.RELEASE/version /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt/artifactId version0.9.1/version /dependency !-- web、security 基础 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-security/artifactId /dependency /dependencies项目启动类无需特殊注解保持默认即可。三、授权服务器Authorization Server实战授权服务器是整个 OAuth2 流程的心脏负责客户端认证、用户认证并颁发令牌。3.1 Spring Security 基础配置首先定义内存用户用于后续密码模式的身份验证。Configuration EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { Bean Override protected UserDetailsService userDetailsService() { InMemoryUserDetailsManager manager new InMemoryUserDetailsManager(); manager.createUser(User.withUsername(user) .password({noop}123456) // {noop} 表示不加密仅用于演示 .roles(USER) .build()); manager.createUser(User.withUsername(admin) .password({noop}654321) .roles(ADMIN) .build()); return manager; } Bean public PasswordEncoder passwordEncoder() { return NoOpPasswordEncoder.getInstance(); // 演示用生产必须用 Bcrypt } Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers(/oauth/**).permitAll() // 认证接口放行 .anyRequest().authenticated() .and() .formLogin().permitAll(); } }3.2 配置授权服务器EnableAuthorizationServer注解会启用 OAuth2 授权服务器并提供一个默认的端点映射如/oauth/token、/oauth/authorize。Configuration EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { Autowired private PasswordEncoder passwordEncoder; Autowired private AuthenticationManager authenticationManager; // 需要暴露 Autowired private UserDetailsService userDetailsService; // 配置客户端详情 Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient(client_app) // 客户端ID .secret(passwordEncoder.encode(123456)) // 客户端密钥需要用加密 .scopes(read, write) // 授权范围 .authorizedGrantTypes(password, refresh_token) // 允许密码模式和令牌刷新 .accessTokenValiditySeconds(7200) // 访问令牌有效期2小时 .refreshTokenValiditySeconds(86400); // 刷新令牌有效期24小时 } // 配置端点将认证管理器与用户服务绑定 Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) { endpoints.authenticationManager(authenticationManager) .userDetailsService(userDetailsService) .tokenStore(jwtTokenStore()) // 使用JWT存储 .accessTokenConverter(jwtAccessTokenConverter()); } // 暴露 AuthenticationManager 为 Bean Bean Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } // JWT 存储与转换器 Bean public TokenStore jwtTokenStore() { return new JwtTokenStore(jwtAccessTokenConverter()); } Bean public JwtAccessTokenConverter jwtAccessTokenConverter() { JwtAccessTokenConverter converter new JwtAccessTokenConverter(); converter.setSigningKey(my-secret-key); // JWT 签名密钥实际应更复杂 return converter; } }代码解释-configure(ClientDetailsServiceConfigurer)定义了哪些客户端可以请求令牌以及它们具备的授权模式、权限范围等。客户端密码必须加密后存储这里我们直接注入PasswordEncoder进行加密。-configure(AuthorizationServerEndpointsConfigurer)绑定了authenticationManager负责用户认证和userDetailsService加载用户并将TokenStore设置为 JWT 存储这样生成的令牌就是 JWT 格式。-jwtTokenStore()和jwtAccessTokenConverter()共同工作JwtAccessTokenConverter负责生成和解析 JWT签名密钥my-secret-key用于对称加密。生产环境中可使用非对称密钥对RSA并配置公私钥。四、资源服务器Resource Server实战资源服务器负责保护 API只有持有有效令牌的请求才能访问。4.1 配置资源服务器Configuration EnableResourceServer public class ResourceServerConfig extends ResourceServerConfigurerAdapter { // 使用与授权服务器相同的 JWT 配置 Bean public JwtAccessTokenConverter jwtAccessTokenConverter() { JwtAccessTokenConverter converter new JwtAccessTokenConverter(); converter.setSigningKey(my-secret-key); // 必须与授权服务器一致 return converter; } Bean public TokenStore jwtTokenStore() { return new JwtTokenStore(jwtAccessTokenConverter()); } Override public void configure(ResourceServerSecurityConfigurer resources) { resources.tokenStore(jwtTokenStore()); } // 配置 URL 保护规则 Override public void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers(/api/public).permitAll() .antMatchers(/api/admin/**).hasRole(ADMIN) .anyRequest().authenticated(); } }资源服务器通过EnableResourceServer注入一个 Spring Security 过滤器该过滤器会解析请求中的Authorization: Bearer token头调用TokenStore验证令牌。由于我们使用了 JWT验证是在本地完成的无状态不需要每次请求都去授权服务器询问。4.2 提供测试 APIRestController public class ApiController { GetMapping(/api/public) public String publicApi() { return 这是一个公开接口无需令牌即可访问。; } GetMapping(/api/user) public String userApi(Principal principal) { return 用户 principal.getName() 的资源访问成功; } GetMapping(/api/admin) public String adminApi() { return 管理员专属数据; } }五、项目启动与测试完成以上配置后启动项目我们通过curl或 Postman 进行测试。5.1 密码模式获取令牌请求POST /oauth/token参数如下-grant_typepassword-usernameuser你定义的内存用户-password123456-client_idclient_app-client_secret123456请求示例curl -X POST http://localhost:8080/oauth/token \ -H Content-Type: application/x-www-form-urlencoded \ -d grant_typepasswordusernameuserpassword123456client_idclient_appclient_secret123456成功响应会返回一个 JSON{ access_token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..., token_type: bearer, refresh_token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..., expires_in: 7199, scope: read write, jti: uuid }5.2 使用令牌访问受保护资源在请求头中添加Authorization: Bearer access_tokencurl -H Authorization: Bearer eyJhbGciOiJIUzI1NiJ9... \ http://localhost:8080/api/user返回用户 user 的资源访问成功如果尝试访问/api/admin由于user的角色为ROLE_USER会收到403 Forbidden。换成admin账号获取令牌后即可访问。5.3 刷新令牌当访问令牌过期时可用刷新令牌获取新的访问令牌curl -X POST http://localhost:8080/oauth/token \ -d grant_typerefresh_tokenrefresh_token你的刷新令牌client_idclient_appclient_secret123456六、常见问题与注意事项6.1 为什么使用 NoOpPasswordEncoder 和 {noop}示例中使用了明文存储密码仅用于本地演示。实际项目必须使用BCryptPasswordEncoder等强哈希算法并在用户存储中使用{bcrypt}前缀或直接通过PasswordEncoder编码。客户端密钥同样需要加密存储。6.2 401 Unauthorized 的可能原因请求头格式错误必须是Authorization: Bearer token令牌过期或无效签名资源服务器与授权服务器使用了不同的 JWT 签名密钥客户端 ID 或密钥错误6.3 403 Forbidden 分析403 表示令牌有效但无权限访问该资源。检查资源服务器的权限规则如hasRole(ADMIN)以及令牌中携带的角色信息。默认情况下Spring Security 会将用户的GrantedAuthority序列化到 JWT 载荷中资源服务器反序列化后使用。如果两边角色格式不匹配会导致 403。可以在JwtAccessTokenConverter中自定义转换规则。6.4 使用非对称密钥推荐生产环境建议使用 RSA 非对称密钥这样资源服务器只持有公钥授权服务器持有私钥。实现方式KeyPair keyPair KeyStoreKeyFactory( new ClassPathResource(jwt.jks), password.toCharArray() ).getKeyPair(jwt); converter.setKeyPair(keyPair);资源服务器则通过公钥验证converter.setVerifierKey(public_key_text);6.5 Spring Authorization Server 迁移如果你从spring-security-oauth2-autoconfigure迁移到 Spring Boot 3.x 或更高版本的 Spring Security 5.7应使用新的

相关新闻

Java ECC加密报错InvalidKeyException解析:加密与签名的本质区别

Java ECC加密报错InvalidKeyException解析:加密与签名的本质区别

1. 项目概述:当“私钥加密,公钥解密”遇上ECC 最近在调试一个Java项目,用到了椭圆曲线加密(ECC)。我本想实现一个“私钥签名,公钥验签”之外的场景——尝试用私钥加密一段数据,然后用公钥去解密…

2026/7/4 13:59:35 阅读更多 →
千笔论文写作工具:本科生学术写作全流程解决方案

千笔论文写作工具:本科生学术写作全流程解决方案

1. 论文写作痛点与解决方案作为一名经历过本科论文写作的过来人,我深知学术写作过程中的种种困扰。每到deadline前夜,图书馆里总能看到无数抓耳挠腮的同学,面对空白的文档界面一筹莫展。这种"学术拖延症"几乎成了大学生群体的通病&…

2026/7/4 13:57:34 阅读更多 →
本土化AI编程助手:从通用模型到场景专家的技术路径与落地实践

本土化AI编程助手:从通用模型到场景专家的技术路径与落地实践

🚀 30款热门AI模型一站整合,DeepSeek/GLM/Claude 随心用,限时 5 折。 👉 点击领海量免费额度 最近在技术圈里,一个关于“拼多多版Codex”融资的消息,引发了不少讨论。很多人第一反应是:又一个…

2026/7/4 13:55:34 阅读更多 →

最新新闻

Si4731与PIC18F87J60打造可编程网络收音机系统

Si4731与PIC18F87J60打造可编程网络收音机系统

1. 项目背景与硬件选型解析这个DIY音频探索项目的核心在于将收音机芯片与微控制器结合,打造一个可编程的旋律捕捉系统。Si4731作为Silicon Labs推出的数字调谐收音机芯片,支持AM/FM/SW接收,而PIC18F87J60则是Microchip旗下集成以太网功能的8位…

2026/7/4 15:02:22 阅读更多 →
大模型量化技术评测与实战指南

大模型量化技术评测与实战指南

1. 大模型量化技术概述在深度学习领域,模型量化已经成为解决大语言模型(LLM)部署难题的关键技术。简单来说,量化就是通过降低模型参数的数值精度来减少存储和计算开销的过程。想象一下,当你需要搬运一堆书籍时,精装版虽然精美但占…

2026/7/4 15:00:21 阅读更多 →
工业级多通道信号采集系统设计与优化实践

工业级多通道信号采集系统设计与优化实践

1. 工业级多通道信号控制系统的核心需求解析在工业自动化、电力监测和精密仪器领域,多通道信号采集与控制系统一直是核心基础设施。这类系统需要同时处理多个传感器信号(如温度、压力、电压等),并对执行机构进行精确控制。传统方案…

2026/7/4 14:58:21 阅读更多 →
如何高效处理Enigma Virtual Box打包文件:evbunpack工具详解

如何高效处理Enigma Virtual Box打包文件:evbunpack工具详解

如何高效处理Enigma Virtual Box打包文件:evbunpack工具详解 【免费下载链接】evbunpack Enigma Virtual Box Unpacker / 解包、脱壳工具 项目地址: https://gitcode.com/gh_mirrors/ev/evbunpack 你正在处理一个Enigma Virtual Box打包的文件,需…

2026/7/4 14:54:17 阅读更多 →
LV30条码扫描器与PIC18F4685微控制器的嵌入式解码方案

LV30条码扫描器与PIC18F4685微控制器的嵌入式解码方案

1. LV30条码扫描器与PIC18F4685微控制器的技术背景 LV30是一款高性能的线性影像式条码扫描引擎,采用先进的CMOS图像传感器技术,能够从各种介质(包括纸张、塑料、金属、玻璃等)表面捕获条码图像。其核心优势在于: 支持…

2026/7/4 14:50:15 阅读更多 →
Kimi赴港IPO:中文AI原生应用的价值重估与商业化验证

Kimi赴港IPO:中文AI原生应用的价值重估与商业化验证

1. 项目概述:这不是一次普通IPO,而是一场AI公司价值重估的临界点“媒体称Kimi正考虑赴港IPO,估值约180亿美元,如何看待Kimi选择在此时冲击上市?”——这句话背后藏着的,远不止一家AI公司的资本动作。作为国…

2026/7/4 14:48:15 阅读更多 →

日新闻

Memcached 1.6.43 发布:关键安全修复版本,多项问题得到解决

Memcached 1.6.43 发布:关键安全修复版本,多项问题得到解决

Memcached 1.6.43 正式发布,这是一个关键的安全修复版本,修复了多个方面的问题,还对部分功能进行了优化。 安全修复亮点 此次发布在安全修复上表现突出。binprot 避免了项目引用计数溢出,mcmc 因安全问题提升了上游版本号&#xf…

2026/7/4 0:04:29 阅读更多 →
终极指南:使用HMCL启动器跨平台畅玩Minecraft的完整解决方案

终极指南:使用HMCL启动器跨平台畅玩Minecraft的完整解决方案

终极指南:使用HMCL启动器跨平台畅玩Minecraft的完整解决方案 【免费下载链接】HMCL A Minecraft Launcher which is multi-functional, cross-platform and popular 项目地址: https://gitcode.com/gh_mirrors/hm/HMCL HMCL(Hello Minecraft! Lau…

2026/7/4 0:06:29 阅读更多 →
KMX63与PIC18F66K40在嵌入式HMI中的硬件协同与低功耗设计

KMX63与PIC18F66K40在嵌入式HMI中的硬件协同与低功耗设计

1. KMX63与PIC18F66K40的硬件协同架构解析KMX63作为一款三轴加速度计和磁力计组合传感器,与PIC18F66K40微控制器的搭配堪称嵌入式HMI开发的黄金组合。这套硬件组合的核心优势在于KMX63提供的高精度运动感知能力与PIC18F66K40强大的信号处理能力形成了完美互补。KMX6…

2026/7/4 0:06:29 阅读更多 →

周新闻

月新闻