簡體   English   中英

Spring Boot REST Controller與角色的測試

[英]Spring Boot REST Controller testing with roles

我從頭開始編寫一個新的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;

}

RoleDTO:

 @Getter
    @NoArgsConstructor
    public class RoleDTO implements GrantedAuthority {

        private String authority;

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

當我以普通用戶身份運行測試時,getUserDetailedInfo()返回200而不是401,但是ROLE_USER 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方法(通過數據庫中的現有現有用戶而不是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 ...

好的,我找到了解決方案。 缺少的是我已添加到SecurityConfig.java類的@EnableGlobalMethodSecurity批注:

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

現在已經添加了注釋,一切正常。 jsr250Enabled參數引用@RolesAllowed批注,而如果我改用@Secured ,則securedEnabled是必需的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM