使用Spring安全测试来测试安全的Spring MVC控制器

时间:2016-02-17 01:37:16

标签: spring-security spring-boot spring-mvc-test spring-security-test

关于使用 Spring Security测试documentation编写针对 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测试。我提供的link提供了一些很好的方法来编写这样的测试,其中 spring-security-test 框架允许插入模拟用户/安全上下文。我采取了

的方法
  1. 定义模拟UserDetails接口和
  2. 使用SecurityContextFactory创建一个SecurityContext,
  3. 应在测试启动期间插入应用程序上下文中。
  4. 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失败。预期输出但得到&lt;&gt;。
    4. 由于“安全上下文”&#39;很可能测试3失败了。从未适当填充。根据文档,它应该有效。不确定我错过了什么。非常感谢任何帮助。

1 个答案:

答案 0 :(得分:2)

您需要添加

$account = new AdAccount('act_' . $account_id);

$fields = array(
    CampaignFields::ID,
    CampaignFields::NAME,
    CampaignFields::START_TIME,
    CampaignFields::STOP_TIME,
    CampaignFields::SPEND_CAP,
    'effective_status'
);

$params = array(
    'effective_status' => array(
        Campaign::STATUS_ACTIVE
    ),
);

$campaignSets = $account->getCampaigns($fields, $params);

到自定义注释,以便在运行时保留注释信息。否则WithSecurityContextTestExecutionListener无法检测到您的注释。