java Spring boot,禁用测试的安全性

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32956829/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 21:01:15  来源:igfitidea点击:

Spring boot, disable security for tests

javaspringspring-mvcspring-securityspring-boot

提问by Hollow.Quincy

I use spring boot version "1.3.0.M5" (I also tried version "1.2.5.RELEASE"). I added spring security:

我使用 Spring Boot 版本“1.3.0.M5”(我也尝试过版本“1.2.5.RELEASE”)。我添加了弹簧安全性:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.security</groupId>
  <artifactId>spring-security-test</artifactId>
  <scope>test</scope>
</dependency>

and code:

和代码:

@SpringBootApplication
public class SpringBootMainApplication {
  public static void main(String[] args) {
    SpringApplication.run(SpringBootMainApplication.class, args);
  }
}

@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
  }
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
    .antMatchers("/api/sampleentity").authenticated()
    .and().authorizeRequests()
    .and().formLogin().permitAll()
    .and().logout().permitAll().logoutUrl("/logout")
    .logoutSuccessUrl("/");
  }
  @Override
  @Bean
  public AuthenticationManager authenticationManagerBean() throws Exception {
    return super.authenticationManagerBean();
  }
}

@RestController
@RequestMapping("/api/sampleentity")
public class SampleEntityController {
  @RequestMapping(method= RequestMethod.GET)
  public Iterable<SampleEntity> getAll() {
    return ImmutableSet.of();
  }
  @RequestMapping(method=RequestMethod.POST)
  @ResponseStatus(value= HttpStatus.CREATED)
  public SampleEntity create(@RequestBody SampleEntity sampleEntity) {
    return sampleEntity;
  }
}

and test that is failing when /api/sampleentity is access: org.springframework.web.client.HttpClientErrorException: 403 Forbidden (...)

并在访问 /api/sampleentity 时测试失败:org.springframework.web.client.HttpClientErrorException: 403 Forbidden (...)

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SpringBootMainApplication.class)
@WebAppConfiguration
@IntegrationTest({"server.port=0"})
public class SampleEntityTest {
  @Value("${local.server.port}")
  private int port;
  private String url;
  private RestTemplate restTemplate;
  @Autowired
  private ApplicationContext context;
  @BeforeClass
  public static void authenticate(){
//ONE TRY
//        Authentication authentication =
//                new UsernamePasswordAuthenticationToken("user", "password",
//                                                        AuthorityUtils.createAuthorityList("USER")); //tried "ROLE_USER"
//        SecurityContextHolder.getContext().setAuthentication(authentication);
  }
  @Before
  public void setUp() {
    url = String.format("http://localhost:%s/api/sampleentity", port);
    restTemplate = new RestTemplate();
//ANOTHER TRY
//        AuthenticationManager authenticationManager = context.getBean(AuthenticationManager.class);
//        Authentication authentication = authenticationManager
//                .authenticate(new UsernamePasswordAuthenticationToken("user", "password", AuthorityUtils.createAuthorityList("USER"))); //tried "ROLE_USER"
//        SecurityContextHolder.getContext().setAuthentication(authentication);
  }
  //THIS METHOD SHOULD WORK !
  @Test
//ANOTHER TRY
//@WithMockUser(username="user",password = "password", roles={"USER"})//tried "ROLE_USER"
  public void testEntity_create() throws Exception {
    SampleEntity sampleEntity = create("name", 1);
    ResponseEntity<SampleEntity> response = restTemplate.postForEntity(url, sampleEntity, SampleEntity.class);
    assertEquals(HttpStatus.CREATED, response.getStatusCode());
  }
  private SampleEntity create(String name, int id) {
    SampleEntity entity = new SampleEntity();
    entity.setName(name);
    entity.setId(id);
    return entity;
  }
}

When I run application from main() and access url: http://localhost:8080/api/sampleentityI am redirected to login page.

当我从 main() 运行应用程序并访问 url: http://localhost:8080/api/sampleentity我被重定向到登录页面。

How can I run my test and disable security or just log in user ?

如何运行测试并禁用安全性或仅登录用户?

--my solution: exclude security from the test using profiles:

--我的解决方案:使用配置文件从测试中排除安全性:

@SpringBootApplication
@EnableAutoConfiguration(exclude = { SecurityAutoConfiguration.class})
public class SpringBootMainApplication {body the same}

@EnableWebSecurity
@Import(SecurityAutoConfiguration.class)
@Profile("!test")
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {body the same}

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SpringBootMainApplication.class)
@WebAppConfiguration
@IntegrationTest({"server.port=0"})
@ActiveProfiles("test")
public class SampleEntityTest {body the same}

回答by BeWu

You have to do some changes to your config and test to solve your problem(s).

您必须对配置进行一些更改并进行测试以解决您的问题。

First I'll explain why your solution isn't working:

首先,我将解释为什么您的解决方案不起作用:

  1. The Spring RestTemplateclass is a possible way to access your REST service but lacks some header informations the way it is constructed (Which doesn't mean it's impossible with the RestTemplate). Thats why the authentication didn't work.
  2. My first solution attempt isn't working because of the usage of the RestTemplateclass, as the RestTemplaterequest is likely to create a new session. It sets an entirely different environment. My code works if you want to test Methods secured with the @PreAuthorizeannotation but only if you want to execute such a method directly in your test and you need a valid authentication.
  3. You can't automatically authorize any user as of your current spring security configuration.
  1. SpringRestTemplate类是一种访问 REST 服务的可能方式,但它的构造方式缺少一些标头信息(这并不意味着它不可能使用RestTemplate)。这就是身份验证不起作用的原因。
  2. 由于RestTemplate该类的使用,我的第一个解决方案尝试不起作用,因为该RestTemplate请求可能会创建一个新会话。它设置了一个完全不同的环境。如果您想测试使用@PreAuthorize注释保护的方法,但仅当您想直接在测试中执行此类方法并且需要有效的身份验证时,我的代码才有效。
  3. 根据当前的 spring 安全配置,您无法自动授权任何用户。

Second, here are the necessary changes to your code:

其次,以下是对代码的必要更改:

First the configuration class

首先是配置类

@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

@Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  auth.inMemoryAuthentication().withUser("user").password("password").roles("USER" );
  }

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.httpBasic().and().csrf().disable()
    .authorizeRequests().antMatchers("/api/sampleentity").authenticated()
    .and().authorizeRequests().antMatchers("/users").hasRole("ADMIN")
    .and().formLogin().permitAll()
    .and().logout().permitAll().logoutUrl("/logout")
    .logoutSuccessUrl("/");
  }

  @Override
  @Bean
  public AuthenticationManager authenticationManagerBean() throws Exception {
    return super.authenticationManagerBean();
  }
}

I had to add httpBasic Authentication support (to enable authentication via http header attribute) and I disabled csrf tokens (the latter just for convenience, you should reenable them according to criticality of your application).

我必须添加 httpBasic Authentication 支持(以通过 http 标头属性启用身份验证)并禁用了 csrf 令牌(后者只是为了方便起见,您应该根据应用程序的重要性重新启用它们)。

And second the Testclass:

其次是测试类:

import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Arrays;

import javax.servlet.Filter;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingHymanson2HttpMessageConverter;
import org.springframework.mock.http.MockHttpOutputMessage;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SpringBootMainApplication.class)
@WebAppConfiguration
@IntegrationTest({ "server.port=0" })
public class SampleEntityTest {

private String url;
private MockMvc mockMvc;
private HttpMessageConverter mappingHymanson2HttpMessageConverter;

private MediaType contentType = new MediaType(
        MediaType.APPLICATION_JSON.getType(),
        MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8"));

@Autowired
private WebApplicationContext webApplicationContext;

@Autowired
private Filter springSecurityFilterChain;

@Autowired
void setConverters(HttpMessageConverter<?>[] converters) {
    for (HttpMessageConverter hmc : Arrays.asList(converters)) {
        if (hmc instanceof MappingHymanson2HttpMessageConverter) {
            this.mappingHymanson2HttpMessageConverter = hmc;
        }
    }

    Assert.assertNotNull("the JSON message converter must not be null",
            this.mappingHymanson2HttpMessageConverter);
}

@Before
public void setUp() {
    url = "/api/sampleentity";
    mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
            .addFilters(springSecurityFilterChain).build();
}

@Test
public void testEntityGet() throws Exception {
    mockMvc.perform(
            get(url)
            .with(httpBasic("user", "password")))
            .andExpect(status().isOk());
}

@Test
public void testEntityPost() throws Exception {
    SampleEntity sampleEntity = new SampleEntity();
    sampleEntity.setName("name");
    sampleEntity.setId(1);
    String json = json(sampleEntity);
    mockMvc.perform(
            post(url)
            .contentType(contentType)
            .content(json)
            .with(httpBasic("user", "password")))
            .andExpect(status().isCreated());
}

protected String json(Object o) throws IOException {
    MockHttpOutputMessage mockHttpOutputMessage = new MockHttpOutputMessage();
    this.mappingHymanson2HttpMessageConverter.write(o,
            MediaType.APPLICATION_JSON, mockHttpOutputMessage);
    return mockHttpOutputMessage.getBodyAsString();
}

}

}

I have used the spring/ spring security test approach here.

我在这里使用了 spring/spring 安全测试方法。

Versions used:

使用的版本:

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.2.5.RELEASE</version>
    </parent>

    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-core</artifactId>
        <version>4.0.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-web</artifactId>
        <version>4.0.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-config</artifactId>
        <version>4.0.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-test</artifactId>
        <version>4.0.2.RELEASE</version>
        <scope>test</scope>
    </dependency>

If you want to test your rest api i can recommend you the Postman plugin for Chrome. As that can help you identify the problem much faster.

如果你想测试你的 rest api,我可以向你推荐 Chrome 的 Postman 插件。因为这可以帮助您更快地识别问题。

I hope this helps you to finally solve your problem.

我希望这可以帮助您最终解决您的问题。

回答by Kunal Vohra

If you want to see what's being auto-configured, launch your web app and access the autoconfig endpoint (e.g., http://localhost:8080/autoconfig). Then search for 'Security' to see which 'AutoConfiguration' classes are being detected.

如果您想查看自动配置的内容,请启动您的 Web 应用程序并访问自动配置端点(例如,http://localhost:8080/autoconfig)。然后搜索“ Security”以查看AutoConfiguration正在检测哪些“ ”类。

You can then disable auto-configuration of security by excluding those classes like this:

然后,您可以通过排除这些类来禁用安全性的自动配置,如下所示:

@EnableAutoConfiguration(exclude = { SecurityAutoConfiguration.class, ManagementSecurityAutoConfiguration.class })

Of course, you won't want to exclude them for production deployments. Thus you'll need to have a separate @Configurationclass for production and tests.

当然,您不会希望将它们排除在生产部署中。因此,您需要有一个单独的@Configuration类用于生产和测试。

Orif you want a detailed answer go for below-mentioned steps

或者,如果您想要详细的答案,请执行以下步骤



Add annotation @Profile(value = {"development", "production"})to my implementation of WebSecurityConfigurerAdapter

@Profile(value = {"development", "production"})在我的实现中添加注释WebSecurityConfigurerAdapter

@Configuration
    @EnableWebSecurity
    @Profile(value = {"development", "production"})
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

Now, in test/resources, create application-test.yml to define properties for test profile and add this -

现在,在 test/resources 中,创建 application-test.yml 来定义测试配置文件的属性并添加这个 -

# Security enable/disable
security:
  basic:
    enabled: false

Now, to your test cases, add this annotation to apply the active profile @ActiveProfiles(value = "test"). This is how my class looked -

现在,在您的测试用例中,添加此注释以应用活动配置文件 @ActiveProfiles(value = "test")。这是我班级的样子——

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebAppConfiguration
@ActiveProfiles(value = "test")
@IntegrationTest({"server.port=0"})
public class SampleControllerIntegrationTest {

Doing this will disabled security for tests. Best of luck!!!

这样做将禁用测试的安全性。祝你好运!!!