Skip to main content

快速开始/后端API/Spring Boot API/

Spring Boot API

授权

配置示例工程

修改配置文件 /src/main/resources/application.yml:

/src/main/resources/application.yml
authok:
audience: https://quickstarts/api
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: https://YOUR_DOMAIN/

验证访问令牌

安装依赖

gradle

build.gradle
plugins {
id 'org.springframework.boot' version '2.5.5'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
}

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'
}

maven

pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.5</version>
<relativePath/>
</parent>

<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>
</dependencies>

配置资源服务器(Resource Server)

src/main/java/com/authok/example/security/SecurityConfig.java
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;

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Override
public void configure(HttpSecurity http) throws Exception {
http.oauth2ResourceServer().jwt();
}
}

验证 audience

src/main/java/com/authok/example/security/AudienceValidator.java
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
import org.springframework.security.oauth2.jwt.Jwt;

class AudienceValidator implements OAuth2TokenValidator<Jwt> {
private final String audience;

AudienceValidator(String audience) {
this.audience = audience;
}

public OAuth2TokenValidatorResult validate(Jwt jwt) {
OAuth2Error error = new OAuth2Error("invalid_token", "The required audience is missing", null);

if (jwt.getAudience().contains(audience)) {
return OAuth2TokenValidatorResult.success();
}
return OAuth2TokenValidatorResult.failure(error);
}
}

更新 SecurityConfig,配置 JwtDecoder 以使用 AudienceValidator:

src/main/java/com/authok/example/security/SecurityConfig.java
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
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.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.JwtDecoders;
import org.springframework.security.oauth2.jwt.JwtValidators;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Value("${authok.audience}")
private String audience;

@Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
private String issuer;

@Bean
JwtDecoder jwtDecoder() {
NimbusJwtDecoder jwtDecoder = (NimbusJwtDecoder)
JwtDecoders.fromOidcIssuerLocation(issuer);

OAuth2TokenValidator<Jwt> audienceValidator = new AudienceValidator(audience);
OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(issuer);
OAuth2TokenValidator<Jwt> withAudience = new DelegatingOAuth2TokenValidator<>(withIssuer, audienceValidator);

jwtDecoder.setJwtValidator(withAudience);

return jwtDecoder;
}
}

保护API端点

路由定义:

  • GET /api/public: 非认证请求.
  • GET /api/private: 认证请求, 需验证 Access Token, 不需要验证 scope.
  • GET /api/private-scoped: 认证请求, 需同时验证 Access Token 和 read:messages scope. 对应修改如下:
src/main/java/com/authok/example/security/SecurityConfig.java
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;

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.mvcMatchers("/api/public").permitAll()
.mvcMatchers("/api/private").authenticated()
.mvcMatchers("/api/private-scoped").hasAuthority("SCOPE_read:messages")
.and().cors()
.and().oauth2ResourceServer().jwt();
}
}

创建 API 控制器

先创建一个Message 模型:

src/main/java/com/authok/example/model/Message.java
public class Message {
private final String message;

public Message(String message) {
this.message = message;
}

public String getMessage() {
return this.message;
}
}

创建一个APIController类来处理请求:

src/main/java/com/authok/example/web/APIController.java
import com.authok.example.model.Message;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping(path = "api", produces = MediaType.APPLICATION_JSON_VALUE)
// For simplicity of this sample, allow all origins. Real applications should configure CORS for their use case.
@CrossOrigin(origins = "*")
public class APIController {

@GetMapping(value = "/public")
public Message publicEndpoint() {
return new Message("请求不需要经过认证.");
}

@GetMapping(value = "/private")
public Message privateEndpoint() {
return new Message("请求经过了认证.");
}

@GetMapping(value = "/private-scoped")
public Message privateScopedEndpoint() {
return new Message("请求经过了 Access Token 认证,还有 'read:messages' 作用域认证");
}
}

运行应用

Gradle

Linux 或 macOS:

./gradlew bootRun

Windows:

gradlew.bat bootRun

Maven

Linux 或 macOS:

mvn spring-boot:run

Windows:

mvn.cmd spring-boot:run

使用 API

在应用中调用API

curl --request GET \
--url http://localhost:3010/api/private \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN'

获取访问令牌

在单页应用 或 移动端/原生应用中, 在授权成功后,你需要获取 访问令牌. 如何获取令牌以及如何调用API将取决于您正在开发的应用程序类型和使用的框架.

更多信息请参考相关应用程序快速入门:

curl --request POST \
--url 'https://YOUR_DOMAIN/oauth/token' \
--header 'content-type: application/x-www-form-urlencoded' \
--data grant_type=client_credentials \
--data 'client_id=YOUR_CLIENT_ID' \
--data client_secret=YOUR_CLIENT_SECRET \
--data audience=YOUR_API_IDENTIFIER

测试API

1. 调用被保护端点

curl --request GET \
--url http://localhost:3010/api/private

以上调用会返回 401 HTTP (Unauthorized) 状态码.

携带 AccessToken 进行调用

curl --request GET \
--url http://localhost:3010/api/private \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN'

此时,会返回成功响应.

2. 调用被作用域保护的端点

curl --request GET \
--url http://localhost:3010/api/private-scoped \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN'