SpelEvaluationException: попытка вызвать метод throwOnError(java.lang.Boolean) для объекта нулевого контекста
У меня возникли проблемы с ассоциированиемaccessDecisionManager
к конфигурации моего сервера ресурсов и с использованием выражений SpEL oauth2.
Я думаю, что правильно понял, какая документация и несколько связанных вопросов здесь, в SO, говорят о том, как это реализовать, но я продолжаю получать ту же ошибку.
Текущая реализация сервера ресурсов выглядит следующим образом:
@Slf4j
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Autowired
private AccessDeniedHandler oauthAccessDeniedHandler;
@Autowired
private ResourceServerTokenServices tokenServices;
@Override
public void configure(HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable()
.anonymous().disable()
.authorizeRequests()
.antMatchers("/authenticated/**")
.access("hasAnyAuthority('ROLE_USER','SCOPE_READ')")
.expressionHandler(oauthExpressionHandler())
.accessDecisionManager(accessDecisionManager())
.and()
.exceptionHandling().accessDeniedHandler(oauthAccessDeniedHandler)
.defaultAuthenticationEntryPointFor(oauthAuthenticationEntryPoint(), new AntPathRequestMatcher("/authenticated/**"));
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources
.tokenServices(tokenServices);
//.expressionHandler(oauthExpressionHandler);
}
@Bean
public OAuth2WebSecurityExpressionHandler oauthExpressionHandler() {
return new OAuth2WebSecurityExpressionHandler();
}
@Bean
protected AuthenticationEntryPoint oauthAuthenticationEntryPoint() {
OAuth2AuthenticationEntryPoint entryPoint = new OAuth2AuthenticationEntryPoint();
entryPoint.setRealmName("RealName");
return entryPoint;
}
@Bean
protected AccessDecisionManager accessDecisionManager() {
List<AccessDecisionVoter<? extends Object>> decisionVoters
= Arrays.asList(
new ScopeVoter(),
new RoleVoter(),
new WebExpressionVoter(),
new AuthenticatedVoter());
return new UnanimousBased(decisionVoters);
}
}
С этой реализацией я продолжаю получать следующую ошибку:
14:52:52 TRACE org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor.traceDebug: Writing [{timestamp=Fri Sep 16 14:52:52 WEST 2022, status=500, error=Internal Server Error, exception=java.lang.IllegalArgumentException, trace=java.lang.IllegalArgumentException: Failed to evaluate expression '#oauth2.throwOnError(hasAnyAuthority('ROLE_USER','SCOPE_READ'))'
at org.springframework.security.access.expression.ExpressionUtils.evaluateAsBoolean(ExpressionUtils.java:33)
at org.springframework.security.web.access.expression.WebExpressionVoter.vote(WebExpressionVoter.java:59)
at org.springframework.security.web.access.expression.WebExpressionVoter.vote(WebExpressionVoter.java:39)
at org.springframework.security.access.vote.UnanimousBased.decide(UnanimousBased.java:68)
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.attemptAuthorization(AbstractSecurityInterceptor.java:239)
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:208)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:113)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:81)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:122)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:116)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:126)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:81)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:149)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter.doFilter(OAuth2AuthenticationProcessingFilter.java:176)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:103)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:89)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:110)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:80)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:55)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:211)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:183)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:540)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:359)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:889)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1735)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1011E: Method call: Attempted to call method throwOnError(java.lang.Boolean) on null context object
at org.springframework.expression.spel.ast.MethodReference.throwIfNotNullSafe(MethodReference.java:154)
at org.springframework.expression.spel.ast.MethodReference.getValueRef(MethodReference.java:83)
at org.springframework.expression.spel.ast.CompoundExpression.getValueRef(CompoundExpression.java:70)
at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:91)
at org.springframework.expression.spel.ast.SpelNodeImpl.getTypedValue(SpelNodeImpl.java:117)
at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:308)
at org.springframework.security.access.expression.ExpressionUtils.evaluateAsBoolean(ExpressionUtils.java:30)
... 67 more
, message=Failed to evaluate expression '#oauth2.throwOnError(hasAnyAuthority('ROLE_USER','SCOPE_READ'))', path=/authenticated/profile}]
если я удалюaccessionDecisionManager
отhttpSecurity
вроде все работает, не понимаю почему. Я бы очень хотел использоватьUnanimousBased
использование этих избирателей вместо использования по умолчанию (насколько я читал, по умолчаниюAffirmativeBased
). Как этого можно добиться/есть ли в этом смысл?
Не знаю, поможет ли это, я использую spring-security-oauth2-2.3.6 и spring-security-core-5.6.2.
1 ответ
Кажется, используетDefaultWebSecurityExpressionHandler
по умолчанию, что приводит к сбою, когда SpEL оценивает выражение#oauth2
:
public class WebExpressionVoter implements AccessDecisionVoter<FilterInvocation> {
private final Log logger = LogFactory.getLog(getClass());
private SecurityExpressionHandler<FilterInvocation> expressionHandler = new DefaultWebSecurityExpressionHandler();
...
public void setExpressionHandler(SecurityExpressionHandler<FilterInvocation> expressionHandler) {
this.expressionHandler = expressionHandler;
}
}
Поскольку этот класс предоставляет метод для изменения используемого expressionHandler, мне просто нужно было изменить его, прежде чем добавлятьWebExpressionVoter
пример для моегоUnanimousBased
:
@Bean
protected AccessDecisionManager accessDecisionManager() {
WebExpressionVoter webExpressionVoter = new WebExpressionVoter();
webExpressionVoter.setExpressionHandler(oauthExpressionHandler);
List<AccessDecisionVoter<? extends Object>> decisionVoters
= Arrays.asList(
new ScopeVoter(),
new RoleVoter(),
webExpressionVoter,
new AuthenticatedVoter());
return new UnanimousBased(decisionVoters);
}