当前位置:网站首页>【OAuth2】二十、OAuth2扩展协议 PKCE
【OAuth2】二十、OAuth2扩展协议 PKCE
2022-08-10 08:23:00 【北城小林】
一、什么是PKCE
PKCE 全称是 Proof Key for Code Exchange(代码交换证明密钥), 在2015年发布, 它是 OAuth 2.0 核心的一个扩展协议, 所以可以和现有的授权模式结合使用,比如 Authorization Code + PKCE, 这也是最佳实践,PKCE 最初是为移动设备应用和本地应用创建的, 主要是为了减少公共客户端的授权码拦截攻击。
PKCE其实主要是通过在授权的过程中增加了code_challenge和code_verifier两个元素来对整个流程进行验证,防止code被第三方截取的情况。 实际上它的原理是客户端提供一个自创建的证明给授权服务器, 授权服务器通过它来验证客户端,把访问令牌(access_token) 颁发给真实的客户端而不是伪造的。
参考
参考2
1、回顾OAuth2.0授权码模式流程:
https://www.rfc-editor.org/rfc/rfc6749
(A)客户端请求资源所有者授权。 可以直接向资源所有者发出授权请求 (如所示),或者最好通过授权间接地 服务器作为中介。
(B)客户端收到授权授予,这是a 表示资源所有者授权的凭据, 使用本例中定义的四种授权类型之一表示 说明或使用延期授权类型方法使用的方法决定了授权授予类型
客户机请求授权和支持的类型 授权服务器。
客户端通过认证请求访问令牌 授权服务器,并给出授权授权。
(D)授权服务器对客户端进行认证和验证 授权授予,如果有效,将发出访问令牌。
2、 授权码模式

- 1、客户端携带 client_id, scope, redirect_uri, state 等信息引导用户请求授权服务器的授权端点下发 code。
- 2、授权服务器验证客户端身份,验证通过则询问用户是否同意授权(此时会跳转到用户能够直观看到的授权页面,等待用户点击确认授权)。
- 3、假设用户同意授权,此时授权服务器会将 code 和 state(如果客户端传递了该参数)拼接在 redirect_uri 后 面,以302(重定向)形式下发 code。
- 4、客户端携带 code, redirect_uri, 以及 client_secret 请求授权服务器的令牌端点下发 access_token。
- 5、授权服务器验证客户端身份,同时验证 code,以及 redirect_uri 是否与请求 code 时相同,验证通过后下发 access_token,并选择性下发 refresh_token,支持令牌的刷新。
参考
3、PKCE的流程
PKCE主要是通过在授权的过程中增加了code_challenge和code_verifier两个元素来对整个流程进行验证,防止code被第三方截取的情况。具体流程如下:
也就是在原来请求的token中添加了code_challenge和code_verifier参数
- code_verifier
一个Client端生成的随机字符串(由字母,数字,- ,. , ,~ 组成)。
调用应用程序(SPA)创建的密钥,该密钥可由授权服务器验证密钥被称为 Code Verifier(代码验证器) - code_challenge
调用应用程序为 Code Verifier 创建一个转换值,称为 Code Challenge,HTTPS 发送该值(Code Challenge)去检索 Authorization Code.
这样就提高了从认证服务器获取 token的安全性。
4、 PKCE的流程说明
- 1、 OAuth2客户端生成 code_verifier,并使用 code_challenge_method 计算 code_challenge,具体的算法稍后会详细讲解。
- 2、OAuth2客户端发起/oauth2/authorize授权请求,携带 code_challenge 和 code_challenge_method 这两个参数。
- 3、OAuth2授权服务器对OAuth2客户端/oauth2/authorize的授权请求进行验证。
- 4、 OAuth2授权服务器检查 code_challenge 和 code_challenge_method 是否存在。
- 5、 如果步骤4存在这两个参数,授权服务器会持久化code_challenge 和 code_challenge_method这两个参数。
- 6、然后OAuth2授权服务器对授权码请求进行响应。
- 7、 OAuth2客户端收到授权码响应开始调用/oauth2/token请求访问令牌,该请求需要额外附加初始请求中生成的code_verifier参数。
- 8、 OAuth2授权服务器收到访问令牌请求,用该请求中携带的code_verifier和步骤5持久化的code_challenge_method进行摘要计算生成一个校验串,该校验串必须和步骤5持久化的code_challenge进行匹配校验。
- 29、如果步骤8中的匹配校验成功,则发放访问令牌access_token,否则该请求被拒绝。
5、PKCE请求演示
5.1 没有PKCE的授权请求:
http://localhost:9000/oauth2/authorize?response_type=code&client_id=felord&scope=message.read message.write&state=46ge_TeI-dHuAnyv67nVmCcAmFgCVSZAqjTi9Om-1aA=&redirect_uri=http://127.0.0.1:8082/test/bar
5.2 有PKCE的授权请求:
http://localhost:9000/oauth2/authorize?response_type=code&client_id=felord&scope=message.read%20message.write&state=NAqBLbmooEhMPGELwleACOHvybODP_hctqV-PuNjuxo%3D&redirect_uri=http://127.0.0.1:8082/test/bar&code_challenge=mQ4xjFrCXg-1P4iURSXcSCmGVc-dloG0b0sdGpICrN0&code_challenge_method=S256
6、Spring Security PKCE的实现
pkce 分支
6.1、 OAuth2 客户端配置
OAuth2客户端要生成code_verifier,并使用 code_challenge_method 计算 code_challenge,并封装到授权请求参数,需要改造OAuth2AuthorizationRequestResolver接口。
@Bean
SecurityFilterChain customSecurityFilterChain(HttpSecurity http, ClientRegistrationRepository clientRegistrationRepository) throws Exception {
OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient = accessTokenResponseClient();
//TODO OAuth2客户端测要生成code_verifier,并使用 code_challenge_method 计算 code_challenge,并封装到授权请求参数,需要改造OAuth2AuthorizationRequestResolver接口。
DefaultOAuth2AuthorizationRequestResolver authorizationRequestResolver = new DefaultOAuth2AuthorizationRequestResolver(clientRegistrationRepository, OAuth2AuthorizationRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI);
authorizationRequestResolver.setAuthorizationRequestCustomizer(builder -> builder.attributes(attributes -> {
if (!attributes.containsKey(PkceParameterNames.CODE_VERIFIER)) {
String codeVerifier = this.secureKeyGenerator.generateKey();
attributes.put(PkceParameterNames.CODE_VERIFIER, codeVerifier);
builder.additionalParameters(additionalParameters -> {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] digest = md.digest(codeVerifier.getBytes(StandardCharsets.US_ASCII));
String codeChallenge = Base64.getUrlEncoder().withoutPadding().encodeToString(digest);
additionalParameters.put(PkceParameterNames.CODE_CHALLENGE, codeChallenge);
additionalParameters.put(PkceParameterNames.CODE_CHALLENGE_METHOD, "S256");
} catch (NoSuchAlgorithmException ex) {
// plain 方式 这种方式几乎作废了
additionalParameters.put(PkceParameterNames.CODE_CHALLENGE, codeVerifier);
}
});
}
}));
http.authorizeRequests((requests) -> requests
.antMatchers("/test/bar", "/oauth2/jwks")
.hasAnyAuthority("ROLE_ANONYMOUS", "SCOPE_userinfo")
.anyRequest().authenticated())
//然后把改造好的OAuth2AuthorizationRequestResolver配置到HttpSecurity: .oauth2Login().authorizationEndpoint().authorizationRequestResolver(authorizationRequestResolver)
.and()
// 获取token端点配置 比如根据code 获取 token
.tokenEndpoint().accessTokenResponseClient(accessTokenResponseClient);
http.oauth2Client()
.authorizationCodeGrant().authorizationRequestResolver(authorizationRequestResolver)
.accessTokenResponseClient(accessTokenResponseClient);
return http.build();
}
6.1、 Spring Authorization Server配置
客户端配置要添加 .requireProofKey(true)
private RegisteredClient createJwtRegisteredClient(final String id) {
return RegisteredClient.withId(id)
// 客户端ID和密码
.clientId("testid")
// 名称 可不定义
.clientName("test")
// jwt 断言必备
.clientAuthenticationMethod(ClientAuthenticationMethod.PRIVATE_KEY_JWT)
.clientSettings(ClientSettings.builder()
.tokenEndpointAuthenticationSigningAlgorithm(SignatureAlgorithm.RS256)
// 开启PKCE
.requireProofKey(true)
// private key jwt
.jwkSetUrl("http://localhost:8082/oauth2/jwks")
.build())
.build();
}
边栏推荐
- day16--抓包工具Charles的使用
- 11111
- iwemeta metaverse: a doll sells for 9999 yuan, and Bubble Mart thinks it is not expensive at all
- CV+Deep Learning——网络架构Pytorch复现系列——classification(三:MobileNet,ShuffleNet)
- 推荐几个高质量的软件测试实战项目
- UGUI - Events, iTween Plugin
- 快速输入当前日期与时间
- Pieces of TensorFlow 2.9 (1)
- PHP笔记 28 29 30 31
- 本地生活商家如何通过短视频赛道,提升销量曝光量?
猜你喜欢

人工神经网络模型的特点,人工神经网络模型定义

Introduction to the delta method

模糊查询除了like+ % 还能用什么方式

2022-08-01 Advanced Network Engineering (23) Advanced VLAN Technology - VLAN Aggregation, MUX VLAN

iwemeta元宇宙:一个娃娃卖9999元,泡泡玛特认为一点也不贵

The implementation of the seemingly useless component (text gradient) in NaiveUI is so simple

34. Talk about why you want to split the database?What methods are there?

nrm 使用详解

VS2013-调试汇编代码-生成asm文件-结构体内存布局-函数参数压栈-调用约定

机器人控制器编程实践指导书旧版-实践一 LED灯(数字量)
随机推荐
uni 小程序腾讯地图polygon背景透明度
组合数模板
初使jest 单元测试
debezium-connector-mysql拉起docker报错:debezium启动docke
Rust learning: 6.4_ enumeration of composite types
CV+Deep Learning - network architecture Pytorch recurrence series - classification (3: MobileNet, ShuffleNet)
2022-08-01 网工进阶(二十三) VLAN高级技术-VLAN聚合、MUX VLAN
搭建 risc-v 编译环境
Solve the problem that the win10win7win8 system cannot find the specified module and cannot register the desert plug-in
Uni applet Tencent map polygon background transparency
NaiveUI中看起来没啥用的组件(文字渐变)实现原来这么简单
ABAP Data Types 和XSD Type 映射关系以及XSD Type属性
VS2013-调试汇编代码-生成asm文件-结构体内存布局-函数参数压栈-调用约定
DGIOT 30 million meters set pressure reading
Rust learning: 6.5_Array of composite types
详解构建mock服务最方便的神器——Moco
Synchronization lock synchronized traces the source
预测股票涨跌看什么指标,如何预测明天股票走势
速卖通卖家如何抓住产品搜索权重
QT下载清华源配置