Spring-Boot-Authentifizierung für die Integration-Tests
Ich versuche zum ausführen eines integration test für meinen controller, aber ich bin in Probleme laufen, wenn ich nicht authentifizieren. Hier 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 diese ausführe, funktioniert alles. Aber wenn ich die folgende Zeile auskommentiert werden:
headers.add("X-Authorization", "Bearer " + loginResponse.get("token").textValue());
Bekomme ich die Fehlermeldung:
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: java.io.PushbackInputStream@272a5bc6; 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: java.io.PushbackInputStream@272a5bc6; 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: java.io.PushbackInputStream@272a5bc6; 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 die Sicherheit Anmerkungen oben nicht arbeiten. Also, was genau ist das Problem und wie behebe ich es?
Bearbeiten 1: ich habe versucht zu tun:
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]}>
Für unsere Sicherheit sind wir mit org.springframework.Sicherheit.die Authentifizierung.AuthenticationProvider
und org.springframework.Sicherheit.die Authentifizierung.AuthenticationManager
Edit 2: Pro skadya ' s Vorschlag, den ich erstellt eine neue Klasse und zwar so:
@Configuration
public class AnonymousConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity web) throws Exception {
web.antMatcher("**/*").anonymous();
}
}
Aber wenn ich nun meine integration test bekomme 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 config.AnonymousConfig$$EnhancerBySpringCGLIB$$ba18b8d7@6291f725, so it cannot be used on security.WebSecurityConfig$$EnhancerBySpringCGLIB$$9d88e7e@1bfaaae1 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 config.AnonymousConfig$$EnhancerBySpringCGLIB$$ba18b8d7@6291f725, so it cannot be used on security.WebSecurityConfig$$EnhancerBySpringCGLIB$$9d88e7e@1bfaaae1 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 aus wie es eine Kollision mit der websecurity-config, die wir in der normalen Projekt. Hier ist die Datei:
@EnableWebSecurity
@EnableWebMvc
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
//configuration
}
Habe ich versucht, indem @Order(1000)
die Feste der oben genannten Problem, aber landete trotzdem in einer 401 Unauthorized
- es hängt von Ihrer Anwendung config, grundsätzlich können Sie die Verwendung von @MockUser von spring security zu beheben, wenn Sie nicht machen Sie Ihre api
stateless
ansonsten müssen Sie passen Sie Ihre security-config nichtstateless
, dann können Sie mit der annotation - Kannst du ein minimales Projekt, sagen wir im Github?
- Einfach nur neugierig. Warum willst du um die Sicherheitsfunktion zu deaktivieren ? Es sollte Teil der integration zu testen. Haben Sie darüber nachgedacht, Spott oder durch die in-memory-set-up für Ihre Bedürfnisse ?
- können Sie erklären, die in-memory eingestellt?
Du musst angemeldet sein, um einen Kommentar abzugeben.
Können Sie versuchen, ohne einige weitere auto-Konfigurationen:
Btw, mehr elegante Weise, ohne Zeug ist durch die Definition
application-test.properties
im test-Quellen und-Kennzeichnung, den test mit@Profile("test")
. Dann fügen Sie einfach diese in deine config:Alle die möglichen Konfigurationen, die ausgeschlossen werden können, die Sie hier finden können: Frühling.Fabriken
org.springframework.security.authentication.AuthenticationProvider
, ändert das etwas? Ich machte edits obenHaben Sie einige Optionen zur Authentifizierung in die spring-boot-integration-test. Sollten Sie ein paar Dinge zu machen, arbeiten alle an Ihrem Ende.
Mock-basierten Ansatz
Nutzt dieser test
WebApplicationContext
injiziert MockMvc mit @WithMockUser Anmerkung Authentifizierung Benutzer-undWithMockUserSecurityContextFactory
erstellen des Sicherheitskontexts für den mock-Benutzer.SecurityMockMvcConfigurers
Registern der Sicherheits-filterspringSecurityFilterChain
mitMockMvc
.In-memory-auth-provider-basierten Ansatz
Diese nutzt in-memory-auth-Anbieter mit basic-Authentifizierung Benutzer.
Registrieren Sie sich in-memory-auth-provider aktivieren und basic auth, deaktivieren Sie den anonymen Zugriff in
HttpSecurity
imWebSecurityConfigurerAdapter
.Wenn der in-memory-Anbieter registriert ist,
DefaultInMemoryUserDetailsManagerConfigurer
schafft die basic-auth-Benutzer in den Speicher.Wenn die Standardauthentifizierung aktiviert ist, werden die
HttpBasicConfigurer
konfiguriertBasicAuthenticationFilter
. Authentifiziert den test user und erstellt den security-Kontext.Security Configuration
Authentifizierung, Endpoint
Pojo
Test-Controller
WebSecurityConfig
im Hauptverzeichnis oder soll ich einen neuenWebSecurityConfig
- Datei in das Verzeichnis test?WebSecurityConfig
im Haupt-Verzeichnis zu umfassen, die oben genannten änderungen. Dies sollte angegeben test user und registrieren Sie sich mit in-memory-auth-provider und konfigurieren der Standardauthentifizierung. Seine nicht zumindest möglich für mich zu erstellen, eine Prüfung der Sicherheit config zu überschreiben, die Haupt-config. Berücksichtigen Sie auch die mock-Lösung zu ( alles so tut, und Sie nicht brauchen, um Bearbeiten Sie die main-config) .1a3434a
, so kann ich geben Sie nur den header ("Kopf" Basic 1a3434a".Bearer
header und hat Gültigkeit für die Zulassung-server, ermöglicht Sie den Zugang zu Ressourcen Endpunkt.Sieht es aus wie Standard-Sicherheits-Konfiguration sind immer gekickt-in. Es sei denn, ich sehe, Ihr Ihre komplette Konfiguration ist es schwer dies zu bestätigen. Wenn möglich, könnten Sie die post Ihre minimal-Projekt (auf github?).
Da Sie nicht wollen, um erzwingen der Authentifizierung während der Ausführung von Integrations-tests, können Sie aktivieren den anonymen Zugriff auf Ihre Ressourcen der Anwendung.
Um den anonymen Zugriff aktivieren, können Sie unter-Klasse unter Ihre test Quelle - Verzeichnis. Es wird konfigurieren Sie den anonymen Zugriff beim bootstrapping der web-Anwendung. (sollte nicht sehen, 401-response-code)
Sieht es aus wie die Authentifizierung funktioniert, aber Sie behandeln die Antwort in der falschen Art und Weise.
Hier ist der code unten, wo Sie versuchen, zu analysieren Antwort als
List<Foo>
Aber da Sie noch keinen authentication header, die server Antworten mit einige benutzerdefinierte Fehler (natürlich eingewickelt in das Json-Objekt), und Sie bekommen diese Ausnahme im test, die sagen, es kann nicht analysieren
ArrayList
von Json-Objekt (die beginnt mitSTART_OBJECT
token, wie{
).Versuchen, mit der Antwort, als Objekt, so dass Sie sehen können, was tatsächlich kommt es.
Aber das wird nicht funktionieren, da eine endgültige Lösung. Ich glaube Sie sollten handle der Antwort der Körper basiert auf dem Http-Antwort-code, wenn das 200 - analysieren, wie
List<>
, anders analysieren alsMap<>
oder was auch immer die Struktur der server zurückgibt.new ParameterizedTypeReference<List<Foo>>() {}
zuObject.class
und setzen Sie einen Haltepunkt gibt.Konfrontiert war dieses Problem für eine lange Zeit. Schließlich habe es gelöst. Sie müssen simulieren der Autorisierungs-server erstellen eines test-Profils und müssen auch zu Verspotten, Spring Security user-details-service. Hier ist der code, den ich gefunden in einem blog.
Test Authorization server
Test-Benutzer-Details-Service
Haupt-Test-Klasse
Ich herausgefunden, wie dies zu tun, ohne in-memory-Authentifizierung und/oder Spott.
Und nutzen spring, aktive profile, die zum ausführen der obigen config nur beim ausführen von Testfällen.
Warum die Security-Konfiguration Erzwungen wird
Gemäß den Frühling Starten Dokumentation, wenn Sie kommentieren Ihre Klasse mit
@SpringBootTest
, und Sie nicht haben, geben Sie eine alternative Konfiguration, dann ist der Frühling mit der Suche nach einem@SpringBootApplication
Klasse dienen als primäre Konfiguration. Der Frühling startet die Suche in der Paket, das Ihre test-Klasse, dann Suche bis das Paket hiearchy. Vermutlich ist es die Suche nach Ihrem primären Konfiguration, und alles was dieses mit sich bringt, einschließlich Ihrer unerwünschten security-Konfiguration.Lösung
Ist die einfachste Lösung, verifiziert im Frühjahr Boot 2.0.3 ist, ändern Sie
@EnableAutoConfiguration(exclude = SecurityAutoConfiguration.class)
zu@SpringBootApplication(exclude = SecurityAutoConfiguration.class)
. Wenn Sie diese änderung vornehmen, ist der Frühling registrieren Sie Ihre test-Klasse, die als primäre configuration-Klasse und wird daher anerkennen, Ihre Ausgrenzung. Alternativ erstellen Sie eine separate Konfiguration der Klasse zu teilen, über alle Ihre integration tests, die sich in einer Basis-Paket und wird gefunden werden, indem alle Ihre integration tests.