1. 概述
本文将演示如何使用Spring MVC测试框架对OAuth安全保护的API进行测试。
⚠️ 注意:本文使用的是Spring OAuth旧版项目。
2. 授权服务器与资源服务器
关于如何设置授权服务器和资源服务器,可参考之前的文章:Spring REST API + OAuth2 + AngularJS。
我们的授权服务器配置要点:
- 使用 JdbcTokenStore 存储令牌
- 定义了一个客户端ID为 fooClientIdPassword,密码为 secret
- 支持 password 授权模式
资源服务器限制 /employee 接口仅允许 ADMIN 角色访问。
⚠️ 踩坑提醒:从Spring Boot 1.5.0开始,安全适配器优先级高于OAuth资源适配器。需要手动调整顺序,在 WebSecurityConfigurerAdapter 类上添加 @Order(SecurityProperties.ACCESS_OVERRIDE_ORDER) 注解。否则Spring会优先使用普通安全规则而非OAuth规则,导致令牌认证时返回403错误。
3. 定义示例API
首先创建一个简单的 Employee POJO类:
public class Employee {
private String email;
private String name;
// 标准构造函数、getter和setter
}
然后定义控制器,包含两个接口:
- 通过邮箱查询员工
- 保存员工信息
@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);
}
}
✅ 关键依赖:需要额外添加 jackson-datatype-jdk8 模块(JDK8 Jackson模块),否则 Optional 类无法正确序列化/反序列化。
4. 测试API
4.1. 测试类配置
创建测试类时需要:
- 使用 @SpringBootTest 注解
- 注入 WebApplicationContext 和 Spring Security Filter Chain
- 初始化 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. 获取访问令牌
OAuth2安全API要求请求头包含:
Authorization: Bearer <access_token>
获取令牌步骤:
- 向 /oauth/token 接口发送POST请求
- 使用HTTP Basic认证(客户端ID和密码)
- 请求参数包含:
- grant_type
- client_id
- username
- password
实现令牌获取方法:
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. 测试GET和POST请求
令牌通过请求头添加:
.header("Authorization", "Bearer " + accessToken)
测试场景列表:
无令牌访问(预期401未授权)
@Test public void givenNoToken_whenGetSecureRequest_thenUnauthorized() throws Exception { mockMvc.perform(get("/employee") .param("email", "test@example.com")) .andExpect(status().isUnauthorized()); }
无效角色访问(USER角色访问ADMIN接口,预期403禁止)
@Test public void givenInvalidRole_whenGetSecureRequest_thenForbidden() throws Exception { String accessToken = obtainAccessToken("user1", "pass"); mockMvc.perform(get("/employee") .header("Authorization", "Bearer " + accessToken) .param("email", "test@example.com")) .andExpect(status().isForbidden()); }
有效令牌完整流程(创建员工后查询)
@Test public void givenToken_whenPostGetSecureRequest_thenOk() throws Exception { String accessToken = obtainAccessToken("admin", "nimda"); String employeeString = "{\"email\":\"jim@example.com\",\"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", "jim@example.com") .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。完整代码可在GitHub项目获取。
运行测试命令:
mvn clean install -Pmvc