我正在休息api。最近,我在项目中添加了Basic Auth,并指定了如下配置:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
public final static String ROLE_ADMIN = "ADMIN";
public final static String ROLE_USER = "USER";
/**
* Determines the resource access for different account types
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/user/create").permitAll()
.antMatchers("/admin/**").hasRole(ROLE_ADMIN)
.anyRequest().authenticated()
.and()
.csrf().disable()
.httpBasic();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(inMemoryUserDetailsManager());
}
/**
* Initially fills Spring Security with default accounts
*/
@Bean
public InMemoryUserDetailsManager inMemoryUserDetailsManager() {
final Properties users = new Properties();
users.put("user","pass,ROLE_USER,enabled"); //login = user, password = pass
users.put("admin","pass,ROLE_ADMIN,enabled"); //login = admin, password = pass
return new InMemoryUserDetailsManager(users);
}
}
我还为他们制作了一些控制器和一些测试:
控制器:
@RestController
public class MovieController {
@Autowired @Qualifier("MovieService")//not relevant
private MovieService ms;
@Autowired @Qualifier("CastService")//not relevant
private CastService cs;
@RequestMapping(value = "admin/movies", method = GET)
public List<Movie> selectAllMovies(){
return ms.selectAll();
}
//the rest of the code..
}
测试:
@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
public class MovieControllerTest {
@Mock
private MovieService movieService;
@Mock
private CastService castService;
@Mock
private ActorService actorService;
@Mock
private UserService userService;
@InjectMocks
private MovieController movieController;
private MockMvc mvc;
@Before
public void setUp(){
MockitoAnnotations.initMocks(this);
mvc = MockMvcBuilders.standaloneSetup(movieController).build();
}
@Test
@WithMockUser(roles = "ADMIN", username = "admin", password = "pass")
@WithUserDetails("admin")
public void testGetAllMovies() throws Exception {
List<Movie> movieList = Arrays.asList(
new Movie("title1", "desc1", MovieType.newest, 10f, true),
new Movie("title2", "desc2", MovieType.newest, 10f, true),
new Movie("title3", "desc3", MovieType.newest, 10f, true));
when(movieService.selectAll()).thenReturn(movieList);
String uri = "admin/movies";
MvcResult result = mvc.perform(MockMvcRequestBuilders.get(uri)
.accept(MediaType.APPLICATION_JSON)).andReturn();
String content = result.getResponse().getContentAsString();
int status = result.getResponse().getStatus();
verify(movieService, times(1)).selectAll();
Assert.assertEquals("failure - expected HTTP status 200", 200, status);
Assert.assertTrue("failure - expected HTTP response body to have a value", content.trim().length() > 0);
}
//the rest of the code..
}
但是当我运行测试时,我从Mockito那里得到了错误:
Wanted but not invoked:
movieService.selectAll();
-> at com.myproject.Controller.MovieControllerTest.testGetAllMovies(MovieControllerTest.java:87)
Actually, there were zero interactions with this mock.
看来,Spring安全性不允许测试调用需要身份验证的网址(“admin / movies”)。正如在配置文件中指定的那样,此URL需要ADMIN角色。有趣的是,当我在控制器和测试中删除url的“admin”部分时,测试工作正常!但是,根据配置,它仍然需要使用USER角色进行身份验证(只有“/ user / create”不需要它,如配置中所示)。
我已尝试使用@WithMockUser(username="admin", password="pass", roles="ADMIN")
但它没有帮助,错误保持不变。
答案 0 :(得分:0)
为什么你使用Mockito进行MVC测试,这似乎是一件奇怪的事情?通常情况下,你会将真正的控制器/服务/存储库@Autowire直接进入测试。
以下是我们的MVC测试基类的外观。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = AnnotationConfigWebContextLoader.class, classes = IntegrationTestConfiguration.class)
@WebAppConfiguration
public abstract class IntegrationTest {
// from some Spring context
@Autowired
protected HttpClientConnectionManager connectionManager;
@Autowired
protected ClientDetailsService clientDetailsService;
@Autowired
protected WebApplicationContext context;
@SuppressWarnings("SpringJavaAutowiringInspection") // this is created by Spring Security
@Autowired
protected FilterChainProxy filterChain;
private MockMvc mvc;
protected TestSession.LoginDetails integrationTestUserDetails;
@Before
public void configureMock() throws InvalidDecryptionKeyException {
// setup spring MVC
MockHttpServletRequestBuilder requestBuilder = get("/");
requestBuilder.secure(true);
requestBuilder.header("origin", "http://localhost");
mvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(SecurityMockMvcConfigurers.springSecurity())
.addFilter(filterChain)
.defaultRequest(requestBuilder)
.build();
}
}
此外,当您使用MovkMvc时,您应该记住,您正在Http上方的级别进行通信,此时servlet容器已将字节流处理为HttpServletRequest。这意味着如果你发布JSON,你通常会创建一个Java对象,将其序列化为像这样的字节。
mvc.perform(post("/internal/signOut")
.session(mySession)
.content(objectMapper.writeValueAsString(signOutRequest))));
答案 1 :(得分:0)
好的,所以看起来Spring Security与我的问题无关。 mockito无法检测到与模拟的交互(控制器从未与之交互过)的真正原因是我投入的网址MockMvcRequestBuilder不正确。只是偶然地发现url必须在url字符串的beginnig处包含斜杠,所以我将"admin/movies"
更改为"/admin/movies"
并且它可以正常工作,即使事实如此,正如您所看到的那样我上面的代码,在控制器中,控制器的映射没有这些斜杠(“admin / movies”)。
疯了,但确实如此:)