authorizeHttpRequests does not support expression-based rules in its DSL. While it is recommended to migrate expressions to concrete authorization manager implementations, applications can use WebExpressionAuthorizationManager as a migration step.

Given that authorizeRequests is due to be deprecated in #15174, it would be nice to make this simpler, if possible. One place of friction is configuring the expression handler to support bean references. To do this, the expression handler must be provided after construction is complete. Aside from causing the parsed expression to be re-parsed after construction, it also makes it cumbersome when there are multiple expressions to replace.

This ticket adds a builder so that applications can do:

@Bean 
WebExpressionAuthorizationManager.Builder authz() {
    return WebExpressionAuthorizationManager.withDefaults();
}

This allows migrating from something like the following:

@Bean 
SecurityFilterChain httpSecurity(HttpSecurity http) throws Exception {
    http
        .authorizeRequests((authorize) -> authorize
            .mvcMatchers(GET, "/api/**").access("@bean.hasPermission('api:read') || hasRole('ADMIN')")
            .mvcMatchers("/api/**").access("@bean.hasPermission('api:write') || hasRole('ADMIN')")
        )
    return http.build();
}

To this:

@Bean 
WebExpressionAuthorizationManager.Builder authz() {
    return WebExpressionAuthorizationManager.withDefaults();
}

@Bean 
SecurityFilterChain httpSecurity(HttpSecurity http, WebExpressionAuthorizationManager.Builder authz) throws Exception {
    http
        .authorizeHttpRequests((authorize) -> authorize
            .requestMatchers(GET, "/api/**").access(authz.expression("@bean.hasPermission('api:read') || hasRole('ADMIN')"))
            .requestMatchers("/api/**").access(authz.expression("@bean.hasPermission('api:write') || hasRole('ADMIN')"))
        )
    return http.build();
}

Instead of this:

@Bean 
DefaultHttpSecurityExpressionHandler expressionHandler() {
    return new DefaultHttpSecurityExpressionHandler();
}

@Bean 
SecurityFilterChain httpSecurity(HttpSecurity http, DefaultHttpSecurityExpressionHandler expressionHandler) throws Exception {
    WebExpressionAuthorizationManager getApi = new WebExpressionAuthorizationManager("@bean.hasPermission('api:read') || hasRole('ADMIN')");
    getApi.setExpressionHandler(expressionHandler);
    WebExpressionAuthorizationManager api = new WebExpressionAuthorizationManager("@bean.hasPermission('api:write') || hasRole('ADMIN')");
    api.setExpressionHandler(expressionHandler);
    http
        .authorizeHttpRequests((authorize) -> authorize
            .requestMatchers(GET, "/api/**").access(getApi)
            .requestMatchers("/api/**").access(api)
        )
    return http.build();
}

NOTE: