在我们的Spring Boot项目中,我们使用 @PreAuthorize 注释来保护每个方法。它检查用户是否具有所请求资源的权限。
这是我们的控制器之一:
@PreAuthorize("@SecurityPermission.hasPermission('role')")
@RequestMapping(value = "/role")
public class RoleController {
@Autowired
private RoleService roleService;
@PreAuthorize("@SecurityPermission.hasPermission('role.list')")
@RequestMapping(value = "/allroles", method = RequestMethod.GET, consumes = "application/json", produces = "application/json")
public JsonData<Role> getListOfRoles() {
JsonData<Role> roleJsonData = new JsonData<>();
roleJsonData.setData(roleService.list());
return roleJsonData;
}
}
问题是:如何正确测试上述方法的权限?
我尝试了以下两个选项:
@RunWith(SpringRunner.class)
@WebMvcTest(RoleController.class)
public class RoleControllerTest {
@Autowired
private MockMvc mvc;
@MockBean
private RoleService roleService;
@Test
public void optionOne() throws Exception {
ArrayList<Role> roles = new ArrayList<>();
roles.add(new Role().setId(1L).setName("administrator"));
roles.add(new Role().setId(2L).setName("user"));
given(roleService.list()).willReturn(roles);
HttpHeaders headers = new HttpHeaders();
headers.set("Content-Type", "application/json");
this.mvc.perform(get("/role/allroles").with(user("testadmin"))
.headers(headers))
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.data[0].name", is( roles.get(0).getName())))
.andExpect(jsonPath("$.data[1].name", is( roles.get(1).getName())));
}
@Test
@WithMockUser(authorities = {"role.list"})
public void optionTwo() throws Exception {
ArrayList<Role> roles = new ArrayList<>();
roles.add(new Role().setId(1L).setName("administrator"));
roles.add(new Role().setId(2L).setName("user"));
given(roleService.list()).willReturn(roles);
HttpHeaders headers = new HttpHeaders();
headers.set("Content-Type", "application/json");
this.mvc.perform(get("/role/allroles")
.headers(headers))
.andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.data[0].name", is( roles.get(0).getName())))
.andExpect(jsonPath("$.data[1].name", is( roles.get(1).getName())));
}
}
optionOne即使模拟用户没有所需权限(“role.list”)也会通过,而optionTwo则以状态403失效。
java.lang.AssertionError: Status
Expected :200
Actual :403
更新:我正在添加WebSecurityConfig类
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
public static final String JWT_TOKEN_HEADER_PARAM = "X-Authorization";
public static final String FORM_BASED_LOGIN_ENTRY_POINT = "/auth/login";
public static final String SEARCH_BASED_ENTRY_POINT = "/search/**";
public static final String TOKEN_REFRESH_ENTRY_POINT = "/auth/token";
public static final String TOKEN_BASED_AUTH_ENTRY_POINT = "/**";
@Autowired
private RestAuthenticationEntryPoint authenticationEntryPoint;
@Autowired
private AuthenticationSuccessHandler successHandler;
@Autowired
private AuthenticationFailureHandler failureHandler;
@Autowired
private AjaxAuthenticationProvider ajaxAuthenticationProvider;
@Autowired
private JwtAuthenticationProvider jwtAuthenticationProvider;
@Autowired
private TokenExtractor tokenExtractor;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private ObjectMapper objectMapper;
@Bean
protected AjaxLoginProcessingFilter buildAjaxLoginProcessingFilter() throws Exception {
AjaxLoginProcessingFilter filter = new AjaxLoginProcessingFilter(FORM_BASED_LOGIN_ENTRY_POINT, successHandler, failureHandler, objectMapper);
filter.setAuthenticationManager(this.authenticationManager);
return filter;
}
@Bean
protected JwtTokenAuthenticationProcessingFilter buildJwtTokenAuthenticationProcessingFilter() throws Exception {
List<String> pathsToSkip = Arrays.asList(TOKEN_REFRESH_ENTRY_POINT, FORM_BASED_LOGIN_ENTRY_POINT, SEARCH_BASED_ENTRY_POINT);
SkipPathRequestMatcher matcher = new SkipPathRequestMatcher(pathsToSkip, TOKEN_BASED_AUTH_ENTRY_POINT);
JwtTokenAuthenticationProcessingFilter filter
= new JwtTokenAuthenticationProcessingFilter(failureHandler, tokenExtractor, matcher);
filter.setAuthenticationManager(this.authenticationManager);
return filter;
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(ajaxAuthenticationProvider);
auth.authenticationProvider(jwtAuthenticationProvider);
}
@Bean
protected Md5PasswordEncoder passwordEncoder() {
return new Md5PasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("utf-8");
filter.setForceEncoding(true);
http.addFilterBefore(filter, CsrfFilter.class);
http.addFilterBefore(new WebSecurityCorsFilter(), ChannelProcessingFilter.class);
http
.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint(this.authenticationEntryPoint)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(FORM_BASED_LOGIN_ENTRY_POINT).permitAll()
.antMatchers(TOKEN_REFRESH_ENTRY_POINT).permitAll()
.antMatchers(SEARCH_BASED_ENTRY_POINT).permitAll()
.and()
.authorizeRequests()
.antMatchers(TOKEN_BASED_AUTH_ENTRY_POINT).authenticated()
.and()
.addFilterBefore(buildAjaxLoginProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(buildJwtTokenAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
答案 0 :(得分:0)
private MockMvc mockMvc;
@Autowired
private WebApplicationContext context;
@Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(context)
.apply(springSecurity())
.build();
}
您还可以参考:How to unit test a secured controller which uses thymeleaf (without getting TemplateProcessingException)? 它与您的问题略有不同,但由于SecurityHandling是一种单独的设置,如果不更好地了解您的项目,很难提供帮助。
如果您尝试测试未授权用户的行为,您还可以执行以下操作:
@Test
public void getLoginSuccessWithAnonymousUserReturnsAccessDeniedException() throws Exception {
MvcResult mvcResult = mockMvc.perform(get("/your-url").with(anonymous()))
.andExpect(status().is3xxRedirection()) //change to your code
.andReturn();
Class result = mvcResult.getResolvedException().getClass();
MatcherAssert.assertThat((result.equals(org.springframework.security.access.AccessDeniedException.class)), is(true));
}