当前位置: 首页 > 知识库问答 >
问题:

如何在POST方法的spring-boot测试中。getPrincipal()?

章哲彦
2023-03-14

org.springframework.web.util.NestedServletException:请求处理失败;嵌套异常是java.lang.ClassCastException:org.springframework.security.core.userDetails.User不能强制转换为ru.pravvich.domain.user

过帐方法:

@PostMapping("/get_all_items/add_item_page/add_item")
public String addItem(@RequestParam(value = "description") final String description) {

    final User user = (User) SecurityContextHolder
            .getContext()
            .getAuthentication()
            .getPrincipal();

    final Item item = new Item();
    item.setDescription(description);
    item.setAuthorId(user.getId());
    service.add(item);
    return "redirect:/get_all_items";
}

和测试:

@Test
@WithMockUser(username = "user", roles = "USER")
public void whenPostThenAdd() throws Exception {

    Item item = new Item();
    item.setDescription("test");

    mvc.perform(
            post("/get_all_items/add_item_page/add_item")
                    .param("description", "test")
    ).andExpect(
            status().is3xxRedirection()
    );

    verify(itemService, times(1)).add(item);
}

谢谢你。

更新:

@Service
public class UserService implements UserDetailsService {

    @Autowired
    private UserRepository userRepo;

        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            User user = userRepo.findByUsername(username);
            if (user == null) user = new User();
            return user;
        }
}

共有1个答案

杜翰林
2023-03-14

@WithMockUser帮助您插入org.springframework.security.core.userdetails.user对象并执行测试,但是在本例中,您需要一个自定义用户对象ru.pravvich.domain.user

为什么它适用于浏览器窗体?这是因为主体来自您的身份验证过程,例如,如果您有自己的UserDetailsService,以便返回您的自定义ru.pravvich.domain.user,则在正常情况下,当您通过web表单执行authetnication时,此过程会发生,但在测试用例场景中,至少您配置了测试用例并通知了您的自定义实现,则不会发生此过程。让我们看一个例子:

从user扩展的自定义用户类(在此处更新)

   //MySpring user is just a class that extends from 
   //org.springframework.security.core.userdetails.User
   //org...security....User implements UserDetailsService that is the 
   //the reason why I can return this class from loadUserByUsername method, you will see it later
   //it helps me to wrap my own User definition
   public class MySpringUser  extends User {

    //this is my own user definition, where all my custom fields are defined
    private MyUser user;

    //the constructor expect for myUser and then I call the super
    //constructor to fill the basic fields that are mandatory for spring 
    //security in order to perform the authentication process
    public MySpringUser(MyUser myUser, Collection<? extends GrantedAuthority> authorities) {
        super(myUser.getUsername(), myUser.getPassword(), myUser.isEnabled()
                , true, true, true, authorities);

        this.setUser(myUser);
    }

    public MyUser getUser() {
        return user;
    }

    public void setUser(MyUser user) {
        this.user = user;
    }
    //this is an example of how to get custom fields a custom id for example
    public Long getId(){
        return this.getUser().getId();
    }
}

自定义UserDetails服务(在此处更新)

@Service
public class MyUserService implements UserDetailsService {

    @Autowired
    MyUserRepository myUserRepository;

    //Principal
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {

        //Here I get the User that I defined for my own domain structure
        MyUser myUser = myUserRepository.findFirstByUsername(s);

        //Here I return a new User object, in this case MySpringUser is
        //just a class that  extends from User class, its constructor 
        //receive myUser object as parameter in order to get my own custom fields later   
        return new MySpringUser(myUser,AuthorityUtils.createAuthorityList("ADMIN"));

    }
}

最后,测试用例使用@WithUserDetails通知测试应该调用由自定义UserDetailsService实现的LoadUserByUserName方法,在这种情况下,您可以自由地将主体强制转换为自定义用户对象。

    @Test
    @WithUserDetails( "myUserNameOnDatabase")
    public void contextLoads() {
        try {
            mockMvc.perform(get("/stores")).andExpect(status().isOk()).andDo(print());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

这是在控制器中执行强制转换的方式,就像你的一样

  @RequestMapping("/stores")
    public List<Store> getStores(Principal principal){

        MySpringUser activeUser = (MySpringUser) ((Authentication) principal).getPrincipal();
        System.out.println(activeUser.getUsername());

        return storeRepository.findByMyUserId();

    }

(此处更新)我还上传了一个完整的示例到我的git repo中,您可以下载并尝试它,以便给您一个更清晰的视图,当然这只是一种选择,还有其他选择,例如,您可以创建自己的类来实现userdetails接口,而不是扩展spring用户类,选择将取决于您的需要。

希望这个替代方案能帮助你。

 类似资料:
  • 在我的spring boot应用程序中,我有以下模型:- 邮递员控制台日志:- 我认为我的应用程序工作正常。我如何通过邮差或curl发出正确的JSON请求?

  • 我对春靴和科特林很陌生。我从一个net的基本应用程序开始写单元测试,但我得到了以下错误: 让我向您展示代码:实体类 控制器:

  • 为了测试Spring Boot应用程序中的组件/bean,Spring Boot文档中的测试部分提供了大量信息和多种方法:、、、以及许多其他方法。 为什么要提供这么多方法呢?如何决定支持的方式? 我是否应该将使用Spring Boot test注解(如、、)注解的测试类视为集成测试? PS:我之所以提出这个问题,是因为我注意到许多开发人员(甚至有经验的开发人员)并没有得到使用注释而不是其他注释的后

  • 我试图在@RequestBody中接收一个我不知道的JSON对象,即JSON对象可以是任何长度和数据。 假设JSON可以如下所示

  • 抱歉,这似乎是一个无用的行为,但我们有没有办法在Spring Boot(1.3.8.RELEASE)的应用程序上进行junit测试。java这个类除了启动Spring Boot应用程序之外什么都不做? 如下所示: 也许我可以尝试捕捉异常?但我还可以测试什么,以便JUnit测试SpringApplication呢。run()?任何例子都值得赞赏。谢谢大家!

  • 我正在使用Spring Boot(打包到没有SpringBoot运行程序的经典WAR),我想在Spock中实现集成测试。当我使用时,只使用标准Spring上下文(没有从Boot获得任何好处,例如。