How do I configure ActiveDirectoryLdapAuthenticationProvider with a username, password or custom searchfilter? - spring

The only configurable parts of this Authenticator seem to be the URL and the Domain. I'm unable to figure out how I would pass on a username or a password or my own search filter.
I looked at the source code of this class but most of the methods seem to be operating on UsernamePasswordAuthenticationToken from which username and password are being extracted. I was unable to find the methods which are used to create this object.
Here's what I'm currently using for AD LDAP Authentication:
package com.test;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider;
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
ActiveDirectoryLdapAuthenticationProvider adProvider = new ActiveDirectoryLdapAuthenticationProvider("domain.org",
"ldap://activedirectory-url:389");
adProvider.setConvertSubErrorCodesToExceptions(true);
adProvider.setUseAuthenticationRequestCredentials(true);
auth.authenticationProvider(adProvider);
}
}

Related

403 forbidden error on authentication filter

I am working on a basic spring boot api using mysql as database
I created an endpoint for signup user("/users") which is bcrypt the password
while login i created a authentication filter which is adding jwt token in the header of response
but while accesing endpoint ("/login") i am getting 403 error,
I have already configured the ant match for request named "/login"
**Web Security Configuration **
package com.mukul.app.mobileappws.security;
import com.mukul.app.mobileappws.security.FIlter.AuthenticationFilter;
import com.mukul.app.mobileappws.services.UserService;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
#Configuration
#EnableWebSecurity
public class ConfigurationSecurity extends WebSecurityConfigurerAdapter {
UserService userService;
BCryptPasswordEncoder bcrypt;
ConfigurationSecurity(UserService u, BCryptPasswordEncoder b) {
this.userService = u;
this.bcrypt = b;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// http.authorizeRequests().antMatchers(HttpMethod.POST,
// "/users").permitAll().anyRequest()
// .authenticated();
//
AuthenticationFilter af = new AuthenticationFilter(authenticationManager());
http.csrf().disable();
http.authorizeRequests().antMatchers(HttpMethod.POST,
"/users").permitAll();
http.authorizeRequests().antMatchers("/login").permitAll();
http.authorizeRequests().anyRequest()
.authenticated();
http.addFilter(af);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(bcrypt);
}
}
Authentication filter
package com.mukul.app.mobileappws.security.FIlter;
import java.io.IOException;
import java.util.Date;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.mukul.app.mobileappws.security.SecurityConstants;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
public class AuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private AuthenticationManager authManager;
public AuthenticationFilter(AuthenticationManager am) {
this.authManager = am;
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
final String email = request.getParameter("email");
final String password = request.getParameter("password");
return authManager.authenticate(new UsernamePasswordAuthenticationToken(email, password));
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
Authentication auth) throws IOException, ServletException {
// generate token
User u = (User) auth.getPrincipal();
String email = u.getUsername();
String token = Jwts.builder()
.setSubject(email)
.setExpiration(new Date(System.currentTimeMillis() + SecurityConstants.EXPIRE))
.signWith(SignatureAlgorithm.HS512, SecurityConstants.SECRET)
.compact();
response.addHeader(SecurityConstants.HEADER, SecurityConstants.PREFIX + token);
super.successfulAuthentication(request, response, chain, auth);
}
}
enter image description here
I think your configuration is okay.
http.addFilter(authFilter) will put filter at appropriate position by examining the filter type.
In your case, I suspect issue is not triggering login request properly. As per the content in given repo, I ran the project and used embedded H2 instead of full blown database.
This is how you need to trigger your request if you are reading from request.getParameter(parameterName). Please note that I have received 404 error because Spring is trying to redirect me to '/' post successful login which doesn't exist. :)
With Spring Security I always had problem with CSRF on login, because the page doesn't have the CSRF token and POST is not allowed without it, try to check with it.

Java Spring - Active Directory- How can I Get AD User Details (telNumber, full name, mail , address, description)?

In my college project i would like to get user informations from an AD Server such as the telephone number, the mail, the full name after an authentication.
So i use the default spring security login page and after the authentication, i get the dn and the permissions with an Authentication object. I would like to know how can i get the details of an ad user.
I would like to get his phone number to send a message with an API. This part is already working. I just need to extract the Ad user details to do it.
You will find my code below :
SecurityConfiguration.java :
package com.le_chatelet.le_chatelet_back.ldap;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider;
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Bean
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider =
new ActiveDirectoryLdapAuthenticationProvider( "mydomain.com", "ldap://adserverip:389");
activeDirectoryLdapAuthenticationProvider.setConvertSubErrorCodesToExceptions(true);
activeDirectoryLdapAuthenticationProvider.setUseAuthenticationRequestCredentials(true);
return activeDirectoryLdapAuthenticationProvider;
}
#Override
protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception{
authenticationManagerBuilder
.authenticationProvider(activeDirectoryLdapAuthenticationProvider());
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception{
httpSecurity
.authorizeRequests()
.anyRequest()
.fullyAuthenticated()
.and()
.formLogin();
}
}
LoginController.java :
package com.le_chatelet.le_chatelet_back.ldap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.stream.Collectors;
#RestController
public class LoginController {
#Autowired
private UserInterface userInterface;
Logger logger = LoggerFactory.getLogger(LoginController.class);
#GetMapping("/hello")
public String sayHello()
{
return "hello world";
}
#GetMapping("/user")
#ResponseBody
public Authentication getLoggedUserDetail(Authentication authentication) {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
//get username
String username = authentication.getName();
logger.info("username : "+username);
// concat list of authorities to single string seperated by comma
String authorityString = authentication
.getAuthorities()
.stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.joining(","));
String role = "role_A";
boolean isCurrentUserInRole = authentication
.getAuthorities()
.stream()
.anyMatch(role::equals);
return authentication;
}
}
If someone can show me code example it would be appreciated.
You can set the a UserDetailsContextMapper on your Provider which allows custom strategy to be used for creating the UserDetails that will be stored as the principal in the Authentication.
provider.setUserDetailsContextMapper(new PersonContextMapper());
Then you can use the #AuthenticationPrincipal annotation in your Controller to get the Person (or a custom class) instance.
#GetMapping("/phone-number")
public String phoneNumber(#AuthenticationPrincipal Person person) {
return "Phone number: " + person.getTelephoneNumber();
}
You can find a full LDAP sample application provided by the Spring Security team.

AuthenticationManager.authenticate method not getting called

I am trying to follow the API Key authentication code from this answer:
https://stackoverflow.com/a/48448901
I created my filter class:
package com.propfinancing.CADData.web;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.LoggerFactory;
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
public class PullAPIKeyFromHeaderFilter extends AbstractPreAuthenticatedProcessingFilter {
private String apiKeyHeaderName;
public PullAPIKeyFromHeaderFilter(String apiKeyHeaderName) {
this.apiKeyHeaderName = apiKeyHeaderName;
}
#Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
String headerValue = request.getHeader(apiKeyHeaderName);
return request.getHeader(headerValue);
}
#Override
protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
return apiKeyHeaderName;
}
}
And then I implemented my security configuration:
package com.propfinancing.CADData.web;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
#Configuration
#EnableWebSecurity
#Order(1)
public class APIKeySecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${caddata.apiKey.header.name}")
private String apiKeyHeaderName;
#Value("${caddata.apiKey}")
private String apiKey;
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
PullAPIKeyFromHeaderFilter pullAPIKeyFromHeaderfilter = new PullAPIKeyFromHeaderFilter(apiKeyHeaderName);
AuthenticationManager authManager = new AuthenticationManager() {
#Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
String principal = (String) authentication.getPrincipal();
if (!apiKey.equals(principal))
throw new BadCredentialsException("Invalid API key");
authentication.setAuthenticated(true);
return authentication;
}
};
pullAPIKeyFromHeaderfilter.setAuthenticationManager(authManager);
httpSecurity.antMatcher("/**");
httpSecurity.addFilter(pullAPIKeyFromHeaderfilter);
httpSecurity.requiresChannel().anyRequest().requiresSecure();
httpSecurity.csrf().disable();
httpSecurity.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry urlAuthConfigurer = httpSecurity.authorizeRequests();
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.AuthorizedUrl authorizedUrl = urlAuthConfigurer.anyRequest();
authorizedUrl.authenticated();
}
}
When I do an external call to the application with the header as part of the request, I get a 403 Forbidden response.
I can see the filter pulling the key from the header. That part is working.
But, the authenticate() method is not being called to check if the header is valid.
I am not sure what I missed, the code looks the same to me.
Any ideas?
Looks like the wrong base class, per https://docs.spring.io/spring-security/site/docs/4.0.x/apidocs/org/springframework/security/web/authentication/preauth/AbstractPreAuthenticatedProcessingFilter.html :
The purpose is then only to extract the necessary information on the
principal from the incoming request, rather than to authenticate them.
Try extending https://docs.spring.io/spring-security/site/docs/4.0.x/apidocs/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.html instead.
I was not able to get the code above to work, but I changed it to use the second answer on the thread (Using a Filter) https://stackoverflow.com/a/63852212 It works as expected.

Add custom claim in authentication filter. Get user id in filter. Spring boot

I want to add user's id as custom claim into my token.
But i cant get users id in filter because dependency injection isnt working in filters. I tried using constructor of my UserService but in this service i have repository which im #Autowiring so in debug mode i have seen that userRepository field is null.
My question is how i will add this custom claim?
Maybe is it another way to add this.
I was following this tutorial (without "Aside" chapter)
https://auth0.com/blog/implementing-jwt-authentication-on-spring-boot/#User-Authentication-and-Authorization-on-Spring-Boot
this is my filter where im trying to add this claim
package com.kamczi.auth;
import com.auth0.jwt.JWT;
import static com.auth0.jwt.algorithms.Algorithm.HMAC512;
import com.fasterxml.jackson.databind.ObjectMapper;
import static com.kamczi.auth.SecurityConstants.EXPIRATION_TIME;
import static com.kamczi.auth.SecurityConstants.HEADER_STRING;
import static com.kamczi.auth.SecurityConstants.SECRET;
import static com.kamczi.auth.SecurityConstants.TOKEN_PREFIX;
import com.kamczi.entities.User;
import com.kamczi.repository.UserRepository;
import com.kamczi.services.UserService;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
/**
*
* #author Kamil
*/
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private AuthenticationManager authenticationManager;
public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
try{
User user = new ObjectMapper()
.readValue(request.getInputStream(), User.class);
return authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
user.getUsername(),
user.getPassword(),
new ArrayList<>())
);
} catch(IOException e){
throw new RuntimeException(e);
}
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
String username = ((org.springframework.security.core.userdetails.User) authResult.getPrincipal()).getUsername();
String token = JWT.create()
//.withClaim("id", userService.findByUsername(username).getUser_id()) need implementation
.withSubject(username)
.withExpiresAt(new Date(System.currentTimeMillis()+EXPIRATION_TIME))
.sign(HMAC512(SECRET.getBytes()));
response.addHeader(HEADER_STRING, TOKEN_PREFIX + token);
}
}
Try to mark your filter with #Component. The #Autowired is working only with Spring-managed beans (components).
Or you can add the filter manually using the constructor and pass the repository to it.
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
UsersRepository usersRepo;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterAt(new JWTAuthenticationFilter(usersRepo), UsernamePasswordAuthenticationFilter.class);
}
}

Swagger and springfox

I'am using swagger version 2.2.2. If I go to the address, http://localhost:8080/swagger-ui.html
, I will directly get the swagger UI. Is there any way, a security layer can be added, like, user should be prompted to enter for user id and password before the swagger UI display?
It is possible, I did it with this security configuration (assuming you are using spring):
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.csrf().disable();
final InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
final PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
manager.createUser(User.withUsername("user").password(encoder.encode("password")).roles("ACTUATOR", "ADMIN").build());
http.authorizeRequests().antMatchers("/swagger-ui**", "/v2/api-docs/**").hasRole("ACTUATOR").and().httpBasic()
.and().userDetailsService(manager);
}
}

Resources