What is the purpose of withDefaults() is spring - spring

As per spring docs it says:
Returns a Customizer that does not alter the input argument.
But what does this exactly mean?
For e.g. what is the result if I use it like this:
#EnableWebSecurity
#Configuration
public class SecurityConfiguration {
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeHttpRequests((authz) -> authz.anyRequest().authenticated())
.httpBasic(withDefaults());
return http.build();
}
}

According to java doc, we have
public HttpSecurity httpBasic(Customizer<HttpBasicConfigurer<HttpSecurity>> httpBasicCustomizer) throws Exception {
httpBasicCustomizer.customize((HttpBasicConfigurer)this.getOrApply(new HttpBasicConfigurer()));
return this;
}
The parameter is of type Customizer<HttpBasicConfigurer<HttpSecurity>> which can be used as a lambda function to pass the changes that you want to be applied in the configurer that you provide to the httpBasic method. This method also returns the built HttpSecurity so the configurer is already applied, when the httpBasic(....) method ends.
A relative to your example let's name it Example 1
httpSecurity.httpBasic(httpSecurityHttpBasicConfigurer -> {
httpSecurityHttpBasicConfigurer.realmName("My Realm");
httpSecurityHttpBasicConfigurer.authenticationEntryPoint(new YourAuthEntryClass());
})
.authorizeRequests().and().csrf().disable().authorizeHttpRequests((authz) -> authz.anyRequest().authenticated());
So the configurer is going to apply the realmName and authenticationEntryPoint to the httpSecurity via the lambda function which you have provided.
In case you didn't want to make any modifications to the httpSecurity inside the httpBasic method you could as well have done the
httpSecurity.httpBasic(httpSecurityHttpBasicConfigurer -> {} )
.authorizeRequests().and().csrf().disable().authorizeHttpRequests((authz) -> authz.anyRequest().authenticated());
And Spring just to avoid writting this nonsese httpSecurityHttpBasicConfigurer -> {} as parameter has also gave you this with static withDefaults method in the functional interface Customizer. Keep in mind that this Customizer is just a generic Interface and will be used in other places as well not just here.
#FunctionalInterface
public interface Customizer<T> {
void customize(T t);
static <T> Customizer<T> withDefaults() {
return (t) -> {
};
}
}
So to avoid the
httpSecurity.httpBasic(httpSecurityHttpBasicConfigurer -> {} )....
you can as well write
httpSecurity.httpBasic(Customizer.withDefaults())....
meaning no configuration will be applied inside the httpBasic method in the httpSecurity object.
Keep in mind however,
that you also have available another method from Java Docs
public HttpBasicConfigurer<HttpSecurity> httpBasic() throws Exception {
return (HttpBasicConfigurer)this.getOrApply(new HttpBasicConfigurer());
}
which could be used as well and this doesn't return the modified httpSecurity object but an HttpBasicConfigurer instead which could be written as to modify the httpSecurity using the builder pattern.
So the Example 1 could now be written as
httpSecurity.httpBasic()
.realmName("My Realm")
.authenticationEntryPoint(new YourAuthEntryClass())
.and().authorizeRequests().and().csrf().disable()
.authorizeHttpRequests((authz) -> authz.anyRequest().authenticated());
And in case you didn't want to apply any basic http configuration changes to the httpSecurity you could just skip the methods realmName and authenticationEntryPoint from the builder pattern, and it would have provide you again with the default basic configurations for httpSecurity
httpSecurity.httpBasic()
.and()
.authorizeRequests().and().csrf().disable()
.authorizeHttpRequests((authz) -> authz.anyRequest().authenticated());
which would be exactly the same with the version of
httpSecurity.httpBasic(Customizer.withDefaults())
.authorizeRequests().and().csrf().disable()
.authorizeHttpRequests((authz) -> authz.anyRequest().authenticated());

use default setting.
for ex,
whom to authorize
how to authorize

Related

what's the proper way to add Spring Security filter only for specific URL? [duplicate]

This question already has answers here:
Filter invoke twice when register as Spring bean
(3 answers)
Closed 5 months ago.
I am trying to add custom filter to only specific URL, however the filter get applied to every request, regardless of URL and method, does anybody know the proper way to fix this using latest from Spring Security, i.e. not using WebSecurityConfigurerAdapter, because it is going to be deprecated.
There are many similar questions here, but they either do not work for me, or they use the "old" approach (like this and this and many other), or both.
I have number of endpoints exposed that all follow the pattern: /api/** however I need to provide some authentication for a specific endpoint: /api/some/url and a particular method (GET in this case), how do I do this properly?
NOTE: the endpoint URLs are all under /api/* (should they be called nested?)
My security configuration looks like this:
#EnableWebSecurity
public class SecurityConfig {
private MyFilter myFilter;
public SecurityConfig(MyFilter pif) {
myFilter = pif;
}
/**
* Handling AuthZ & AuthN for most APIs. No AuthZ & AuthN.
*/
#Bean
#Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain defaultSecurity(HttpSecurity http) throws Exception {
http.requestMatchers((requests) ->
requests.antMatchers("/"))
.authorizeHttpRequests((authorize) -> authorize.anyRequest()
.permitAll());
return http.build();
}
/**
* Handling AuthZ & AuthN for GET /api/some/url.
*/
#Bean
public SecurityFilterChain keyApiSecurity(HttpSecurity http) throws Exception {
http.requestMatchers((requests) -> requests
.antMatchers(HttpMethod.GET, "/api/some/url").and())
.addFilterBefore(myFilter,
BasicAuthenticationFilter.class)
.authorizeHttpRequests((authorize) -> authorize.anyRequest().permitAll());
return http.build();
}
}
When you expose some GenericFilter implementation as a bean in spring-boot, it automatically puts it in a common filter chain for any request, because it doesn't know if it's a security filter or not - it could be a logging filter or anything else.
So this filter bean will be executed regardless of spring-security.
Your defaultSecurity filter chain doesn't have this custom filter, so MyFilter will be executed after spring security filter chain due to the order.
At the same time, keyApiSecurity filter chain sets this custom filter before BasicAuthenticationFilter, so it will be executed there, and will not be executed the second time, because basic doFilter() implementation of OncePerRequestFilters method checks whether the request was already filtered by the filter.
So, if you want your filter to work only as a security filter, you should not expose it as a bean, and you should set it in a security filter chain like this:
.addFilterBefore(new MyFilter(), BasicAuthenticationFilter.class)
Also you should think about setting the lowest priority for a "default" security filter chain, because if it's selected first - other security filter chains will be totally ignored. So I think some specific filter security chains should have higher priority.
EDIT:
If you can't set your security filter with the new operator because you rely on bean injection in this Filter implementation, you can override shouldNotFilter(HttpServletRequest request) method of OncePerRequestFilter, for example like this:
#Component
public class MyFilter extends OncePerRequestFilter {
private final RequestMatcher uriMatcher =
new AntPathRequestMatcher("/api/some/url", HttpMethod.GET.name());
// some bean injection...
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
// filter logic here...
}
#Override
protected boolean shouldNotFilter(HttpServletRequest request) {
RequestMatcher matcher = new NegatedRequestMatcher(uriMatcher);
return matcher.matches(request);
}
}
Then it will filter only matched requests, and putting it into the security filter chain will set its order.

How to remove AbstractHttpConfigurer from default HttpSecurity

I'm creating an internal lib, and I want to perform some autoconfiguration that involve removing security customizers that are added by default, for example LogoutConfigurer, as if it was not part of the default HttpSecurity prototype bean:
#Bean
public SecurityFilterChain authFilter(HttpSecurity http) throws Exception {
return http
// I want to make this unnecessary by not being part of the (adjusted) HttpSecurity
//.logout().disable()
.authorizeRequests()
.mvcMatchers("/secured").hasRole("ROLE")
.anyRequest().denyAll()
.and()
.build(); // (1)
}
The way to customize security in a cross-cutting way like that seems to be implementing AbstractHttpConfigurer beans, yet those are only triggered as part of the HttpScurity#build() method that generates a SecurityFilterChain as part of the main application security configuration code (marked as (1) above). That is too late, as my bean would need to be undoing the configuration done by another customizer just before it, which is complicated and maybe not possible (removing filters, etc).
The only alternative I found so far seems to be overriding the AbstractHttpConfigurer#setBuilder(B) to manipulate the given builder (the HttpSecurity object) into removing the customizers, given that this method is called right after HttpSecurity is created and before making it accessible as a prototype bean:
public class MyHttpConfigurer extends AbstractHttpConfigurer<MyHttpConfigurer, HttpSecurity> {
#Override
public final void setBuilder(HttpSecurity http) {
super.setBuilder(http);
try {
// Do this and/or whatever else you want to do
http.logout().disable();
} catch (Exception e) {
throw new RuntimeException("It failed", e);
}
}
#Override
public final void configure(final HttpSecurity http) throws Exception {}
}
It works as I want, but that looks unstable, abusing the API, and feels it might break without warning. I also found no way to replace the default HttpSecurity prototype builder as it is not conditional.
Is there a cleaner or documented way to achieve this?
I think that the cleanest approach for you to achieve your functionality would be to provide a BeanPostProcessor.
e.g.
#Configuration(proxyBeanMethods = false)
public class CustomSecurityConfiguration {
private static final String HTTP_SECURITY_DEFAULT_BEAN_NAME = "org.springframework.security.config.annotation.web.configuration.HttpSecurityConfiguration.httpSecurity";
#Bean
public static BeanPostProcessor httpSecurityBeanPostProcessor() {
return new BeanPostProcessor() {
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof HttpSecurity && HTTP_SECURITY_DEFAULT_BEAN_NAME.equals(beanName)) {
HttpSecurity http = (HttpSecurity) bean;
try {
http.logout().disable();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return bean;
}
};
}
}
This is really similar to the example you've proposed. In any case, I do not thing that it is abusing the API since it allows for accessing the HttpSecurity

Springboot Security how to get role inside "Principal"

I have this fonts
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login").permitAll()
.antMatchers("/**").hasRole("myRole")
.anyRequest().authenticated()
.and()
.oauth2Login(Customizer.withDefaults());
}
I know "hasRole" looks at permissions inside securityContext.authentication.authorities but is there a way for "hasRole" to another place?
My roles are inside securityContext.authentication.principal.attributes.role :
https://i.stack.imgur.com/yl6bC.png
I even created an endpoint that returns if the role I want exists, but I don't know how it would help me inside the "configure" method:
public boolean isAllowed() {
UserAttributesDTO user = getUser();
if (nonNull(user)) {
return user.getRoles().stream().anyMatch(role -> role.equals("admin_cadastro_externo"));
}
return false;
}
Since you're using the built-in oauth2Login() and your principal is a DefaultOidcUser, you want to use .hasAuthority("myAuthority") instead. You can influence what authorities are present by providing a GrantedAuthoritiesMapper as an #Bean.
If you must access the attributes in place, you may be interested in .access(...) using an #Bean reference.
See Referring to Beans in Web Security Expressions. In that case, you should use the authentication passed to the method instead of the SecurityContextHolder to access the authentication.

Spring Security add/remove antMatchers and roles dynamically at runtime

Is there a way to change this place dynamically? In other words, invoke the method that adds or removes antMatchers or override completely. map roles, etc.
#EnableWebSecurity
public class WebSecurityConfigAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
//Change this configs dynamically at runtime
}
}
In Spring Security version 5.6, which is in 5.6.0.M3 as of now, you can create an AuthorizationManager bean and define place your rules anywhere you want, like so:
#Autowired
private MyCustomAuthorizationManager access;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().access(access);
}
Or even better, you can define a SecurityFilterChain bean and make use of method parameter injection, instead of extending WebSecurityConfigurerAdapter:
#Bean
SecurityFilterChain app(HttpSecurity http, MyCustomAuthorizationManager access) throws Exception {
...
http.authorizeRequests().access(access);
...
return http.build();
}
There is a great presentation showing how to do this.
I ended up with this solution. The solution is to close the current context and run the new one. Of course, it has the disadvantage because it causes downtime but I use a load balancer and several nodes so it's was ok for me.

AntMatcher and contextPath for API security

I have spring boot application. I have configured OAuth2 - both authorization and resource servers (separated). In the resource server (application.properties) I have:
server.servlet.context-path=/api
as well as:
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
(...)
#Override
public void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.and()
.authorizeRequests()
.antMatchers("/actuator/**", "/api-docs/**").permitAll()
.antMatchers("/api/**" ).authenticated();
}
}
The problem is that, api is not actually secured at all. Thanks to doc and #dur's answer I know that
The pattern must not contain the context path
Indeed, changing from:
.antMatchers("/api/**" ).authenticated();
to:
.antMatchers("/**" ).authenticated();
works fine. But the question is: is it possible to use context-path in this use case, instead of using /** ? I could repeat .antMatchers() for each and every controller (or use /**) but maybe there is a way to use context-path ?
Inject the property into variable and use that in code
I am also demonstrating IMHO nicer way of writing the conf with lambda where you do not need to use ".and()" and you can see scope blocks better.
Empty .requestMatchers().and() does nothing, so you can remove it.
which would also be more obvious in lambda notation :
.requestMatchers(matchers -> matchers)
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
(...)
#Value("${server.servlet.context-path:''}")
private String contextPath; // <<<< I am the path !
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorize -> authorize
.antMatchers(contextPath + "/actuator/**", "/api-docs/**").permitAll()
.antMatchers(contextPath + "/**" ).authenticated()
);
}
}
But if you really want to, you can also write the code the old way too.
It has no effect on using the variable.
:
http.
.authorizeRequests()
.antMatchers(contextPath + "/actuator/**", "/api-docs/**")
.permitAll()
.antMatchers(contextPath + "/**" )
.authenticated()
.and());

Resources