Security Config - configureGlobal - spring-session

Playing with Spring Session REST example, I'm curious is it possible to make the following flow:
1. In first time, an user passes its credentials, usename and password.
2. Spring Session will generate a token, puts it into Redis
3. Next time a user asks for some resources it passes its tiken.
My problem is that the following code is hardcoded:
code
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
}
code`
How can I do it to be dynamic?
Thanks in advance.

Spring Session works independent of the security framework you choose, so the answer depends on which security framework you want to use. This means selecting the username / password for your users is totally independent of Spring Session.
Since the question is unrelated to Spring Session, you should consult the documentation of the security framework you choose. In this instance, it is Spring Security so you can consult Spring Security's documentation on authentication.
The first step is to determine how you want to authenticate.
For example, if you want to use JDBC authentication, you can use something like the following.
#Autowired
private DataSource dataSource;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.jdbcAuthentication()
.dataSource(dataSource)
.withDefaultSchema()
.withUser("user").password("password").roles("USER").and()
.withUser("admin").password("password").roles("USER", "ADMIN");
}
By default jdbcAuthentication expects that:
select username, password, enabled from users where username = ?
will return the username, the password, and if that user is enabled. You can customize this query if you like using properties on jdbcAuthentication(). See the javadoc for details.
NOTE: It is important to understand that withDefaultSchema and withUser are really only intended for in memory databases since it will try to add the schema and the user every time. In a production environment your users and schema should be added using other mechanisms (i.e. liquibase).
The most flexible option is to implement a UserDetailsService. You can then look up users in any way you want. For example:
#Service
public class UserRepositoryUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
#Autowired
public UserRepositoryUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
#Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
MyUser user = userRepository.findByEmail(username);
if(user == null) {
throw new UsernameNotFoundException("Could not find user " + username);
}
List<GrantedAuthority> authorities = convert(user.getRoles());
return new User(user.getUsername(), user.getPassword(), authorities);
}
}
Then you can configure it using:
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth, UserDetailsService userDetailsService) throws Exception {
auth
.userDetailsService(userDetailsService);
}
You can find a complete example of using a UserDetailsService in my Spring Security 4 talk.

Related

How can I configure multiple authentication managers depending on the incoming URL for an authorization server?

With Spring Security <5.2
In a legacy project, for a password grant scenario, I need to configure an authorization server.
Currently it is done extending AuthorizationServerConfigurerAdapter, and the authorization endpoints are configured overriding the configure(AuthorizationEndpointsServerConfigurer) method.
My problem is that this configurer takes one AuthenticationManager for the password grant, when I would need something like an AuthenticationManagerResolver (but I can't upgrade to 5.2) to be able to apply a different authentication depending on the incoming URL (an authentication manager for admin URLs, e.g. "/admin/**", and another one for non-admin).
How can I do that? I can change the approach, but again I can't upgrade.
You can try implementing your own DelegatingAuthenticationManager, inject the list of your AuthenticationManagers in it, and put your logic in authenticate method. E.g:
#Component("delegatingAM")
public class DelegatingAuthenticationManager implements AuthenticationManager {
private final List<AuthenticationManager> ams;
#Autowire
public DelegatingAuthenticationManager(#Qualifier("x") AuthenticationManager amX, #Qualifier("y") AuthenticationManager amY) {
this.ams = List.of(amX, amY); // Arrays.asList(amX, amY);
// you can inject variables for your conditions here
}
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (...) this.ams.get(0).authenticate(authentication);
if (...) this.ams.get(0).authenticate(authentication);
// Or you can loop over the list like AuthenticationManager is implemented with AuthenticatioProvider
}
}
Then inject it to AuthorizationServerConfigurerAdapter
#Configuration
#EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
#Qualifier("delegatingAM")
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
.authenticationManager(this.authenticationManager)
}
...
}
Hope it will help, for the worse case, you could start to think about using many AuthorizationServerSecurityFilterChains, one AuthenticationManager for each. And based on the URL, direct the request to the right SecurityFilterChain.

Spring Security Custom Login Fallback

Can someone please point me to some resources that can guide me on how I can implement a custom spring security authentication that first test the credentials of the found user on an ldap server first if the field for the ldap username exists for that user, and failing to authenticate (either because the ldap username doesn't exist or the password given doesn't authenticate the username on the server) there would attempt to authenticate against the local password kept hashed in the local database for the user.
Thank you.
It seems there aren't too many good answers to that specific question available.
While I have not stood up a full working LDAP example, there should be a good start to that part in the ldap sample that #Marcus linked in the comments. Having said that, you can easily register two authentication providers in the order you want, with the default DaoAuthenticationProvider being second:
#Configuration
public class SecurityConfiguration {
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// #formatter:off
return http
.authorizeRequests(authorizeRequests -> authorizeRequests
.anyRequest().authenticated()
)
.formLogin(withDefaults())
.authenticationProvider(ldapAuthenticationProvider())
.authenticationProvider(daoAuthenticationProvider())
.build();
// #formatter:on
}
#Bean
public AuthenticationProvider ldapAuthenticationProvider() {
LdapContextSource contextSource = null; // TODO See sample for example
return new LdapAuthenticationProvider(new BindAuthenticator(contextSource));
}
#Bean
public AuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userDetailsService());
return authenticationProvider;
}
#Bean
public UserDetailsService userDetailsService() {
UserDetails userDetails = User.builder()
.username("user")
.password("{noop}password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(userDetails);
}
}
You will obviously want to provide your own user details which uses a database instead of in-memory. The username-password sample would get you started with that, and you can replace the MapCustomUserRepository in that example with e.g. a spring data #Repository.

Spring Security Authentication using DynamoDB

Does anyone know how to provide spring security authentication using DynamoDB? In other words, how would you convert/represent the following configAuthentication method using DynamoDB?
#Autowired
public void configAuthentication(AuthenticationManagerBuilder auth)
throws Exception {
auth.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery("select username, password, enabled from appuser where username = ?")
.authoritiesByUsernameQuery("select username, rolename from appuser natural join user_role natural join role where username = ?;");
}
You can use anything as a Spring Security authentication backend. You would need to write custom query logic in a class that implements the UserDetailsService interface and return a domain object that implements the UserDetails interface, but you can use that class.
Your configuration would like something like
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myCustomDynamoDbDetailService);
}
Where myCustomDynamoDBDetailService is your class that implements UserDetailService and does the lookup by username from DynamoDB.

rest with Spring boot + spring security with custom users table

I want to configure Spring boot and spring security for some rest services.
I have tables User, priviledge, priviledge_user
I have this Configuration files
WebSecurityConfig
#EnableWebSecurity
#Configuration
class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().fullyAuthenticated().and().
httpBasic().and().
csrf().disable();
}
}
and AuthenticationConfiguration
#Configuration
public class AuthenticationConfiguration extends GlobalAuthenticationConfigurerAdapter {
#Autowired
UserRepository userRepository;
Logger log = LoggerFactory.getLogger(this.getClass());
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService());
}
#Bean
UserDetailsService userDetailsService() {
return new UserDetailsService() {
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User account = userRepository.findByUsername(username);
log.info("The user ({}) logged has the password {}", username, account.getPassword());
org.springframework.security.core.userdetails.User userSession = new org.springframework.security.core.userdetails.User(account.getUsername(), account.getPassword(), true, true, true, true,
AuthorityUtils.createAuthorityList("USER"));
return userSession;
}
};
}
}
debuging the code seems Authentication is working well cause I have the user from database(I am using JNDI datasoure and have this configured in application.properties)
but When I try to get anything on http://localhost:8080/ and set my credentials I cant access and always get the prompt for credentials
Whats I am doing bad?
You are not instantiating the UserDetails object with the correct authorities.
Instead of always passing "USER" to AuthorityUtils.createAuthorityList method, you should pass a List with all the roles/privileges in the privilege_user table for the user you are loading. This roles by default are in the form of "ROLE_USER", "ROLE_ADMIN"...
So loading the User from the database successfully doesn't mean the authentication process is working well. You still have to tell him how to load the authorities for that User.
The problem is, you are using the DB audit with either of #CreatedBy or #LastModifiedBy annotation and yet you are trying to insert a document before any user is logged in. This use to happen, when you create a new user or create operations in a landing page controller. To overcome this, have a system user in your DB, say, system#yourdomain.com then set principal in the corresponding controller if the input params are right.
While doing so, the DBAuditor will get your system user id for the annotated filed. Make sure, you load newly created user principal after this operation.

Lookup LDAP entries from inside LdapAuthoritiesPopulator

I'm using Spring Security and spring-security-ldap for authentication in a Spring Boot application.
I want to implement an LdapAuthoritiesPopulator that looks up some entries from the LDAP server to decide the roles of the user. These entries are not under the user, so the userData object provided to getGrantedAuthorities method is not enough. (I have no control over the LDAP server so I cannot add the roles to user entries).
I thought about injecting the ContextSource that Spring Security creates when calling auth.ldapAuthentication().contextSource() (see (1) in the code below) into the LdapAuthoritiesPopulator. But that doesn't work because the LdapAuthoritiesPopulator is created before the WebSecurityConfigurerAdapter, and so the ContextSource does not exist yet.
My WebSecurityConfigurerAdapter contains the following method:
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.ldapAuthentication()
.contextSource() // (1)
.url("ldap://example.org/o=organization")
.and()
.ldapAuthoritiesPopulator(ldapAuthoritiesPopulator)
.userSearchFilter("uid={0}");
}
ldapAuthoritiesPopulator is autowired, the class is currently a dummy implementation that simply adds ROLE_USER:
#Component
public class CustomLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator {
#Override
public Collection<? extends GrantedAuthority> getGrantedAuthorities(
DirContextOperations userData, String username) {
// I want to look up some entries in LDAP here, so I need a ContextSource
// but I can't inject it because it does not exist when this bean is created
Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority("USER"));
return authorities;
}
}
You don't need the roles populated till after the application is running (I suppose). So one thing that would work would be to inject the ApplicationContext instead of the ContextSource and look up the latter lazily. If that works there might be a truck with #Lazy.

Resources