一、简介
1.1 什么是 OAuth2 协议?
OAuth2(Open Authorization 2.0)
协议是 RFC 6749 文件,是一种 用于授权的开放标准协议,用于通过第三方应用程序访问用户在某个服务提供商上存储的资源,而无需共享用户的凭证(例如用户名和密码)。
OAuth 引入了一个授权层,用来分离两种不同的角色:客户端和资源所有者。…资源所有者同意以后,资源服务器可以向客户端颁发令牌。客户端通过令牌,去请求数据。
OAuth2.0 的运行流程如下图所示:
(A)用户打开客户端以后,客户端要求用户给予授权。
(B)用户同意给予客户端授权。
(C)客户端使用上一步获得的授权,向认证服务器申请令牌。
(D)认证服务器对客户端进行认证以后,确认无误,同意发放令牌。
(E)客户端使用令牌,向资源服务器申请获取资源。
(F)资源服务器确认令牌无误,同意向客户端开放资源。
简而言之:你要获取我(资源服务器)的资源的话,得先找他(授权服务器)授权。
1.2 OAuth2 的4个角色
OAuth2.0 认证过程中,涉及到的四个角色如下 :
客户端(Client)
:代表资源所有者与授权服务器进行交互的应用程序。可以是Web应用程序、移动应用程序或第三方服务。资源所有者(Resource Owner)
:即用户或系统的代表,拥有受保护资源的所有权。授权服务器(Authorization Server)
:负责验证资源所有者的身份并颁发访问令牌(Access Token)给客户端。它通常是一个独立的服务器,可以与资源服务器分离或合并。资源服务器(Resource Server)
:存储受保护的资源,并根据令牌的有效性进行访问控制。资源服务器可以是一个或多个服务,可以授权服务器分离或合并。
1.3 OAuth2 的3种令牌
- 授权许可(Authorization Grant):资源所有者授权客户端访问受保护资源的凭证,如:授权码、隐式授权、密码授权、客户端凭证等。
- 令牌(Access Token):用于标识授权许可的凭证,包括访问令牌、刷新令牌和身份令牌等。
- 令牌端点(Token Endpoint):客户端与授权服务器交互以获取或刷新令牌的API端点。
1.4 OAuth2 的5种认证方式
OAuth2.0 提供了五种认证方式:
授权类型 | grant_type 值 | 适用场景 | 安全性 |
---|---|---|---|
授权码模式 | authorization_code | 有后端的 Web 应用 | 高 |
简化模式 | implicit | 纯前端应用(如 SPA) | 中 |
密码模式 | password | 高度信任的客户端(如第一方应用) | 低 |
客户端凭证模式 | client_credentials | 客户端访问自己的资源(M2M) | 中 |
刷新令牌模式 | refresh_token | 获取新的访问令牌 | 高 |
这里我们举例的是 授权码模式
,篇幅有限,没有集成数据库。如果需要支持其他模式,需要对 AuthorizationConfig.java
中的内容进行改造。
1.5 OAuth2 内置接口地址
OAuth2.0 内置了6个接口地址,如下所示:
- /oauth/authorize:授权端点(核心)
- /oauth/token:获取令牌端点(核心)
- /oauth/confirm_access:用户确认授权提交端点
- /oauth/error:授权服务错误信息端点
- /oauth/check_token:用于资源服务访问的令牌解析端点
- /oauth/token_key:提供公有密匙的端点,如果你使用JWT令牌的话
二、项目集成过程
在示例项目中,包含两个部分:
- springboot-oauth2-server:授权服务器
- springboot-oauth2-client:资源服务器
整体项目目录结构如下:
2.1 Maven依赖
<!-- Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- OAuth2.0 -->
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.6.0</version>
</dependency>
2.2 springboot-oauth2-server 授权服务器模块搭建和配置
1)目录结构
授权服务器目录结构如下:
2)SecurityConfig——授权服务器的 Security 配置
授权服务器的 Spring Security 配置代码如下:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* Spring Security 配置
*/
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("zhangsan")
.password(passwordEncoder().encode("123456"))
.authorities("/*");
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.csrf()
.disable();
}
}
3)AuthorizationConfig——授权服务器的 Authorize 配置
授权服务器的 Authorize 相关配置如下:
(@EnableAuthorizationService
注解切记需要配置哈。)
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.stereotype.Component;
/**
* Spring OAuth2 配置
*/
@Component
@EnableAuthorizationServer
public class AuthorizationConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private PasswordEncoder passwordEncoder;
/**
* 对应于配置AuthorizationServer安全认证的相关信息,创建ClientCredentialsTokenEndpointFilter核心过滤器
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.allowFormAuthenticationForClients()
.checkTokenAccess("permitAll()");
}
/**
* 配置客户端详情(Client Details)
* @param clients 用于配置客户端信息的工具类
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("security_appid")
.secret(passwordEncoder.encode("security_secret"))
.authorizedGrantTypes("authorization_code")
.scopes("all")
.resourceIds("security_resource")
.redirectUris("https://www.baidu.com/callback");
}
}
4)yml配置
server:
port: 8110
5)授权服务器验证
这里我们采用两种模式进行验证:授权码模式
、密码模式
。
获取token方式一:授权码模式
启动授权服务器后,请求如下地址:
/oauth/authorize
是 OAuth 框架封装的请求,网络请求路径必须像上面这样。其中 client_id
必须是我们在 Authorize 中 withClient
配置的数据,否则会报错。
输入上面的请求后,会被 Security 框架资源拦截,需要先进行登录。输入我们在 Security 中配置的账号密码:zhangsan/123456。
这是登录后的结果:
选择 同意 后,会出现跳转到 https://www.baidu.com/callback 目录并且会进行数据回调,如下所示:(注意这里在地址栏生成的 code
,下一步会用到)

接下来,我们使用 PostMan 请求 /oauth/token
接口来获取 Token 令牌。下面这些请求参数很重要,注意是 POST 请求方式。
获取token方式二:密码模式
不同的认证方式我们需要设置不同的 grant_type
值,具体参数如下所示:
可以看到,两种方式都成功获取了 access_token
,这样授权服务器就结束了。
2.3 springboot-oauth2-client 资源服务器模块搭建和配置
1)目录结构
资源服务器目录结构如下:
2)ResourceConfig——资源服务器配置
代码如下:
(注意,这里的 setCheckTokenEndpointUrl()
需要和授权服务器的配置相匹配才行。)
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
/**
* 资源服务器配置
*/
@Configuration
@EnableResourceServer
public class ResourceConfig extends ResourceServerConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
@Primary
public RemoteTokenServices remoteTokenServices() {
RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
remoteTokenServices.setCheckTokenEndpointUrl("http://localhost:8110/oauth/check_token");
remoteTokenServices.setClientId("security_appid");
remoteTokenServices.setClientSecret("security_secret");
return remoteTokenServices;
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("security_resource").stateless(true);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
http.authorizeRequests().anyRequest().authenticated();
}
}
3)yml配置
server:
port: 8111
4)DemoController——模拟接口资源
import com.demo.common.Result;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
/**
* <p> @Title DemoController
* <p> @Description 测试Controller
*
* @author ACGkaka
* @date 2025/2/7 11:46
*/
@RestController
public class DemoController {
@RequestMapping("test")
@ResponseBody
public Result<Object> test() {
return Result.succeed();
}
}
5)资源服务器验证
资源服务器启动之后,我们请求上面创建的接口地址:
可以看到,请求失败,在没有携带token的情况下会被鉴权拦截,返回 401
状态码,如下所示:
我们在请求头中加入上面在授权服务器中获取的token,就可以 成功访问资源 了,如下所示:
整理完毕,完结撒花~🌻
参考地址:
1.【Spring底层原理高级进阶】【SpringCloud整合Spring Security OAuth2】深入了解 Spring Security OAuth2:底层解析+使用方法+实战,https://cloud.tencent.com/developer/article/2392742
2.Oauth2详解-介绍(一),https://www.jianshu.com/p/84a4b4a1e833
3.SpringBoot整合Oauth2快速入门(入坑),https://blog.csdn.net/weixin_45982841/article/details/114378146
4.Spring Cloud Security OAuth2 中实现密码模式,https://cloud.tencent.com/developer/article/2264281
5.OAuth 2.0 的四种方式,http://www.ruanyifeng.com/blog/2019/04/oauth-grant-types.html
6.SpringBoot整合spring-security-oauth2完整实现例子,https://cloud.tencent.com/developer/article/1786077