@WithMockUser가 없는 Spring Boot 응용 프로그램에서 "A Authentication Object not found in the SecurityContext"를 제거할 수 없음
저는 이미 며칠을 꼬박 꼬박 제가 무엇을 잘못하고 있는지 알아내기 위해 노력했지만, 왜 그것이 작동하지 않는지 전혀 모르겠습니다.먼저, 다음 구성은 대부분 제가 작업 중인 다른 프로젝트에서 복사한 것이며, 해당 프로젝트는 아무런 문제 없이 작동할 수 있습니다(다만 약간 다르게 구성되어 있고 이전의 Spring/Spring Boot 버전을 사용함).이 클래스들이 잘못 구성되어 있고 다음 구성 클래스에서 오타나 다른 것을 볼 수 없다고 생각하기 때문에 코드를 줄일 수 없습니다.처음부터 다시 쓰고 싶지만 이번에는 그렇지 않습니다.(이름이 다음으로 시작하는 구성요소I스프링 프레임워크의 일부가 아니라 제 것입니다.
여기서 예외는 다음과 같습니다.
org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.credentialsNotFound(AbstractSecurityInterceptor.java:379) ~[spring-security-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:223) ~[spring-security-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:65) ~[spring-security-core-4.2.1.RELEASE.jar:4.2.1.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.5.RELEASE.jar:4.3.5.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:656) ~[spring-aop-4.3.5.RELEASE.jar:4.3.5.RELEASE]
at FOO.BAR.AuthenticationController$$EnhancerBySpringCGLIB$$b4949cda.getSelf(<generated>) ~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_65]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_65]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_65]
at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_65]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:220) ~[spring-web-4.3.5.RELEASE.jar:4.3.5.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:134) ~[spring-web-4.3.5.RELEASE.jar:4.3.5.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:116) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) [spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861) [spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:622) ~[tomcat-embed-core-8.5.6.jar:8.5.6]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) [spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE]
at org.springframework.test.web.servlet.TestDispatcherServlet.service(TestDispatcherServlet.java:65) [spring-test-4.3.5.RELEASE.jar:4.3.5.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729) ~[tomcat-embed-core-8.5.6.jar:8.5.6]
at org.springframework.mock.web.MockFilterChain$ServletFilterProxy.doFilter(MockFilterChain.java:167) [spring-test-4.3.5.RELEASE.jar:4.3.5.RELEASE]
at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134) [spring-test-4.3.5.RELEASE.jar:4.3.5.RELEASE]
at org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:155) [spring-test-4.3.5.RELEASE.jar:4.3.5.RELEASE]
at FOO.BAR.AbstractControllerTest.get(AbstractControllerTest.java:55) [web-test-0-SNAPSHOT.jar:na]
at FOO.BAR.AuthenticationControllerOkTest.testAuthenticate(AuthenticationControllerOkTest.java:31) [test-classes/:na]
<...JUnit stuff...>
내가 웹에서 찾은 유일한 비슷한 질문은 이것입니다.하지만 제가 틀리지 않았다면 약간 다른 경우를 설명하는 것 같습니다.물론 아무 가치도 없는 일: 추가@WithMockUser테스트에 예외가 발생하는 것은 아니지만 인증 컨트롤러를 테스트하고 있기 때문에 이 주석을 사용할 수 없습니다(물론 프로덕션 모드에서는 불가능합니다).
추상 사용자 정의GlobalMethodSecurityConfiguration 유형
사용자 지정 유형 지원을 추가하기 위해 사용하는 보일러 플레이트 클래스입니다.@PreAuthorize. 꽤 쉽다고 생각하는데요, 그리고 이건 의심스러워 보이지 않습니다.
public abstract class AbstractCustomTypesGlobalMethodSecurityConfiguration
extends GlobalMethodSecurityConfiguration {
@Nonnull
protected abstract ApplicationContext applicationContext();
@Nonnull
protected abstract ConversionService conversionService();
@Nonnull
protected abstract PermissionEvaluator permissionEvaluator();
@Nonnull
@SuppressWarnings("DesignForExtension")
protected Object filter(@Nonnull final MethodSecurityExpressionHandler handler, @Nonnull final Object filterTarget,
@Nonnull final Expression filterExpression, @Nonnull final EvaluationContext context) {
return handler.filter(filterTarget, filterExpression, context);
}
@Override
protected final MethodSecurityExpressionHandler createExpressionHandler() {
final ApplicationContext applicationContext = applicationContext();
final TypeConverter typeConverter = new StandardTypeConverter(conversionService());
final DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler() {
@Override
public StandardEvaluationContext createEvaluationContextInternal(final Authentication authentication, final MethodInvocation methodInvocation) {
final StandardEvaluationContext decoratedStandardEvaluationContext = super.createEvaluationContextInternal(authentication, methodInvocation);
return new ForwardingStandardEvaluationContext() {
@Override
protected StandardEvaluationContext standardEvaluationContext() {
return decoratedStandardEvaluationContext;
}
@Override
public TypeConverter getTypeConverter() {
return typeConverter;
}
};
}
@Override
public Object filter(final Object filterTarget, final Expression filterExpression, final EvaluationContext context) {
return AbstractCustomTypesGlobalMethodSecurityConfiguration.this.filter(this, filterTarget, filterExpression, context);
}
};
handler.setApplicationContext(applicationContext);
handler.setPermissionEvaluator(permissionEvaluator());
return handler;
}
}
보안 구성
기본적으로 하기 구성은 템플릿 방법 설계 패턴을 사용하여 필요한 콩을 제공하는 후자의 구성을 확장하는 것입니다.의심할 만한 건 없어요, 제 생각엔 다른 건 빼고요@EnableGlobalMethodSecurity, 그러나 주석이 작동하는 것처럼 보이고 플래그를 활성화/비활성화하는 것은 전체적인 동작에도 영향을 미칩니다. (주석을 다른 구성으로 이동하는 것도 경우에 따라 작동하지 않을 수 있습니다.)
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
class SecurityConfiguration
extends AbstractCustomTypesGlobalMethodSecurityConfiguration {
@Autowired
private ApplicationContext applicationContext;
@Autowired
private ConversionService conversionService;
@Nonnull
@Override
protected ApplicationContext applicationContext() {
return applicationContext;
}
@Nonnull
@Override
protected ConversionService conversionService() {
return conversionService;
}
@Nonnull
@Override
protected final PermissionEvaluator permissionEvaluator() {
return getAlwaysPermittedPermissionEvaluator();
}
@Nonnull
@Override
protected final Object filter(@Nonnull final MethodSecurityExpressionHandler handler, @Nonnull final Object filterTarget,
@Nonnull final Expression filterExpression, @Nonnull final EvaluationContext context) {
final MethodSecurityExpressionOperations operations = (MethodSecurityExpressionOperations) context.getRootObject().getValue();
operations.setFilterObject(filterTarget);
return filterExpression.getValue(context, Object.class);
}
}
웹 보안 구성
서비스 엔드포인트에 액세스하기 위한 몇 가지 규칙을 정의하는 사소한 웹 보안 구성입니다.필터는 다음과 같이 "빈(bean)"됩니다.authenticationTokenProcessingFilter예외가 먼저 발생하므로 호출되지 않습니다.
@Configuration
@EnableWebSecurity
class WebSecurityConfiguration
extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private ITokenAuthenticationService tokenAuthenticationService;
@Override
protected final void configure(final HttpSecurity httpSecurity)
throws Exception {
httpSecurity
.authorizeRequests()
.antMatchers(POST, "/api/v0/authentication").permitAll()
.antMatchers("/api/v0/**").fullyAuthenticated()
.antMatchers("/**").permitAll();
httpSecurity
.csrf().disable()
.httpBasic()
.authenticationEntryPoint(customAuthenticationEntryPoint());
httpSecurity
.sessionManagement()
.sessionCreationPolicy(STATELESS);
httpSecurity
.addFilterBefore(authenticationTokenProcessingFilter(), UsernamePasswordAuthenticationFilter.class);
}
@Bean
AuthenticationEntryPoint customAuthenticationEntryPoint() {
return getCustomAuthenticationEntryPoint();
}
@Bean
GenericFilterBean authenticationTokenProcessingFilter() {
return getAuthenticationTokenProcessingFilter(tokenAuthenticationService);
}
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Autowired
void registerGlobalAuthentication(final AuthenticationManagerBuilder managerBuilder)
throws Exception {
managerBuilder
.userDetailsService(userDetailsService)
.and()
.eraseCredentials(false);
}
}
그것은 진단이 필요할 수도 있는 거의 모든 코드이고 바라건대 완전한 코드이기를 바랍니다.고장 났거나 그런 것 같지는 않은데, 왜 예외를 받는지 이유를 아직도 모르겠어요.머리가 희끗희끗해지는 느낌이 들기 시작했습니다.
어떤 도움이라도 주시면 대단히 감사하겠습니다!
종속성:
- 스프링 골조부트:스프링-부트 종속성:1.4.3.RELEASE:폼
- 스프링 골조부팅:spring-boot-starter-web:1.4.3.RELEASE
- 스프링 골조보안:spring-security-config:4.2.1.RELEASE
- 스프링 골조보안:스프링-보안-코어:4.2.1.RELEASE
- 스프링 골조보안:spring-security-web:4.2.1.RELEASE
편집 1
@Test
@DatabaseSetup(DATASET)
// @WithMockUser is commented out -- we're authenticating as Alice ourselves to obtain the authentication token
public void testAuthenticate()
throws Exception {
final MockHttpServletResponse response = post("/authentication", asJson(), identityWithKeyGsonIncomingDto("Alice", "alice123"))
// Here is where it fails: the exception causes HTTP 500 rather than HTTP 201
.andExpect(status().isCreated())
.andReturn()
.getResponse();
@SuppressWarnings("unchecked")
final Map<String, Object> responseMap = gson.fromJson(response.getContentAsString(), Map.class);
final String token = (String) responseMap.get("token");
get("/users/self", headers("Authorization", token))
.andExpect(status().isOk());
}
편집2
public final class AuthenticationTokenProcessingFilter
extends GenericFilterBean {
...
@Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
throws IOException, ServletException {
@Nullable
final String authenticationToken = getAuthenticationToken(request);
if ( authenticationToken != null ) {
try {
final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
final Authentication authentication = tokenAuthenticationService.authenticate(authenticationToken, httpServletRequest);
setCurrentAuthentication(authentication);
} catch ( final AuthenticationException ex ) {
...
}
}
chain.doFilter(request, response);
}
}
유감스럽게도 위 필터가 어떤 이유로 제어하기 전에 예외가 발생합니다.이 필터는 특정 상황에서만 현재 사용자 인증을 설정하기 위한 것이며, 익명으로 설정하는 것은 아닙니다.적어도 제 다른 모듈에서는 이렇게 작동합니다.
제가 제공한 수많은 코드가 문제의 진짜 원인을 밝히지 못해 죄송합니다.몇 가지 실험을 더 해본 결과, 생산 모드로 사용 케이스를 실행하라는 제안을 받았고(첫 번째 테스트 때문에 완전히 잊어버렸습니다), 문제없이 생산 모드로 작동합니다.테스트로 범위를 좁혀 테스트 주석을 먼저 확인하여 다음을 포함한 모든 주석이 있는지 확인했습니다.@WithSecurityContextTestExecutionListener다른 모듈이 가지고 있는 것처럼.을 놓쳤다는 되었습니다.을 미칠 수 범위는 단 객체다)입니다. 청취자들이 영향을 미칠 수 있는 가장 작은 범위는 단일 테스트이며 아마도 조롱당한 MVC 객체일 것입니다.MockMvc원래 질문에 포함하지 않은 것은 단순히 구성 문제라고 생각했기 때문입니다)가 잘 구성되어 있지 않습니다.네님.MockMvc과 같은 되었습니다).@Before테스트 슈퍼 클래스 중 하나의 메서드):
mvc = webAppContextSetup(webApplicationContext)
.build();
가MockMvc인스턴스도 구성해야 합니다.
mvc = webAppContextSetup(webApplicationContext)
.apply(springSecurity()) // this is the key
.build();
"주석은 필요한 모든 것을 스스로 할 수 있습니다."라고 믿지 않는 좋은 예입니다.안타깝게도 많은 시간을 낭비했지만 결국 그 원인을 찾을 수 있어서 기쁩니다.
언급URL : https://stackoverflow.com/questions/43008149/cannot-get-rid-of-an-authentication-object-was-not-found-in-the-securitycontext
'programing' 카테고리의 다른 글
| MySQL에 순차 번호 삽입 (0) | 2023.10.07 |
|---|---|
| Zepto와 jQuery 2의 차이점은 무엇입니까? (0) | 2023.10.07 |
| 프로필과 함께 봄에 외부 application.yml을 지정하는 방법 (0) | 2023.10.07 |
| 특정 테이블에 연결된 모든 외부 키 제약 조건 사용 안 함 (0) | 2023.10.07 |
| 대화형 사용자를 통한 Excel COM 자동화는 사용자 로그오프 시 작동 중지 (0) | 2023.10.07 |