mirror of
https://github.com/logto-io/logto.git
synced 2025-01-20 21:32:31 -05:00
refactor(console): update spring boot api protection guide (#6018)
update spring boot api protection guide
This commit is contained in:
parent
dc6fbe212e
commit
a02c20a664
1 changed files with 43 additions and 38 deletions
|
@ -35,9 +35,10 @@ dependencies {
|
|||
Since Spring Boot and Spring Security have built-in support for both OAuth2 resource server and JWT validation,
|
||||
you DO NOT need to add additional libraries from Logto to integrate.
|
||||
|
||||
See [Spring Security OAuth 2.0 Resource Server](https://docs.spring.io/spring-security/reference/servlet/oauth2/resource-server/index.html)
|
||||
and [Spring Security Architecture](https://spring.io/guides/topicals/spring-security-architecture)
|
||||
for more details.
|
||||
See [Spring Security OAuth 2.0 Resource Server](https://docs.spring.io/spring-security/reference/servlet/oauth2/resource-server/index.html)
|
||||
and [Spring Security Architecture](https://spring.io/guides/topicals/spring-security-architecture)
|
||||
for more details.
|
||||
|
||||
</InlineNotification>
|
||||
|
||||
</Step>
|
||||
|
@ -51,14 +52,16 @@ and signed with [JWK](https://datatracker.ietf.org/doc/html/rfc7517)
|
|||
Before moving on, you will need to get an issuer and a JWKS URI to verify the issuer and the signature of the Bearer Token (`access_token`).
|
||||
|
||||
<p>
|
||||
All the Logto Authorization server configurations can be found by requesting <code>{appendPath(props.endpoint, '/oidc/.well-known/openid-configuration')}</code>, including the <strong>issuer</strong>, <strong>jwks_uri</strong> and other authorization configs.
|
||||
All the Logto Authorization server configurations can be found by requesting{' '}
|
||||
<code>{appendPath(props.endpoint, '/oidc/.well-known/openid-configuration')}</code>, including the{' '}
|
||||
<strong>issuer</strong>, <strong>jwks_uri</strong> and other authorization configs.
|
||||
</p>
|
||||
|
||||
An example of the response:
|
||||
|
||||
<pre>
|
||||
<code className="language-json">
|
||||
{`{
|
||||
{`{
|
||||
// ...
|
||||
"issuer": "${appendPath(props.endpoint, '/oidc')}",
|
||||
"jwks_uri": "${appendPath(props.endpoint, '/oidc/jwks')}"
|
||||
|
@ -74,8 +77,8 @@ An example of the response:
|
|||
Use an `application.yml` file (instead of the default `application.properties`) to configure the server port, audience, and OAuth2 resource server.
|
||||
|
||||
<pre>
|
||||
<code className="language-yaml">
|
||||
{`# path/to/project/src/main/resources/application.yaml
|
||||
<code className="language-yaml">
|
||||
{`# path/to/project/src/main/resources/application.yaml
|
||||
server:
|
||||
port: 3000
|
||||
|
||||
|
@ -89,7 +92,7 @@ spring:
|
|||
jwt:
|
||||
issuer-uri: ${appendPath(props.endpoint, '/oidc')}
|
||||
jwk-set-uri: ${appendPath(props.endpoint, '/oidc/jwks')}`}
|
||||
</code>
|
||||
</code>
|
||||
</pre>
|
||||
|
||||
- `audience`: The unique API identifier of your protected API resource.
|
||||
|
@ -113,8 +116,6 @@ import org.springframework.security.oauth2.jwt.Jwt;
|
|||
|
||||
public class AudienceValidator implements OAuth2TokenValidator<Jwt> {
|
||||
|
||||
private final OAuth2Error oAuth2Error = new OAuth2Error("invalid_token", "Required audience not found", null);
|
||||
|
||||
private final String audience;
|
||||
|
||||
public AudienceValidator(String audience) {
|
||||
|
@ -124,18 +125,21 @@ public class AudienceValidator implements OAuth2TokenValidator<Jwt> {
|
|||
@Override
|
||||
public OAuth2TokenValidatorResult validate(Jwt jwt) {
|
||||
if (!jwt.getAudience().contains(audience)) {
|
||||
return OAuth2TokenValidatorResult.failure(oAuth2Error);
|
||||
return OAuth2TokenValidatorResult.failure(new OAuth2Error("invalid_token", "Required audience not found", null));
|
||||
}
|
||||
|
||||
// Optional: For RBAC validate the scopes of the JWT.
|
||||
String scopes = jwt.getClaimAsString("scope");
|
||||
if (scopes == null || !scopes.contains("read:profile")) {
|
||||
return OAuth2TokenValidatorResult.failure(new OAuth2Error("invalid_token", "Insufficient permission", null));
|
||||
}
|
||||
|
||||
|
||||
return OAuth2TokenValidatorResult.success();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<InlineNotification>
|
||||
For <a href="https://docs.logto.io/docs/recipes/rbac/" target="_blank" rel="noopener">🔐 RBAC</a>, scope validation is also required.
|
||||
</InlineNotification>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Configure Spring Security">
|
||||
|
@ -154,17 +158,19 @@ import com.nimbusds.jose.proc.SecurityContext;
|
|||
import io.logto.springboot.sample.validator.AudienceValidator;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.security.config.Customizer;
|
||||
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.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
|
||||
import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
|
||||
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
|
||||
import org.springframework.security.oauth2.jwt.Jwt;
|
||||
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
||||
import org.springframework.security.oauth2.jwt.JwtValidators;
|
||||
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
|
||||
import org.springframework.security.web.DefaultSecurityFilterChain;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfiguration {
|
||||
|
||||
|
@ -180,6 +186,8 @@ public class SecurityConfiguration {
|
|||
@Bean
|
||||
public JwtDecoder jwtDecoder() {
|
||||
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri(jwksUri)
|
||||
// Logto uses the ES384 algorithm to sign the JWTs by default.
|
||||
.jwsAlgorithm(ES384)
|
||||
// The decoder should support the token type: Access Token + JWT.
|
||||
.jwtProcessorCustomizer(customizer -> customizer.setJWSTypeVerifier(
|
||||
new DefaultJOSEObjectTypeVerifier<SecurityContext>(new JOSEObjectType("at+jwt"))))
|
||||
|
@ -194,14 +202,17 @@ public class SecurityConfiguration {
|
|||
}
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
http.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt).cors().and()
|
||||
.authorizeRequests(customizer -> customizer
|
||||
// Only authenticated requests can access your protected APIs
|
||||
.mvcMatchers("/", "/secret").authenticated()
|
||||
// Anyone can access the public profile.
|
||||
.mvcMatchers("/profile").permitAll()
|
||||
);
|
||||
public DefaultSecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.securityMatcher("/api/**")
|
||||
.oauth2ResourceServer(oauth2 -> oauth2
|
||||
.jwt(Customizer.withDefaults()))
|
||||
.authorizeHttpRequests(requests -> requests
|
||||
// Allow all requests to the public APIs.
|
||||
.requestMatchers("/api/.wellknown/**").permitAll()
|
||||
// Require jwt token validation for the protected APIs.
|
||||
.anyRequest().authenticated());
|
||||
|
||||
return http.build();
|
||||
}
|
||||
}
|
||||
|
@ -226,20 +237,14 @@ import org.springframework.web.bind.annotation.RestController;
|
|||
@CrossOrigin(origins = "*")
|
||||
@RestController
|
||||
public class ProtectedController {
|
||||
|
||||
@GetMapping("/")
|
||||
public String protectedRoot() {
|
||||
return "Protected root.";
|
||||
@GetMapping("/api/profile")
|
||||
public String protectedProfile() {
|
||||
return "Protected profile.";
|
||||
}
|
||||
|
||||
@GetMapping("/secret")
|
||||
public String protectedSecret() {
|
||||
return "Protected secret.";
|
||||
}
|
||||
|
||||
@GetMapping("/profile")
|
||||
public String publicProfile() {
|
||||
return "Public profile.";
|
||||
@GetMapping("/api/.wellknown/config.json")
|
||||
public String publicConfig() {
|
||||
return "Public config.";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -274,7 +279,7 @@ Request your protected API with the Access Token as the Bearer token in the Auth
|
|||
|
||||
<pre>
|
||||
<code className="language-bash">
|
||||
{`curl --include '${appendPath(props.endpoint, '/secret')}' \\
|
||||
{`curl --include '${appendPath(props.endpoint, '/api/profile')}' \\
|
||||
--header 'Authorization: Bearer <your-access-token>'`}
|
||||
</code>
|
||||
</pre>
|
||||
|
|
Loading…
Add table
Reference in a new issue