SpelEvaluationException 解释 ResourceServerConfigurerAdapter 中的 "access" 字符串

SpelEvaluationException interpreting "access" string in ResourceServerConfigurerAdapter

对此有什么想法吗?

来自 Tomcat:

Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1011E:(pos 8): Method call: Attempted to call method throwOnError(java.lang.Boolean) on null context object

返回给客户:

java.lang.IllegalArgumentException: Failed to evaluate expression '#oauth2.throwOnError(#oauth2.hasScope('read') and #oauth2.hasScope('write') and #oauth2.hasAnyRole('ROLE_USER','ROLE_ADMIN'))'
    org.springframework.security.access.expression.ExpressionUtils.evaluateAsBoolean(ExpressionUtils.java:13)
    org.springframework.security.web.access.expression.WebExpressionVoter.vote(WebExpressionVoter.java:34)
    org.springframework.security.web.access.expression.WebExpressionVoter.vote(WebExpressionVoter.java:18)
    org.springframework.security.access.vote.UnanimousBased.decide(UnanimousBased.java:77)

我对我的授权服务器 /oauth/token 执行 POST 并获得一个令牌。

如果我使用该令牌并将 Authorization: Bearer header 添加到对资源服务器的 GET 请求,我会收到该错误。

在我的 ResourceServerConfigurerAdapter 子类中,它炸毁的行在这里:

public void configure(HttpSecurity http) throws Exception {

    http
    .authorizeRequests()
    .antMatchers("/api/**")

.access("#oauth2.hasScope('read') and #oauth2.hasScope('write') and #oauth2.hasAnyRole('ROLE_USER','ROLE_ADMIN')")

    .accessDecisionManager(accessDecisionManager())
    .anyRequest()
    .fullyAuthenticated();

我知道资源服务器可以识别令牌,因为如果我将其遗漏,我会得到正确的错误。如果我编了一个假的,那么我会收到 "invalid token" 消息,这是预期的。如果我使用实际令牌 Spring 会跳入并在 .access()

上爆炸

在此先感谢您的帮助。我将我的 ResourceReserver 的代码放在下面:

@Configuration
@EnableWebSecurity
@EnableResourceServer
public class ResourceServerConfigurer extends ResourceServerConfigurerAdapter {

    @Autowired
    private OAuth2AuthenticationEntryPoint oAuth2AuthenticationEntryPoint;


    @Autowired
    private ResourceServerTokenServices tokenServices;

    @Autowired
    private TokenStore tokenStore;

    @Autowired
    @Qualifier("oauth2ResourceId")
    private String oauth2ResourceId; 

    @Autowired
    @Qualifier("oauth2Realm")
    private String oauth2Realm;

    @Bean
    OAuth2AuthenticationEntryPoint oAuth2AuthenticationEntryPoint() {
        final OAuth2AuthenticationEntryPoint entryPoint = new OAuth2AuthenticationEntryPoint();
        entryPoint.setRealmName(oauth2Realm);
        entryPoint.setTypeName("Basic");
        return entryPoint;
    }

    private AccessDecisionManager accessDecisionManager() {
        return new UnanimousBased(Arrays.<AccessDecisionVoter>asList(new ScopeVoter(),
                                                                     new AuthenticatedVoter(),
                                                                     new WebExpressionVoter()));
    }

    private AuthenticationManager getAuthenticationManager() {
        final OAuth2AuthenticationManager oAuth2AuthenticationManager = new OAuth2AuthenticationManager();
        oAuth2AuthenticationManager.setTokenServices(tokenServices);

        return oAuth2AuthenticationManager;
    }

    public void configure(HttpSecurity http) throws Exception {

        http
        .authorizeRequests()
        .antMatchers("/api/**")
        .access("#oauth2.hasScope('read') and #oauth2.hasScope('write') and #oauth2.hasAnyRole('ROLE_USER','ROLE_ADMIN')")
        .accessDecisionManager(accessDecisionManager())
        .anyRequest()
        .fullyAuthenticated();

        http
        .anonymous()
        .disable();

        http
        .sessionManagement()
        .sessionCreationPolicy(SessionCreationPolicy.NEVER);

        http
        .logout()
        .logoutUrl("/oauth/logout")
        .logoutSuccessHandler(logoutSuccessHandler())
        .invalidateHttpSession(true);

/*        
        http
        .requiresChannel()
        .antMatchers("/oauth/api/**")
        .requiresSecure();

        http
        .portMapper()
        .http(8080)
        .mapsTo(8443);
*/  

    }


    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources
        .authenticationManager(getAuthenticationManager())
        .tokenServices(tokenServices)
        .tokenStore(tokenStore)
        .resourceId(oauth2ResourceId);
    }

    private LogoutSuccessHandler logoutSuccessHandler() {
        return new OAuth2SuccessLogoutHandler(tokenStore);
    }

    static final class OAuth2SuccessLogoutHandler implements LogoutSuccessHandler {

        private final TokenStore tokenStore;

        public OAuth2SuccessLogoutHandler(final TokenStore tokenStore) {
            this.tokenStore = tokenStore;
        }

        @Override
        public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
               request.toString();
        }


    }

}

hasAnyRole() 方法与 OAuth2 无关,因此不在 #oauth2 变量中(它在根目录中,因此您不需要限定它)。