1. 概要

この記事では、SpringMVCテストサポートでOAuthを使用して保護されたAPIをテストする方法を紹介します。

:この記事ではSpringOAuthレガシープロジェクトを使用しています。

2. 承認およびリソースサーバー

承認およびリソースサーバーをセットアップする方法のチュートリアルについては、次の前の記事を参照してください: Spring REST API + OAuth2 +AngularJS

承認サーバーはJdbcTokenStoreを使用し、ID “ fooClientIdPassword” とパスワード“ secret” でクライアントを定義し、password付与タイプをサポートします。

リソースサーバーは、 / employeeURLをADMINロールに制限します。

Spring Bootバージョン1.5.0以降、セキュリティアダプターはOAuthリソースアダプターよりも優先されるため、順序を逆にするには、 WebSecurityConfigurerAdapterクラスに@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)アノテーションを付ける必要があります。 )

そうしないと、SpringはSpringOAuthルールではなくSpringSecurityルールに基づいて要求された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<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);
    }
}

これを機能させるには、追加のJDK8Jacksonモジュールが必要であることに注意してください。 そうしないと、オプションクラスが正しくシリアル化/逆シリアル化されません。 jackson-datatype-jdk8の最新バージョンはMavenCentralからダウンロードできます。

4. APIのテスト

4.1. テストクラスの設定

APIをテストするために、 @SpringBootTest で注釈が付けられたテストクラスを作成します。このクラスは、AuthorizationServerApplicationクラスを使用してアプリケーション構成を読み取ります。

Spring MVCテストサポートを使用してセキュリティで保護されたAPIをテストするには、WebAppplicationContextおよびSpringSecurity FilterChainBeanを挿入する必要があります。 テストを実行する前に、これらを使用して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. アクセストークンの取得

簡単に言えば、で保護されたAPI OAuth2 Authorizationヘッダーを受け取ることを期待していますの値でベアラー

必要なAuthorizationヘッダーを送信するには、最初に / oauth /tokenエンドポイントにPOSTリクエストを行って有効なアクセストークンを取得する必要があります。 このエンドポイントには、OAuthクライアントのIDとシークレットを使用したHTTP基本認証と、 client_id grant_type username 、およびパスワード

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. 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の役割を持つユーザーのみが/employeeURLにアクセスできるように指定しました。 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", "jim@yahoo.com"))
      .andExpect(status().isForbidden());
}

次に、POSTリクエストを送信して Employee オブジェクトを作成し、次にGETリクエストを送信して作成されたオブジェクトを読み取ることにより、有効なアクセストークンを使用してAPIをテストしましょう。

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

    String employeeString = "{\"email\":\"jim@yahoo.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@yahoo.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. 結論

このクイックチュートリアルでは、SpringMVCテストサポートを使用してOAuthで保護されたAPIをテストする方法を示しました。

例の完全なソースコードは、GitHubプロジェクトにあります。

テストを実行するために、プロジェクトには mvc プロファイルがあり、コマンド mvn cleaninstall-Pmvc。を使用して実行できます。