Spring Security Fundamentals - Lesson 4 - Multiple authentication providers
Vložit
- čas přidán 11. 08. 2022
- In this stream, we discuss Spring Security using the latest available version in 2022. We'll discuss configurations for authentication and authorization, tips and tricks, how to learn Spring Security, vulnerabilities, OAuth 2, and many more.
Code on GitHub: github.com/lspil/youtubechann... - Věda a technologie
Your drawing are the best, lesson 4 so far, expecting to learn a lot by the end of the series, thanks for your great effot to bring this content to all of us who are getting started in web development
Thanks, Emerson
As you said, it is realy helpful for us to understand what is going on on the code when you draw a schema. Thanks.
The best Spring Security tutorial for beginners. thank you so much
Thanks a lot for the video. It helped me to move to a new way of configuring spring security. I was stuck on how to provide an authentication manager in a new way.
Thanks you so much Laur Spilca, This playlist is amazing for Spring Security. 🥰
I really appreciate this tutorial video. Many thanks 🙂
Code on GitHub: github.com/lspil/youtubechannel/tree/master/ss_2022_c4_e1
You are rocking brother. Thanks for the tutorials.
Hi
Great work. Thanks for this playlist.
Thank you for preparing these great videos
thanks for your effort im looking forward to new episodes 😍😍
thank you for your work it's soo great and helpfull hope u continue the playlist it perfct time i want to have deep knowledge abt spring i make some basics application and ur lecture i learnt from uu soo muchh thankk again keep it up plzz
Well first of all it is just a fabulous playlist and this tutorial in particular, thank you so much. I've tried to implement exactly what's shown here: kinda api-key (custom) + oauth2-resource-server (default) authentications, and both reading official documentation and debugging the framework didn't really help. Spring Security Docs describes all the components involved quite abstractly, but it's not enough at all to understand how to implement this kind of stuff.
Although I would implement this chain filter-authManager-authProvider in a more 'Springy' way, which is making them all beans (probably using @Component) and inject to each other and injecting the key into the ApiKeyProvider via @Value directly. From the general perspective it seems a bit cleaner then creating new Manager and Provider with every request and pass the Key through the chain of constructors. But in this case Spring Security somehow registers our ApiKeyProvider (as it becomes a Bean) as a provider for the parent of ProviderManager used by BearerTokenAuthenticationFilter😵💫 Crazy things, looks like it doesn't break anything, just ignored due to token type mismatch, but still a bit annoying.
Is this a viable approach or these beans will backfire at some point?
Thanks for your great effot.
Great work as always, would be cool if you mention ProviderMaanager and AbstractAuthenticationProcessingFilter, that last one is pretty nice as it's the "opposite" of OncePerRequestFilter
Thanks, Santiago.
Yeah, extending AbstractAuthenticationProcessingFilter helps you avoid a lot of the boilerplate code!
Hello Laurentiu, can you tell me why don't you return from the function after line 29 on 59:04? Even if the chain continues to the next filter, won't the doFilterInternal function still be executed till the end? Thanks.
Hi Sandro. Excellent spot! Yes. I think I should have returned there. My mistake :) But hopefully, you got the whole idea of the example.
Hey, I was just reading the source code of GlobalMethodSecurityConfiguration and found a way to access the default AuthenticationManager without extending the WebSecurityConfigurerAdapter.
You can inject the AuthenticationConfiguration bean and then call the .getAuthenticationManager() method. Didn't test in in a custom filter yet, but in a controller, it works. It retrieves me the default instance of the ProviderManager.
Hey. Thanks for sharing. I will test it.
Hey Michael and for anyone looking to get hold of the default authentication manager..I was able to get access to the default AuthenticationManager by using AuthenticationManagerBuilder object in my CustomFilter class and one of it's instance method called getObject() which returns the current Authentication Manager in the context. Thanks.
thank you.
Hey Laurentiu.
First of all, many thanks for updating the playlist regarding Spring Security. It's very useful.
I wonder if it is possible to have multiple Custom Authentication Providers for the single Custom Authentication Manager?
If is it correct I would like to clarify one point according to their proper responsibilities of them.
Is it true for proper authentication enough only one successful executing authenticate method into one of the custom providers or authentication should be passed through the multiple custom providers' chain?
Hello. Yes. It is possible to have multiple custom authentication providers for a single custom authentication manager. I'm not sure if I understood the second question. So, it is enough one of the providers to give a go, but at the same time if one of them throws AuthenticationException it's a no go. So if an authentication provider cannot decide, either its supports() method should return false or the authenticate() method should return null.
28:00 Maybe filter is called again if request is forwarded? That would mean that if I authenticate, perform an action and forward to a JSP, then there will be no need to authenticate again with the JSP page URL. That's my guess!
Hi,
Is it not good to directly access the key from the property file in the authentication provider and put the key from the header in the filter while passing the same to the authentication manager? Why did you carry the secret key from the property file all along from the filter till the authentication provider?
While I tried to get it directly from the property file in the auth-provider, Spring does not generate the password for me while the key authentication works fine.
Kindly help.
Hi, thanks again for the video!
I have two questions:
- Does multiple authentication mean that the user can either authenticate (in this case) with http basic OR apikey?
-Last week in order to add a filter you replaced it with UsernamePasswordAuthenticationFilter and this time you added one before BasicAuthenticationFilter. How can I always know where/when to add them?
Hi. Thanks for the questions. Let me try answering them here:
1. Yes, that's perfectly right. The client will be able to use either of the authentication methods.
2. UsernamePasswordAuthenticationFilter is used by the formLogin() method and it's in the same place like BasicAuthenticationFilter (which is for HTTP Basic). So, technically speaking it works the same if you use one or the other.
Hi @Laur,
First of all thanks for such a great content.
I've doubt:
I'm trying to follow this video with a little change by putting the custom Authentication manager and provider in the context using the @Component annotation. As soon I do so the http-basic auth doesn't seem to work.
Can you please explain it?
Hi Anuj. Thanks for the question. I'm not sure I can provide an answer seeing nothing on what you did. Most likely you missed or misconfigured something.
Thank you so much for this amazing Playlist.
I have a question, in the previous lesson you used addfilterat to replace the pre-configured filter which was UsernamePasswordFilter with your Customfilter in this lesson you used addFillterBefore and you add you filter before the default filter why did you use BasicAuthenticationFilter instead of UsernamePasswordFilter
Hi Mahmoud. Good question. One filter managers the form login,the other the basic auth. In main, it's the same what I did since they are at the same position in the filter chain.
@@laurspilca got it thank you for answering, waiting for the next lesson 👌
Amazing Lesson! thank you very much for your generosity. I have got one problem it tried to use both httpBasic and the request header at the same time, by using Basic Auth and by setting request header and it throws NullPointerException in the BasicAuthenticationFilter as httpBasic Auth uses the getName() method of ApiKeyAuthentication. the problem only occurs when i use the correct header value. FYI i used spring boot 3.2. Many thanks again.
Hello. I would need to debug that to see what the problem could be. I'm not sure I could figure out by heart where the problem comes from.
Hello, thank you for the lesson, hope you explain in the next live the following question:
What you have shown is that on every request we are going throught the filters, but in reality the user authenticate only once, afterwars he might send a coockie or sessionId, could you please show how it is handled in the backend? should we create a new filter to check for the session or the coockie? or it is handled by spring security already? can you show an examlple on that?
Hi. Thanks for the question. I will explain it in the next session. Please remind me if I forget until next week :)
Ahhh and it seems there is a small bug: in ApiKeyFilter if requestKey == null we just call doFilter, but then when execution comes back to this method (since it's a chain of responsibility) it starts executing the whole ApiKeyAuthentication code block, which should probably be wrapped by else { }
As you said, we can have multiple AuthenticationProvider classes. However, when I declared 'CustomAuthenticationProvider' implements 'AuthenticationProvider', the generated security password from Spring Security disappeared. Could you explain this ? Thank you very much.
Hello. The default configuration is made to be overridden. Once the developer creates their custom implementation the default one doesn't exist anymore. If you wish to use both, its your (the developer) duty to configure them both. Hope this helps.
You could get the key from the application.properties file directly in the CustomAuthenticationProvider. Is there any specific reason why you propagated the key through filter, manager and finally to provider?
Hi. No. No specific reason. It's just an example.
When you add a custom security Filter, how do you know where to place it in the SecurityFilterChain?
In this lesson we use: `addFilterBefore(new ApiKeyFilter(key), BasicAuthenticationFilter.class)`
But the previous lesson used: `addFilterAt(customAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)`
Where can we learn about these existing Filters and how to place our own Filters in relation to them?
Hi. Thanks for the question. I think the answer it "it depends". Usually you know depending on what your custom filter does if it should be for example before or after authentication, or before or after CORS, or before or after CSRF. It mostly depends on what logic you want to be executed before and after it.
Hi Laurentiu, I was wondering about this approach of having the AuthenticationManager be a manually managed object rather than a bean, doesn't it impact performance negatively?
By this I mean, correct me if I'm wrong, by having the managers and providers as @Beans, they are instantiated once at application startup and they keep being reused at every request. If the new approach implies the custom manager not being a bean (because otherwise it overrides the default one), this means that a new manager is instantiated at every request, which, in a stateless session application, would be a big performance hit I believe.
What is your take on this issue?
Hi Federico, This is a good point. But I'd not be concerned about it. In the end, is only an object instance, isn't it? At the same time, you could say the same thing about any HTTP request and response parameter. I doubt that instance would make a big impact.
@@laurspilca Thank you for your answer, this makes sense, you're right, it wouldn't be as impactful as I was thinking!
You don't need to create an instance per request. You can just initialize the CustomAuthenticationManager as a field once. Or, because OncePerRequestFilter extends GenericFilterBean, you can just inject your authentication manager bean.
@@michaelj7677 Hi Michel. I'm not sure you have an option to inject the authentication manager bean with the last Spring Security versions. If you define an authentication manager as a bean, you'll override the existing one. If that's what you need, that's ok, but if you don't want that, then you won't be able to inject it either. So it really depends on what you want to do.
Hi, great work. Learning alot from your tutorials.
1 doubt I had was, shouldn't we have custom authentication logic inside `else` block like below. As I see without else block, even if we are passing basic auth, the custom filter gets executed or am I missing anything ?
```java
if (authKey == null || "null".equalsIgnoreCase(authKey)) {
filterChain.doFilter(request, response);
} else {
CustomAuthentication customAuthentication = new CustomAuthentication(authKey);
CustomAuthenticationManger customAuthenticationManger = new CustomAuthenticationManger(key);
final var authentication = customAuthenticationManger.authenticate(customAuthentication);
if (authentication.isAuthenticated()) {
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request, response);
}
}
```
you can return in the if block, that is what spring does in its filter implementations too
There was a lot of changes in spring security in a last few years!
Yeap!
Thank you for this great content. I was trying to do the same in spring boot 3.0 and I was getting exception in BasicAuthenticationFilter.java "Cannot invoke "String.equals(Object)" because the return value of "org.springframework.security.core.Authentication.getName()" is null". For some reason BasicAuthenticationFilter is using the same CustomAuthentication object (created for API key authentication, it is looking for username inside cusomAuthentication object and gets NP exception) . Then in my webconfigsecurityConfig.java file i changed the filter configuration to addFilterAfter basicAuthenticationFilter and it started working. Is it because of spring boot versions or am I doing something wrong.
the same case mate
Hi, awesome video as always
There is a point that i don't get it. On 1:00:36 , line 38.After authenticating by key, the filter chain is delegated to httpBasic filter, but why the httpBasic does not ask the user and password again? (as in the case of line 29 where the same .doFilter apply)
Hi, Since I configured my own filter and didn't add the HTTP Basic configuration, there's not HTTP Basic filter at all to ask for those credentials.
Hi, in case I just want to add a custom provider, should I go through the process of making a custom filter, custom manager, then the new provider? “Coz in the good old spring security oauth2 library, this is possible….my use case is I want to get a bearer token, process it to get the “sub” data and convert it to a UserDetail object, then make it as a principal of the custom authrntication….I want the authenticated principal to be a UserDetail object and not an object carrying token data
Hi. Yes. You can alwasys configure the authoization manager with any custom provider you want. That's actually what I also do in this lesson.
59:03 After the null check don't you need to put the remaining code in the else case?
Yes. I think I should have. However, to be honest with you it's so long since I made this video that I don't remember which were my intentions at that time :)
Hello Laur, thanks for this tutorial. My confusion is when we put ApiKeyFilter before BasicFilter and when we authenticate by using ApiKeyFilter does it skip BasicFilter? Or there is a mechanism in BasicAuthenticationFilter to skip when user is already authenticated?
Hi. THe ApiKeyFilter is put before the position of BasicFilter. But since we don't configure a basic filter at the SecurityFilterChain level, that filter is not in the chain.
Hi Laur, first thank you,
But I don't get this, if we didn't have BasicFilter how the authentication worked when we send username and the generated password.
@@mohammedwk6901 Ok. So I checked again the example. I was confusing it with another one (sorry, I have many and do not always have time to check). So I see here that I have two different authentication providers. You can use either. Spring Security knows to find the appropriate one. When one succeeds, the SecurityContext is filled with the authentication so the other doesn't check anymore. I hope it makes sense. You can also do some debugging on the framework's code to understand deeper what happens there.
@@laurspilca got it
And
Thank you so much for this great content
@@laurspilca Thank you. It is clear now.
When I tried to annotate my CustomeManager and CustomeProveides with @component to inject it into the filter bean like in the previous video but the auto-generated password did not appear. can you please explain why?!
Most likely the way you wrote the code made Spring Boot to exclude the auto configuration of the UserDetailsService.
UserDetailsServiceAutoConfugration class has a @conditionalOnMissingBean annotation which has AuthenticationProvider as it's value. I think this is the reason why auto generated password not occurs.
Hello Laurentiu,
and first of all: Thanks for the great tutorials.
For the AuthenticationManager I found a solution to use only one (the default) AuthenticationManager of Spring Security:
I need to build a Configurer for that:
@AllArgsConstructor
public class ApiKeyHttpConfigurer extends AbstractHttpConfigurer {
private final String key;
@Override
public void configure(HttpSecurity http) throws Exception {
final AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
http.addFilterBefore(new ApiKeyFilter(authenticationManager), BasicAuthenticationFilter.class);
http.authenticationProvider(new ApiKeyProvider(key));
}
}
This configurer can then be used in the SecurityFilterChain as follow:
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http.httpBasic()
.and().apply(new ApiKeyHttpConfigurer(key))
.and().authorizeRequests().anyRequest().authenticated()
.and().build();
}
I hope it helps to reduce unnecessary code for a second AuthenticationManager.
Used source: stackoverflow.com/a/71449312
Hey. Thanks. Someone told me that using getSharedObject() this way is not recommended. because things can change internally in how the framework works. But I will double check.
Halo Laurenti, In lesson 3 you created the Filter 'CustomAuthenticationFilter' as a component and then you inject it in the SecurityConfig class, while in this lesson you created Similar filter 'APIKeyFilter' which serves same role as lesson 3 but here you configured a new instance of it in the SecurityConfig class. why is this difference?
Hello. Thanks for the question. The only difference between the two would be that the one in lesson 3 is in the Spring context. This way I would be able to use specific Spring features with that instance (in other words, that instance is a bean in the Spring context and Spring knows how to manage it). In lesson 4 I create an instance using the new operator. Since I don't add it to the Spring context I wouldn't be able to use any capability such as transactionability, logging or dependency injection with it. From a Spring Security point of view, it makes not difference if the instance is or not a bean. But depending on how you'd want to use if further you need to decide if it makes sense for your case to be a bean.
Thank you very much for clarification @@laurspilca
are filters, managers and providers beans in the spring context?
Hi. Not necessarily. It's your choice if you want to make them beans. Of course, you'd choose that according to general Spring rules - in case it helps you that object to be in the context, then you make it a bean.
@@laurspilca I actually tried using a custom filter and a default filter, like in your tutorial and made the custom filter, manager and provider as beans. But it was not working.
The custom manager was able to pick up the custom provider, but the ProviderManager was not able to pick up the default provider (i was using a mysql database to store users).
But once i removed the custom provider from the context and instantiated it, the default provider was working.
Can you explain this behavior?
Hello.
First of all, your teaching skills, awesome. Im grateful i found your channel, actually not too long ago:))
I followed the video step by step, but the api authentication by using api key does not work. Only basic auth works.
I also copied your code as is and the result is the same.
I made sure i removed any cookies from Postman but there is nothing else i can think of.
Any suggestions?
Hi Nae. I recommend you watch the new playlist Spring Security Fundamentals 2022. It's difficult for me to know what exactly you do wrong, but could it be that you try to use new versions with old ways to configure them?
@@laurspilca Hello
I'm watching the 2022 playlist:))
I made sure I use same spring boot version, copied your code from repo but still does not work. Maybe something related to postman?
@@naebara1297 same problem.
@@naebara1297 but after debugging i found error in my code i.e @Value("${the.secret}"), I was forget to use $ sign, Now its working. I think you should check your code again.
I am getting an error something like
IllegalArgurment there are 4 beans for AuthenticationManager found but none is primary.
Dors anyone has any clue, how to fix it. I want to know when does this happen? And how to fix it.
Hey. I don't think I can be of any help with this few information. Can you be more explicit please?
@@laurspilca hi could you please share your mail id? I will share my configurations and the error
Thanks
@@laurspilcai am getting error because I have multiple authentication managers like for kerbos, saml etc configured in different xml as per profile.
But i have configured filters for OAUTH in a java configuration and I think when it runs through it, it finds those authentication managers present in different xmls. And gives the above error that none is marked as primary.
So is there any way I can explicitly tell which authentication manager to use . I will create one bean for oauth in the same java configuration file. But I don’t know ow how do i tell spring to use that only when looking for authentication managers.
May be in my filters if i could write any code which could say use this particular authentication manager specifically
It's terribly complicated and confusing what options I have. You have to learn it by heart and IntelliJ doesn't really help
Custom and ugly authentication🤣
httpBasic() is deprecated and marked for removal, how do i go about it?, lesson 4
Thank you for the videos sir, I need help.
I was making my own implementation loosely based on your code. I'm always having this error message whenever I send a request :
No primary or single unique constructor found for interface org.springframework.security.core.context.SecurityContext
What could be posibly causing this problem?T hank you
Hey. I'm not sure. It depends on what you are doing. Maybe you are trying to make the security context a bean? If that's the case, it won't work since the class has more than one constructors.