WebHU - Programozási kérdések és válaszok

Tavaszi teszt: A @PreAuthorize(@SecurityPermission.hasPermission('somepermission')) által biztosított módszer tesztelése

Spring Boot projektünkben minden módszert @PreAuthorize megjegyzéssel biztosítottunk. Ellenőrzi, hogy a felhasználó rendelkezik-e engedéllyel a kért erőforráshoz.

Íme az egyik vezérlőnk:

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

}    

A kérdés a következő: Hogyan lehet megfelelően tesztelni a fent említett módszer jogosultságait?

A következő két lehetőséggel próbálkoztam:

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

}

Az optionOne akkor is megfelel, ha a hamisított felhasználó nem rendelkezik szükséges engedéllyel ("role.list"), míg az optionTwo sikertelen 403-as állapot esetén.

java.lang.AssertionError: Status 
Expected :200
Actual   :403

FRISSÍTÉS: Hozzáadom a WebSecurityConfig osztályt

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

}

  • Elegendő lenne, ha a SecurityPermission.hasPermission('role') módszert tesztelné, mert a megjegyzést a megfelelő könyvtárban tesztelték, és működnie kell. Tényleg szeretnél ehhez egy integrációs tesztet? 27.03.2018
  • Ha valaki ideiglenesen megjegyzést fűz a megjegyzéshez, és elfelejti, a tesztek sikertelenek, jelezve, hogy a kód hibás. 27.03.2018
  • Igaz, ha ilyesmi történik, javítania kell a minőségbiztosítási folyamatot. Ha az alábbi válasz nem oldja meg a problémát, meg tudná mutatni a bean SecurityPermission kódját? Mert itt történik az ellenőrzés. 27.03.2018
  • A szerep a JWT hasznos adattartalmából származik, és minden szerepkör rendelkezik engedélykészlettel 27.03.2018
  • A módszer biztonsága az engedélyeken alapul 27.03.2018
  • Ó, most vettem észre, hogy az optionTwo() meghívja a this.mvc.perform(get("/role/allroles")-t with(user('someone')) nélkül. Ez megmagyarázza a viselkedést? Az optionOne() megkapja az adminisztrátor felhasználót, de az optionTwo() névtelen felhasználóval rendelkezik. 27.03.2018
  • Az optionTwo() a @WithMockUser(authorities = {role.list}) parancsot használja. 27.03.2018

Válaszok:


1

Néhány hónapja ugyanez volt a problémám, de kicsit másképp. Úgy gondolom, hogy a kontextus nincs megfelelően beállítva, mivel tesztelési célból SpringSecurity explicititást kell alkalmaznia:

private MockMvc mockMvc;

@Autowired
private WebApplicationContext context;

@Before
public void setup() {
    this.mockMvc = MockMvcBuilders.webAppContextSetup(context)
            .apply(springSecurity())
            .build();
}

Ezt is megtekintheti: Hogyan lehet tesztelni egy biztonságos vezérlőt, amely thymeleaf-et használ (a TemplateProcessingException kivétel nélkül)? Ez kissé eltér az Ön problémájától, de mivel a SecurityHandling egyfajta egyéni beállítás, nehéz segíteni anélkül, hogy jobban ismerné a projektet.

Ha egy nem jogosult felhasználó viselkedését próbálja tesztelni, akkor valami ilyesmit tehet:

@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));
}
27.03.2018
Új anyagok

A rádiógomb ellenőrzött eseményének használata a jQueryben
Ebben a cikkben látni fogjuk, hogyan kell dolgozni a jquery választógombbal ellenőrzött eseményeivel. A választógombok HTML gombok, amelyek segítenek kiválasztani egyetlen értéket egy csoportból...

Körkörös függőségek megoldása terraformban adatforrásokkal – lépésről lépésre
Mi az a körkörös függőségek Dolgozzunk egy egyszerű eseten, amikor az SQS-sor és az S3-vödör közötti körkörös függőség problémája van egy egymástól függő címkeérték miatt. provider..

Miért érdemes elkezdeni a kódolást 2023-ban?
01100011 01101111 01100100 01100101 — beep boop beep boop Világunk folyamatosan fejlődik a technológia körül, és naponta fejlesztenek új technológiákat a valós problémák megoldására. Amint..

🎙 Random Noise #2  – Örökbefogadás és hit
az analitika íratlan világának gondozása Szeretné, hogy ezek a frissítések a postaládájába kerüljenek? Iratkozzon fel itt . "Ha önvezető autókat gyártanak, akkor mi miért ne..

A legrosszabb politika és prediktív modellek májátültetésre jelöltek számára az Egyesült Államokban
A máj (vagy óangolul lifer) az emberi test legnehezebb belső szervére utal, amely csendesen működik a nap 24 órájában. Mit csinál a máj? 500 feladatot hajt végre a szervezet egészségének..

5 webhely, amely 2022-ben fejleszti front-end fejlesztői készségeit
Frontendmentor.io A tényleges projektek létrehozásával a Frontendmentor.io segítséget nyújt a front-end kódolási képességeinek fejlesztésében. A kódolást azután kezdheti meg, hogy..

Mikor kell használni a Type-t az interfészhez képest a TypeScriptben?
A TypeScript a JavaScript gépelt szuperkészlete, amely statikus gépelést ad a nyelvhez. Ez megkönnyíti a robusztus és karbantartható kód írását azáltal, hogy a hibákat a fordítási időben..