3

Spring Security Testを 使用して、Spring Security の背後に配線されているSpring MVC アプリのテストを作成する方法に関するドキュメントに従ってください。

これは、典型的なスプリング セキュリティ ワイヤリングを採用したバニラ スプリング ブート アプリケーションです。ここにメインの Application.java があります

@SpringBootApplication
public class Application {

    private static final Logger log = LoggerFactory.getLogger(Application.class);

    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }
}

スプリングセキュリティの配線は次のとおりです。

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    @Qualifier("customUserDetailsService")
    UserDetailsService userDetailsService;

    @Autowired
    public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/","/sign_up").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .and()
            .csrf()
                .and()
            .exceptionHandling()
                .accessDeniedPage("/access_denied")
                .and()
            .logout()
                .permitAll();
    }
}

ご覧のとおり、"/" と "/sign_up" のリクエストを除いて、すべてのリクエストが認証される必要があります。アプリケーションをデプロイして、認証スキームが正常に機能することを確認しました。


ここからが興味深い部分です。Spring MVC テストの作成です。私が提供したリンクは、 spring-security-testフレームワークがモック ユーザー/セキュリティ コンテキストの挿入を許可するようなテストを作成するためのいくつかの良い方法を提供します。というアプローチをとりました

  1. モック UserDetails インターフェイスを定義し、
  2. SecurityContextFactory を使用して SecurityContext を作成します。
  3. テストの起動中にアプリケーション コンテキストに挿入する必要があります。

1. のコードは次のとおりです。

@WithSecurityContext(factory=WithMockUserDetailsSecurityContextFactory.class)
public @interface WithMockUserDetails {
    String firstName() default "apil";
    String lastName() default "tamang";
    String password() default "test";
    long id() default 999;
    String email() default "apil@test.com";


}

2. のコードは次のとおりです。

final class WithMockUserDetailsSecurityContextFactory
    implements WithSecurityContextFactory<WithMockUserDetails>{


    @Override
    public SecurityContext createSecurityContext(WithMockUserDetails mockUserDetails) {


        /*
         * Use an anonymous implementation for 'UserDetails' to return a
         * mock authentication object, which is then set to the SecurityContext
         * for the test runs.
         */
        UserDetails principal=new UserDetails() {
            @Override
            public Collection<? extends GrantedAuthority> getAuthorities() {

                //another anonmyous interface implementation.
                GrantedAuthority auth=new GrantedAuthority() {
                    @Override
                    public String getAuthority() {
                        return "ROLE_USER";
                    }
                };
                List<GrantedAuthority> authorities=new ArrayList<>();
                authorities.add(auth);
                return authorities;
            }

            @Override
            public String getPassword() {
                return mockUserDetails.password();
            }

            @Override
            public String getUsername() {
                return mockUserDetails.email();
            }

            @Override
            public boolean isAccountNonExpired() {
                return true;
            }

            @Override
            public boolean isAccountNonLocked() {
                return true;
            }

            @Override
            public boolean isCredentialsNonExpired() {
                return true;
            }

            @Override
            public boolean isEnabled() {
                return true;
            }
        };
        Authentication authentication=new
                UsernamePasswordAuthenticationToken(principal,principal.getPassword(),principal.getAuthorities());
        SecurityContext context= SecurityContextHolder.createEmptyContext();
        context.setAuthentication(authentication);
        return context;
    }
}

最後に、テストクラスは次のとおりです。

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {Application.class})
@WebAppConfiguration
public class UserControllerTest {

    @Autowired
    private WebApplicationContext context;

    @Autowired
    private Filter springSecurityFilterChain;

    private MockMvc mvc;

    @Before
    public void setup(){
        mvc= MockMvcBuilders
                .webAppContextSetup(context)                    
                .apply(springSecurity())
                .build();
    }

    @Test
    public void testRootIsOk() throws Exception {
        mvc.perform(get("/"))
                .andExpect(status().isOk());
    }

    @Test
    public void expectRedirectToLogin() throws Exception {
        mvc.perform(get("/testConnect"))
                .andExpect(redirectedUrl("http://localhost/login"));

    }

    @Test
    @WithMockUserDetails
    public void testAuthenticationOkay() throws Exception {
        mvc.perform(get("/testConnect"))
                .andExpect(content().string("good request."));
    }

}

テスト実行の出力:

  1. テスト 1 に合格します。
  2. 2 パスをテストします。
  3. テスト 3 は失敗します。出力が期待されていましたが、<> が得られました。

「SecurityContext」が適切に設定されていないため、テスト 3 が失敗した可能性が非常に高いです。ドキュメントによると、それはうまくいったはずです。私が逃したものがわからない。どんな助けでも大歓迎です。

4

1 に答える 1

2

追加する必要があります

@Retention(RetentionPolicy.RUNTIME)

実行時に注釈情報を保持するために、カスタム注釈に追加します。そうWithSecurityContextTestExecutionListenerしないと、注釈を検出できません。

于 2016-02-17T09:25:02.747 に答える