In my Spring Boot application based on version 1.3.0.BUILD-SNAPSHOT, I have the static resources (images, css, js) in the static folder under resources.
I see some examples related to security configuration like the following:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(final WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/static/**");
}
}
Is that example correct?
What should be the effect?
How to verify that it works (e.g. doing a request to localhost:8080/something?
What cool things can I do with WebSecurity?
Your example means that Spring (Web) Security is ignoring URL patterns that match the expression you have defined ("/static/**"). This URL is skipped by Spring Security, therefore not secured.
Allows adding RequestMatcher instances that should that Spring Security should ignore. Web Security provided by Spring Security (including the SecurityContext) will not be available on HttpServletRequest that match. Typically the requests that are registered should be that of only static resources. For requests that are dynamic, consider mapping the request to allow all users instead.
See WebSecurity API documentation for more info.
You can have as many URL patterns secured or unsecured as you want.
With Spring Security you have authentication and access control features for the web layer of an application. You can also restrict users who have a specified role to access a particular URL and so on.
Read the Spring Security reference for more details:
http://docs.spring.io/spring-security/site/docs/current/reference/html/
Ordering Priority of URL Patterns
When matching the specified patterns against an incoming request, the matching is done in the order in which the elements are declared. So the most specific matches patterns should come first and the most general should come last.
There are multiple children to the http.authorizeRequests() method
each matcher is considered in the order they were declared.
Patterns are always evaluated in the order they are defined. Thus it is important that more specific patterns are defined higher in the list than less specific patterns.
Read here for more details:
http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#filter-security-interceptor
Example 1
General use of WebSecurity ignoring() method omits Spring Security and none of Spring Security’s features will be available.
WebSecurity is based above HttpSecurity
(in an XML configuration you can write this: <http pattern="/resources/**" security="none"/>).
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/resources/**")
.antMatchers("/publics/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/publics/**").hasRole("USER") // no effect
.anyRequest().authenticated();
}
WebSecurity in the above example lets Spring ignore /resources/** and /publics/**. Therefore the .antMatchers("/publics/**").hasRole("USER") in HttpSecurity is unconsidered.
This will omit the request pattern from the security filter chain entirely.
Note that anything matching this path will then have no authentication or authorization services applied and will be freely accessible.
Example 2
Patterns are always evaluated in order. The below matching is invalid because the first matches every request and will never apply the second match:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/**").hasRole("USER")
.antMatchers("/admin/**").hasRole("ADMIN"):
}
Well in the code you shared, if you had your static files i.e. CSS/JS etc in a folder called static then all your static resources will be added to the page whereas if you left out
web.ignoring()
.antMatchers("/static/**");
none of your static resources will be loaded.
Spring Security is extremely powerful, Spring has great documentation so you should just go read about it fully appreciate/understand it.
Here is a
link
Related
I have a Spring Boot REST API. Due to a security policy I need to have CSRF protection enabled for endpoints accessed by the browser. However, this API will also be accessed by non-browsers. Is there a way I can create two sets of endpoints, one accessible by browsers only with CSRF enabled and the other accessible by non-browsers only with CSRF disabled?
When you configure your CSRF protection using the DSL, like this http.csrf()... you can tell which requests you want the CSRF protection to be applied by passing a RequestMatcher, like so:
http.csrf(csrf -> csrf.requireCsrfProtectionMatcher(new MyBrowserRequestMatcher()));
And your implementation of RequestMatcher could verify if the HttpServletRequest contains the header X-Requested-With: XMLHttpRequest or check the User-Agent.
Just keep in mind that the headers can be changed and you have no guarantee that the request actually come from a browser or non-browser app.
I think you could have separate URL bases for the browser requests and API requests.
For example, you could have all the endpoints that are to be queried by non-browsers under /api/... and in your SpringBootSecurityConfiguration class and configure(HttpSecurity http) method you could conditionally disable CSRF with http.csrf().disable(); if the pattern matches (great tutorial can be found here)
Edit: here is another answer that might be useful.
As #ferrouskid said, I created two URL one for browsers and other for non-browsers:
In spring security config:
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.csrf().ignoringAntMatchers("/withoutCsrf/**")
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.cors().disable();
//complete your configuration }
In controller:
#Controller
#RequestMapping({"books","withoutCsrf/books"})
public class BookController {}
Currently in my SecurityConfig.java class file where I define my KeycloakWebSecurityConfigurerAdapter I want to define so that every GET request can be done by two different roles. But only one role can do the other types of HTTP requests (POST, PUT, PATCH etc). How can this be achieved in my code below:
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.authorizeRequests()
.antMatchers(HttpMethod.GET).hasAnyRole("user", "admin")
.anyRequest().hasRole("admin");
}
What happens is that when trying to do POST request I get access denied 403. GET requests works fine. Any ideas?
You should disable csrf on your configure method :
public void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.csrf().disable().authorizeRequests().anyRequest().authenticated();
}
}
You should not use KeycloakWebSecurityConfigurerAdapter nor anything else from Keycloak libs for Spring, it is deprecated.
Instead, you can follow this tutorial which proposes two solutions based on:
spring-boot-starter-oauth2-resource-server which requires quite some Java conf
spring-addons-webmvc-jwt-resource-server which enables to configure most of security from properties (way simpler than preceding)
All tutorials linked here show how to map Keycloak roles to spring-security authorities (and will keep CSRF protection enabled, even for stateless resource-servers).
I have experienced a strange problem while trying to configure HTTP security by using WebSecurityConfigurerAdapter. Here is the full configuration class I tried so far:
#Slf4j
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
#ConditionalOnProperty(name = "module.client.security.enabled")
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${management.endpoints.web.base-path}")
private String managementEndpoint;
#Autowired
private List<ModuleProperties> moduleProperties;
#Override
public void configure(WebSecurity web) {
web.ignoring()
.antMatchers(this.managementEndpoint + "/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().disable();
http.formLogin().disable();
// FIXME also doesn't work because of the later http.requestMatchers().antMatchers() calls
http.authorizeRequests()
.antMatchers("/**").anonymous();
http.requestMatchers()
.antMatchers("/app/**")
.and()
.addFilterBefore(new ClientResolveFilter(), FilterSecurityInterceptor.class);
}
What I would like to do is actually enabling anonymous authentication for all endpoints — to prevent NullPointerExceptions when operating on SecurityContextHolder — Plus, enabling/adding a custom filter to only a subset or different endpoint path which is /app/** in this case.
I expected the above code would work but what exactly happens is the AnonymousAuthenticationFilter disabled for all and both filters only work for the path /app/** only.
If I remove the http.requestMatchers().antMatchers("/app/**") part, then AnonymousAuthenticationFilter works for all paths as normal. I suspect that the second .antMatchers("/app/**") call kinda replaces the former one or substitutes it implicitly which doesn't make sense to me, but I could be wrong.
I tried diving into the source but still confused and cannot find a clean solution to make it work as my expectation. Any ideas and help will be appreciated.
Cheers!
EDIT: I'm using Spring Boot 2.5.2 and the Spring Security version is 5.5.1
The addFilterBefore (and other addFilter*) method will add the filter to the filter chain which apply to all request. If you want the filter to apply to certain requests only, then you have to check inside the filter (eg. using HttpServletRequest.getgetRequestURI() to check the url).
After #Dickson's advice, I found a special bean called FilterRegistrationBean provided by spring boot.
Thus, I configured it as a bean which applies a specific servlet filter to only configured paths:
#Bean
public FilterRegistrationBean<ClientResolveFilter> clientResolveFilter(){
final FilterRegistrationBean<ClientResolveFilter> frb = new FilterRegistrationBean<>();
frb.setFilter(new ClientResolveFilter());
frb.addUrlPatterns("/app/*");
return frb;
}
This solution worked perfectly for my requirement.
Pay attention to that the path string is not an ant matcher now — must be written with single /app/* instead of double /app/** — it's actually the pattern when we manually configured web.xml file like in the old days :)
I just need to understand something in Spring Security Configuration. Using the example below...
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.and()
.authorizeRequests().antMatchers("/secret/**").authenticated()
.and()
.authorizeRequests().antMatchers("/**").permitAll();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**");
}
}
What is the purpose of configure(WebSecurity web) method?
Can't I just add /resources/** in the configure(HttpSecurity http) method in this line .authorizeRequests().antMatchers("/**", "/resources/**").permitAll();
Shouldn't it work the same i.e. permitting all requests to /resources/** without any authentication?
General use of WebSecurity ignoring() method omits Spring Security and none of Spring Security’s features will be available.
WebSecurity is based above HttpSecurity.
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/resources/**")
.antMatchers("/publics/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/publics/**").hasRole("USER") // no effect
.anyRequest().authenticated();
}
WebSecurity in the above example lets Spring ignore /resources/** and /publics/**. Therefore the .antMatchers("/publics/**").hasRole("USER") in HttpSecurity is unconsidered.
This will omit the request pattern from the security filter chain entirely.
Note that anything matching this path will then have no authentication or authorization services applied and will be freely accessible.
configure(HttpSecurity) allows configuration of web-based security at a resource level, based on a selection match - e.g. The example below restricts the URLs that start with /admin/ to users that have ADMIN role, and declares that any other URLs need to be successfully authenticated.
configure(WebSecurity) is used for configuration settings that impact global security (ignore resources, set debug mode, reject requests by implementing a custom firewall definition). For example, the following method would cause any request that starts with /resources/ to be ignored for authentication purposes.
Let's consider the below code, we can ignore the authentication for the endpoint provided within antMatchers using both the methods.
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/login", "/register", "/api/public/**");
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/login", "/register", "/api/public/**").permitAll()
.anyRequest().authenticated();
}
configure(WebSecurity web)
Endpoint used in this method ignores the spring security filters, security features (secure headers, csrf protection etc) are also ignored and no security context will be set and can not protect endpoints for Cross-Site Scripting, XSS attacks, content-sniffing.
configure(HttpSecurity http)
Endpoint used in this method ignores the authentication for endpoints used in antMatchers and other security features will be in effect such as secure headers, CSRF protection, etc.
When you use HttpSecurity and try to permitAll() requests. Your requests will be allowed to be accessed from the Spring Security Filter Chain. This is costly as there will be requests other requests which would also come into this filter chain which needs to be allowed or disallowed based on Authentication/Authorization.
HttpSecurity.authorizeRequests().antMatchers("/**", "/resources/**").permitAll();
But when you use, any requests to resources will completely by pass the Spring Security Filter Chain all together. It is safe because you don't need any Authentication/Authorization to be in place to see an image or read a javascript file.
WebSecurity.ignoring().antMatchers("/resources/**");
I want to contribute to the great answers by including some code. There are three beans that are super important in Spring Security. based on their types they are DelegatingFilterProxy, FilterChainProxy and SecurityFilterChain.
DelegatingFilterProxy delegates the job of filtering requests to a bean of type FilterChainProxy which its name is springSecurityFilterChain, and FilterChainProxy is configured like this:
#Bean(
name = {"springSecurityFilterChain"}
)
public Filter springSecurityFilterChain() throws Exception {
boolean hasConfigurers = this.webSecurityConfigurers != null && !this.webSecurityConfigurers.isEmpty();
if (!hasConfigurers) {
WebSecurityConfigurerAdapter adapter = (WebSecurityConfigurerAdapter)this.objectObjectPostProcessor.postProcess(new WebSecurityConfigurerAdapter() {
});
this.webSecurity.apply(adapter);
}
return (Filter)this.webSecurity.build();
}
springSecurityFilterChain (or FilterChainProxy) itself has a list of SecurityFilterChain. SecurityFilterChain itself has a list of Filter instances that do the actual logic.
Every time we extend WebSecurityConfigurerAdapter and override the configure(HttpSecurity httpSecurity) method, we actually created a SecurityFilterChain that is going to be used by springSecurityFilterChain
How springSecurityFilterChain selects the appropriate SecurityFilterChain from the list? based on the boolean matches(HttpServletRequest request) method that is defined in the SecurityFilterChain interface.
So HttpSecurity is used to create a customized SecurityFilterChain.
Now when WebSecurity actually comes into play? WebSecurity actually allow us to customize springSecurityFilterChain(or FilterChainProxy). take a look at how springSecurityFilterChain is created.
It is the performBuild method of WebSecurity that is called for creating springSecurityFilterChain bean.
#Override
protected Filter performBuild() throws Exception {
Assert.state(
!securityFilterChainBuilders.isEmpty(),
() -> "At least one SecurityBuilder<? extends SecurityFilterChain> needs to be specified. "
+ "Typically this done by adding a #Configuration that extends WebSecurityConfigurerAdapter. "
+ "More advanced users can invoke "
+ WebSecurity.class.getSimpleName()
+ ".addSecurityFilterChainBuilder directly");
int chainSize = ignoredRequests.size() + securityFilterChainBuilders.size();
List<SecurityFilterChain> securityFilterChains = new ArrayList<>(
chainSize);
for (RequestMatcher ignoredRequest : ignoredRequests) {
securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
}
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
securityFilterChains.add(securityFilterChainBuilder.build());
}
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
if (httpFirewall != null) {
filterChainProxy.setFirewall(httpFirewall);
}
filterChainProxy.afterPropertiesSet();
Filter result = filterChainProxy;
if (debugEnabled) {
logger.warn("\n\n"
+ "********************************************************************\n"
+ "********** Security debugging is enabled. *************\n"
+ "********** This may include sensitive information. *************\n"
+ "********** Do not use in a production system! *************\n"
+ "********************************************************************\n\n");
result = new DebugFilter(filterChainProxy);
}
postBuildAction.run();
return result;
}
As you can see when Spring wants to registers SecurityFilterChain into springSecurityFilterChain bean for each web.ignoring().... Spring is going to add a DefaultSecurityFilterChain which is a custom implementation of SecurityFilterChain into the beginning of the list.
When a request comes along springSecurityFilterChain is going to check its list of SecurityFilterChain in order to delegate the filtering job to that SecurityFilterChain. springSecurityFilterChain is going to call match method of each SecurityFilterChain. if the request URL starts with "/resources/**" in your case Spring delegates the job of filtering request to an instance of DefaultSecurityFilterChain in the beginning of the list and our custom SecurityFilterChains which is added by this line:
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : securityFilterChainBuilders) {
securityFilterChains.add(securityFilterChainBuilder.build());
}
completely ignored.
configure(HttpSecurity) : It allows configuring web based security for specific http requests. It is used for configuration of web based security at a resource level, based on a selection match.
configure (WebSecurity) : Allows adding RequestMatcher instances that Spring Security should ignore.
My security config class (which inherits from WebSecurityConfigurerAdapter) has a method like the following.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/restaurant/**").access("hasRole('ROLE_USER')")
.and()
.formLogin();
}
However I'd rather use #PreAuthorize on my controllers instead. If I remove the method everything requires auth. What should my method look like so everything is available and access is only determined by PreAuthorize?
As has been already stated, it is not very common to use method level security to secure controller methods but rather to secure methods with business logic. And even if you need to perform authorization based on request attributes, it should be possible to achieve this with URL based security and web security expressions.
Available expressions are defined by WebSecurityExpressionRoot class, an instance of which is used as the expression root object when evaluation web-access expressions. This object also directly exposed the HttpServletRequest object under the name request so you can invoke the request directly in an expression.
Here you can find more details on when to use URL based security and when method level security.
It is rather uncommon to use #PreAuthorize on controller methods, but there may me use cases, if the decision depends on request parameters ...
If you do not want to do any authorization at the request level, you can simply have :
#Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin();
}
You only declare a form login, and no request security. But do not forget that request security uses less resources than method security.
Instead of .access("hasRole('ROLE_USER')"), try .access("permitAll"). Note that for request mappings that doesn't have a #PreAuthorize, everyone will be given access.