Make DaoAuthenticationProvider Work with the AuthenticationManager after WebSecurityConfigurerAdapter Deprecation - spring

I am creating a complete user login and registration Backend system with Email Verification and usage of PostgreSQL to store the user's credentials. I've come to a point where I am having problems at the security layer. To be more specific I am having the following code which since WebSecurityConfigurerAdapter deprecation, I want to change:
OLD VERSION BEFORE DEPRECATION
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(daoAuthenticationProvider());
}
#Bean
public DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider provider =
new DaoAuthenticationProvider();
provider.setPasswordEncoder(bCryptPasswordEncoder);
provider.setUserDetailsService(applicationUserService);
return provider;
}
I've searched this question and found that AuthenticationManagerBuilder can now be accessed as follows:
NEWEST VERSION OF AUTHENTICATION MANAGER
#Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
My problem is that I can't find a way to inject my daoAuthenticationProvider to the newest method of AuthenticationManager. Any proposals???

You should not need the AuthenticationConfiguration for that, you could just create your own bean, like so:
#Bean
public AuthenticationManager authenticationManager(PasswordEncoder passwordEncoder, UserDetailsService userDetailsService) {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setPasswordEncoder(passwordEncoder);
provider.setUserDetailsService(userDetailsService);
return new ProviderManager(provider);
}

Adding a custom authentication provider is configured in the SecurityFilterChain bean. Although looking at the given code, standard DAO authentication would automatically be added with http.formLogin() without the need for an AuthenticationProvider.
#Bean
public SecurityFilterChain filterChain(DaoAuthenticationProvider daoAuthenticationProvider) throws Exception
{
http.authenticationProvider(daoAuthenticationProvider);
return http.build();
}
See also https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter

Related

Spring Security Active Directory Authentication - infinite login pop-up

I am using Spring Boot (2.7.2) security. My security config is:
public class WebSecurityConfig {
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().fullyAuthenticated().and().httpBasic();
return http.build();
}
#Bean
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider(
"company.com", "ldap://ldap-company.com:389");
provider.setSearchFilter("(&(objectClass=user)(sAMAccountName={0}))");
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
return provider;
}
}
Now when I hit my URI I keep getting the login pop-up infinitely.
The username and password I am providing is correct. No error(s) at the console whatsoever.
What am I doing wrong here or missing?
While I am still waiting for the right answer, I got the idea from here and it works.
So this is what I ended up with:
public class WebSecurityConfig extends GlobalAuthenticationConfigurerAdapter {
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated()
// .fullyAuthenticated()
.and().httpBasic();
return http.build();
}
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(
"ldap://ldap-company.com:389/dc=company,dc=com");
contextSource.setUserDn("CN=MYBindUser,OU=Ldap,dc=COMPANY,dc=com");
contextSource.setPassword("ComplexP#ssw0rd");
contextSource.setReferral("follow");
contextSource.afterPropertiesSet();
LdapAuthenticationProviderConfigurer<AuthenticationManagerBuilder> ldapAuthenticationProviderConfigurer = auth
.ldapAuthentication();
ldapAuthenticationProviderConfigurer
.userSearchFilter("(&(cn={0}))")
// .userSearchFilter("(sAMAccountName=%s)")
.userSearchBase("")
// .groupSearchBase("(&(objectCategory=group)(cn={0}))")
.contextSource(contextSource);
}
}
Now my HTTPBasic Authentication with ActiveDirectory LDAP works just fine.

Spring Boot JWT - Adding BCrypt Security - Getting Access Denied

I have an application server which uses Spring boot framework with JWT token. I want to encrypt the user password, but running into login issues. I am able to encrypt user's password using
userModel.setPassword(new BCryptPasswordEncoder().encode(userModel.getPassword()));
but when trying to login I am getting Encoded password does not look like BCrypt. I tried to change my authenticate method and encrypt the password from login but it didn't work.
new UsernamePasswordAuthenticationToken(
authenticationRequest.getUsername(),
new BCryptPasswordEncoder().encode( authenticationRequest.getPassword())));
I would appreciate your help, if you could point me to the direct direction or give me solution. below is my Security config file.
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final MyUserDetailService myUserDetailService;
private final JwtRequestFilter jwtRequestFilter;
public SecurityConfig(MyUserDetailService myUserDetailService, JwtRequestFilter jwtRequestFilter) {
this.myUserDetailService = myUserDetailService;
this.jwtRequestFilter = jwtRequestFilter;
}
#Override
// Authentication : User --> Roles
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.authenticationProvider(authProvider());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public DaoAuthenticationProvider authProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(myUserDetailService);
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
#Override
// Authorization : Role -> Access
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.formLogin().disable()
.headers().frameOptions().disable()
.and()
.authorizeRequests()
.antMatchers("/authenticate").permitAll() .and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
return source;
}
}
I am not able to authenticate(login) using the encrypted password or the raw password. Please let me know what I can do to fix.
Thank you for your help.
Hey i had the same problem like you, i am just from solving it.
new UsernamePasswordAuthenticationToken(
authenticationRequest.getUsername(),
authenticationRequest.getPassword())
);
replace that part of your code with mine.
By calling again new BCryptPasswordEncoder().encode you create another salt value different from the one previously created when saving the user in your Database.
And by login you will never get the same value as the one in the Database.
The exception is thrown if the stored password in the database is not encrypted correctly.
Make sure it starts with $2a$ or $2b$ or $2y$ and is exactly 60 chars long.

Configuring multiple authentication providers in conjunction with oauth2+jwt

I am trying to configure my Spring Boot 2, OAuth2 with JWT Authorization Server, which needs to do the following:
take a username/password, build a CustomUserDetails object based on db data and salesforce data, and return a JWT token if authentication passes (this works)
take a refresh token and return a new JWT refresh and access token (this works)
(NEW) take a refresh token, check against the db for a stored token ID before returning a new JWT access + refresh token (this is the trouble spot) The point of this is to ensure only one device is logged in with the user's credentials at a time.
In order to do #3, I am trying to customize the PreAuthenticatedAuthenticationProvider by giving it a custom UserDetailsService, and the AuthenticationManagerBuilder bean needs to be passed both the customized PreAuthenticatedAuthenticationProvider and the DaoAuthenticationProvider.
Assuming I am going in the right direction with that, here is my configuration code:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsService userDetailsService;
#Autowired
PasswordEncoder passwordEncoder;
//implements AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken>
#Autowired
CustomPreauthenticatedUserDetailsService customPreauthenticatedUserDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/actuator/**").permitAll()
.antMatchers("/swagger-ui**","/webjars/**","/swagger-resources/**", "/v2/**").permitAll()
.antMatchers("/oauth/token/revokeById/**").permitAll()
.antMatchers("/oauth/token/**").permitAll()
.anyRequest().authenticated()
.and().csrf().disable();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder authenticationManager) throws Exception {
authenticationManager.authenticationProvider(preauthAuthProvider());
authenticationManager.authenticationProvider(dbAuthProvider());
}
#Bean
#Qualifier(value = "authenticationManagerBean")
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean(value="preAuthProvider")
public PreAuthenticatedAuthenticationProvider preauthAuthProvider() {
PreAuthenticatedAuthenticationProvider preauthAuthProvider = new PreAuthenticatedAuthenticationProvider();
LOGGER.info("Setting customPreauthenticatedUserDetailsService");
preauthAuthProvider.setPreAuthenticatedUserDetailsService(customPreauthenticatedUserDetailsService);
return preauthAuthProvider;
}
#Bean(value="dbAuthProvider")
public DaoAuthenticationProvider dbAuthProvider() {
DaoAuthenticationProvider dbAuthProvider = new DaoAuthenticationProvider();
dbAuthProvider.setUserDetailsService(userDetailsService);
dbAuthProvider.setPasswordEncoder(passwordEncoder);
return dbAuthProvider;
}
}
On the AuthorizationServerConfig side:
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
private static final Integer ACCESS_TOKEN_VALIDITY_SECONDS = 300;
private static final Integer REFRESH_TOKEN_VALIDITY_SECONDS = 60 * 60 * 24 * 60;
#Autowired
public AuthorizationServerConfig(AuthenticationManager authenticationManagerBean, PasswordEncoder passwordEncoder, CustomTokenEnhancer customTokenEnhancer, TokenStore tokenStore, JwtAccessTokenConverter accessTokenConverter) {
this.authenticationManagerBean = authenticationManagerBean;
this.passwordEncoder = passwordEncoder;
this.customTokenEnhancer = customTokenEnhancer;
this.tokenStore = tokenStore;
this.accessTokenConverter = accessTokenConverter;
}
private AuthenticationManager authenticationManagerBean;
private PasswordEncoder passwordEncoder;
private CustomTokenEnhancer customTokenEnhancer;
private JwtAccessTokenConverter accessTokenConverter;
private TokenStore tokenStore;
#Override
public void configure(AuthorizationServerSecurityConfigurer authorizationServerSecurityConfigurer) {
authorizationServerSecurityConfigurer.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(ClientDetailsServiceConfigurer clientDetailsServiceConfigurer) throws Exception {
clientDetailsServiceConfigurer.inMemory().withClient("someclient")
.authorizedGrantTypes("password", "refresh_token")
.scopes("read", "write").accessTokenValiditySeconds(ACCESS_TOKEN_VALIDITY_SECONDS)
.refreshTokenValiditySeconds(REFRESH_TOKEN_VALIDITY_SECONDS)
.secret(this.passwordEncoder.encode("somepassword"));
}
#Override
public void configure(final AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(customTokenEnhancer, accessTokenConverter));
endpoints.tokenStore(tokenStore).tokenEnhancer(tokenEnhancerChain)
.authenticationManager(this.authenticationManagerBean);
}
}
Upon start up of the app, the first sign of trouble I see is:
s.c.a.w.c.WebSecurityConfigurerAdapter$3 : No authenticationProviders and no parentAuthenticationManager defined. Returning null.
Oddly, that appears to be a lie, or it's talking about something else, because when I try to login with username and password - it works, and I get both my refresh and access tokens. The debugger shows that the ProviderManager has both my custom authentication provider classes and uses them for authentication.
However, when I try to retrieve a new token with the refresh token, the debugger shows that the app appears to be taking a different ProviderManager path - this ProviderManager only has PreAuthenticatedAuthenticationProvider in its list, and that provider is NOT the one I configured. This PreAuthenticatedAuthenticationProvider is trying to retrieve a UserDetailsService from WebSecurityConfigurerAdapter$UserDetailsServiceDelegator, and the result is an error:
2019-06-08 13:27:24.764 ERROR 8731 --- [nio-8080-exec-3] o.s.s.o.provider.endpoint.TokenEndpoint : Handling error: IllegalStateException, UserDetailsService is required.
So, what configuration step am I missing? Why does the refresh token call go elsewhere? Is it the authenticationManagerBean that I'm passing in the AuthorizationServerConfig?

Combine form based and oauth2 based login providers in a spring boot 2 + spring security 5 app

I currently have a working setup for a form based login in my app using spring boot 2.0.6 and spring security 5:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private MyUserDetailsService userDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/", "/home").permitAll().anyRequest().authenticated().and().formLogin()
.loginPage("/login").permitAll().and().logout().permitAll();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(encoder());
return authProvider;
}
#Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder(11);
}
}
There are also examples of adding support for oauth2 with the following spring dependency:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-client</artifactId>
</dependency>
and a helpful blog post that does well to show how you can use it:
https://spring.io/blog/2018/03/06/using-spring-security-5-to-integrate-with-oauth-2-secured-services-such-as-facebook-and-github
How do I extend my current security config to add support for Oauth2 logins such as facebook?
Thank you kindly,
Jason

Spring Security SessionRegistry returning empty list

Friends I have search a lot and try every solution available on the internet, but my problem not solved.
I want check (in Spring Boot web application) currently user logged in with the credentials specify in the login page, if there is a session with username currently, then invalidate that first a login again for the request.
I want to ensure there will be one session for the user, if session exist then invalidate and login forcefully.
I am trying to get all the principle from the SessionRegistry, but it always returning [] empty list even after multiple user logged in the system.
Here is my spring security config
...
#Autowired private CustomUserDetailsService customUserDetailsService;
#Autowired private CustomUrlAuthenticationSuccessHandler customUrlAuthenticationSuccessHandler;
#Autowired private PasswordEncoder passwordEncoder;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider());
}
#Bean
JCaptchaAuthenticationFilter jCaptchaAuthenticationFilter() throws Exception {
JCaptchaAuthenticationFilter jCaptchaAuthenticationFilter = new JCaptchaAuthenticationFilter();
jCaptchaAuthenticationFilter.setAuthenticationManager(authenticationManager());
jCaptchaAuthenticationFilter.setAuthenticationFailureHandler(customLoginFailureHandler());
jCaptchaAuthenticationFilter.setAuthenticationSuccessHandler(customUrlAuthenticationSuccessHandler);
return jCaptchaAuthenticationFilter;
}
#Bean
public DaoAuthenticationProvider authProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(customUserDetailsService);
authProvider.setPasswordEncoder(passwordEncoder);
return authProvider;
}
#Bean
public CustomLoginFailureHandler customLoginFailureHandler() {
CustomLoginFailureHandler customLoginFailureHandler = new CustomLoginFailureHandler("/login");
return customLoginFailureHandler;
}
#Override
protected void configure(HttpSecurity http) throws Exception {// #formatter:off
http.authorizeRequests().antMatchers("/js/**", "/fonts/**", "/css/**", "/images/**", "/favicon.ico").permitAll()
.antMatchers("/", "/register/**", "/email/**", "/captcha.png/**").permitAll().antMatchers("/login/**")
.permitAll()// Basically I'm allowing parameters for login so
// .antMatchers("/services/**").permitAll()
.antMatchers("/forgot/password/**", "/user/verify/**").permitAll().antMatchers("/user/resetPassword*")
.hasAuthority("CHANGE_PASSWORD_PRIVILEGE").anyRequest().authenticated().and()
.addFilterBefore(jCaptchaAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class).formLogin()
.loginPage("/login").permitAll().and()
.csrf().disable()
.sessionManagement().invalidSessionUrl("/invalidSession")
.maximumSessions(1)
.maxSessionsPreventsLogin(true)
.sessionRegistry(sessionRegistry()).and()
.sessionFixation().newSession().and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/")
.invalidateHttpSession(false).deleteCookies("JSESSIONID").permitAll();
http.headers().frameOptions().sameOrigin();
}
....
#Bean
SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
#Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
Here is i am getting sessions list
List<String> userList = sessionRegistry.getAllPrincipals().stream()
.filter(u -> !sessionRegistry.getAllSessions(u, true).isEmpty())
.map(Object::toString)
.collect(Collectors.toList());
But above code always return empty list. I have checked is there any double sessionRegistry loading by disabling sessionRepositry, but spring throw exception that bean not found.
Please help friends.

Resources