使用 Spring MVC 測試 OAuth 安全 API(使用 Spring Security OAuth 遺留棧)

Spring MVC,Spring Security,Testing
Remote
0
12:33 AM · Nov 30 ,2025

1. 概述

在本文中,我們將演示如何使用 OAuth 與 Spring MVC 測試支持測試受 OAuth 保護的 API。

注意:本文使用了 Spring OAuth 遺留項目

2. 授權和資源服務器

為了瞭解如何設置授權和資源服務器,請參考之前的文章: Spring REST API + OAuth2 + AngularJS。

我們的授權服務器使用 JdbcTokenStore,並定義了一個客户端,其 id 為 “fooClientIdPassword”,密碼為 “secret”,並支持 password grant 類型。

資源服務器限制了 /employee URL 到 ADMIN 角色。

從 Spring Boot 版本 1.5.0 開始,安全適配器優先於 OAuth 資源適配器,因此為了反轉順序,我們需要使用 @Order(SecurityProperties.ACCESS_OVERRIDE_ORDER) 註解標記 WebSecurityConfigurerAdapter 類。

否則,Spring 將嘗試根據 Spring Security 規則訪問請求的 URL,而不是 Spring OAuth 規則,並且在使用令牌認證時,我們將會收到 403 錯誤。

3. 定義一個示例API

首先,讓我們創建一個簡單的POJO,名為Employee,其中包含兩個我們將通過API進行操作的屬性:

public class Employee {
    private String email;
    private String name;
    
    // 標準構造函數,getter,setter
}

接下來,讓我們定義一個帶有兩個請求映射的控制器,用於獲取和將Employee對象保存到列表:

@Controller
public class EmployeeController {

    private List<Employee> employees = new ArrayList<>();

    @GetMapping("/employee")
    @ResponseBody
    public Optional<Employee> getEmployee(@RequestParam String email) {
        return employees.stream()
          .filter(x -> x.getEmail().equals(email)).findAny();
    }

    @PostMapping("/employee")
    @ResponseStatus(HttpStatus.CREATED)
    public void postMessage(@RequestBody Employee employee) {
        employees.add(employee);
    }
}

請注意,為了使這部分代碼正常工作,我們需要一個額外的JDK8 Jackson模塊。 否則,Optional類將無法正確序列化/反序列化。 您可以從 Maven Central 下載最新版本的 jackson-datatype-jdk8

4. Testing the API

4.1. Setting Up the Test Class

為了測試我們的API,我們將創建一個帶有@SpringBootTest註解的測試類,該類使用AuthorizationServerApplication類來讀取應用程序配置。

對於使用Spring MVC測試支持測試受保護的API,我們需要注入WebAppplicationContextSpring Security Filter Chain Bean。 我們將使用這些來獲取MockMvc實例,在測試運行之前:

@RunWith(SpringRunner.class)
@WebAppConfiguration
@SpringBootTest(classes = AuthorizationServerApplication.class)
public class OAuthMvcTest {

    @Autowired
    private WebApplicationContext wac;

    @Autowired
    private FilterChainProxy springSecurityFilterChain;

    private MockMvc mockMvc;

    @Before
    public void setup() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac)
          .addFilter(springSecurityFilterChain).build();
    }
}

4.2. Obtaining an Access Token

簡單來説,使用OAuth2的API期望收到帶有Authorization頭的值Bearer <access_token>

為了發送所需的Authorization頭,我們首先需要通過向/oauth/token端點發出POST請求來獲取有效的訪問令牌。 此端點需要HTTP Basic身份驗證,包括id和secret的OAuth客户端,以及指定client_idgrant_typeusernamepassword的參數列表。

使用Spring MVC測試支持,參數可以封裝在MultiValueMap中,客户端身份驗證可以使用httpBasic方法發送。

讓我們創建一個方法來發送POST請求以獲取令牌,並從JSON響應中讀取access_token的值:

private String obtainAccessToken(String username, String password) throws Exception {
 
    MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
    params.add("grant_type", "password");
    params.add("client_id", "fooClientIdPassword");
    params.add("username", username);
    params.add("password", password);

    ResultActions result 
      = mockMvc.perform(post("/oauth/token")
        .params(params)
        .with(httpBasic("fooClientIdPassword","secret"))
        .accept("application/json;charset=UTF-8"))
        .andExpect(status().isOk())
        .andExpect(content().contentType("application/json;charset=UTF-8"));

    String resultString = result.andReturn().getResponse().getContentAsString();

    JacksonJsonParser jsonParser = new JacksonJsonParser();
    return jsonParser.parseMap(resultString).get("access_token").toString();
}

4.3. Testing GET and POST Requests

訪問令牌可以添加到請求中使用header(“Authorization”, “Bearer “+ accessToken)方法。

讓我們嘗試在沒有Authorization頭的情況下訪問我們的受保護映射,並驗證我們是否收到unauthorized狀態碼:

@Test
public void givenNoToken_whenGetSecureRequest_thenUnauthorized() throws Exception {
    mockMvc.perform(get("/employee")
      .param("email", EMAIL))
      .andExpect(status().isUnauthorized());
}

我們已經指定只有具有ADMIN角色的用户才能訪問/employee URL。 讓我們創建一個測試,其中我們使用USER角色獲取訪問令牌,並驗證我們是否收到forbidden狀態碼:

@Test
public void givenInvalidRole_whenGetSecureRequest_thenForbidden() throws Exception {
    String accessToken = obtainAccessToken("user1", "pass");
    mockMvc.perform(get("/employee")
      .header("Authorization", "Bearer " + accessToken)
      .param("email", "[email protected]"))
      .andExpect(status().isForbidden());
}

接下來,讓我們使用有效的訪問令牌測試我們的API,通過向創建Employee對象的POST請求發送請求,然後通過向讀取創建的對象的GET請求發送請求:

@Test
public void givenToken_whenPostGetSecureRequest_thenOk() throws Exception {
    String accessToken = obtainAccessToken("admin", "nimda");

    String employeeString = "{\"email\":\"[email protected]\",\"name\":\"Jim\"}";
        
    mockMvc.perform(post("/employee")
      .header("Authorization", "Bearer " + accessToken)
      .contentType(application/json;charset=UTF-8)
      .content(employeeString)
      .accept(application/json;charset=UTF-8))
      .andExpect(status().isCreated());

    mockMvc.perform(get("/employee")
      .param("email", "[email protected]")
      .header("Authorization", "Bearer " + accessToken)
      .accept("application/json;charset=UTF-8"))
      .andExpect(status().isOk())
      .andExpect(content().contentType(application/json;charset=UTF-8))
      .andExpect(jsonPath("$.name", is("Jim")));
}

5. 結論

在本快速教程中,我們演示瞭如何使用 Spring MVC 測試支持測試 OAuth 保護的 API。

要運行測試,項目有一個 mvc 配置文件,可以使用命令 mvn clean install -Pmvc.

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.