如何为Spring Security编写SecurityConfig的单元测试

pn9klfpd  于 2023-02-04  发布在  Spring
关注(0)|答案(1)|浏览(261)

我有一个类的Spring安全,验证令牌从用户。我得到的代码从Auth 0网站和修改antMatcher部分为我的配置。以下是代码:

@EnableWebSecurity
public class SecurityConfig {

    @Value("${auth0.audience}")
    private String audience;

    @Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
    private String issuer;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        /*
        This is where we configure the security required for our endpoints and setup our app to serve as
        an OAuth2 Resource Server, using JWT validation.
        */
        http
            .csrf().disable()
            .authorizeRequests()
            .antMatchers(HttpMethod.GET, "/data/actuator/**").permitAll()
            .antMatchers(HttpMethod.PUT, "/data/**").hasAuthority("SCOPE_data:write")
            .anyRequest().authenticated()
            .and().cors()
            .and().oauth2ResourceServer().jwt();
        return http.build();
    }

    @Bean
    JwtDecoder jwtDecoder() {
        /*
        By default, Spring Security does not validate the "aud" claim of the token, to ensure that this token is
        indeed intended for our app. Adding our own validator is easy to do:
        */
        NimbusJwtDecoder jwtDecoder = (NimbusJwtDecoder)
                JwtDecoders.fromOidcIssuerLocation(issuer);
        OAuth2TokenValidator<Jwt> audienceValidator =
                new com.nuance.pindata.health.importer.security.AudienceValidator(audience);
        OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(issuer);
        OAuth2TokenValidator<Jwt> withAudience = new DelegatingOAuth2TokenValidator<>(withIssuer, audienceValidator);
        jwtDecoder.setJwtValidator(withAudience);
        return jwtDecoder;
    }
}

我现在正在尝试编写单元测试,但是没有好的方法来测试它。我可以实际测试改变方法/路径,但是如何编写这个单元测试并不直接,可以通过集成(自动化)测试来完成。
Spring Security HttpSecurity Configuration Testing开始,他就建议这种安全配置也不要写单元测试了,这里的正确方法是什么?如果我应该写单元测试,我该怎么做呢?

h7wcgrx3

h7wcgrx31#

您可以只在集成测试(@SpringBootTest)中测试执行器端点访问控制。对于您自己的安全@Components,您也可以在单元测试(this repo中的许多示例)中进行:

  • @Controller@WebMvcTest(如果您使用的是React式应用,则为@WebfluxTest
  • 使用测试组件的@ExtendWith(SpringExtension.class)@EnableMethodSecurity@Import(具有方法安全性的@Service@Repository,如@PreAuthorize表达式)的普通JUnit,以获得使用安全性进行检测的自动连接示例

spring-security-test附带了一些MockMvc请求后处理器(在您的情况下,请参见org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.jwt)以及WebTestClient mutator(请参见org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockJwt),用于配置正确类型的Authentication(在您的情况下,请参见JwtAuthenticationToken),并在测试安全上下文中设置它,但这仅限于MockMvc和WebTestClient以及@Controller测试。
执行机构启动的集成测试(@SpringBootTest)中的示例用法(但您了解单元测试的想法):

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-test</artifactId>
    <scope>test</scope>
</dependency>
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.jwt;

@SpringBootTest(webEnvironment = WebEnvironment.MOCK)
@AutoConfigureMockMvc
class ApplicationIntegrationTest {

    @Autowired
    MockMvc api;

    @Test
    void givenUserIsAnonymous_whenGetLiveness_thenOk() throws Exception {
        api.perform(get("/data/actuator/liveness"))
            .andExpect(status().isOk());
    }

    @Test
    void givenUserIsAnonymous_whenGetMachin_thenUnauthorized() throws Exception {
        api.perform(get("/data/machin"))
            .andExpect(status().isUnauthorized());
    }

    @Test
    void givenUserIsGrantedWithDataWrite_whenGetMachin_thenOk() throws Exception {
        api.perform(get("/data/machin")
                .with(jwt().jwt(jwt -> jwt.authorities(List.of(new SimpleGrantedAuthority("SCOPE_data:write"))))))
            .andExpect(status().isOk());
    }

    @Test
    void givenUserIsAuthenticatedButNotGrantedWithDataWrite_whenGetMachin_thenForbidden() throws Exception {
        api.perform(get("/data/machin")
                .with(jwt().jwt(jwt -> jwt.authorities(List.of(new SimpleGrantedAuthority("SCOPE_openid"))))))
            .andExpect(status().isForbidden());
    }
}

您也可以使用this libs I maintain中的@WithMockJwtAuth,这个repo包含了许多用于任何类型@Component(当然是@Controllers,但是也可以使用@Services或修饰了方法安全性的@Repositories)的单元测试和集成测试的示例。
上述样品变为:
一个二个一个一个

Spring-附加起动器

在与测试注解相同的存储库中,您将找到简化资源服务器安全配置的入门程序(还可以改进CORS配置,同步会话和CSRF保护禁用,因为第二个会话不应在活动会话中禁用...)。
用法非常简单,要切换到另一个OIDC授权服务器,您需要更改的只是属性。例如,由于繁忙而被迫这样做,就可能发生这种情况(如果他们认为Auth0太贵或者不再可信),或者可能是因为您发现在您的开发机器上使用独立的Keycloak更方便(它可以离线使用,我经常这样做)。
不要直接导入spring-boot-starter-oauth2-resource-server,而是导入一个围绕它的薄 Package 器(仅限composed of 3 files):

<dependency>
    <groupId>com.c4-soft.springaddons</groupId>
    <artifactId>spring-addons-webmvc-jwt-resource-server</artifactId>
    <version>6.0.12</version>
</dependency>

默认情况下,用户必须经过身份验证才能访问com.c4-soft.springaddons.security.permit-all属性(见下文)中列出的路由以外的任何路由。

@Configuration
@EnableMethodSecurity
public class SecurityConfig {
}

spring.security.oauth2.resourceserver属性替换为:

# Single OIDC JWT issuer but you can add as many as you like
com.c4-soft.springaddons.security.issuers[0].location=https://dev-ch4mpy.eu.auth0.com/

# Mimic spring-security default converter: map authorities with "SCOPE_" prefix
# Difference with your current conf is authorities source is not only "scope" claim but also "roles" and "permissions" ones 
# I would consider map authorities without "SCOPE_" prefix (the default behaviour of my starters) and update access control expressions accordingly
com.c4-soft.springaddons.security.issuers[0].authorities.claims=scope,roles,permissions
com.c4-soft.springaddons.security.issuers[0].authorities.prefix=SCOPE_

# Fine-grained CORS configuration can be set per path as follow:
com.c4-soft.springaddons.security.cors[0].path=/data/api/**
com.c4-soft.springaddons.security.cors[0].allowed-origins=https://localhost,https://localhost:8100,https://localhost:4200
com.c4-soft.springaddons.security.cors[0].allowedOrigins=*
com.c4-soft.springaddons.security.cors[0].allowedMethods=*
com.c4-soft.springaddons.security.cors[0].allowedHeaders=*
com.c4-soft.springaddons.security.cors[0].exposedHeaders=*

# Comma separated list of ant path matchers for resources accessible to anonymous
com.c4-soft.springaddons.security.permit-all=/data/actuator/**

傻瓜,不是吗?

相关问题