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

时间:2017-08-27 20:48:20

标签: java spring spring-mvc spring-security

我有一个POST方法,用于发送Item。每个项目包含作者的用户ID。为获取作者ID,我使用:(User)SecurityContextHolder.getContext().getAuthentication().getPrincipal(); 但是在测试中,由于结果,这不起作用:

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.ClassCastException: org.springframework.security.core.userdetails.User cannot be cast to ru.pravvich.domain.User

POST方法:

@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);
}

为什么ClassCastException没有丢弃然后我从浏览器表单发送数据?如何解决这个问题?

谢谢。

更新

@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;
        }
}

2 个答案:

答案 0 :(得分:4)

.可帮助您注入WHERE t1.siteID = 1 AND deleted = 0 AND (t2.toUserID = 3 OR t2.userID = 3) GROUP BY个对象并执行测试,但在这种情况下,您需要一个自定义用户对象LIMIT 1

为什么它适用于浏览器表单?这是因为主体来自您的身份验证过程,例如,如果您有自己的 BITS 16 ORG 100h start: ;I want to write a byte into a register on the sound card or NIC or ;whatever. So, I'm using a move instruction to accomplish that where X ;is the register's memory mapped or IO mapped address. mov X,11h times 510 - ($ - $$) db 0 dw 0xaa55 以便返回自定义@WithMockUser,则当您通过Web执行身份验证时,此过程会在正常情况下发生表单,但在测试用例场景中,至少您配置测试用例并告知您的自定义实现并不会发生。让我们看一个例子:

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

org.springframework.security.core.userdetails.User

自定义用户详细信息服务(在此更新

ru.pravvich.domain.User

最后使用UserDetailsService测试用例通知测试应该调用自定义ru.pravvich.domain.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(); } } 方法,在这种情况下,您可以自由地将主体转换为您的自定义用户对象。

@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

在这里更新)我还上传了一个完整的例子到我的git repo,你可以下载它并尝试它以便给你一个更清晰的视图,当然这只是一个选项,还有其他的,例如,而不是extends spring User类,您可以创建自己的类来实现loadUserByUsername接口,替代方案将取决于您的需求。

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

My git Repo example of how use @WithUserDetails

答案 1 :(得分:0)

Spring Security&#39; @WithMockUser使用org.springframework.security.core.userdetails.User对象填充安全上下文,这就是您遇到此异常的原因。 documentation对此很清楚:

  

身份验证的主体是Spring Security的用户对象

ClassCastException的一个可能解决方案是简单地重复使用Spring User类并使用getUsername()返回作者ID,或者如果您必须使用{{1}对象,找到另一种方法来填充测试的安全上下文。