Spring MVCを使用したOAuthセキュアAPIのテスト
1. 概要
この記事では、test an API which is secured using OAuth with the Spring MVC test supportを実行する方法を示します。
2. 承認およびリソースサーバー
承認サーバーとリソースサーバーを設定する方法のチュートリアルについては、この前の記事Spring REST API + OAuth2 + AngularJSを参照してください。
承認サーバーはJdbcTokenStoreを使用し、ID“fooClientIdPassword”とパスワード“secret”でクライアントを定義し、password付与タイプをサポートします。
リソースサーバーは、/employeeのURLをADMINロールに制限します。
Spring Bootバージョン1.5.0以降、セキュリティアダプターはOAuthリソースアダプターよりも優先されるため、順序を逆にするには、WebSecurityConfigurerAdapterクラスに@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)の注釈を付ける必要があります。
そうしないと、Springは、Spring OAuthルールではなく、Spring Securityルールに基づいて要求されたURLにアクセスしようとし、トークン認証の使用時に403エラーを受け取ります。
3. サンプルAPIの定義
まず、APIを介して操作する2つのプロパティを持つEmployeeという単純なPOJOを作成しましょう。
public class Employee {
private String email;
private String name;
// standard constructor, getters, setters
}
次に、Employeeオブジェクトを取得してリストに保存するために、2つのリクエストマッピングを使用してコントローラーを定義しましょう。
@Controller
public class EmployeeController {
private List employees = new ArrayList<>();
@GetMapping("/employee")
@ResponseBody
public Optional 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);
}
}
in order to make this work, we need an additional JDK8 Jackson moduleであることに注意してください。 そうしないと、Optionalクラスが適切にシリアル化/逆シリアル化されません。 jackson-datatype-jdk8の最新バージョンは、MavenCentralからダウンロードできます。
4. APIのテスト
4.1. テストクラスの設定
APIをテストするために、AuthorizationServerApplicationクラスを使用してアプリケーション構成を読み取る@SpringBootTestアノテーションが付けられたテストクラスを作成します。
Spring MVCテストサポートを使用してセキュリティで保護されたAPIをテストするには、WebAppplicationContextおよびSpring Security Filter ChainBeanを注入する必要があります。 これらを使用して、テストを実行する前に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. アクセストークンの取得
簡単に言えば、OAuth2expects to receive a the Authorization headerで保護されたAPIで、値はBearer <access_token>です。
必要なAuthorizationヘッダーを送信するには、最初に/oauth/tokenエンドポイントにPOSTリクエストを送信して有効なアクセストークンを取得する必要があります。 このエンドポイントには、OAuthクライアントのIDとシークレットを使用したHTTP基本認証と、client_id、grant_type、username、およびpasswordを指定するパラメーターのリストが必要です。
Spring MVCテストサポートを使用すると、パラメーターをMultiValueMapでラップでき、クライアント認証をhttpBasicメソッドを使用して送信できます。
Let’s create a method that sends a POST request to obtain the tokenおよびJSON応答からaccess_token値を読み取ります。
private String obtainAccessToken(String username, String password) throws Exception {
MultiValueMap 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)メソッドを使用してリクエストに追加できます。
Authorizationヘッダーなしで保護されたマッピングの1つにアクセスして、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をテストし、POSTリクエストを送信してEmployeeオブジェクトを作成し、次に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をテストする方法を示しました。
例の完全なソースコードはGitHub projectにあります。
テストを実行するために、プロジェクトには、コマンドmvn clean install -Pmvc.を使用して実行できるmvcプロファイルがあります。