SpringBoot + method based hierarchical roles security: ServletContext is required - spring

I added method-based security and added role hierarchy. I keep getting the following exception during build:
org.springframework.beans.BeanInstantiationException: Failed to
instantiate [org.springframework.web.servlet.HandlerMapping]: Factory
method 'defaultServletHandlerMapping' threw exception; nested
exception is java.lang.IllegalArgumentException: A ServletContext is
required to configure default servlet handling
I tried a lot of different configuration alternatives and none works...
Here is my basic web security configuration class (I added role hierarchy-related beans as you see):
#Configuration
#EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
(...)
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf()
.disable()
(...)
.expressionHandler(webExpressionHandler())
(...)
.anyRequest().authenticated();
httpSecurity
.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
}
#Bean
public SecurityExpressionHandler<FilterInvocation> webExpressionHandler() {
DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler();
defaultWebSecurityExpressionHandler.setRoleHierarchy(roleHierarchy());
return defaultWebSecurityExpressionHandler;
}
#Bean
public RoleHierarchy roleHierarchy() {
RoleHierarchyImpl r = new RoleHierarchyImpl();
r.setHierarchy(Role.getHierarchy());
return r;
}
(...)
}
Here is the separate config file that I created basing on this thread:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class GlobalMethodSecurityConfig extends GlobalMethodSecurityConfiguration {
#Autowired
private RoleHierarchy roleHierarchy;
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
DefaultMethodSecurityExpressionHandler expressionHandler = (DefaultMethodSecurityExpressionHandler) super.createExpressionHandler();
expressionHandler.setRoleHierarchy(roleHierarchy);
return expressionHandler;
}
}
I struggled with this for many hours. The related topics I tried are here:
Spring Boot + Spring Security + Hierarchical Roles
Error creating bean with name 'defaultServletHandlerMapping
Error creating bean with name defaultServletHandlerMapping
Every help appreciated!

OK, found it.
I played around with the class annotations and came to a simple solution: I removed #EnableGlobalMethodSecurity from GlobalMethodSecurityConfig and moved it to WebSecurityConfiguration, where it was before. So it looks like this:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { (...) }
and this:
#Configuration
public class GlobalMethodSecurityConfig extends GlobalMethodSecurityConfiguration { (...) }
The most interesting part is that the accepted answer from here advises what didn't work for me.

Related

How can I provide a Spring configuation bean in a WebSecurityConfigurerAdapter for testing?

I use the following SecurityConfiguration class for securing endpoints in a Spring Boot application. This class depends on the ApiConfiguration class which provides the username and password for the in-memory authentication.
When starting a #WebMvcTest for a controller, then Spring also tries to initialize the security configuration but fails to load the application context.
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'ApiConfiguration' available: expected at least 1 bean which qualifies as autowire candidate.
I tried adding a MockBean to the test class:
#MockBean
private ApiConfiguration apiConfiguration;
This resolves the above issue, but then the username and password are null.
Is there any Spring support for providing this configuration bean for testing?
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration {
#Configuration
#Order(1)
public static class ApiSecurityConfiguration extends WebSecurityConfigurerAdapter {
private static final String API_USER_ROLE = "API_USER";
private final ApiConfiguration apiConfig;
public ApiSecurityConfiguration(ApiConfiguration apiConfig) {
this.apiConfig = apiConfig;
}
#Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser(apiConfig.getUsername())
.password(passwordEncoder().encode(apiConfig.getPassword()))
.roles(API_USER_ROLE);
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.csrf().disable()
.formLogin().disable()
.mvcMatcher("/api/**")
.authorizeRequests()
.anyRequest().hasRole(API_USER_ROLE)
.and()
.httpBasic()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
}
I ended up creating a CustomApiConfiguration annotated with #TestConfiguration. This seems to do the trick. At least, Spring is able to read the properties when setting up the SecurityConfiguration class.
#TestConfiguration
public class CustomApiConfiguration {
#Bean
public ApiConfiguration apiConfiguration() {
final ApiConfiguration config = new ApiConfiguration();
config.setUsername("api-username");
config.setPassword("api-password");
return config;
}
}

EL1057E: No bean resolver registered in the context to resolve access to bean

I am using this resource server for authorizing request but whenever it tries to evaluate expression-"#ipWhitelistingProvider.isItValid(request)" it gives error stating- EL1057E: No bean resolver registered in the context to resolve access to bean 'ipWhitelistingProvider'.
#Profile("default")
#EnableResourceServer
#EnableWebSecurity
#Configuration
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Autowired
IpWhitelistingProvider ipWhitelistingProvider;
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/config/**").access("#ipWhitelistingProvider.isItValid(request) or #oauth2.hasScope('config')")
.anyRequest().authenticated()
.and().csrf().disable();
}
#Component("ipWhitelistingProvider")
public class IpWhitelistingProvider{
#Value("${ip.whitelist:1}")
List<String>whitelist;
String ipAcessWhitelistingString;
public boolean isItValid(HttpServletRequest request) {
String ip=request.getRemoteAddr();
if(whitelist.contains(ip)) {
return true;
}
else {
return false;
}
}
}
I managed to workaround this issue by doing the following:
My default OAuth2 Expression Handler's Bean Resolver was null, so I added a #Bean with OAuth2WebSecurityExpressionHandler that explicitly sets the application context.
#Bean
public OAuth2WebSecurityExpressionHandler oAuth2WebSecurityExpressionHandler(ApplicationContext applicationContext) {
OAuth2WebSecurityExpressionHandler expressionHandler = new OAuth2WebSecurityExpressionHandler();
expressionHandler.setApplicationContext(applicationContext);
return expressionHandler;
}
In my ResourceServerConfigurerAdapter, I configured resources and passed above bean
#Autowired
private OAuth2WebSecurityExpressionHandler expressionHandler;
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.expressionHandler(expressionHandler);
}

#Value annotation return empty value in an AbstractAuthenticationProcessingFilter filter

I'm developing a springboot application with spring security.
I'm trying to make my custom authentication filter reading some properties from the application.properties file without success.
I've read this other question which is similar but within a different context (not related to spring security filters). The reason for the failure makes sense to me but I've tried the way suggested with the DelegatingFilterProxy but without success (to be fair, I didn't really get the meaning of the part added to the Application class). The other solution does not fit my case as I don't have any onStartup method to override.
Here is the code I'm using:
public class JWTAuthenticationFilter extends
AbstractAuthenticationProcessingFilter {
#Value("${app.jwtSecret}")
public String SECRET2;
Almost the same code, in a controller class, works fine:
#RestController
#RequestMapping("/api")
#CrossOrigin
#EnableAutoConfiguration
public class UsersController {
#Value("${app.jwtSecret}")
public String SECRET2;
But I can't make it work in the filter. I'm using springboot 2.0.3.
Any suggestion? Is the DelegatingFilterProxy the right approach in this situation? In that case, any example/article I could follow?
Thanks,
Michele.
UPDATE:
to fully answer to the first comment, the filter is called by the following class:
#EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
#Autowired
private LdapAuthenticationProvider ldapAuthenticationProvider;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.POST, "/api/secureLogin").permitAll()
.antMatchers(HttpMethod.GET, "/api").permitAll()
.antMatchers("/api/**").authenticated()
.and()
.addFilterBefore(new JWTAuthenticationFilter(authenticationManager()), UsernamePasswordAuthenticationFilter.class)
.addFilter(new JWTAuthorizationFilter(authenticationManager()))
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(ldapAuthenticationProvider);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
No need to use #Value in filter class:
public class JWTAuthenticationFilter extends
AbstractAuthenticationProcessingFilter {
private String secret;
//... setter for secret
But inject the secret in the config class:
#EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
#Value("${app.jwtSecret}")
public String secret;
//...
#Override
protected void configure(HttpSecurity http) throws Exception {
JWTAuthorizationFilter jwtFilter = new JWTAuthorizationFilter(authenticationManager());
//set secret
//...
}

Using EnableGlobalMethodSecurity does not seem to have an effect

I have a spring controller where I have a method meant to be accessed by admin users:
#Controller
#RequestMapping("/*")
public class HomeController {
#RequestMapping(value = "addset", method = RequestMethod.GET, consumes = "application/json")
#Secured("ROLE_ADMIN")
public #ResponseBody Message addSet() {
return new Message(100, "Congratulations!", "Set added");
}
}
My Applicaton.java is as follows:
#Configuration
#ComponentScan
#EnableAutoConfiguration
#EnableGlobalMethodSecurity(securedEnabled = true)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public ApplicationSecurity applicationSecurity() {
return new ApplicationSecurity();
}
#Order(Ordered.HIGHEST_PRECEDENCE)
#Configuration
protected static class AuthenticationSecurity extends
GlobalAuthenticationConfigurerAdapter {
#Autowired
private CustomUserDetailsService users;
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(users);
}
}
protected static class ApplicationSecurity extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/signup","/about").permitAll()
.anyRequest().authenticated()
.and()
.httpBasic().and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.ALWAYS);
// #formatter:on
}
}
}
However, I am able to access the method from general users as well. The EnableGlobalMethodSecurityis not having any effect. I suspect it is because the annotation is over the Application class which has a different scope that the HomeController class. If I try to move the annotation over the HomeController class then I get an error:
Error creating bean with name 'methodSecurityInterceptor' defined in class path resource [org/springframework/security/config/annotation/method/configuration/GlobalMethodSecurityConfiguration.class]: Instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanDefinitionStoreException: Factory method [public org.aopalliance.intercept.MethodInterceptor org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration.methodSecurityInterceptor() throws java.lang.Exception] threw exception; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'methodSecurityInterceptor': Requested bean is currently in creation: Is there an unresolvable circular reference?
The entire error is at: http://pastebin.com/NtN7Ai7u
I am not sure why the circular reference is happening. Also, is the annotation ineffective because of its incorrect placing as I suspect?
Update
After, incorporating most of the changes that #Vaelyr suggested, I still have the same result.
My Application.java now is:
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
I created a new class ApplicationSecurity as follows:
#EnableWebSecurity
#Configuration
#Order(1)
#EnableGlobalMethodSecurity(securedEnabled = true)
public class ApplicationSecurity extends WebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetailsService users;
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/signup","/about").permitAll()
.anyRequest().authenticated()
.and()
.httpBasic().and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.ALWAYS);
//http.sessionManagement().sessionAuthenticationStrategy().
// #formatter:on
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(users);
}
}
My controller is now:
#RestController
public class HomeController {
#RequestMapping(value = "addset", method = RequestMethod.GET, consumes = "application/json")
#Secured("ROLE_ADMIN")
public #ResponseBody Message addSet() {
return new Message(100, "Congratulations!", "Set added");
}
}
I can share details of my CustomUserDetailService or my android client code if required.

Spring Security PreAuthorize Custom Method Bean resolver is not registered?

Im just learning Spring, going through tutorials and testing possibilities. One of my goals is to secure a Service Method using a Custom Method and the PreAuthorize annotation. Unfortunaly the Bean holding the custom Method cannot be resolved and I dont know why. Maybe someone can see the error at first sight.
Bean holding the custom Method:
#Component("mySecurityService")
public class MySecurityService {
public boolean hasPermission() {
return true; //simple implementation just to look if has permission is called
}
}
Service to be Secured:
public interface OrderService {
#PreAuthorize("#mySecurityService.hasPermission()")
public AllOrdersEvent requestAllOrders(RequestAllOrdersEvent requestAllCurrentOrdersEvent);
public OrderDetailsEvent requestOrderDetails(RequestOrderDetailsEvent requestOrderDetailsEvent);
public OrderStatusEvent requestOrderStatus(RequestOrderStatusEvent requestOrderStatusEvent);
public OrderCreatedEvent createOrder(CreateOrderEvent event);
public OrderUpdatedEvent setOrderPayment(SetOrderPaymentEvent setOrderPaymentEvent);
public OrderDeletedEvent deleteOrder(DeleteOrderEvent deleteOrderEvent);
}
Java Security Config:
#EnableWebSecurity
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("letsnosh").password("noshing").roles("USER");
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean(name = "mySecurityService")
MySecurityService createSecurityService(){return new MySecurityService();}
#Override
protected void configure(HttpSecurity http) throws Exception {
/*
http.authorizeUrls()
.antMatchers("/aggregators*//**//**").hasRole("USER")
.anyRequest().anonymous()
.and()
.httpBasic();
*/
}
}
Error:
No bean resolver registered in the context to resolve access to bean 'mySecurityService'
Hello I solved the problem. It was connected to the Version of Spring Security.
I got the Version from the official Spring Rest Tutotrial : 3.2.0.M2
In this version I had to declare the Security Context as follows:
#EnableWebSecurity
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("letsnosh").password("noshing").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeUrls()
.antMatchers("/aggregators/**").hasRole("USER")
.anyRequest().anonymous()
.and()
.httpBasic();
}
}
Here the error was thrown.
But using a newer Version of Spring Security: 3.2.5.RELEASE
I could declare the Config this way:
#EnableWebSecurity
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeUrls()
.antMatchers("/aggregators*//**//**").hasRole("USER")
.anyRequest().anonymous()
.and()
.httpBasic();
}
And the bean could be resolved, using either #Component Annotaions directly on the MySecurityService class or #Bean annotations on a config class method which returns a MySecurityService instance.

Resources