SprinBoot app launched from IDE - SpelEvaluationException - spring-boot

I have a simple SpringBoot app with web security and method security config using a custom SecurityExpressionRoot.
When I launch the SB app from an IDE (e.g. STS or IDEA) and call an endpoint I get
org.springframework.expression.spel.SpelEvaluationException: EL1004E:
Method call: Method hasAnyAccess(java.lang.String) cannot be found on
org.springframework.security.access.expression.method.MethodSecurityExpressionRoot type
When I launch the SB app from CLI with the app jar file the endpoint call succeeds:
INFO 11282 --- [nio-8080-exec-4] c.e.CustomMethodSecurityExpressionRoot :
< CustomMethodSecurityExpressionRoot(): [org.springframework.security.authentication.UsernamePasswordAuthenticationToken#442a8f33: Principal: org.springframework.security.core.userdetails.User#36ebcb: Username: user; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#166c8: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: 36E17CAA657E897B1682BFF27EA7DA1F; Granted Authorities: ROLE_USER]
INFO 11282 --- [nio-8080-exec-4] c.e.CustomMethodSecurityExpressionRoot :
> hasAnyAccess(): [[FULL_ACCESS]]
ANY help is highly appreciated.
Web security configuration:
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
}
Method security configuration:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
CustomMethodSecurityExpressionHandler expressionHandler = new CustomMethodSecurityExpressionHandler();
expressionHandler.setPermissionEvaluator(new CustomPermissionEvaluator());
return expressionHandler;
}
}
Method security expression handler:
public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {
private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
#Override
protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, MethodInvocation invocation) {
CustomMethodSecurityExpressionRoot root = new CustomMethodSecurityExpressionRoot(authentication);
root.setPermissionEvaluator(getPermissionEvaluator());
root.setTrustResolver(this.trustResolver);
root.setRoleHierarchy(getRoleHierarchy());
return root;
}
}
Security expression root:
public class CustomMethodSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {
public CustomMethodSecurityExpressionRoot(Authentication authentication) {
super(authentication);
LOGGER.info("< CustomMethodSecurityExpressionRoot(): [{}]", authentication);
}
public final boolean hasAnyAccess(String... accessLevels) {
LOGGER.info("> hasAnyAccess(): [{}]", Arrays.asList(accessLevels));
return true;
}
}
Rest controller:
#RestController
#RequestMapping("/")
public class MyController {
#PreAuthorize("hasAnyRole('ROLE_USER') && hasAnyAccess('FULL_ACCESS')")
#RequestMapping(value = "hello", method = RequestMethod.GET)
public String hello(#RequestParam String name) {
return "Hello '" + name + "' at " + System.currentTimeMillis() + "\n";
}
}
Update 1:
The same SpelEvaluationException happens when the SB app is launched with
mvn spring-boot:run
Update 2:
From the logs it seem when launching from IDE my MethodSecurityConfig extension is overridden by GlobalMethodSecurityConfiguration:
INFO 5927 --- [ main] o.s.b.f.s.DefaultListableBeanFactory :
Overriding bean definition for bean 'methodSecurityInterceptor' with a different definition:
replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=methodSecurityConfig; factoryMethodName=methodSecurityInterceptor; initMethodName=null; destroyMethodName=(inferred);
defined in class path resource [com/example/MethodSecurityConfig.class]]
with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration; factoryMethodName=methodSecurityInterceptor; initMethodName=null; destroyMethodName=(inferred);
defined in class path resource [org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfiguration.class]]
INFO 5927 --- [ main] o.s.b.f.s.DefaultListableBeanFactory :
Overriding bean definition for bean 'methodSecurityMetadataSource' with a different definition:
replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=methodSecurityConfig; factoryMethodName=methodSecurityMetadataSource; initMethodName=null; destroyMethodName=(inferred);
defined in class path resource [com/example/MethodSecurityConfig.class]]
with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration; factoryMethodName=methodSecurityMetadataSource; initMethodName=null; destroyMethodName=(inferred);
defined in class path resource [org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfiguration.class]]
When launching from JAR my MethodSecurityConfig extension overrides the GlobalMethodSecurityConfiguration:
INFO 6092 --- [ main] o.s.b.f.s.DefaultListableBeanFactory :
Overriding bean definition for bean 'methodSecurityInterceptor' with a different definition:
replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration; factoryMethodName=methodSecurityInterceptor; initMethodName=null; destroyMethodName=(inferred);
defined in class path resource [org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfiguration.class]]
with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=methodSecurityConfig; factoryMethodName=methodSecurityInterceptor; initMethodName=null; destroyMethodName=(inferred);
defined in class path resource [com/example/MethodSecurityConfig.class]]
INFO 6092 --- [ main] o.s.b.f.s.DefaultListableBeanFactory :
Overriding bean definition for bean 'methodSecurityMetadataSource' with a different definition:
replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration; factoryMethodName=methodSecurityMetadataSource; initMethodName=null; destroyMethodName=(inferred);
defined in class path resource [org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfiguration.class]]
with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=methodSecurityConfig; factoryMethodName=methodSecurityMetadataSource; initMethodName=null; destroyMethodName=(inferred);
defined in class path resource [com/example/MethodSecurityConfig.class]]

After some investigation it turned out the #EnableGlobalMethodSecurity(prePostEnabled = true) annotation specified for both
WebSecurityConfigurerAdapter extension and
GlobalMethodSecurityConfiguration extension
causes the problem. Removing the annotation from WebSecurityConfigurerAdapter solves the issue.

Related

Spring cloud stream binder - Invalid bean definition

I am using:
some-eventhub.some-service-1.errors in my application's Service Activator to listen to the error channel.
Stream binder 1.2.8 and Spring boot 2.2.2
I get the below exception messages frequently in my app:-
Invalid bean definition with name 'some-eventhub.some-service-1.errors.recoverer' defined in null: Cannot register bean definition
[Root bean: class [org.springframework.integration.handler.advice.ErrorMessageSendingRecoverer]; scope=; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null] for bean 'some-eventhub.some-service-1.errors.recoverer': There is already [Root bean: class [org.springframework.integration.handler.advice.ErrorMessageSendingRecoverer]; scope=; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null] bound.
It is because it keeps trying to register an error channel recoverer which has already been registered. You probably have more than one binding to the same error topic ("some-eventhub") by the same consumer group ("some-service-1").

Test Controller that uses #QueryDsl Predicate and Page

I have a Controller, that is annotated with #RestController. For GET requests it is implemented like this:
#GetMapping
public Map<String, Object> getFilteredCars(
#QuerydslPredicate(root = Car.class) Predicate predicate,
#RequestParam(name = "page", defaultValue = "0") int page,
#RequestParam(name = "size", defaultValue = "150") int size
) {
return this.carService.getFilteredCars(
predicate,
PageRequest.of(page, size)
);
}
The Controller works fine, I can query with different parameters and get a page with a defined size.
Now I want to test the controller.
#RunWith(MockitoJUnitRunner.class)
#SpringBootTest
class CarControllerTest {
#Mock
private CarService carService;
#InjectMock
private CarController carController;
#BeforeEach
void init(){
RestAssured.baseURI = "http://localhost";
RestAssured.port = 8810;
}
#Test
void givenUrl_whenSuccessOnGetCarsAndReturnsPage_thenCorrect() {
when(this.CarService.getFilteredCars(any(),any()))
.thenReturn(expectedResponse);
given()
.standaloneSetup(new CarController(this.carService))
.when()
.get("/cars?color=red&page=0&size=200")
.then()
.statusCode(200);
}
Running this gave me the exception:
No primary or default constructor found for interface com.querydsl.core.types.Predicate
Like in this question I added
#EnableSpringDataWebSupport
Now I get a new exception:
java.lang.IllegalStateException: Failed to load ApplicationContext
...
Caused by: org.springframework.beans.factory.support.BeanDefinitionOverrideException:
Invalid bean definition with name 'pageableResolver' defined in class path resource [org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.class]:
Cannot register bean definition
[Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;
factoryMethodName=pageableResolver; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/data/rest/webmvc/config/RepositoryRestMvcConfiguration.class]] for bean 'pageableResolver':
There is already [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.data.web.config.HateoasAwareSpringDataWebConfiguration;
factoryMethodName=pageableResolver; initMethodName=null; destroyMethodName=(inferred);
defined in class path resource [org/springframework/data/web/config/HateoasAwareSpringDataWebConfiguration.class]] bound.
...
I don't understand what is happening. Has anyone an idea, how to resolve the problem?

Not able to execute DataJpaTest

I have a spring-data-jpa repository called TagRepository. My spring-boot version is 2.1.2. I am using H2 dependency with runtime scope and I intend to use it for both the application and integration testing. I want to write a DataJpaTest for the TagRepository. I have written the following code:
#RunWith(SpringRunner.class)
#EnableAutoConfiguration
#DataJpaTest
#ContextConfiguration(classes={TagRepository.class})
public class TagRepositoryTest {
#Autowired
private TestEntityManager testEntityManager;
#Autowired
private TagRepository tagRepository;
#Test
public void findByTagTest() {
Tag tag = new Tag("java");
testEntityManager.persistAndFlush(tag);
Optional<Tag> optionalTag = tagRepository.findByTag(tag.getTag());
if(!optionalTag.isPresent()) {
fail("Tag not found hence test failed");
}
assertThat(optionalTag.get()).isEqualTo(tag);
}
}
However, when I execute the test it says Application failed to start and I get following error:
Invalid bean definition with name 'tagRepository' defined in null: Cannot register bean definition [Root bean: class [org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null] for bean 'tagRepository': There is already [Generic bean: class [com.upday.task.repository.TagRepository]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null] bound.
The bean 'tagRepository', defined in null, could not be registered. A bean with that name has already been defined in null and overriding is disabled.
Action:
Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
The test class itself resides in a different package than jpa repository.
Just remove
#ContextConfiguration(classes={TagRepository.class})
from your test.
It is meant for passing a custom #Configuration not a regular bean.
UPD:
When data jpa test and the repository under test resides in a different packages spring runner needed a little hint to scan classes from another place. Pointing out the application class via
#ContextConfiguration(classes={SpringBootApplication.class})
will extend the component scan scope and allow test environment to pick up additional beans.

Registering test bean with same name in Spring Framework 5.1 [duplicate]

This question already has answers here:
SpringBoot - BeanDefinitionOverrideException: Invalid bean definition
(8 answers)
Closed 3 years ago.
I'm having following config in my production files:
#Configuration
internal class Config {
#Bean
fun clock() = Clock.systemUTC()
}
In tests:
#Configuration
class ClockTestConfiguration {
#Bean
fun clock() = SetableClock()
}
My test annotations:
#SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = [
MyApplication::class,
ClockTestConfiguration::class
]
)
class MyTest {
...
When I was using Spring Boot 2.0.5.RELEASE it worked like a charm. After upgrading to 2.1.0.RELEASE it fails during bean registration.
Caused by: org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'clock' defined in com.foo.clock.ClockTestConfiguration:
Cannot register bean definition [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=clockTestConfiguration; factoryMethodName=clock; initMethodName=null; destroyMethodName=(inferred);
defined in com.foo.clock.ClockTestConfiguration] for bean 'clock': There is already [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=config; factoryMethodName=clock; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [com/foo/clock/Config.class]] bound.
at org.springframework.beans.factory.support.DefaultListableBeanFactory.registerBeanDefinition(DefaultListableBeanFactory.java:894)
Is there a clean way to override such bean?
You could use the properties attribute of #SpringBootTest to set spring.main.allow-bean-definition-overriding=true.

Invalid bean definition with name 'configServicePropertySource'

I have defined ConfigServicePropertySourceLocator bean like following
#Primary
#Bean
public ConfigServicePropertySourceLocator configServicePropertySource(
ConfigClientProperties configClientProperties) {
ConfigServicePropertySourceLocator sourceLocator = new ConfigServicePropertySourceLocator(
configClientProperties);
sourceLocator.setRestTemplate(clientOnlyRestTemplate());
return sourceLocator;
}
but i get the following exception (This is how it is printed in docker) although my bean is marked as #Primary
WARN 1 --- [ main] s.c.a.AnnotationConfigApplicationContext : Exception encountered during context initialization - cancelling refresh attempt:
org.springframework.beans.factory.support.BeanDefinitionOverrideException:
Invalid bean definition with name 'configServicePropertySource' defined in de.ig.client.security.configuration.ConfigClientSecurityConfiguration:
Cannot register bean definition [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3;
dependencyCheck=0; autowireCandidate=true; primary=true; factoryBeanName=configClientSecurityConfiguration;
factoryMethodName=configServicePropertySource; initMethodName=null; destroyMethodName=(inferred);
defined in de.ig.client.security.configuration.ConfigClientSecurityConfiguration] for bean 'configServicePropertySource':
There is already [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0;
autowireCandidate=true; primary=false; factoryBeanName=configServiceBootstrapConfiguration;
factoryMethodName=configServicePropertySource; initMethodName=null; destroyMethodName=(inferred);
defined in org.springframework.cloud.config.client.ConfigServiceBootstrapConfiguration] bound.
Solved it by setting
spring.main.allow-bean-definition-overriding to true
New from boot version 2.1

Resources