rest with Spring boot + spring security with custom users table - spring-security

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.

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.

Can not authorize with userDetailsService [duplicate]

This question already has answers here:
Springboot Security hasRole not working
(3 answers)
Closed 3 years ago.
I am learning Spring Security, this is my configure class, very simple:
#Configuration
#EnableWebSecurity
public class Config extends WebSecurityConfigurerAdapter {
#Autowired
MyUserDetailsService myUserDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/")
.permitAll()
.antMatchers("/users")
.hasRole("isAdmin")
.antMatchers("/articles")
.hasRole("isUser")
.and()
.formLogin()
.permitAll();
}
}
import org.springframework.security.core.userdetails.User;
#Service
public class MyUserDetailsService implements UserDetailsService {
#Autowired
UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
List<SimpleGrantedAuthority> simpleGrantedAuthorities = new ArrayList<>();
simpleGrantedAuthorities.add(new SimpleGrantedAuthority("isUser"));
return new User(username, "{noop}123456", simpleGrantedAuthorities);
}
}
From this configuration, that I allow anyone to login with any username and a certain password 123456, and I give each user isUser roles.
I wish anyone can login with any username and get permit to access /articles.
But unfortunately, when I try to login, yeah, login is successful, but failed to access to /articles.
I can not find the reason, I think already give the user permission, what's wrong?
The answer can be found in javadoc of hasRole method (link):
Shortcut for specifying URLs require a particular role. If you do not
want to have "ROLE_" automatically inserted see hasAuthority(String).
Parameters: role - the role to require (i.e. USER, ADMIN, etc). Note,
it should not start with "ROLE_" as this is automatically inserted.
Thus, either add ROLE_ prefix to your role isUser, or use hasAuthority method instead of hasRole.

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.

Security Config - configureGlobal

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.

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