Configuring a filter within a WebSecurityConfigurerAdapter - spring-security

I've been having all sorts of giggles configuring some custom filters in the spring security filter chain. The primary problem surrounds a custom AbstractAuthenticationProcessingFilter. This filter requires the AuthenticationManager as well as the SessionAuthenticationStrategy. Using the adapter:
#Override
protected void configure(HttpSecurity http) throws Exception {
....
AbstractAuthenticationProcessingFilter processingFilter = new MyAuthenticationProcessingFilter();
processingFilter.setAuthenticationManager(authenticationManager());
processingFilter.setSessionAuthenticationStrategy(http.getSharedObject(SessionFixationProtectionStrategy.class));
http.addFilterBefore(processingFilter, UsernamePasswordAuthenticationFilter.class);
First, I had problems with the authenticationManager() returning null. Tracing that down showed that during the creation/initialization of the adapter and specifically the WebSecurity.init() method invoking getHttp(), this code ultimately calls AuthenticationManagerBuilder.build() which returns null at performBuild()
if(!isConfigured()) {
logger.debug("No authenticationProviders and no parentAuthenticationManager defined. Returning null.");
return null;
}
Sadly, this then flags the AuthenticationManager as initialized (to null) and forever returns the wrong result.. all this before it even gets to my adapter code! I finally figured out I could use authenticationManagerBean() instead.
But I also need to set the sessionAuthenticationStrategy. Sadly, this is also null at this time. I've noticed the 'standard' filters all use a 'Configurer' to set them up, and apparently by this time, all the beans are correctly defined. But I haven't figured out how to register my 'configurer' instead of my filter directly.
So.. how does one build a custom AuthenticationProcessingFilter with the required attributes defined within a java configuration adapter?

Well, I figured out how to setup a configurer for my custom filter. HttpSecurity.apply() takes a configurer and you can invoke that during the WebSecurityConfigurerAdapter.configure(http) method. Turns out trying to set up filters at that time is probably a bad idea.. at least filters that depend on other portions of the security infrastructure.
Inside your configurer, create and add a security filter using init(http) and configure(http). By the time the configurers are invoked, many shared objects have been defined and inserted into http, things such as the AuthenticationManager and the SessionAuthenticationStrategy instances.

Related

Spring Security Reactive WebFilterChainProxy only calling a single filter chain

I need to add security into a Webflux based app and have requirements that mean I need to add multiple filter chains. However, the current implementation of WebFilterChainProxy uses Flux.filterWhen(...) which, if I'm reading the docs correctly, will only ever return the first match in the chain.
Given the above I have three questions:-
My react knowledge is very limited, can someone confirm if my understanding of what filterWhen does is correct?
If so, can anyone suggest a way to get multiple filter chains to work in the new Spring Security 5 reactive model?
If I have misunderstood how the filterWhen method works, could any one make any suggestions why only one of my filter chains is processed?
The way I am adding filters to the chain is in multiple configuration methods annotated with #Order, similar to the code block below.
#Configration
#EnableWebFluxSecurity
public class SecurityConfig
...
#Bean
#Order(1)
SecurityWebFilterChain chain1(ServerHttpSecurity http) {
return http.httpBasic().disable()......
}
#Bean
#Order(2)
SecurityWebFilterChain chain2(ServerHttpSecurity http) {
return http.httpBasic().disable()......
}
#Bean
#Order(3)
SecurityWebFilterChain chain3(ServerHttpSecurity http) {
return http.httpBasic().disable()......
}
...
}
When I debug the application I can see that all three filters have been added to the WebFilterChainProxy but I only ever get one matched filter back. I need to find a way of returning all filters that match.
Can anyone help please?
I was able to finally resolve this by using ServerWebExchangeMatchers. My use case involved enabling Basic Authentication when accessing Spring Actuator endpoints and no authentication on other paths. I was able to accomplish this by the following code:
#Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity httpSecurity)
{
httpSecurity
.csrf().disable()
.logout().disable()
.formLogin().disable();
httpSecurity.securityMatcher(ServerWebExchangeMatchers.pathMatchers("/actuator/**"))
.httpBasic()
.and()
.authorizeExchange()
.pathMatchers("/actuator/**")
.hasRole(ACTUATOR_ADMIN_ROLE);
return httpSecurity.build();
}
I'm answering my own question(s) here in the hope it might help the underdstanding of others.
My react knowledge is very limited, can someone confirm if my understanding of what filterWhen does is correct?
Yes, my understanding here is correct, Flux.filterWhen(...) will return only the first item matched.
If so, can anyone suggest a way to get multiple filter chains to work in the new Spring Security 5 reactive model?
Multiple filter chains 'work' out-of-box in the Reactive parts of Spring Security 5, they just work in a mutually exclusive way, e.g. only a single filter will be applied given the use of Flux.filterWhen(...).
If I have misunderstood how the filterWhen method works, could any one make any suggestions why only one of my filter chains is processed?
See the answer to 2. above.
From a wider perspective, my original requirement was to have multiple chains, where more than one chain would be applied but, given the constraints already outlined, this simply wasn't possible. I had to modify what we were doing so that it worked with the framework rather than against it.
We still required multiple filter chains as security requirements were different for different flows and we ended up using the same ServerWebExchangeMatchers in a similar manner as described in #KRam answer.

Dependency injection in `ActionFiliter` vs. calling `Activator.CreateInstance()`

Requiring to sometimes use dependency injection in ActioFilter or other attributes running before or after an action API or result is inevitable. However, it is carried out through passing the type to be injected to the attribute using the typeof keyword. In order to simplify the case, when having various implementations for an interface, I have found it much simpler to manually instantiate the type than using the built-in dependency injection framework. For example:
public TestAttribute: Attribute, IActionFilter {
private Type injectionType;
public TestAttribute(Type injectionType){
...
}
...
public void OnActionExecuting(ActionExecutingContext context) {
InjectedTypeInterface injectedTypInterface = (InjectedTypeInterface) Activator.CreateInstance(injectedType, arg1, arg2, ...);
...
}
}
I want to know, from the point of view of other people here, that would this approach cause problems that using the built-in dependency injection framework would not? (Injected implementation will be always Transient in this case and not Scoped or Singleton)
I don't recommend doing the route of Activator.CreateInstance, here are some reasons why to avoid it and stick with the official way:
You'd need to pass in all instances of the parameters (i.e. of the type you want to instantiate has other dependencies) to it
The instance created this way isn't tracked by the scoped container. This also means, it won't automatically get disposed (Updated note this of course will only happen if the service implements IDisposable interface) at the end of the request and instead be disposed at some indeterminable time in future, when the GC kicks in and will keep resources open for longer then intended (i.e. holding connection or file handle open for longer then intended) unless you dispose it explicitly
Like you already recognized, you can't do so with scoped and singleton instances
For your concrete examples, there are easier ways to get a specific instance from DI - aside from the official supported ways (Filters - Dependency Injection) - you can also resolve from HttpContext, assuming you have access to it in the type of filter you are using.
For ActionFilter/IActionFilter
public void OnActionExecuting(ActionExecutingContext context) {
InjectedTypeInterface injectedTypInterface = context.HttpContext
.RequestServices.GetService<InjectedTypeInterface>();
...
}

Including Domain Object Security #PostFilter in Spring Data repositories Pageable endpoints

In my project I use Spring-Data, Spring-Data-Rest and Spring-Security.
What I need to accomplish is to implement domain object security (ACL) over these repositories. Specificaly #PostFilter over Pageable.findAll() method.
Method level security is easily implemented as outlined here.
There is also a section in docs about using security expression with #Query here.
But although I can use hasPermission(..) method inside #Query too, there is no way to include the object (SQL row) in this method - to be specific do this:
#Query("select u from #{#entityName} u where 1 = ?#{security.hasPermission(u, 'read') ? 1 : 0}")
Now I understand that this is way different than modifying the query pre-execution like this:
#Query("select m from Message m where m.to.id = ?#{ principal?.id }")
I also found the following jira issue:
https://jira.spring.io/browse/DATACMNS-293
Which I suspect that once it gets resolved there will be a solution to this, but it doesn't seem like it's going to be anytime soon.
I still need to implement this functionality and for that I would like to get your input and pointers on possible solutions.
Right now I am thinking about creating my custom annotation that will mimmick the #PostFilter one and use the same syntax but will get invoked manually inside my own BaseRepositoryImplementation. There I will get the repository interface from type and Repositories#getRepositoryInformationFor(type)#getRepositoryInterface(), find the annotation on respective method and manually invoke the security check.
Do you maybe have a different solution, or some notes about my proposed solution?
Also do you happen to know if there is any timetable on the mentioned jira issue?
One lightweight way is to do it is using the hasPermission() method and implementing your own "Permission Evaluator" at the Controller level, if that's an option for you.
#PreAuthorize("hasPermission(#employee, 'edit')")
public void editEmployee(Employee employee) {
...
}
#Component
public class PermissionEvaluatorImpl implements PermissionEvaluator {
#Override
public boolean hasPermission(Authentication auth,
Object targetDomainObject, Object permission) {
// return true if "auth" has "permission" permission for the user.
// Current-user can be obtained from auth.
}
...
}
This is described in more detail here: http://www.naturalprogrammer.com/spring-domain-object-security-logged-in-user/

Spring OAuth2 - custom "OAuth Approval" page at oauth/authorize

what is recommended way to create custom pages OAuth Approval page:
I have to completely override the stuff on the page, need to add styles, branding etc. What is the right way to achieve that? Where could I see the source of the default page to use it as a starting point?
I also need to override the /login page but I think the approach of overriding it is going to be pretty much the same.
The recommended way is to provide a normal Spring MVC #RequestMapping for the "/oauth/confirm_access". You can look at WhitelabelApprovalEndpoint for the default implementation. Don't forget to use #SessionAttributes("authorizationRequest") in your controller.
In addition to #DaveSyer's answer, which should work for the most of the cases. Sometimes based on configuration and customization the aforementioned method may not work, if Framew‌orkEndpointHandlerMa‌pping from Spring Security OAuth package has higher order than RequestMappingHandlerMapping of your application. If this is the case, then servlet dispatcher will never reach you mapping and will always show the default page.
One way to fix it is to change the order of mappers, given that Framew‌orkEndpointHandlerMa‌pping's order is Order.LOWEST_PRECEDENCE - 2.
Another way is to set the approval page to a custom URL, not mapped by Framew‌orkEndpointHandlerMa‌pping, thus servlet dispatcher will reaches you application's mapping
#Configuration
#EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthorizationEndpoint authorizationEndpoint;
#PostConstruct
public void init() {
authorizationEndpoint.setUserApprovalPage("forward:/oauth/custom_confirm_access");
authorizationEndpoint.setErrorPage("forward:/oauth/custom_error");
}
}
With such a configuration mappings of /oauth/custom_confirm_access and /oauth/custom_error will be used as a confirmation page and an error page respectively.

SpringSecurity: how to specify remember me authentication?

I'm developing an app based on Grails and Vaadin 7. I managed to make them work with SpringSecurity for authentication and authorization, but I had to develop my own Service that calls the Spring Security authentication manager to make it work with Vaadin:
class SecurityService {
static transactional = true
def springSecurityService
def authenticationManager
void signIn(String username, String password) {
try {
def authentication = new UsernamePasswordAuthenticationToken(username, password)
SCH.context.authentication = authenticationManager.authenticate(authentication)
} catch (BadCredentialsException e) {
throw new SecurityException("Invalid username/password")
}
}
}
The problem is that now I need to implement a remember me authentication and I don't know from where to start.
How do I make the authenticationManager know that I want it to use remeberMeAuthentication? I can get a boolean value from a checkbox on the login View, but what do I do with it next?
Since your question is specific to the handling of checkbox value (remember me flag) coming from login page, the answer is that you have to call loginSuccess or loginFail method of RememberMeServices. The loginSuccess adds auto-login cookie in the response and loginFail removes that cookie.
But I guess above answer won't help you much unless you are sure that you have RememberMeServices configured in your app. Maybe following steps that configure RememberMeServices will help you do whole thing your way (or help you understand the out of the box functionality):
(1) Create a class (call it myRememberMeServices) that implements RememberMeServices and LogoutHandler.
(2) In autoLogin method, create an authentication object (UsernamePasswordAuthenticationToken) after parsing the cookie value.
(3) In loginFail method, cancel the cookie.
(4) In loginSuccess method, create an auto-login cookie. Add value that you would use in autoLogin method. Usually cookie value is encrypted.
(5) In logout method , cancel the cookie.
(6) Inject myRememberMeServices in following four places and call appropriate method:
(a) At the time of successful login (if checkbox value is set),
(b) At the time of failed login,
(c) On logout, and
(d) In filter that does autologin
It is worth noting that RememberMeAuthenticationFilter takes authenticationManager and RememberMeServices in its constructor.
Answer to your other question is that the authenticationManager doesn't need to know anything about remember me. It is the filter (or any class handling auto login) that needs to know about authenticationManager and RememberMeServices. (In other words, ask RememberMeServices for a token and pass it to authenticationManager to do auto login).
Spring Security's architecture is based on servlet filters. The sign-in mechanism you have implemented above is normally done by the UsernamePasswordAuthenticationFilter. Another filter called RememberMeAuthenticationFilter takes the responsibility for the remember-me functionality. The authenticationManager is not aware at all whether the remember-me feature is used by the application or not.
If you want to integrate Spring Security with another web-framework, first try to find out how the filters of the two frameworks can play together.

Resources