I use LDAP authentication in my app.
I use this code:
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
String domain = customProperties.getAdDomain();
String url = customProperties.getAdUrl();
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider(domain,url);
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
provider.setUserDetailsContextMapper(userDetailsContextMapper());
auth.authenticationProvider(provider);
auth.userDetailsService(new MyUserDetailsService());
}
Authentication takes place with an empty password. I know that I need to insert a check for an empty password, because Not all LDAP servers return an error in this case. How and where is it better to insert a check for a blank password?
Instead of using the ActiveDirectoryLdapAuthenticationProvider, you can make use of Spring's LdapTemplate to have a custom implementation of how you authenticate users against the LdapServer. You can refer to the recommendation here and here to configure the LDAP template.
Then, you can create a CustomAuthenticationProvider class to handle the authentication.
CustomAuthenticationProvider.class
public class CustomAuthenticationProvider implement AuthenticationProvider{
#Autowired
private LdapTemplate ldapTemplate;
#Override
public Authentication authenticate(Authentication auth) throws AuthenticationException{
String username = auth.getName;
String password = auth.getCredentials().toString();
.. Your code to check whether password is blank ..
AndFilter andFilter = new AndFilter();
andFilter.and(new EqualFilter("<LDAP USER ATTRIBUTE>",username))
.and(new EqualFilter("<LDAP GROUP ATTRIBUTE>","<USER GROUP>"));
boolean isValidUser = ldapTemplate.authenticate("",andFilter.encode(),password);
... Your code to complete the authentication ...
{
I prefer this approach as it gives me finer control on how to authenticate the user. Here is the link to the sample I implemented previously.
Related
Hi I have a Rest WS using WebSecurityConfigurerAdapter to implement HTTP Basic auth.
The password is allowed to be updated and I need to let the WS to pick up updated password without restarting server
Following are the codes:
SecurityConfig
// init a user with credentials admin/password
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
//disable csrf
.csrf().disable()
//authentic all requests
.authorizeRequests().anyRequest().authenticated().and().httpBasic()
//disable session
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(inMemoryUserDetailsManager());
}
#Bean
public InMemoryUserDetailsManager inMemoryUserDetailsManager() {
Properties users = new Properties();
users.put("admin", "password,USER,enabled");
return new InMemoryUserDetailsManager(users);
}
}
The controller that will update password
#RestController
public class someController{
#Autowired
public InMemoryUserDetailsManager inMemoryUserDetailsManager;
// update password from password -> pass
#RequestMapping(...)
public updatePass(){
ArrayList<GrantedAuthority> grantedAuthoritiesList = new ArrayList<>();
grantedAuthoritiesList.add(new SimpleGrantedAuthority("USER"));
this.inMemoryUserDetailsManager.updateUser(new User("admin", "pass", grantedAuthoritiesList));
}
// another way that also doesn’t work
#RequestMapping(...)
public newUpdate(){
ArrayList<GrantedAuthority> grantedAuthoritiesList = new ArrayList<>();
grantedAuthoritiesList.add(new SimpleGrantedAuthority("USER"));
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken("admin", "pass",
grantedAuthoritiesList);
SecurityContext context = SecurityContextHolder.getContext();
context.setAuthentication(auth);
SecurityContextHolder.setContext(context);
}
}
After calling updatePass() with credential admin/password for the first time, I can see that the password has been updated to "pass" in debugger
I assume that if I'm to call updatePass() again, I should use admin/pass. However it turned out to be still using the old admin/password.
Sources I referred to when writing this code source1 source2
*I'm using Advance Rest Client to make the calls
When you update the password, you have to set the UserDetails in springSecurityContext object if the user is authenticated.
instead of using SecurityContext, I overwrote function loadUserByUsername of interface UserDetailsService to let spring security always pick up the latest pwd from DB.
while experimenting around with spring boot, security, and data.
i just came across this scenario:
i use H2 in memory DB and poblate it with one user with liquibase on startup
with username and password.
now i want spring security to authenticate against H2. for that purpose i have this code:
#Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsServiceImp);
}
and im implementing the userDetails as follows:
#Override
public UserDetails loadUserByUsername(String username) {
//this works, the user with pass is pulled
com.fix.demo.logic.user.User byUsername =
userRepository.findByUsername(username);
if (byUsername == null) {
System.out.println("No user found with username: ");
return null; //trow ex here
}
User user = new User(byUsername.getUsername(),
byUsername.getPassword(), true, true,
true, true, getAuthorities(Collections.singletonList("user")));
//System.out.println(user.toString());
//System.out.println(byUsername.toString()+ " "+byUsername.getPassword());
return user;
}
but my tests keep failing with
Authentication should not be null
and trying to log in will give me
bad credentials
what is necessary for my custom implementation of UserDetailsService to work?
this is the failing test:
#Test
public void loginWithValidUserThenAuthenticated() throws Exception {
FormLoginRequestBuilder login = formLogin()
.user("admin")
.password("root");
mockMvc.perform(login)
.andExpect(authenticated().withUsername("admin"));
}
One of the reasons is, the password might my encoded and you need to tell spring security to use an encoder. Add the following line to the configure override.
auth.setPasswordEncoder(passwordEncoder());
define the passwordEncoder bean.
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
I have configured a JDBC data source and autowired the JDBCTemplate to execute custom SQL queries. I also have a simple HTTP Basic authentication:
auth.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
However, I would like to use the user and password used for HTTP Basic to authenticate the user to the data base itself, i.e pass through the credentials of HTTP Basic to the data source and execute queries as the user who logged in with HTTP Basic authentication. I'm facing two issues here, one is that the username and password are in the application.properties file that I want to override every time a user authenticates and also (reload?) execute queries as that user instead of the ones specified in the properties file.
Update 1:
I could programmatically use username and password like below:
#Bean
#Primary
public DataSource dataSource() {
return DataSourceBuilder
.create()
.username("")
.password("")
.url("")
.driverClassName("")
.build();
}
But how to call this every time a user logs with the HTTP Basic auth with those credentials?
Use UserCredentialsDataSourceAdapter as #"M. Deinum" have suggested with some kind of filter or handling AuthenticationSuccessEvent.
Basically you should just call setCredentialsForCurrentThread method with current principal username and password.
You'll have to disable credential erasure for authentication manager in order to be able to retrieve user password after authentication.
#EnableWebSecurity
public static class Security extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.eraseCredentials(false) // for password retrieving
.inMemoryAuthentication()
.withUser("postgres").password("postgres1").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().and().authorizeRequests().mvcMatchers("/").fullyAuthenticated();
}
}
Datasource adapter:
#Bean
public UserCredentialsDataSourceAdapter dataSource(DataSourceProperties properties) {
final UserCredentialsDataSourceAdapter dataSourceAdapter = new UserCredentialsDataSourceAdapter();
dataSourceAdapter.setTargetDataSource(DataSourceBuilder.create()
.driverClassName(properties.getDriverClassName())
.url(properties.getUrl())
.username(properties.getUsername())
.password(properties.getPassword())
.type(SimpleDriverDataSource.class) // disable pooling
.build());
((SimpleDriverDataSource) dataSourceAdapter.getTargetDataSource()).setDriverClass(org.postgresql.Driver.class); //binder won't set it automatically
return dataSourceAdapter;
}
AuthenticationSuccessHandler:
#Component
public static class AuthenticationHandler /*implements ApplicationListener<AuthenticationSuccessEvent> use that if your spring version is less than 4.2*/ {
private final UserCredentialsDataSourceAdapter dataSourceAdapter;
#Autowired
public AuthenticationHandler(UserCredentialsDataSourceAdapter dataSourceAdapter) {
this.dataSourceAdapter = dataSourceAdapter;
}
#EventListener(classes = AuthenticationSuccessEvent.class)
public void authenticationSuccess(AuthenticationSuccessEvent event) {
final Authentication authentication = event.getAuthentication();
final User user = (User) authentication.getPrincipal();
dataSourceAdapter.setCredentialsForCurrentThread(user.getUsername(), user.getPassword()); // <- the most important part
}
}
Or you can use Filter instead of event listener:
#Component
public static class DataSourceCredentialsFilter extends GenericFilterBean {
private final UserCredentialsDataSourceAdapter dataSourceAdapter;
#Autowired
public DataSourceCredentialsFilter(UserCredentialsDataSourceAdapter dataSourceAdapter) {
this.dataSourceAdapter = dataSourceAdapter;
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
final User user = (User) authentication.getPrincipal();
dataSourceAdapter.setCredentialsForCurrentThread(user.getUsername(), user.getPassword());
chain.doFilter(request, response);
dataSourceAdapter.removeCredentialsFromCurrentThread();
}
}
See full example here.
I have two tables 'user' and 'role'.I want to create a login api (e.g '/login') which will take username and password as a json data. I want to check if given credential is a valid credential and if it is,then I want to set the user as authenticated user so that he/she may have the protected resources. I am new to spring boot framework and I don't know how to do so.I have read the offical documentation but cannot find any resources.Could someone help me on this?
You have number of choices to implement such authentication in Spring.
Case 1:- If you are building REST services then you can implement security in following ways:
i) - you can use Basic-Authentication to authenticate your user.
ii) - you can use OAuth2 to authenticate and authorize your user.
Case 2: If you are building web application
i) - you can use auth token (in case of Single page application SPA)
ii) - you can use session based authentication (traditional login form and all)
I Guess you are in beginner mode so i will recommend you to firstly understand the control flow user authentication in web app via login form. So Let's go through some code.
I'm assuming that you have set a basic spring project and now you are implementing security.
USER - Hibernate entity for your user table;
ROLE - Hibernate entity for your role table
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomAuthProvider customAuthProvider;
#Override
protected void configure(HttpSecurity http) throws Exception {
// everyone is allowed tp view login page
http.authorizeRequests().antMatchers("/login").permitAll().and();
http.authorizeRequests().antMatchers("custom_base_path" + "**").authenticated().and().
formLogin().loginPage("/loginForm).loginProcessingUrl("/loginUser")
.usernameParameter("username").passwordParameter("password")
.defaultSuccessUrl("custom_base_path+ "home", true);
#Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthProvider);
}
//CustomAuthProvider
#Component
public class CustomAuthentiationProvider implements AuthenticationProvider{
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String userid = authentication.getName();
String password = authentication.getCredentials().toString();
Authentication auth = null;
try {
//write your custom logic to match username, password
boolean userExists = your_method_that_checks_username_and_password
if(userExists ){
List<Role> roleList= roleDao.getRoleList(userid);
if (roleList == null || roleList.isEmpty()) {
throw new NoRoleAssignedException("No roles is assigned to "+userid);
}
auth = new UsernamePasswordAuthenticationToken(userid, password,getGrantedAuthorities(roleList));
}
} catch (Exception e) {
log.error("error", e);
}
return auth;
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
public List<GrantedAuthority> getGrantedAuthorities(List<Role> roleList) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
for (Role role : roleList) {
authorities.add(new SimpleGrantedAuthority(role.getRoleName());
}
return authorities;
}
}
NOTE: Please consider these codes to understand the logic of authentication. don't consider as perfect code(Not for production env.). You can ping me anytime i'll suggest you more about that.
I'm creating authentication service in Spring.
I'm using UserDetailsService to get form variables, but i found that loadUserByUsername has only one variable - userName.
How to get password ?
public class userAuthentication implements UserDetailsService{
private #Autowired
ASPWebServicesUtils aspWebServicesUtils;
#Override
public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
//how to get password ?
User user = new User("test", "test", true, true, true, true, getAuthorities(true));
return user;
}
private List<GrantedAuthority> getAuthorities(boolean isAdmin){
List<GrantedAuthority> authorityList = new ArrayList<GrantedAuthority>(2);
authorityList.add(new SimpleGrantedAuthority("USER_ROLE"));
if(isAdmin){
authorityList.add(new SimpleGrantedAuthority("ADMIN_ROLE"));
}
return authorityList;
}
//...
}
Thanks
If you look at the User object, the second parameter in the constructor is the password.
The UserDetailsService is used to load the user from a back-end structure like database. The loadUserByUsername method is called when a user tries to login with a username and password, then it is the responsibility of the service to load the user definition and return it to the security framework. The required details includes data like username, password, accountNonExpired, credentialsNonExpired, accountNonLocked and authorities.
Once the spring security receives the user object, it will validate the user against the password entered by the user and other data like user account status (accountNonExpired, credentialsNonExpired etc)
Some of the standard (out-of-the-box) mechanisms to retrieve the user information and provide authentication information are:
inMemoryAuthentication
jdbcAuthentication
ldapAuthentication
userDetailsService
If the above does not suit your purpose and you need to have a custom solution, you can create and configure a new authentication provider like so:
Security Configuration:
#Configuration
#EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
#Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(new CustomAuthenticationProvider());
}
....
}
Authentication Provider:
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
String name = authentication.getName();
// You can get the password here
String password = authentication.getCredentials().toString();
// Your custom authentication logic here
if (name.equals("admin") && password.equals("pwd")) {
Authentication auth = new UsernamePasswordAuthenticationToken(name,
password);
return auth;
}
return null;
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
I believe a UserDetailsService is supposed to be used to acquire a UserDetails object from some back end storage, database, flat file, etc. Once you have that UserDetails, spring security (or you) have to compare it to the username (or other principals) and password (the credentials) provided by the user in order to authenticate that user.
I don't think you are using it the way it is intended.
Get password in UserDetailsService implementation by request.getParameter("password"):
public class MyUserDetailsService implements UserDetailsService {
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String password = request.getParameter("password"); // get from request parameter
......
}
}
RequestContextHolder is base on ThreadLocal.
If your project is base on Spring Framework (not Spring Boot), add RequestContextListener to web.xml
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
XML Implementation:
<authentication-manager alias="loginAuthenticationManager">
<authentication-provider ref="loginAuthenticationProvider" />
</authentication-manager>
<!-- Bean implementing AuthenticationProvider of Spring Security -->
<beans:bean id="loginAuthenticationProvider" class="com.config.LoginAuthenticationProvider">
</beans:bean>
AuthenticationProvider:
public class LoginAuthenticationProvider implements AuthenticationProvider {
#Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
String name = authentication.getName();
// You can get the password here
String password = authentication.getCredentials().toString();
// Your custom authentication logic here
if (name.equals("admin") && password.equals("pwd")) {
List<GrantedAuthority> grantedAuths = new ArrayList<>();
grantedAuths.add(new SimpleGrantedAuthority("ROLE_USER"));
return new UsernamePasswordAuthenticationToken(name, password, grantedAuths);
}
return null;
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
the loadUserByUsername(String name) is a method defined on an interface (userServicedetails I think), which your service implements. You have to write the implementation.
Just as you have to write the implementation for getPassword() or similar ... spring does not provide that. I imagine the password is stored in your user object, but you wrote that ... did you create a getPassword() method ?