it-swarm.com.de

Spring Boot-Authentifizierung für Integrationstests

Ich versuche, einen Integrationstest für meinen Controller auszuführen, habe jedoch Probleme, wenn ich mich nicht authentifiziere. Hier ist mein Controller:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestPropertySource(properties = {"security.basic.enabled=false", "management.security.enabled=false"})
@EnableAutoConfiguration(exclude = {org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration.class})
public class HelloControllerIT {
    private final ObjectMapper mapper = new ObjectMapper();
    @Autowired private TestRestTemplate template;

    @Test
    public void test1() throws Exception {
        ObjectNode loginRequest = mapper.createObjectNode();
        loginRequest.put("username","name");
        loginRequest.put("password","password");
        JsonNode loginResponse = template.postForObject("/authenticate", loginRequest.toString(), JsonNode.class);

        HttpHeaders headers = new HttpHeaders();
        headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
        headers.add("X-Authorization", "Bearer " + loginResponse.get("token").textValue());
        headers.add("Content-Type", "application/json");
        return new HttpEntity<>(null, headers);

        HttpEntity request = getRequestEntity();
        ResponseEntity response = template.exchange("/get",
                                                    HttpMethod.GET,
                                                    request,
                                                    new ParameterizedTypeReference<List<Foo>>() {});
        //assert stuff
    }
}

Wenn ich das starte, funktioniert alles. Aber wenn ich die Zeile auskommentiere:

headers.add("X-Authorization", "Bearer " + loginResponse.get("token").textValue());

Ich bekomme den Fehler:

org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON document: Can not deserialize instance of Java.util.ArrayList out of START_OBJECT token
 at [Source: [email protected]; line: 1, column: 1]; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of Java.util.ArrayList out of START_OBJECT token
 at [Source: [email protected]; line: 1, column: 1]

    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.Java:234)
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.Java:219)
    at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.Java:95)
    at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.Java:917)
    at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.Java:901)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.Java:655)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.Java:613)
    at org.springframework.web.client.RestTemplate.exchange(RestTemplate.Java:559)
    at org.springframework.boot.test.web.client.TestRestTemplate.exchange(TestRestTemplate.Java:812)
    at com.test.HelloControllerIT.test1(HelloControllerIT.Java:75)
    at Sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at Sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.Java:62)
    at Sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.Java:43)
    at Java.lang.reflect.Method.invoke(Method.Java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.Java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.Java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.Java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.Java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.Java:26)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.Java:75)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.Java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.Java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.Java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.Java:252)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.Java:94)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.Java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.Java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.Java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.Java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.Java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.Java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.Java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.Java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.Java:191)
    at org.junit.runner.JUnitCore.run(JUnitCore.Java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.Java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.Java:51)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.Java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.Java:70)
Caused by: com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of Java.util.ArrayList out of START_OBJECT token
 at [Source: [email protected]; line: 1, column: 1]
    at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.Java:270)
    at com.fasterxml.jackson.databind.DeserializationContext.reportMappingException(DeserializationContext.Java:1234)
    at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.Java:1122)
    at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.Java:1075)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.handleNonArray(CollectionDeserializer.Java:338)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.Java:269)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.Java:259)
    at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.Java:26)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.Java:3798)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.Java:2922)
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.Java:231)
    ... 38 more

Offensichtlich funktionieren die Sicherheitsanmerkungen oben nicht. Was genau ist das Problem und wie kann ich es beheben?

Edit 1: Ich habe versucht:

Object response = template.exchange("/get", HttpMethod.GET, request, Object.class);

Und bekam:

<401 Unauthorized,{status=401, message=Authentication failed, errorCode=10, timestamp=1497654855545},{X-Content-Type-Options=[nosniff], X-XSS-Protection=[1; mode=block], Cache-Control=[no-cache, no-store, max-age=0, must-revalidate], Pragma=[no-cache], Expires=[0], X-Frame-Options=[DENY], Content-Type=[application/json;charset=ISO-8859-1], Content-Length=[89], Date=[Fri, 16 Jun 2017 23:14:15 GMT]}>

Zu unserer Sicherheit verwenden wir org.springframework.security.authentication.AuthenticationProvider und org.springframework.security.authentication.AuthenticationManager

Edit 2: Auf Vorschlag von skadya habe ich eine neue Klasse erstellt:

@Configuration
public class AnonymousConfig extends WebSecurityConfigurerAdapter {
    @Override
    public void configure(HttpSecurity web) throws Exception {
        web.antMatcher("**/*").anonymous();
    }
}

Wenn ich nun meinen Integrationstest durchführe, erhalte ich folgende Fehlermeldung:

Java.lang.IllegalStateException: Failed to load ApplicationContext

    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.Java:124)
    at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.Java:83)
    at org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener.prepareTestInstance(SpringBootDependencyInjectionTestExecutionListener.Java:47)
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.Java:230)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.Java:228)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.Java:287)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.Java:12)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.Java:289)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.Java:247)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.Java:94)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.Java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.Java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.Java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.Java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.Java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.Java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.Java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.Java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.Java:191)
    at org.junit.runner.JUnitCore.run(JUnitCore.Java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.Java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.Java:51)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.Java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.Java:70)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration': Injection of autowired dependencies failed; nested exception is Java.lang.IllegalStateException: @Order on WebSecurityConfigurers must be unique. Order of 100 was already used on [email protected], so it cannot be used on [email protected]ae1 too.
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.Java:372)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.Java:1264)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.Java:553)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.Java:483)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.Java:306)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.Java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.Java:302)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.Java:197)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.Java:761)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.Java:866)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.Java:542)
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.Java:122)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.Java:737)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.Java:370)
    at org.springframework.boot.SpringApplication.run(SpringApplication.Java:314)
    at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.Java:120)
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.Java:98)
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.Java:116)
    ... 23 more
Caused by: Java.lang.IllegalStateException: @Order on WebSecurityConfigurers must be unique. Order of 100 was already used on [email protected], so it cannot be used on [email protected]ae1 too.
    at org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration.setFilterChainProxySecurityConfigurer(WebSecurityConfiguration.Java:148)
    at Sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at Sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.Java:62)
    at Sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.Java:43)
    at Java.lang.reflect.Method.invoke(Method.Java:498)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.Java:701)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.Java:88)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.Java:366)
    ... 40 more

Sieht so aus, als kollidierte es mit der Websecurity-Konfiguration, die wir im normalen Projekt haben. Hier ist diese Datei:

@EnableWebSecurity
@EnableWebMvc
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
//configuration
}

Ich habe versucht, @Order(1000) hinzuzufügen, was das obige Problem behoben hat, aber trotzdem in einem 401 Unauthorized landete

18
Richard

Sie können versuchen, einige weitere automatische Konfigurationen auszuschließen:

@EnableAutoConfiguration(exclude = {
org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration.class,
org.springframework.boot.autoconfigure.security.SecurityFilterAutoConfiguration.class,
org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration.class,
org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration.class
})

Übrigens, eine elegantere Methode zum Ausschluss von Daten besteht darin, application-test.properties in Ihren Testquellen zu definieren und Ihren Test mit @Profile("test") zu markieren. Dann fügen Sie dies einfach zu Ihrer Konfiguration hinzu:

spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration,org.springframework.boot.autoconfigure.security.SecurityFilterAutoConfiguration,org.springframework.boot.autoconfigure.security.FallbackWebSecurityAutoConfiguration,org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration

Alle möglichen Konfigurationen, die ausgeschlossen werden können, finden Sie hier: spring.factories

12
Danylo Zatorsky

Sie haben mehrere Möglichkeiten, die Authentifizierung im Spring Boot-Integrationstest bereitzustellen. Möglicherweise müssen Sie einige Dinge anpassen, damit alles an Ihrem Ende funktioniert.

Mock-basierter Ansatz 

Hierbei wird test WebApplicationContext in MockMvc mit @WithMockUser annotation eingefügt, um Authentifizierungsbenutzer bereitzustellen und WithMockUserSecurityContextFactory den Sicherheitskontext für den Mock-Benutzer zu erstellen.

SecurityMockMvcConfigurers registriert den Sicherheitsfilter springSecurityFilterChain mit MockMvc.

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.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
public class HelloControllerIT  {

    @Autowired
    private WebApplicationContext context;

    private  MockMvc mvc;

    @Before
    public void setup() {
        mvc = MockMvcBuilders
                .webAppContextSetup(context)
                .apply(springSecurity()) // enable security for the mock set up
                .build();
    }

    @WithMockUser(value = "test", password = "pass")
    @Test
    public void test() throws Exception {
        String contentType = MediaType.APPLICATION_JSON + ";charset=UTF-8";

        String authzToken = mvc
                .perform(
                        post("/authenticate")
                                .contentType(
                                        MediaType.APPLICATION_JSON).
                            content("")).
                 andExpect(status().isOk())
                .andExpect(content().contentType(contentType))
                .andExpect(jsonPath("$.token", is(notNullValue())))
                .andReturn().getResponse().getContentAsString();

        System.out.print(authzToken);//{"token":"1a3434a"}

    }

}

In-Memory-Authentifizierungsanbieter-basierter Ansatz

Hierbei wird ein In-Memory-Authentifizierungsanbieter mit Standardauthentifizierungsbenutzer verwendet.

Registrieren Sie den speicherinternen Auth-Provider und aktivieren Sie die grundlegende Authentifizierung. Deaktivieren Sie den anonymen Zugriff in HttpSecurity in der WebSecurityConfigurerAdapter.

Wenn der speicherinterne Anbieter registriert ist, erstellt DefaultInMemoryUserDetailsManagerConfigurer den Basisauthentifizierungsbenutzer im Speicher.

Wenn die Basisauthentifizierung aktiviert ist, konfiguriert HttpBasicConfigurerBasicAuthenticationFilter. Authentifiziert den Testbenutzer und erstellt den Sicherheitskontext.

Sicherheitskonfiguration

@EnableWebSecurity
@EnableWebMvc
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
                    // register test user with in memory authentication provider 
        auth.inMemoryAuthentication().withUser("test").password("pass").roles("ROLES");
    }

                @Override
    public void configure(HttpSecurity http) throws Exception {
                    // enable basic authentication & disable anoymous access
        http.authorizeRequests().anyRequest().authenticated().and().httpBasic().and().anonymous().disable();    
    }

}

Authentifizierungsendpunkt

@Controller
@RequestMapping("/authenticate")
public class AuthenticationController {

    @RequestMapping(method = RequestMethod.POST)
    @ResponseBody
    public TokenClass getToken() {
        TokenClass tokenClass = new TokenClass();
        tokenClass.setToken("1a3434a");
        return tokenClass;
    }

}

Pojo

public class TokenClass {

    private String token;

    public String getToken() {
        return token;
    }

    public void setToken(String token) {
        this.token = token;
    }
}

Controller testen

import com.fasterxml.jackson.databind.JsonNode;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.*;
import org.springframework.test.context.junit4.SpringRunner;

import Java.util.Arrays;
import Java.util.Base64;

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class HelloControllerIT  {

    @Autowired
    private TestRestTemplate template;

    @Test
    public void test() throws Exception {
        HttpHeaders authHeaders = new HttpHeaders();
        String token = new String(Base64.getEncoder().encode(
                ("test" + ":" + "pass").getBytes()));
        authHeaders.set("Authorization", "Basic " + token);
        JsonNode loginResponse = template.postForObject("/authenticate", new HttpEntity<>(null, authHeaders), JsonNode.class);

        HttpHeaders authzHeaders = new HttpHeaders();
        authzHeaders.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
        authzHeaders.add("X-Authorization", "Bearer " + loginResponse.get("token").textValue());
        authzHeaders.add("Content-Type", "application/json");

        ResponseEntity response = template.exchange("/secure",
                HttpMethod.GET,
                new HttpEntity<>(null, authzHeaders),
                String.class
        );
    }
}
3
Veeram

Es sieht so aus, als ob die standardmäßige Sicherheitskonfiguration eingeworfen wird. Wenn ich nicht Ihre vollständige Konfiguration sehe, ist es schwierig, dies zu bestätigen. Könnten Sie, falls möglich, Ihr Minimalprojekt (auf Github?) Posten?.

Da Sie die Authentifizierung während der Ausführung von Integrationstests nicht erzwingen möchten, können Sie den anonymen Zugriff auf Ihre Anwendungsressourcen aktivieren.

Um den anonymen Zugriff zu ermöglichen, können Sie unterhalb des Verzeichnisses test source Folgendes hinzufügen. Der anonyme Zugriff wird beim Bootstrapping der Webanwendung konfiguriert. (sollte den Antwortcode 401 nicht sehen)

@Configuration
public class AllowAnonymousWebAccess extends WebSecurityConfigurerAdapter {
    @Override
    public void configure(HttpSecurity web) throws Exception {
        web.antMatcher("**/*").anonymous();
    }
}
2
skadya

War lange Zeit mit diesem Problem konfrontiert. Endlich wurde es gelöst. Sie müssen den Autorisierungsserver spekulieren, um ein Testprofil zu erstellen, und müssen auch den Mock Spring Security-Benutzerdetailservice nutzen. Hier ist der Code, den ich in einem blog gefunden habe.

Testen Sie den Autorisierungsserver

@Configuration
@EnableAuthorizationServer
@ActiveProfiles("test")
public class AuthorizationTestServer extends AuthorizationServerConfigurerAdapter {

    private AuthenticationManager authenticationManager;


    @Autowired
    public AuthorizationTestServer(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
        oauthServer.checkTokenAccess("permitAll()");
        oauthServer.allowFormAuthenticationForClients();
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        super.configure(clients);
        clients.inMemory()
                .withClient("user")
                .secret("password")
                .authorizedGrantTypes("password")
                .scopes("openid");
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        super.configure(endpoints);
        endpoints.authenticationManager(this.authenticationManager);
    }
}

Testen Sie den Benutzerdetails-Dienst

@Service
@ActiveProfiles("test")
public class UserDetailTestService implements UserDetailsService {

    @Override
    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {

            return new User("dummyUser","dummyPassword",true,true,
                    true,true, AuthorityUtils.createAuthorityList("USER"));
    }
}

Haupttestklasse

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("test")
@AutoConfigureMockMvc
public class JmStudentServiceApplicationTests {

   @Autowired
   private WebApplicationContext wac;

   @Autowired
   private MockMvc mockMvc;


   @Autowired
   private TestRestTemplate restTemplate;

   @Autowired
   private StudentRepository studentRepository;

   @Test
   public void test() throws Exception{

      String accessToken = obtainAccessToken("dummyUser", "dummyPassword");
      Student student = new Student();
      student.setId("2222");
      student.setName("test student");

      studentRepository.createStudent(student);
      assertTrue(studentRepository.getStudentById("2222").getName().equals("test student"));

      MvcResult result = mockMvc.perform(get("/students/by-id/2222")
            .header("Authorization", "Bearer " + accessToken)
            .accept(MediaType.APPLICATION_JSON))
            .andExpect(status().isOk())
            .andReturn();


      String str = result.getResponse().getContentAsString();
      assertTrue(str.contains("\"id\":\"2222\""));
   }

   private String obtainAccessToken(String username, String password) throws Exception {

      MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
      params.add("grant_type", "password");
      params.add("username", username);
      params.add("password", password);
      params.add("scope", "openid");

      String base64ClientCredentials = new String(Base64.encodeBase64("user:password".getBytes()));


      ResultActions result
            = mockMvc.perform(post("/oauth/token")
            .params(params)
            .header("Authorization","Basic " + base64ClientCredentials)
            .accept("application/json;charset=UTF-8"))
            .andExpect(status().isOk());

      String resultString = result.andReturn().getResponse().getContentAsString();

      JacksonJsonParser jsonParser = new JacksonJsonParser();
      return jsonParser.parseMap(resultString).get("access_token").toString();
   }

}
1
Bablu

Es sieht so aus, als würde die Authentifizierung funktionieren, aber Sie gehen mit der Antwort falsch um.

Hier ist der Code, wo Sie versuchen, die Antwort als List<Foo> zu analysieren.

ResponseEntity response = template.exchange("/get",
    HttpMethod.GET,
    request,
    new ParameterizedTypeReference<List<Foo>>() {}
);

Da Sie jedoch keinen Authentifizierungsheader bereitgestellt haben, antwortet der Server mit einem benutzerdefinierten Fehler (der offensichtlich in Json Object eingeschlossen ist), und Sie erhalten diese Ausnahme im Test, der besagt, dass ArrayList nicht in Json Object (das mit START_OBJECT-Token beginnt) analysiert werden kann {).

Could not read JSON document: Can not deserialize instance of Java.util.ArrayList out of START_OBJECT token

Versuchen Sie, die Antwort als Objekt zu behandeln, damit Sie sehen können, was tatsächlich da ist.

template.exchange("/get", HttpMethod.GET, request, Object.class);

Dies funktioniert jedoch nicht als endgültige Lösung. Ich glaube, Sie sollten den Antworttext basierend auf HTTP-Antwortcode behandeln , wenn das 200 ist - als List<> analysieren, andernfalls als Map<> oder wie auch immer die Struktur des Servers zurückgeben.

1
ikryvorotenko

Ich habe herausgefunden, wie ich dies ohne In-Memory-Authentifizierung und/oder Spott machen kann.

public class TestConf extends WebSecurityConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.csrf()
                .disable()
                .authorizeRequests()
                .anyRequest()
                .permitAll();
    }
}

Verwenden Sie Spring Active-Profile, um die obige Konfiguration nur beim Ausführen von Testfällen auszuführen.

0
Richard

Warum ist die Sicherheitskonfiguration erzwungen?

Laut Spring Boot documentation , wenn Sie Ihre Klasse mit @SpringBootTest kommentieren und Sie keine Konfigurationsalternative angeben, dann Spring mit Suche nach einer @SpringBootApplication-Klasse, die als primäre Konfiguration dienen soll. Spring startet die Suche im Paket Ihrer Testklasse und durchsucht dann das Paket Hiearchy. Vermutlich handelt es sich dabei um Ihre primäre Konfiguration und alles, was dazu gehört, einschließlich Ihrer unerwünschten Sicherheitskonfiguration. 

Lösung

Die einfachste Lösung, die in Spring Boot 2.0.3 überprüft wurde, besteht darin, @EnableAutoConfiguration(exclude = SecurityAutoConfiguration.class) in @SpringBootApplication(exclude = SecurityAutoConfiguration.class) zu ändern. Wenn Sie diese Änderung vornehmen, registriert Spring Ihre Testklasse als primäre Konfigurationsklasse und bestätigt damit Ihren Ausschluss. Erstellen Sie alternativ eine separate Konfigurationsklasse, um alle Ihre Integrationstests gemeinsam zu nutzen, die sich in einem Basispaket befinden und von allen Ihren Integrationstests gefunden werden.