所以我正在尝试测试我的安全过滤器,我觉得我错过了一些明显的东西。这段代码的某些部分在过去工作,我将我的项目分成几个库(并从maven迁移到gradle),所以我可能已经离开了类路径中的依赖项或某个库。让我们从测试开始
@SpringBootTest
@AutoConfigureMockMvc
@RunWith( SpringRunner.class )
public class AuthenticationTest extends AbstractSecurityTest {
@Autowired
private MockMvc mvc;
@Autowired
private Gson gson;
@SpyBean
private JsonAuthenticationFilter filter;
@Test
public void validLogin() throws Exception {
log.debug( "posting password" );
String json = gson.toJson( new PasswordCredential( name, pass ) );
this.mvc.perform(
post( JsonAuthenticationFilter.AUTH_PASS )
.contentType( MediaType.APPLICATION_JSON )
.content( json ) )
.andExpect( status().is2xxSuccessful() )
.andExpect( cookie().exists( "SESSION" ) );
verify( filter, times( 1 ) )
.doFilter( any( ServletRequest.class ), any( ServletResponse.class ), any( FilterChain.class ) );
}
@Test
public void invalidLogin() throws Exception {
String json = gson.toJson( new PasswordCredential( "xenoterracide@gmail.com", "password" ) );
this.mvc.perform(
post( JsonAuthenticationFilter.AUTH_PASS )
.contentType( MediaType.APPLICATION_JSON )
.accept( MediaType.APPLICATION_JSON )
.content( json ) )
.andExpect( status().is4xxClientError() )
.andExpect( status().isUnauthorized() );
verify( filter, times( 1 ) )
.doFilter( any( ServletRequest.class ), any( ServletResponse.class ), any( FilterChain.class ) );
}
@Test
public void unsupportedMediaType() throws Exception {
this.mvc.perform(
post( JsonAuthenticationFilter.AUTH_PASS )
.param( "pass", "password" )
.param( "user", "xenoterracide@gmail.com" ) )
.andExpect( status().is4xxClientError() );
verify( filter, times( 1 ) )
.doFilter( any( ServletRequest.class ), any( ServletResponse.class ), any( FilterChain.class ) );
}
}
我的假设是@AutoConfigureMockMvc
默认情况下应该包含过滤器。
这是我的身份验证过滤器
@Component
class JsonAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
static final String AUTH_PASS = Routes.PUBLIC + "/authentication/password";
private static final RequestMatcher MATCHER = new AndRequestMatcher( Arrays.asList(
new AntPathRequestMatcher( AUTH_PASS, HttpMethod.POST.name() ),
new MediaTypeRequestMatcher( new ContentTypeContentNegotiationStrategy(),
MediaType.APPLICATION_JSON, MediaType.APPLICATION_JSON_UTF8 )
) );
private final ObjectMapper objectMapper;
private final Validator validator;
protected JsonAuthenticationFilter( ObjectMapper objectMapper, Validator validator ) {
super( MATCHER );
this.objectMapper = objectMapper;
this.validator = validator;
}
@Override
public Authentication attemptAuthentication( HttpServletRequest request, HttpServletResponse response )
throws AuthenticationException, IOException {
PasswordCredential credentials = objectMapper.readValue( request.getReader(), PasswordCredential.class );
try {
DataBinder dataBinder = new DataBinder( credentials );
dataBinder.setBindingErrorProcessor( new DefaultBindingErrorProcessor() );
dataBinder.setValidator( validator );
dataBinder.validate();
dataBinder.close();
}
catch ( BindException e ) {
throw new BadRequestException( "field errors", e );
}
AbstractAuthenticationToken authRequest = credentials.toAuthenticationToken();
setDetails( request, authRequest );
return this.getAuthenticationManager().authenticate( authRequest );
}
private void setDetails( HttpServletRequest request, AbstractAuthenticationToken authRequest ) {
authRequest.setDetails( authenticationDetailsSource.buildDetails( request ) );
}
@Override
@Autowired( required = false )
public void setRememberMeServices( RememberMeServices rememberMeServices ) {
super.setRememberMeServices( rememberMeServices );
}
@Override
@Autowired
public void setAuthenticationManager( AuthenticationManager authenticationManager ) {
super.setAuthenticationManager( authenticationManager );
}
}
我知道它是@Autowired
,并且设置了AuthenticationManager
,但是没有调用继承的doFilter
。
plugins {
`java-library`
`maven-publish`
`checkstyle`
`idea`
id("com.github.spotbugs").version("1.6.0")
id("net.ltgt.errorprone").version("0.0.13")
id("io.spring.dependency-management").version("1.0.4.RELEASE")
}
repositories {
maven(System.getenv("JAR_REPOSITORY_URI"))
jcenter()
}
group = "com.xenoterracide.rpf"
version = "0.1.0-SNAPSHOT"
description = "Security DTOs"
configurations.all({
resolutionStrategy({
cacheChangingModulesFor(1, TimeUnit.MINUTES)
})
})
dependencyManagement {
resolutionStrategy({
cacheChangingModulesFor(1, TimeUnit.MINUTES)
})
imports {
mavenBom("com.xenoterracide:bom:0.1.0-SNAPSHOT")
}
}
// In this section you declare the dependencies for your production and test code
dependencies {
errorprone("com.google.guava:guava:latest.release")
errorprone("com.google.errorprone:error_prone_core:latest.release")
compileOnly("com.google.code.findbugs:jsr305")
compileOnly("com.google.errorprone:error_prone_annotations:latest.release")
implementation("javax.validation:validation-api")
implementation("javax.servlet:javax.servlet-api")
implementation("com.fasterxml.jackson.core:jackson-annotations")
implementation("com.fasterxml.jackson.core:jackson-core")
implementation("com.fasterxml.jackson.core:jackson-databind")
implementation("org.hibernate:hibernate-validator")
implementation("org.springframework:spring-core")
implementation("org.springframework:spring-context")
implementation("org.springframework:spring-tx")
implementation("org.springframework.data:spring-data-commons")
implementation("org.springframework.data:spring-data-jpa")
implementation("org.springframework.security:spring-security-core")
implementation("org.springframework.security:spring-security-web")
implementation("com.xenoterracide.rpf:constants:0.1.0-SNAPSHOT")
implementation("com.xenoterracide.rpf:sec-dtos:0.1.0-SNAPSHOT")
implementation("com.xenoterracide.rpf:sec:0.1.0-SNAPSHOT")
testRuntimeOnly("com.h2database:h2")
testRuntimeOnly("org.springframework.boot:spring-boot-starter-security")
testRuntimeOnly("org.springframework.boot:spring-boot-starter-data-jpa")
testImplementation("junit:junit")
testImplementation("org.mockito:mockito-core")
testImplementation("org.apache.commons:commons-lang3")
testImplementation("com.google.code.gson:gson")
testImplementation("org.assertj:assertj-core")
testImplementation("org.springframework:spring-test")
testImplementation("org.springframework.boot:spring-boot-test")
testImplementation("org.springframework.boot:spring-boot-test-autoconfigure")
}
更新添加请求的课程
class ContentTypeContentNegotiationStrategy implements ContentNegotiationStrategy {
/**
* {@inheritDoc}
*
* @throws HttpMediaTypeNotAcceptableException if the 'Content-Type' header cannot be parsed
*/
@Override
public List<MediaType> resolveMediaTypes( NativeWebRequest request )
throws HttpMediaTypeNotAcceptableException {
String[] headerValueArray = request.getHeaderValues( HttpHeaders.CONTENT_TYPE );
if ( headerValueArray == null ) {
return Collections.emptyList();
}
List<String> headerValues = Arrays.asList( headerValueArray );
try {
List<MediaType> mediaTypes = MediaType.parseMediaTypes( headerValues );
MediaType.sortBySpecificityAndQuality( mediaTypes );
return mediaTypes;
}
catch ( InvalidMediaTypeException ex ) {
throw new HttpMediaTypeNotAcceptableException(
"Could not parse 'Accept' header " + headerValues + ": " + ex.getMessage() );
}
}
}
和测试失败的输出
MockHttpServletRequest:
HTTP Method = POST
Request URI = /v0/public/authentication/password
Parameters = {}
Headers = {Content-Type=[application/json;charset=UTF-8]}
Handler:
Type = null
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 401
Error message = Full authentication is required to access this resource
Headers = {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], Strict-Transport-Security=[max-age=31536000 ; includeSubDomains], WWW-Authenticate=[Basic realm="Spring"]}
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
MockHttpServletRequest:
HTTP Method = POST
Request URI = /v0/public/authentication/password
Parameters = {}
Headers = {Content-Type=[application/json;charset=UTF-8], Accept=[application/json]}
Handler:
Type = null
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 401
Error message = Full authentication is required to access this resource
Headers = {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], Strict-Transport-Security=[max-age=31536000 ; includeSubDomains], WWW-Authenticate=[Basic realm="Spring"]}
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
MockHttpServletRequest:
HTTP Method = POST
Request URI = /v0/public/authentication/password
Parameters = {pass=[password], user=[xenoterracide@gmail.com]}
Headers = {}
Handler:
Type = null
Async:
Async started = false
Async result = null
Resolved Exception:
Type = null
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 401
Error message = Full authentication is required to access this resource
Headers = {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], Strict-Transport-Security=[max-age=31536000 ; includeSubDomains], WWW-Authenticate=[Basic realm="Spring"]}
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
Wanted but not invoked:
jsonAuthenticationFilter.doFilter(
<any>,
<any>,
<any>
);
-> at com.xenoterracide.rpf.sec.authn.AuthenticationTest.unsupportedMediaType(AuthenticationTest.java:76)
Actually, there were zero interactions with this mock.
Wanted but not invoked:
jsonAuthenticationFilter.doFilter(
<any>,
<any>,
<any>
);
-> at com.xenoterracide.rpf.sec.authn.AuthenticationTest.unsupportedMediaType(AuthenticationTest.java:76)
Actually, there were zero interactions with this mock.
at com.xenoterracide.rpf.sec.authn.AuthenticationTest.unsupportedMediaType(AuthenticationTest.java:76)
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)
我需要做些什么来让我的测试上下文调用过滤器?
答案 0 :(得分:0)
我错过了相应的WebSecurityAdapter
配置
@TestConfiguration
static class Config extends WebSecurityConfigurerAdapter {
@Override
protected void configure( HttpSecurity http ) throws Exception {
http.authorizeRequests()
.mvcMatchers( JsonAuthenticationFilter.AUTH_PASS ).permitAll()
.anyRequest().authenticated()
.and().csrf().disable();
}
}