According to #Configuration documentation:
Indicates that a class declares one or more #Bean methods and may be processed by the Spring container to generate bean definitions and service requests for those beans at runtime, for example:
#Configuration
public class AppConfig {
#Bean
public MyBean myBean() {
//instantiate, configure and return bean ...
}
}
As I remember always I came across classes extending WebSecurityConfigurerAdapter which didn't contain any #Bean methods and were annotated with #Configuration.
It is even in official blog and some examples, see:
https://spring.io/blog/2013/07/03/spring-security-java-config-preview-web-security
#Configuration
#EnableWebSecurity
public class HelloWebSecurityConfiguration
extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
}
or here: https://docs.spring.io/spring-security/site/docs/current/reference/html/jc.html
#Order(1) 2
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/api/**") 3
.authorizeRequests(authorizeRequests ->
authorizeRequests
.anyRequest().hasRole("ADMIN")
)
.httpBasic(withDefaults());
}
}
Why this classes are annotated with #Configuration even though there are no #Bean methods?
Beans are imported using the secondary "#Enable" annotation
Spring features such as asynchronous method execution, scheduled task execution, annotation driven transaction management, and even Spring MVC can be enabled and configured from #Configuration classes using their respective "#Enable" annotations. See #EnableAsync, #EnableScheduling, #EnableTransactionManagement, #EnableAspectJAutoProxy, and #EnableWebMvc for details.
from EnableWebSecurity:
Add this annotation to an #Configuration class to have the Spring Security configuration defined in any WebSecurityConfigurer or more likely by extending the WebSecurityConfigurerAdapter base class and overriding individual methods:
The context
REST API implemented as Spring boot 1.5.3 project without #EnableWebMvc
The objective
For each API call create a UUID string and inject it into controller methods for audit purposes (the UUID is used in response body and for logging). Should be used as follows:
#PostMapping("/reserveCredits")
public ResponseEntity<Result> reserveCredits(String uuid) {
...
... new Result(uuid) ...
According to the documentation this can be achieved like so:
#Configuration
#EnableWebMvc
public class MyWebMvcConfig extends WebMvcConfigurerAdapter {
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new MyCustomArgumentResolver());
}
}
The problem
My whole project uses only REST controllers. I'm currently not using #EnableWebMvc and I don't want to introduce it now due to possible conflicts with my existing configuration. When I try using ...
#Autowired
private RequestMappingHandlerAdapter requestMappingHandlerAdapter;
... in my #Configuration bean I get BeanCreationException: Error creating bean with name 'defaultServletHandlerMapping' due to ServletContext is required.
The questions
How does Spring boot register its default argument resolvers without #EnableWebMvc?
Can I add custom argument resolver without #EnableWebMvc?
Is using #EnableWebMvc highly recommendable and I should retrofit it into my code?
Should I go for alternative solution?
The alternatives
Invasive AOP that overrides method parameter value
HandlerInterceptor that adds the uuid to request parameters and also updates response body
The answer based on the comment by #M. Denium:
Yes it's possible without #EnableWebMvc. Configuration via extension of WebMvcConfigurerAdapter does not have risk of regression impact. RequestMappingHandlerAdapter doesn't have to be autowired into the config class.
#Configuration
public class MyWebMvcConfig extends WebMvcConfigurerAdapter {
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new MyCustomArgumentResolver());
}
}
These two classes:
WebSecurityConfigurerAdapter
GlobalAuthenticationConfigurerAdapter
seem to do the same thing to me. They both provide different methods configure(...) to customize WebSecurity, such as to configure UserDetailsService. In some examples found on the internet, I saw that both classes are extended (like this one, http://ryanjbaxter.com/2015/01/06/securing-rest-apis-with-spring-boot/):
#Configuration
class WebSecurityConfiguration extends GlobalAuthenticationConfigurerAdapter {...}
and
#EnableWebSecurity
#Configuration
class WebSecurityConfig extends WebSecurityConfigurerAdapter {...}
but in some example, only WebSecurityConfigurerAdapter was needed (extended).
I am not sure about the difference between the two? What one can do that the other cannot? Or if they are both needed, then which of them is for what aspect of Spring security?
The only difference I've seen is that #EnableWebSecurity is often annotated above class that extends WebSecurityConfigurerAdapter, but not in the class that extends GlobalAuthenticationConfigurerAdapter
=============EXPERIMENT==================
I tried deleting the class that extends GlobalAuthenticationConfigurerAdapter, and carrying the code related to UserDetailsService to the class that extends WebSecurityConfigurerAdapter (See the link above for the actual code), and it still works.
Basically WebSecurityConfigurerAdapter is used to create the FilterChainProxy
refer to this docs as to GlobalAuthenticationConfigurerAdapter is used as SecurityConfigurerthat can be used to easily build in memory authentication, LDAP authentication, JDBC based authentication, adding UserDetailsService, and adding AuthenticationProvider's. refer to this docs hope this helps!
This should be really simple but i cannot figure how to add ProtobufHttpMessageConverter for Spring Controllers while keeping default HttpMessageConverters.
I have setup client side (RestTemplate) but for every request i send there is error 415: content not supported.
Every example i have found so far refers to either Spring Boot or XML configuration, however neither of these work for me.
In the
answer about similar issue,
extending the WebMvcConfigurerAdapter apparently removes default handlers.
It is stated to extend WebMvcConfigurationSupport to keep default handlers, but given implementation doesn't work for Spring 4x as method call super.addDefaultHttpMessageConverters(); requires List of converters.
I have tried variantions on theme but neither seems to work:
#EnableWebMvc
#Configuration
#ComponentScan
public class RestServiceConfiguration extends WebMvcConfigurationSupport {
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new ProtobufHttpMessageConverter());
// getMessageConverters().add(new ProtobufHttpMessageConverter());
// super.configureMessageConverters(getMessageConverters());
super.addDefaultHttpMessageConverters(converters);
}
}
Could somebody help me to add ProtobufHttpMessageConverter while keeping default converters, without xml configuration ?
With your approach you could make it work. However due to the fact that you extended WebMvcConfigurationSupport and used #EnableWebMvc is isn't working. You are basically configuring web support twice now, as #EnableWebMvc is importing WebMvcConfigurationSupport (actually DelegatingWebMvcConfiguration).
To make your current setup work remove the #EnableWebMvc annotation.
#Configuration
#ComponentScan
public class RestServiceConfiguration extends WebMvcConfigurationSupport {
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new ProtobufHttpMessageConverter());
super.addDefaultHttpMessageConverters(converters);
}
}
However there is a better way, instead of extend WebMvcConfigurationSupport you should extend WebMvcConfigurerAdapter and implement the extendMessageConverters method instead of the configureMessageConverters.
#EnableWebMvc
#Configuration
#ComponentScan
public class RestServiceConfiguration extends WebMvcConfigurerAdapter {
#Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new ProtobufHttpMessageConverter());
}
}
Note: The extendMessageConverters method has been added in Spring 4.1.3 for earlier versions use the first method!
I am trying to set up a method security annotation using #Secured("ADMIN") (without any XML, only java config, Spring Boot). But access via roles does not work.
Security Config:
#Configuration
#EnableWebSecurity
public class AppSecurityConfiguration extends WebSecurityConfigurerAdapter{
.....
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/api/**").fullyAuthenticated().and()
.addFilterBefore(tokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
.....
}
I want restrict access to the method of the controller:
#RestController
#RequestMapping("/api/groups")
public class GroupController {
#Autowired
private GroupService groupService;
#Secured("ADMIN")
#RequestMapping
public List<Group> list() {
return groupService.findAll();
}
}
Restrict access by the url is working, with:
.antMatchers("/api/**").hasAuthority("ADMIN")
Maybe I forgot to specify that I want restrict by roles?
UPD:
By the rules, At what layer must be #PreAuthorize("hasRole('ADMIN')") in Controller layer or in Service layer?
Kindly add this
#EnableGlobalMethodSecurity(securedEnabled = true)
This element is used to enable annotation-based security in your application (by setting the appropriate attributes on the element), and also to group together security pointcut declarations which will be applied across your entire application context specifically for #Secured.
Hence your code should look like this
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class AppSecurityConfiguration extends WebSecurityConfigurerAdapter{..
I know this thread is quite old and my answer alludes to portions of the answers by various people in this thread; but here is a list combined list of pitfalls and answers:
When using #Secured, and the role name is (e.g.) ADMIN; this means an annotation of #Secured("ROLE_ADMIN").
WebSecurityConfigurerAdapter must have #EnableGlobalMethodSecurity(securedEnabled = true)
As with most Spring related proxies, make sure that the class and the secured methods are not in any way final. For Kotlin this means "open" every method as well as the class.
When the class and its methods are virtual ("open"), then there is no implied need for an interface.
Here is part of a working Kotlin example:
#RestController
#RequestMapping("api/v1")
open class DiagnosticsController {
#Autowired
lateinit var systemDao : SystemDao
#RequestMapping("ping", method = arrayOf(RequestMethod.GET))
#Secured("ROLE_ADMIN")
open fun ping(request : HttpServletRequest, response: HttpServletResponse) : String { ...
}
and
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
open class WebSecurityConfig : WebSecurityConfigurerAdapter() {
Regards
There may be many reasons for which method security on a controller does not work.
First because it is never cited as example in Spring Security manual ... joking but it may be tricky to take Spring tools where they do not want to go.
More seriously, you should enable method security as already said by #Mudassar. The manual says :
We can enable annotation-based security using the #EnableGlobalMethodSecurity annotation on any #Configuration instance. For example, the following would enable Spring Security’s #Secured annotation.
#Configuration
#EnableGlobalMethodSecurity(securedEnabled = true)
public class MethodSecurityConfig {
// ...
}
Note that Mudassar's answer is correct till here.
But method security is based on AOP, which by default uses JDK proxying on interfaces. That's the reason why all examples applies method security on the service layer, because the service classes are normally injected in controllers as interfaces.
You can of course use it on controller layer, but :
either all your controllers implement interfaces for you all #Secured annotated methods
or you must switch to class proxying
The rule that I try to follow is :
if I want to secure an URL, I stick to HTTPSecurity
if I need to allow finer grained access, I add security at service layer
This issue was solved.
I add #EnableGlobalMethodSecurity(prePostEnabled = true)
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class AppSecurityConfiguration extends WebSecurityConfigurerAdapter{
}
And in controller i changed #Secured("ADMIN") to #PreAuthorize("hasRole('ADMIN')")
I want to share my decision, may be it will be helpful.
Used spring mvc + spring security, version 4.2.9.RELEASE
For example, i have a Service with method annotated #Secured
#Secured("ACTION_USER_LIST_VIEW")
List<User> getUsersList();
But, it didn't work, because GlobalMethodSecurityConfiguration has inside method.
protected AccessDecisionManager accessDecisionManager()
in which initialized the new RoleVoter() with default rolePrefix = "ROLE_"; (this makes it impossible to use beans to set your rolePrefix) that give to us not working annotations, because RoleVoter expects annotation value which starts with 'ROLE_'
For resolving this problem i override GlobalMethodSecurityConfiguration like this
#Configuration
#EnableGlobalMethodSecurity(securedEnabled = true)
public class AppMethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {
#Override
protected AccessDecisionManager accessDecisionManager() {
List<AccessDecisionVoter<? extends Object>> decisionVoters = new ArrayList<>();
ExpressionBasedPreInvocationAdvice expressionAdvice = new ExpressionBasedPreInvocationAdvice();
expressionAdvice.setExpressionHandler(getExpressionHandler());
decisionVoters.add(getRoleVoter());
decisionVoters.add(new AuthenticatedVoter());
return new AffirmativeBased(decisionVoters);
}
private RoleVoter getRoleVoter() {
RoleVoter e = new RoleVoter();
e.setRolePrefix("");
return e;
}
}
You need to use #secured(ROLE_ADMIN) instead of #secured(ADMIN). You are required to write "ROLE_" infront of your role name. Please find the example mentioned below which is making sure only a user with Admin role can access list() method.
#RestController
#RequestMapping("/api/groups")
public class GroupController {
#Autowired
private GroupService groupService;
#Secured("ROLE_ADMIN")
#RequestMapping
public List<Group> list() {
return groupService.findAll();
}
}
Maybe you should register your AppSecurityConfiguration to same context as WebMvcConfig (that extends WebMvcConfigurerAdapter).
AnnotationConfigWebApplicationContext mvcContext = new AnnotationConfigWebApplicationContext();
mvcContext.register(WebMvcConfig.class, SecurityConfig.class);