Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/spring/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 在单元测试Spring REST控制器时注入@AuthenticationPrincipal_Java_Spring_Spring Mvc_Spring Security_Spring Test - Fatal编程技术网

Java 在单元测试Spring REST控制器时注入@AuthenticationPrincipal

Java 在单元测试Spring REST控制器时注入@AuthenticationPrincipal,java,spring,spring-mvc,spring-security,spring-test,Java,Spring,Spring Mvc,Spring Security,Spring Test,我在尝试测试一个REST端点时遇到问题,该端点接收一个UserDetails,作为用@AuthenticationPrincipal注释的参数。 似乎没有使用在测试场景中创建的用户实例,但尝试使用默认构造函数进行实例化:org.springframework.beans.beanInstationException:未能实例化[com.andrucz.app.AppUserDetails]:未找到默认构造函数 休息端点: @RestController @RequestMapping("

我在尝试测试一个REST端点时遇到问题,该端点接收一个
UserDetails
,作为用
@AuthenticationPrincipal注释的参数。

似乎没有使用在测试场景中创建的用户实例,但尝试使用默认构造函数进行实例化:
org.springframework.beans.beanInstationException:未能实例化[com.andrucz.app.AppUserDetails]:未找到默认构造函数

休息端点:

@RestController
@RequestMapping("/api/items")
class ItemEndpoint {

    @Autowired
    private ItemService itemService;

    @RequestMapping(path = "/{id}",
                    method = RequestMethod.GET,
                    produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public Callable<ItemDto> getItemById(@PathVariable("id") String id, @AuthenticationPrincipal AppUserDetails userDetails) {
        return () -> {
            Item item = itemService.getItemById(id).orElseThrow(() -> new ResourceNotFoundException(id));
            ...
        };
    }
}

如何解决该问题而不必切换到
webAppContextSetup
?我想编写能够完全控制服务模拟的测试,所以我使用的是
standaloneSetup。

这可以通过将
HandlerMethodArgumentResolver
注入模拟MVC上下文或独立设置来完成。假设您的
@AuthenticationPrincipal
类型为
参与者详细信息

private HandlerMethodArgumentResolver putAuthenticationPrincipal = new HandlerMethodArgumentResolver() {
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.getParameterType().isAssignableFrom(ParticipantDetails.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        return new ParticipantDetails(…);
    }
};
这个参数解析器可以处理类型
ParticipantDetails
,并且只是凭空创建它,但是您可以看到您获得了大量的上下文。稍后,此参数解析器将附加到模拟MVC对象:

@BeforeMethod
public void beforeMethod() {
    mockMvc = MockMvcBuilders
            .standaloneSetup(…)
            .setCustomArgumentResolvers(putAuthenticationPrincipal)
            .build();
}

这将导致您的
@AuthenticationPrincipal
带注释的方法参数填充来自解析程序的详细信息。

出于某种原因,Michael Piefel的解决方案不适用于我,因此我提出了另一个解决方案

首先,创建抽象配置类:

@RunWith(SpringRunner.class)
@SpringBootTest
@TestExecutionListeners({
    DependencyInjectionTestExecutionListener.class,
    DirtiesContextTestExecutionListener.class,
    WithSecurityContextTestExecutionListener.class})
public abstract MockMvcTestPrototype {

    @Autowired
    protected WebApplicationContext context;

    protected MockMvc mockMvc;

    protected org.springframework.security.core.userdetails.User loggedUser;

    @Before
    public voivd setUp() {
         mockMvc = MockMvcBuilders
            .webAppContextSetup(context)
            .apply(springSecurity())
            .build();

        loggedUser =  (User)  SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    } 
}
然后您可以编写如下测试:

public class SomeTestClass extends MockMvcTestPrototype {

    @Test
    @WithUserDetails("someUser@app.com")
    public void someTest() throws Exception {
        mockMvc.
                perform(get("/api/someService")
                    .withUser(user(loggedUser)))
                .andExpect(status().isOk());

    }
}
@Before
public void before() throws Exception {
    SecurityContextHolder.getContext().setAuthentication(myAuthentication);
    SecurityContextHolderAwareRequestFilter authInjector = new SecurityContextHolderAwareRequestFilter();
    authInjector.afterPropertiesSet();
    mvc = MockMvcBuilders.standaloneSetup(myController).addFilters(authInjector).build();
}
@AuthenticationPrincipal应该将您自己的用户类实现注入控制器方法

public class SomeController {
...
    @RequestMapping(method = POST, value = "/update")
    public String update(UdateDto dto, @AuthenticationPrincipal CurrentUser user) {
        ...
        user.getUser(); // works like a charm!
       ...
    }
}

我知道这个问题已经很老了,但对于那些仍在寻找答案的人来说,使用
@AuthenticationPrincipal
编写Spring启动测试(这可能不适用于所有实例)的有效方法是使用mockuser(“testuser1”)注释测试


对于
@WithMockUser

上的Spring文档,虽然没有很好的文档记录,但有一种方法可以将
身份验证
对象作为MVC方法的参数注入独立的MockMvc。如果在
SecurityContextHolder
中设置
身份验证
,则过滤器
SecurityContextHolderAwareRequestFilter
通常由Spring Security实例化,并为您注入身份验证

您只需将该过滤器添加到MockMvc设置中,如下所示:

public class SomeTestClass extends MockMvcTestPrototype {

    @Test
    @WithUserDetails("someUser@app.com")
    public void someTest() throws Exception {
        mockMvc.
                perform(get("/api/someService")
                    .withUser(user(loggedUser)))
                .andExpect(status().isOk());

    }
}
@Before
public void before() throws Exception {
    SecurityContextHolder.getContext().setAuthentication(myAuthentication);
    SecurityContextHolderAwareRequestFilter authInjector = new SecurityContextHolderAwareRequestFilter();
    authInjector.afterPropertiesSet();
    mvc = MockMvcBuilders.standaloneSetup(myController).addFilters(authInjector).build();
}
简化:

见:


您需要。因此,没有办法将StandaloneStup与身份验证结合使用?它在哪里说的?我不确定,但我如何才能获得所需的FilterChainProxy?您也可以使用webAppContextSetup,在使用
@ContextConfiguration
保持对该上下文中bean的完全控制的同时,这是一种很好的方法,但要小心!默认情况下注册了约20个参数解析器,它们负责处理感兴趣的类型。在我的例子中,验证参数由ServletRequestMethodArgumentResolver处理,我的处理程序被忽略。新的Spring链接:
@ExtendWith(SpringExtension.class)
@SpringBootTest
@Transactional
@AutoConfigureMockMvc
public class ControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    @WithUserDetails(value = "user@gmail.com")
    void get() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get(URL))
                .andExpect(status().isOk())
                .andDo(print());
    }