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
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.
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.
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.
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);
}
}
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);
}
}