org.springframework.security.core.userdetails.User 不能转换为 MyUserDetails (Junit 5)

Posted

技术标签:

【中文标题】org.springframework.security.core.userdetails.User 不能转换为 MyUserDetails (Junit 5)【英文标题】:org.springframework.security.core.userdetails.User cannot be cast to MyUserDetails (Junit 5) 【发布时间】:2018-12-15 22:44:01 【问题描述】:

我正在使用 Junit 5 测试 Web 应用程序,除了

SecurityContextHolder.getContext().getAuthentication().getPrincipal()

在运行测试用例时控制器类中的线

的线返回对象代码

org.springframework.security.core.userdetails.User 而不是 MyUserDetails (而在 Spring Boot 中运行的应用程序返回对象是 MyUserDetails 并且代码运行良好)

我的测试用例类

@ExtendWith(SpringExtension.class)
@WebMvcTest(VendorController.class)
@AutoConfigureMockMvc
class VendorControllerTest 

    @Autowired
    private MockMvc mvc;

    @Test
    void test() throws Exception 
    mvc.perform(MockMvcRequestBuilders.post("/vendor/user/getVendorList").with(csrf().asHeader())
            .param("iDisplayStart","0")
            .param("iDisplayLength", "10")
            .param("iSortCol_0", "0")
            .param("sSortDir_0", "asc")
            .param("sSearch","")
            .with(user("username").password("password").roles("2"))).andExpect(status().isOk());
     

我缺少什么代码,以便在使用 Junit 5 运行测试用例时返回 org.springframework.security.core.userdetails.User 对象,但如果我通过 Spring Boot 运行项目,则可以正常工作。

添加 UserDetailsS​​ervice 实现代码

@Service("userDetailsService")

public class MyUserDetailsService implements UserDetailsService 

@Autowired

private UserSvc userSvc;

@Transactional(readOnly=true)

@Override

public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException 

    com.esociety.entity.UserEntity userEntity = userSvc.findByUserName(username);

    List<GrantedAuthority> authorities = buildUserAuthority(userEntity.getUserType());

    return buildUserForAuthentication(userEntity, authorities);



private User buildUserForAuthentication(com.esociety.entity.UserEntity userEntity, List<GrantedAuthority> authorities) 

     MyUserDetails myUserDetails = new MyUserDetails (userEntity.getUsername(), userEntity.getPassword(), userEntity.isEnabled(), userEntity.isAccountNonExpired(), userEntity.isCredentialsNonExpired(),userEntity.isAccountNonLocked(),authorities,userEntity.getUserId(),userEntity.getSocietyId(),userEntity.getUserType(),userEntity.getFirstName(),userEntity.getMiddleName(),userEntity.getLastName());

     return myUserDetails;



private List<GrantedAuthority> buildUserAuthority(String userType) 

    Set<GrantedAuthority> setAuths = new HashSet<GrantedAuthority>();

    // Build user's authorities

    setAuths.add(new SimpleGrantedAuthority(userType));

    List<GrantedAuthority> Result = new ArrayList<GrantedAuthority>(setAuths);

    return Result;




MyUserDetails 类

public class MyUserDetails extends User 
 public MyUserDetails(String username, String password, boolean enabled,
        boolean accountNonExpired, boolean credentialsNonExpired,
        boolean accountNonLocked,
        Collection<? extends GrantedAuthority> authorities, Long userId,
        Long societyId, String userType, String firstName,
        String middleName, String lastName) 
    super(username, password, enabled, accountNonExpired,
            credentialsNonExpired, accountNonLocked, authorities);
    this.userId = userId;
    this.societyId = societyId;
    this.userType = userType;
    this.firstName = firstName;
    this.middleName = middleName;
    this.lastName = lastName;
 
// getter setter goes here


【问题讨论】:

您是否使用UserDetailsService 的实现来返回MyUserDetails?如果是这样,您如何使该服务可用于测试(因为您没有加载整个 SpringApplication 上下文)? @AndreiDamian-Fekete 我添加了 userDetailsS​​ervice 实现代码。当我登录应用程序时,会调用 buildUserForAuthentication 方法并创建一个扩展 User 的 MyUserDetails 对象,但我相信如果在 Junit 测试时应该调用 buildUserForAuthentication 方法,那么我将不会遇到类转换异常问题。 【参考方案1】:

问题是您使用的是SecurityMockMvcRequestPostProcessors.user(String username)RequestPostProcessor,它将创建并返回一个org.springframework.security.core.userdetails.User,它是UserDetails 的实现,但不是您想要的MyUserDetails

相反,您可以使用SecurityMockMvcRequestPostProcessors.user(UserDetails user),它允许提交您自己的UserDetails 实现,在本例中为您想要的MyUserDetails

改变你的测试如下:

@Test
void test() throws Exception 
mvc.perform(MockMvcRequestBuilders.post("/vendor/user/getVendorList").with(csrf().asHeader())
        .param("iDisplayStart","0")
        .param("iDisplayLength", "10")
        .param("iSortCol_0", "0")
        .param("sSortDir_0", "asc")
        .param("sSearch","")
        .with(user(new MyUserDetails ("username", "password", true, true, true,true, Arrays.asList(new SimpleGrantedAuthority("2")),1,1,USER_TYPE,"FirstName","MiddleName","LastName")))).andExpect(status().isOk());
 

参考 Spring Security Source code

【讨论】:

它说将参数 1 转换为 RequestPostProcessor 或让 MyUserDetails 实现 RequestPostProcessor .with( (RequestPostProcessor) new MyUserDetails ("username", "password", true, true, true,true, Arrays.asList(new SimpleGrantedAuthority("2")),2L,1L,"ROLE_2","Firstname","middlename","Lastname"))).andExpect(status().isOk());,但我仍然遇到同样的错误 您不能将 MyUserDetails 转换为 RequestPostProcessor。您的代码更改甚至运行了吗? 如果我不强制转换,它会给出编译错误,是的,代码运行正常 对代码进行了少量更改,并且可以正常工作 .with( user( new MyUserDetails ("username", "password", true, true, true,true, Arrays.asList(new SimpleGrantedAuthority("2")),2L,1L,"ROLE_2","Firstname","middlename","Lastname")))).andExpect(status().isOk()); 参考 custom_UserDetails SecurityMockMvcRequestPostProcessors.user(UserDetails user) 是我使用@WithMockUser 编写集成单元测试以获得精细角色和权限的关键。感谢您为我指明正确的方向。

以上是关于org.springframework.security.core.userdetails.User 不能转换为 MyUserDetails (Junit 5)的主要内容,如果未能解决你的问题,请参考以下文章