Spring 启动 OAuth2 资源服务器:库如何在没有 public 密钥的情况下验证 JWT 令牌?
Spring Boot OAuth2 Resource Server: How library can verify JWT token without public key?
我有以下 spring 最小配置的启动应用程序
application.properties
server.port=8081
spring.security.oauth2.resourceserver.jwt.issuer-uri = http://localhost:8080/auth/realms/master
pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.nurgasemetey</groupId>
<artifactId>springboot-keycloak</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-keycloak</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
控制器
@RestController
@RequestMapping("/users")
public class UsersController {
@GetMapping("/status/check")
public String status(@AuthenticationPrincipal Jwt principal) {
return "working";
}
}
似乎 Spring Boot Oauth2 不使用 public 密钥,正如我在代码中看到的那样:
OAuth2ResourceServerProperties
/**
* JSON Web Key URI to use to verify the JWT token.
*/
private String jwkSetUri;
/**
* JSON Web Algorithm used for verifying the digital signatures.
*/
private String jwsAlgorithm = "RS256";
/**
* URI that can either be an OpenID Connect discovery endpoint or an OAuth 2.0
* Authorization Server Metadata endpoint defined by RFC 8414.
*/
private String issuerUri;
/**
* Location of the file containing the public key used to verify a JWT.
*/
private Resource publicKeyLocation;
但是我没有给publicKeyLocation
,但是app可以不用public密钥验证
在后台它使用 JwtIssuerValidator
和 JwtTimestampValidator
验证器。
另一方面,对于 express-jwt
,它需要 public 密钥进行离线验证
const express = require('express');
const jwt = require('express-jwt');
const app = express();
const secret = 'secret';
const fs = require('fs');
var publicKey = fs.readFileSync('public.pub');
app.get('/protected', jwt({ secret: publicKey, algorithms: ['RS256'] }), (req, res) => {
res.send('protected');
})
app.listen(3000, () => console.log('server started'));
Spring Boot Oauth 如何在没有 public 密钥的情况下进行验证?
自己回答。
首先,http://localhost:8080/auth/realms/master
似乎公开了 public 密钥。正如这个 和@Thomas Kåsene
对这个问题的评论所说
其次,我挖掘了 spring 启动 oauth2 代码并在
中偶然发现了这段代码
ReactiveOAuth2ResourceServerJwkConfiguration
@Bean
@Conditional(IssuerUriCondition.class)
ReactiveJwtDecoder jwtDecoderByIssuerUri() {
return ReactiveJwtDecoders.fromIssuerLocation(this.properties.getIssuerUri());
}
JwtDecoderProviderConfigurationUtils
private static Map<String, Object> getConfiguration(String issuer, URI... uris) {
String errorMessage = "Unable to resolve the Configuration with the provided Issuer of " +
"\"" + issuer + "\"";
for (URI uri : uris) {
try {
RequestEntity<Void> request = RequestEntity.get(uri).build();
ResponseEntity<Map<String, Object>> response = rest.exchange(request, typeReference);
Map<String, Object> configuration = response.getBody();
if (configuration.get("jwks_uri") == null) {
throw new IllegalArgumentException("The public JWK set URI must not be null");
}
return configuration;
} catch (IllegalArgumentException e) {
throw e;
} catch (RuntimeException e) {
if (!(e instanceof HttpClientErrorException &&
((HttpClientErrorException) e).getStatusCode().is4xxClientError())) {
throw new IllegalArgumentException(errorMessage, e);
}
// else try another endpoint
}
}
throw new IllegalArgumentException(errorMessage);
}
似乎从 application.properties
中给出的 issuer-uri
中获取 public 密钥。获取后,它使用获取的 public 密钥验证 jwt 令牌。
待测
关闭你的 jwt 提供程序,在我的例子中是 keycloak 和 运行 spring 引导应用程序,然后它会给出
Caused by: java.lang.IllegalArgumentException: Unable to resolve the Configuration with the provided Issuer of "http://localhost:8080/auth/realms/master"
我有以下 spring 最小配置的启动应用程序
application.properties
server.port=8081
spring.security.oauth2.resourceserver.jwt.issuer-uri = http://localhost:8080/auth/realms/master
pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.nurgasemetey</groupId>
<artifactId>springboot-keycloak</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-keycloak</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
控制器
@RestController
@RequestMapping("/users")
public class UsersController {
@GetMapping("/status/check")
public String status(@AuthenticationPrincipal Jwt principal) {
return "working";
}
}
似乎 Spring Boot Oauth2 不使用 public 密钥,正如我在代码中看到的那样:
OAuth2ResourceServerProperties
/**
* JSON Web Key URI to use to verify the JWT token.
*/
private String jwkSetUri;
/**
* JSON Web Algorithm used for verifying the digital signatures.
*/
private String jwsAlgorithm = "RS256";
/**
* URI that can either be an OpenID Connect discovery endpoint or an OAuth 2.0
* Authorization Server Metadata endpoint defined by RFC 8414.
*/
private String issuerUri;
/**
* Location of the file containing the public key used to verify a JWT.
*/
private Resource publicKeyLocation;
但是我没有给publicKeyLocation
,但是app可以不用public密钥验证
在后台它使用 JwtIssuerValidator
和 JwtTimestampValidator
验证器。
另一方面,对于 express-jwt
,它需要 public 密钥进行离线验证
const express = require('express');
const jwt = require('express-jwt');
const app = express();
const secret = 'secret';
const fs = require('fs');
var publicKey = fs.readFileSync('public.pub');
app.get('/protected', jwt({ secret: publicKey, algorithms: ['RS256'] }), (req, res) => {
res.send('protected');
})
app.listen(3000, () => console.log('server started'));
Spring Boot Oauth 如何在没有 public 密钥的情况下进行验证?
自己回答。
首先,http://localhost:8080/auth/realms/master
似乎公开了 public 密钥。正如这个
其次,我挖掘了 spring 启动 oauth2 代码并在
中偶然发现了这段代码ReactiveOAuth2ResourceServerJwkConfiguration
@Bean
@Conditional(IssuerUriCondition.class)
ReactiveJwtDecoder jwtDecoderByIssuerUri() {
return ReactiveJwtDecoders.fromIssuerLocation(this.properties.getIssuerUri());
}
JwtDecoderProviderConfigurationUtils
private static Map<String, Object> getConfiguration(String issuer, URI... uris) {
String errorMessage = "Unable to resolve the Configuration with the provided Issuer of " +
"\"" + issuer + "\"";
for (URI uri : uris) {
try {
RequestEntity<Void> request = RequestEntity.get(uri).build();
ResponseEntity<Map<String, Object>> response = rest.exchange(request, typeReference);
Map<String, Object> configuration = response.getBody();
if (configuration.get("jwks_uri") == null) {
throw new IllegalArgumentException("The public JWK set URI must not be null");
}
return configuration;
} catch (IllegalArgumentException e) {
throw e;
} catch (RuntimeException e) {
if (!(e instanceof HttpClientErrorException &&
((HttpClientErrorException) e).getStatusCode().is4xxClientError())) {
throw new IllegalArgumentException(errorMessage, e);
}
// else try another endpoint
}
}
throw new IllegalArgumentException(errorMessage);
}
似乎从 application.properties
中给出的 issuer-uri
中获取 public 密钥。获取后,它使用获取的 public 密钥验证 jwt 令牌。
待测
关闭你的 jwt 提供程序,在我的例子中是 keycloak 和 运行 spring 引导应用程序,然后它会给出
Caused by: java.lang.IllegalArgumentException: Unable to resolve the Configuration with the provided Issuer of "http://localhost:8080/auth/realms/master"