spring-security 无法使用@springboot.web创建安全上下文,评论ApiControllerTest $WithMockCustomUser

mwecs4sa  于 2022-11-11  发布在  Spring
关注(0)|答案(1)|浏览(111)

我正在尝试创建自定义SecurityContext,并通过@WithMockCustomUser(自定义注解)使用它来测试Comments_post功能。我使用@WithMockCustomeUser的原因是我需要用户的电子邮件来验证用户。
我指的是
https://docs.spring.io/spring-security/site/docs/4.2.x/reference/html/test-method.html来使用它。
但不知怎么的,我已经超越了错误。
我认为安全性和测试之间存在一些误解。
这是我的测试代码。

final class WithUserDetailsSecurityContextFactory implements WithSecurityContextFactory<WithUserDetails> {
    private final UserDetailsService userDetailsService;
    @Autowired
    public WithUserDetailsSecurityContextFactory(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }
    public org.springframework.security.core.context.SecurityContext createSecurityContext(WithUserDetails withUser) {
        String username = withUser.value();
        Assert.hasLength(username, "value() must be non-empty String");
        UserDetails principal = userDetailsService.loadUserByUsername(username);
        Authentication authentication = new UsernamePasswordAuthenticationToken(principal, principal.getPassword(), principal.getAuthorities());
        SecurityContext context = SecurityContextHolder.createEmptyContext();
        context.setAuthentication(authentication);
        return context;
    }
}

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Transactional
public class CommentsApiControllerTest {

    @LocalServerPort
    private int port;

    @Autowired
    private PostsRepository postsRepository;

    @Autowired
    private CommentRepository commentRepository;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private PostsService postsService;

    @Autowired
    private CommentService commentService;

    @Autowired
    private UserDetailService userDetailsService;

    @Autowired
    private WebApplicationContext context;

    @Autowired ObjectMapper objectMapper;

    private MockMvc mvc;

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

    @Retention(RetentionPolicy.RUNTIME)
    @WithSecurityContext(factory = WithUserDetailsSecurityContextFactory.class, setupBefore = TestExecutionEvent.TEST_EXECUTION)
    public @interface WithMockCustomUser {
        String name() default "testName";

        String email() default "testemail@gmail.com";

        Role role() default Role.USER;
    }

    @After
    public void tearDown() throws Exception {
        postsRepository.deleteAll();
        commentRepository.deleteAll();
    }

    @Test
    @WithMockCustomUser
    @Transactional // 프록시 객체에 실제 데이터를 불러올 수 있게 영속성 컨텍스트에서 관리
    public void comment_등록() throws Exception {
        // given
        String title = "title";
        String content = "content";
        User user = userRepository.save(User.builder()
                .name("name")
                .email("fake@naver.com")
                .picture("fakePic.com")
                .role(Role.USER)
                .build());

        PostsSaveRequestDto requestDto = PostsSaveRequestDto.builder()
                .title(title)
                .content(content)
                .user(user)
                .build();
        postsRepository.save(requestDto.toEntity());

        String comment = "comment";
        Posts posts = postsRepository.findAll().get(0);

        CommentSaveRequestDto saveRequestDto = CommentSaveRequestDto.builder()
                .comment(comment)
                .posts(posts)
                .build();

        Long id = posts.getId();

        String url = "http://localhost:"+ port + "/api/posts/" + id + "/comments";

        //when

        mvc.perform(post(url)
                        .contentType(MediaType.APPLICATION_JSON_UTF8)
                        .content(objectMapper.writeValueAsString(saveRequestDto)))
                .andExpect(status().isOk())
                .andDo(print());

    }

而且是完整的错误跟踪

java.lang.IllegalStateException: Unable to create SecurityContext using @springboot.web.CommentsApiControllerTest$WithMockCustomUser(name=testName, email=testemail@gmail.com, role=USER)

    at org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener.createTestSecurityContext(WithSecurityContextTestExecutionListener.java:126)
    at org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener.createTestSecurityContext(WithSecurityContextTestExecutionListener.java:96)
    at org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener.beforeTestMethod(WithSecurityContextTestExecutionListener.java:62)
    at org.springframework.test.context.TestContextManager.beforeTestMethod(TestContextManager.java:291)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
    at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
Caused by: java.lang.ClassCastException: com.sun.proxy.$Proxy8 cannot be cast to org.springframework.security.test.context.support.WithUserDetails
    at springboot.web.WithUserDetailsSecurityContextFactory.createSecurityContext(CommentsApiControllerTest.java:64)
    at org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener.createTestSecurityContext(WithSecurityContextTestExecutionListener.java:123)
    ... 26 more

提前感谢!

ruarlubt

ruarlubt1#

看一看stacktrace,实际的错误是:

Caused by: java.lang.ClassCastException: com.sun.proxy.$Proxy8 cannot be cast to org.springframework.security.test.context.support.WithUserDetails
    at springboot.web.WithUserDetailsSecurityContextFactory.createSecurityContext(CommentsApiControllerTest.java:64)

您在测试中没有使用@WithUserDetails,您使用的是@WithMockCustomUser,因此我认为您的类声明应该是:

final class WithUserDetailsSecurityContextFactory implements WithSecurityContextFactory<WithMockCustomUser> {
    // ...
}

相关问题