
纲要OAuth2 协议的核心角色Resource Owner资源所有者Client客户端应用Authorization Server授权服务器Resource Server资源服务器客户端注册与凭据client_id、client_secret及回调地址主要授权流程概览授权码流程Authorization Code资源所有者密码流程Resource Owner Password Credentials客户端凭证流程Client Credentials刷新令牌流程Refresh Token基于 Spring Security OAuth2 的实践项目结构依赖与配置授权服务器配置资源服务器与安全配置客户端注册示例OAuth2 协议的核心角色OAuth2 是一种业界标准的授权协议广泛用于微服务、社交登录等场景解决跨系统间的授权问题。一个典型的 OAuth2 交互涉及四个角色Resource Owner资源所有者拥有受保护资源的最终用户例如微博账号的持有人。当第三方应用希望访问其资源时需要获得该用户的明确许可。Client客户端希望访问用户资源的第三方应用。它可以是 Web 应用、移动 App 或后端服务。例如使用微博登录的“简书”网站在发起授权请求的那一刻就是客户端。Authorization Server授权服务器负责对用户进行身份认证并颁发令牌。它提供授权页面询问用户是否同意客户端访问特定资源。微博的授权页面就是一个授权服务器逻辑上可以与资源服务器部署在一起但角色是分离的。Resource Server资源服务器托管受保护资源的服务器仅接受持有有效令牌的请求。微博的用户信息、微博内容、评论等都属于资源服务器的范畴。在同一个系统中授权服务器本身也可以是资源服务器例如用户与角色管理。这些角色之间的交互解决了“如何在无需暴露用户密码的情况下让第三方应用安全访问用户资源”的问题。客户端注册与凭据任何客户端在使用 OAuth2 之前都需要先向授权服务器注册。注册时通常需要提供应用名称与描述回调地址重定向 URI可能的图标或主页地址授权服务器会为该客户端生成一对凭据client_id客户端的唯一标识公开client_secret客户端的密钥必须保密在某些授权流程如客户端凭证流程中client_id和client_secret就相当于客户端自身的“用户名”和“密码”用于向授权服务器证明自己的身份。对于不需要用户参与的场景如微服务间的调用仅凭这对凭据即可完成认证。主要授权流程OAuth2 定义了多种授权类型以覆盖不同场景。Spring Security 对以下四种流程提供了原生支持。授权码流程 (Authorization Code)这是最主流、最安全的流程适用于 Web 应用、SPA 或移动端 App。其核心特征是“两次跳转、两次请求”用户在客户端点击“微博登录”浏览器被重定向至授权服务器的登录页。用户在授权服务器上使用自己的账号登录并同意客户端访问特定资源的授权。授权服务器将浏览器重定向回客户端预先注册的回调地址并在 URL 中携带一个授权码。客户端在后端用该授权码结合client_id和client_secret向授权服务器换取访问令牌access_token和刷新令牌refresh_token。客户端使用access_token请求资源服务器的 API获取用户数据或执行操作。以下是该流程的时序图资源服务器授权服务器客户端用户(资源所有者)资源服务器授权服务器客户端用户(资源所有者)点击社交登录重定向到授权端点 (含client_id,redirect_uri等)显示登录页与授权页输入凭证并授权重定向回回调地址并携带授权码使用授权码换取令牌 (client_idsecret)返回 access_token (及refresh_token)请求资源 (Header中携带Bearer token)返回受保护资源资源所有者密码流程 (Resource Owner Password Credentials)此流程适用于受高度信任的官方应用用户体验更好但安全性较低。用户直接将用户名和密码提供给客户端由客户端携带这些凭证去授权服务器直接换取令牌。注意第三方客户端不应使用此模式因为它需要直接接触用户的明文密码。客户端收集用户的用户名和密码。客户端向授权服务器发送 POST 请求包含grant_typepassword、username、password以及自己的client_id/client_secret。授权服务器验证成功后直接返回令牌无需跳转或授权码。客户端凭证流程 (Client Credentials)专门为后端服务间调用设计整个交互不涉及用户。客户端仅凭自己的client_id和client_secret直接获取访问令牌用于访问其他微服务的资源。服务 A 使用自己的client_id与client_secret请求授权服务器的令牌端点grant_typeclient_credentials。授权服务器验证服务身份后返回令牌。服务 A 使用该令牌访问服务 B 的 API服务 B 通过验证令牌来确认调用方身份。该模式中没有“用户名密码”概念服务的身份等价于client_id。刷新令牌流程 (Refresh Token)访问令牌access_token通常具有较短的有效期。当它过期时客户端无需让用户重新登录可以使用刷新令牌refresh_token来换取新的访问令牌。客户端向授权服务器发送请求grant_typerefresh_token并附上有效的refresh_token以及客户端凭据。授权服务器验证后返回一对新的access_token和refresh_token旧令牌失效实现令牌轮换。基于 Spring Security OAuth2 的实践下面构建一个最小的授权服务器与资源服务器演示授权码流程。所有代码可直接运行基于 Spring Boot 2.x spring-security-oauth2-autoconfigure。项目结构src/main/java/com/example/oauth2 ├── config │ ├── AuthorizationServerConfig.java │ ├── ResourceServerConfig.java │ └── SecurityConfig.java ├── OAuth2Application.java src/main/resources └── application.properties依赖配置使用 Spring Boot 2.7.x 与spring-security-oauth2-autoconfigure。在pom.xml中添加parentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion2.7.18/version/parentdependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-security/artifactId/dependencydependencygroupIdorg.springframework.security.oauth.boot/groupIdartifactIdspring-security-oauth2-autoconfigure/artifactIdversion2.7.18/version/dependency/dependencies配置文件application.properties中定义客户端信息这里以内嵌方式简化生产环境应从数据库加载# 授权服务器端口 server.port8080 # 内存中注册的客户端 security.oauth2.client.client-idmyclient security.oauth2.client.client-secretmysecret security.oauth2.client.authorized-grant-typesauthorization_code,refresh_token,password,client_credentials security.oauth2.client.scoperead,write security.oauth2.client.redirect-urihttp://localhost:8080/login/oauth2/code/myclient # 令牌签名密钥演示用对称密钥 security.oauth2.authorization.jwt.key-valuemy-jwt-signing-key授权服务器配置packagecom.example.oauth2.config;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.context.annotation.Configuration;importorg.springframework.security.crypto.password.NoOpPasswordEncoder;importorg.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;importorg.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;importorg.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;importorg.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;ConfigurationEnableAuthorizationServerpublicclassAuthorizationServerConfigextendsAuthorizationServerConfigurerAdapter{Overridepublicvoidconfigure(ClientDetailsServiceConfigurerclients)throwsException{// 在内存中注册一个客户端clients.inMemory().withClient(myclient).secret(NoOpPasswordEncoder.getInstance().encode(mysecret))// 生产环境请使用 BCrypt.authorizedGrantTypes(authorization_code,refresh_token,password,client_credentials).scopes(read,write).redirectUris(http://localhost:8080/login/oauth2/code/myclient).autoApprove(true);// 演示时自动批准授权实际可设为false}Overridepublicvoidconfigure(AuthorizationServerEndpointsConfigurerendpoints){// 使用默认端点即可实际可注入 AuthenticationManager 等}}安全配置为授权服务器提供用户身份认证资源所有者。这里创建一个内存用户。packagecom.example.oauth2.config;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.security.config.annotation.web.builders.HttpSecurity;importorg.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;importorg.springframework.security.core.userdetails.User;importorg.springframework.security.core.userdetails.UserDetailsService;importorg.springframework.security.provisioning.InMemoryUserDetailsManager;ConfigurationpublicclassSecurityConfigextendsWebSecurityConfigurerAdapter{BeanOverrideprotectedUserDetailsServiceuserDetailsService(){InMemoryUserDetailsManagermanagernewInMemoryUserDetailsManager();manager.createUser(User.withUsername(user).password({noop}password)// 明文密码仅用于演示.roles(USER).build());returnmanager;}Overrideprotectedvoidconfigure(HttpSecurityhttp)throwsException{http.authorizeRequests().antMatchers(/oauth/**).permitAll().anyRequest().authenticated().and().formLogin();// 提供表单登录页}}资源服务器与受保护资源为了在同一进程中演示资源服务器启用EnableResourceServer并提供一个 REST 接口。packagecom.example.oauth2.config;importorg.springframework.context.annotation.Configuration;importorg.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;ConfigurationEnableResourceServerpublicclassResourceServerConfig{}创建受保护 APIpackagecom.example.oauth2;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RestController;importjava.security.Principal;RestControllerpublicclassUserController{GetMapping(/user)publicPrincipaluser(Principalprincipal){returnprincipal;// 返回当前认证用户的身份信息}}启动类packagecom.example.oauth2;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;SpringBootApplicationpublicclassOAuth2Application{publicstaticvoidmain(String[]args){SpringApplication.run(OAuth2Application.class,args);}}运行与验证启动应用后可以通过如下步骤体验授权码流程浏览器访问授权端点http://localhost:8080/oauth/authorize?response_typecodeclient_idmyclientredirect_urihttp://localhost:8080/login/oauth2/code/myclientscoperead系统会跳转到登录页面输入用户名user、密码password。登录后会展示授权页本例已设置autoApprove(true)因此自动批准随后重定向到回调地址并在 URL 中携带授权码。使用 curl 或 Postman 换取令牌POST http://localhost:8080/oauth/tokenAuthorization: Basic myclient:mysecretContent-Type: application/x-www-form-urlencodedbody: grant_typeauthorization_codecode上一步获得的coderedirect_urihttp://localhost:8080/login/oauth2/code/myclient获取access_token后携带令牌访问/userGET http://localhost:8080/userAuthorization: Bearer access_token总结本文梳理了 OAuth2 的四大角色及三种主要授权流程授权码、密码、客户端凭证并结合 Spring Security OAuth2 给出了一个可运行的授权码示例。在实际项目中授权服务器与资源服务器往往独立部署且令牌通常采用 JWT 格式以实现无状态验证。通过理解这些核心角色与流程开发者可以灵活构建微服务间的安全通信或为应用集成社交登录功能。