【问题标题】:Spring Boot REST Controller testing with roles使用角色进行 Spring Boot REST 控制器测试
【发布时间】:2019-03-07 08:45:52
【问题描述】:

我正在从头开始编写一个新的 Spring Boot 应用程序,以便更好地了解其配置的工作原理。现在,我正在尝试创建无法正常工作的访问控制。

我的 UserDetailsS​​ervice 系统使用自定义 UserDTO 对象进行验证:

@Transactional(readOnly = true)
    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
                return new UserDTO(
                        userRepository.findByEmailAddress(email)
                                .orElseThrow(() -> new UsernameNotFoundException("User '" + email + "' not found."))
                );
    }

用户实体:

@Entity
@Table(name = "USERS")
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@Builder
public class User extends BaseEntity {

    private String firstName;
    private String lastName;
    private String emailAddress;
    private String nickname;
    private String password;
    private boolean accountNonExpired;
    private boolean accountNonLocked;
    private boolean credentialsNonExpired;
    private boolean enabled;

    @ManyToMany
    @JoinTable(
            name = "USER_ROLE",
            joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
            inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"))
    private Set<Role> roles;

    public Set<Role> getRoles() {
        return roles;
    }
}

这是 UserDTO 类的样子:

@Getter
@Setter
@NoArgsConstructor
public class UserDTO implements UserDetails {
    private Long id;
    private String firstName;
    private String lastName;
    private String username;
    private String nickname;
    private String password;
    private Set<RoleDTO> authorities;
    private boolean accountNonExpired;
    private boolean accountNonLocked;
    private boolean credentialsNonExpired;
    private boolean enabled;

    public UserDTO(User user){
        this.id = user.getId();
        this.firstName = user.getFirstName();
        this.lastName = user.getLastName();
        this.username = user.getEmailAddress();
        this.nickname = user.getNickname();
        this.password = user.getPassword();
        this.authorities = user.getRoles().stream().map(RoleDTO::new).collect(Collectors.toSet());
        this.accountNonExpired = user.isAccountNonExpired();
        this.accountNonLocked = user.isAccountNonLocked();
        this.credentialsNonExpired = user.isCredentialsNonExpired();
        this.enabled = user.isEnabled();
    }

}

H2中的角色表:

INSERT INTO PUBLIC.ROLES(ID, VERSION, AUTHORITY) VALUES
(1, 0, 'ROLE_USER'),
(2, 0, 'ROLE_MODERATOR'),
(3, 0, 'ROLE_ADMIN');

还有控制器:

@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public UserSimpleDTO createUser(@RequestBody UserDTO user){
    return userService.createUser(user);
}

@GetMapping("/{id}")
@ResponseStatus(HttpStatus.OK)
public UserSimpleDTO getUserGeneralData(@PathVariable long id){
    return userService.getUserGeneralData(id);
}

@GetMapping("/{id}/details")
@ResponseStatus(HttpStatus.OK)
@RolesAllowed("ROLE_MODERATOR")
public UserDTO getUserDetailedInfo(@PathVariable long id) {
    return userService.getUserDetailedInfo(id);
}

我的角色实体和 DTO 类如下所示:

@Entity
@Table(name = "ROLES")
@Getter
@NoArgsConstructor
public class Role extends BaseEntity implements GrantedAuthority {

    private String authority;

}

角色DTO:

 @Getter
    @NoArgsConstructor
    public class RoleDTO implements GrantedAuthority {

        private String authority;

        public RoleDTO(Role role){
           this.authority = role.getAuthority();
        }
    }

当我以普通用户身份运行测试时,getUserDetailedInfo() 返回 200 而不是 401,但模拟的 User 对象显然只有 ROLE_USER

缺少什么导致@RolesAllowed 注释无法正常工作,或者此代码中的某处有错误?

编辑:

这是我用来测试给定方法的测试套件,但我忘了添加:

@Test
@WithAnonymousUser
public void getUserDetailedInfoDoesNotAllowAnonymous2() {
    getUserDetailedInfoREST(1, 401);
}

@Test
@WithMockUser
public void getUserDetailedInfoDoesNotAllowUser2() {
    getUserDetailedInfoREST(1, 403);
}

@Test
@WithMockModerator
public void getUserDetailedInfoAllowsModerator2() {
    getUserDetailedInfoREST(1, 200);
}

@Test
@WithMockAdmin
public void getUserDetailedInfoDAllowsAdmin2() {
    getUserDetailedInfoREST(1, 200);
}

这是getUserDetailedInfoREST()测试方法的实现:

private String getUserDetailedInfoREST(long userId, int expectedStatus) {
        try {
            String response = this.mockMvc.perform(get("/users/" + userId + "/details"))
                    .andDo(print())
                    .andExpect(status().is(expectedStatus))
                    .andDo(this::mapMvcResultToUserDTO)
                    .andReturn().getResponse().getContentAsString();
            return response;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

我的自定义注释:

@Retention(RetentionPolicy.RUNTIME)
@WithMockUser(value="admin",roles= {"USER", "MODERATOR", "ADMIN"})
public @interface WithMockAdmin {
}

@Retention(RetentionPolicy.RUNTIME)
@WithMockUser(value="moderator",roles= {"USER", "MODERATOR"})
public @interface WithMockModerator {
}

我还使用我自己的MockHttpServletRequestBuilder 方法对其进行了测试,这些方法通过数据库中的真实现有用户而不是模拟用户,我也想知道它的好坏做法,因为我很难决定。它使测试的可读性降低了一点,但是使用真实用户而不是模拟用户感觉非常诱人:

private static final String ADMIN_USERNAME = "admin@mail.com";
    private static final String ADMIN_PASSWORD = "admin123";

public static MockHttpServletRequestBuilder getAsAdmin(String urlTemplate, Object... uriVars) {
        return MockMvcRequestBuilders.get(urlTemplate, uriVars).with(httpBasic(ADMIN_USERNAME, ADMIN_PASSWORD));
    }

//... and the other HTTP methods+users implementations ...

【问题讨论】:

  • 哪个模拟用户?您添加了很多代码,但忘记了测试。
  • 感谢指出,我已经更新了问题

标签: java rest unit-testing spring-boot spring-security


【解决方案1】:

好的,我找到了解决方案。缺少的是我添加到 SecurityConfig.java 类中的 @EnableGlobalMethodSecurity 注释:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(jsr250Enabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//...
}

现在注释已添加,一切正常。 jsr250Enabled 参数指的是@RolesAllowed 注释,而如果我使用@Secured 代替securedEnabled,则将是必需的。

【讨论】:

    猜你喜欢
    • 2018-06-23
    • 1970-01-01
    • 1970-01-01
    • 2019-07-07
    • 2012-05-25
    • 2021-02-08
    • 1970-01-01
    • 2018-01-02
    • 1970-01-01
    相关资源
    最近更新 更多