web-dev-qa-db-ja.com

@WithMockUserはSpringSecurity認証資格情報を選択しません

次のように、従来の方法でSpringSecurityを使用してコントローラーに基本認証を設定しました。

@EnableWebSecurity
@EnableGlobalMethodSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {


    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("user").password("user").roles("USER")
                .and()
                .withUser("admin").password("admin").roles("USER", "ADMIN");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers(....);
    }
}

テストのポイントになると、私は@ WithMockUserを使用してテストに注釈を付けています。 GETのテストは次のようになります。

@RunWith(SpringRunner.class)
@WebMvcTest(controllers = SomeController.class)
public class SomeControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void test1() {
                  mockMvc.perform(get(...)).andExpect(...);
    }

またはPOSTの場合は次のようになります。

@RunWith(SpringRunner.class)
@WebMvcTest(controllers = SomeController.class)
public class SomeControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void test1() {
                  mockMvc.perform(post(...)).andExpect(...);
    }

その後、予期しないことが起こります。

  • テストメソッドに@ WithMockUserの注釈が付けられていない場合、基本認証が満たされていないため、401ステータス(未承認)が原因で失敗します。
  • テストメソッドに単に空の@ WithMockUser資格情報を指定せずにの注釈を付けると、合格し始めます。これは、私が提供しなかったため、合理的ではありません。認証用の正しいデータ(むしろ空のままにしました)
  • この時点で、@ WithMockUser(username = "user"、password = "user"、roles = "USER")のように正しい資格情報を入力するときにも、テストメソッドは合格です。

[〜#〜]質問[〜#〜]:何が起こっているのですか?この不正行為を修正する方法は?

Spring Securityがアクティブ化されているように見えますが、私のテストでは、使用されると予想される認証を使用していません。認証データを自分でモックする必要がありますか?

[〜#〜] edit [〜#〜]完全なconfigureメソッドは次のとおりです

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
            .antMatchers(URL1).hasAnyRole(ROLE_ADMIN)
            .antMatchers(URL2).hasAnyRole(ROLE_USER)
            .antMatchers(URL3).permitAll()
            .and()
            .httpBasic()
            .and()
            .csrf().disable();
}
4
Johan

この動作の背後には2つの理由があります。

  1. @WithMockUserアノテーションは、認証を実行するためのものではありません。すでに認証されているユーザーを作成します。デフォルトでは、彼の資格情報はuserpasswordです。
  2. @WebMvcTestは実行されませんMySecurityConfig.Java。このアノテーションは、テスト用にSpring mockMvcオブジェクトセキュリティのデフォルトを使用を作成します。これらのセキュリティのデフォルトはorg.springframework.boot.test.autoconfigure.web.servlet.MockMvcSecurityAutoConfigurationによって適用されます。MySecurityConfigメソッドにブレークポイントを設定し、デバッグモードでテストを再実行することで、これを再確認できます。ブレークポイントはヒットしません。

問題1の解決

@WithMockUserアノテーションの機能にアプローチを変更するだけです。すでにログインしているユーザーを表示します。具体的なユーザー名、パスワード、およびロールを指定して、URLのセキュリティとロールの構成をテストすることは引き続き可能です。

問題2の解決
すべての統合テストの基本クラスを作成します。 SpringSecurityが適用されたmockMvcを構成します。 @SpringBootTestアノテーションにも注意してください。今すぐテスト使用しますMySecurityConfig.Java

import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;

import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.context.WebApplicationContext;

@RunWith(SpringRunner.class)
@SpringBootTest
public abstract class IT {
    @Autowired
    protected WebApplicationContext wac;

    @Autowired
    private FilterChainProxy springSecurityFilterChain;

    protected MockMvc mockMvc;

    @Before
    public void applySecurity() {
        this.mockMvc = webAppContextSetup(wac)
            .apply(springSecurity(springSecurityFilterChain))
            .build();
    }
}

このようにテストを書き直してください。 http基本認証を使用すると仮定します。資格情報はテスト内で提供されます。注:モックユーザーアノテーションはありません。

package com.example.demo;

import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;

import org.junit.Test;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;

public class SomeControllerIT extends IT {

  @Test
  public void test1() throws Exception {
    mockMvc.perform(get("/some")
        .with(httpBasic("user", "user")))
        .andExpect(MockMvcResultMatchers.content().string("hello"));
  }
}
6
Etruskas

スプリングセキュリティの構成を使用してmockMVCテストを実行する方法は次のとおりです。USERロールの場合...

@RunWith(SpringRunner.class)
@WebMvcTest(controllers = SomeController.class)
public class SomeControllerTest {

    @Autowired
    private WebApplicationContext context;

    @Autowired
    private MockMvc mockMvc;

    @Before
    public void setup() {
      mockMvc = MockMvcBuilders
        .webAppContextSetup(context)
        .defaultRequest(get("/")
        .with(user("user").password("password").roles("USER")))
        .apply(springSecurity())
        .build();
    }


    @Test
    public void test1() {
                  mockMvc.perform(get(...)).andExpect(...);
    }
}

この変更を行った後、GETテストが機能するはずです。

spring Securityは、POSTやDELETEなどのhttpリクエストに対してクロスサイトリクエストフォージェリ保護を提供するため、crsf()を使用してこれらの特定のテストを実行する必要があります。

@Test
    public void shouldPost() {
                  mockMvc.perform(post(...)).with(csrf().asHeader())
                         .andExpect(...);
    }
3
cblanto7