How to resolve error spring boot oauth2 google in heroku? - java

I am using Google OAuth to login into my springboot apps. It works perfectly in my local computer. But it shows this error when I deploy it to heroku.
Method springSecurityFilterChain in org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration required a bean of type 'org.springframework.security.oauth2.client.registration.ClientRegistrationRepository' that could not be found.
Action: Consider defining a bean of type 'org.springframework.security.oauth2.client.registration.ClientRegistrationRepository' in your configuration.
Here is my WebSecurityConfiguration.
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDao userDao;
#Autowired
private PenggunaService penggunaService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/login", "/oauth/**", "/js/**","/css/**","/image/**").permitAll()
.antMatchers("/admin").hasAnyAuthority("admin")
.anyRequest().authenticated()
.and()
.oauth2Login()
.userInfoEndpoint()
.userAuthoritiesMapper(authoritiesMapper())
.and()
.successHandler(new AuthenticationSuccessHandler() {
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication auth) throws IOException, ServletException {
DefaultOidcUser oauthUser = (DefaultOidcUser) auth.getPrincipal();
String email = oauthUser.getAttribute("email");
penggunaService.processOAuthPostLogin(email, auth);
response.sendRedirect("/");
}
})
.and()
.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/").permitAll();
}
private GrantedAuthoritiesMapper authoritiesMapper(){
return (authorities) -> {
String emailAttrName = "email";
String email = authorities.stream()
.filter(OAuth2UserAuthority.class::isInstance)
.map(OAuth2UserAuthority.class::cast)
.filter(userAuthority -> userAuthority.getAttributes().containsKey(emailAttrName))
.map(userAuthority -> userAuthority.getAttributes().get(emailAttrName).toString())
.findFirst()
.orElse(null);
if (email == null) {
return authorities; // data email tidak ada di userInfo dari Google
}
Pengguna user = userDao.findByEmailPengguna(email);
if(user == null) {
return authorities; // email user ini belum terdaftar di database
}
List<Permission> userAuthorities = user.getRole().getPermissions();
if (userAuthorities.isEmpty()) {
return authorities; // Return the 'unmapped' authorities
}
return Stream.concat(
authorities.stream(),
userAuthorities.stream()
.map(Permission::getIdPermission)
.map(SimpleGrantedAuthority::new)
).collect(Collectors.toCollection(ArrayList::new));
};
}
#Bean
public SpringSecurityDialect springSecurityDialect() {
return new SpringSecurityDialect();
}
}
Thank you in advance.

Related

Create JWT Token within UsernamePasswordAuthenticationFilter or Controller?

I try to generate a JWT Token with Spring Security but I don't how to do it correctly (with the best practice).
Do I should "intercept" the authenticate method within UsernamePasswordAuthenticationFilter somewhere and generate token inside it ?
Or it is better to use AuthenticationManager autowired in the controller '/login' ?
I'm afraid to authenticate the user twice if I use the controller mechanism.
I used this tutorial : tutorial Jwt Token
Here is my code :
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserService userService;
#Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
// #Autowired
// private UserDetailsService jwtUserDetailsService;
#Autowired
private JwtTokenFilter jwtTokenFilter;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.and()
.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.cors().and()
.csrf().disable()
.authorizeRequests()
.antMatchers("/css/**", "/login/**", "/register/**").permitAll()
.antMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
//.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
.formLogin()
.usernameParameter("email")
//.loginPage("http://localhost:4200/login").failureUrl("/login-error")
.and()
.logout()
.permitAll();
http
.addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class);
}
#Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
#Bean
public CustomDaoAuthenticationProvider authenticationProvider() {
CustomDaoAuthenticationProvider authenticationProvider = new CustomDaoAuthenticationProvider();
authenticationProvider.setPasswordEncoder(new BCryptPasswordEncoder());
authenticationProvider.setUserDetailsService(userService);
return authenticationProvider;
}
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebConfig() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins(
"http://localhost:4200")
.allowedMethods("GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS")
.allowedHeaders("Content-Type", "X-Requested-With", "accept", "Origin", "Access-Control-Request-Method",
"Access-Control-Request-Headers", "Authorization", "Cache-Control",
"Access-Control-Allow-Origin")
.exposedHeaders("Access-Control-Allow-Origin", "Access-Control-Allow-Credentials")
.allowCredentials(true).maxAge(3600);
}
};
}
}
Token Filter
public class JwtTokenFilter extends GenericFilterBean {
private JwtTokenProvider jwtTokenProvider;
public JwtTokenFilter(JwtTokenProvider jwtTokenProvider) {
this.jwtTokenProvider = jwtTokenProvider;
}
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain) throws IOException, ServletException {
String token = jwtTokenProvider.resolveToken((HttpServletRequest) req);
if (token != null && jwtTokenProvider.validateToken(token)) {
Authentication auth = token != null ? jwtTokenProvider.getAuthentication(token) : null;
SecurityContextHolder.getContext().setAuthentication(auth);
}
filterChain.doFilter(req, res);
}
}
Token Provider
#Component
public class JwtTokenProvider {
#Value("${security.jwt.token.secret-key:secret}")
private String secretKey = "secret";
#Value("${security.jwt.token.expire-length:3600000}")
private long validityInMilliseconds = 3600000; // 1h
#Autowired
private UserDetailsService userDetailsService;
#PostConstruct
protected void init() {
secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes());
}
public String createToken(String username, List<String> roles) {
Claims claims = Jwts.claims().setSubject(username);
claims.put("roles", roles);
Date now = new Date();
Date validity = new Date(now.getTime() + validityInMilliseconds);
return Jwts.builder()//
.setClaims(claims)//
.setIssuedAt(now)//
.setExpiration(validity)//
.signWith(SignatureAlgorithm.HS256, secretKey)//
.compact();
}
public Authentication getAuthentication(String token) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(getUsername(token));
return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
}
public String getUsername(String token) {
return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject();
}
public String resolveToken(HttpServletRequest req) {
String bearerToken = req.getHeader("Authorization");
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7, bearerToken.length());
}
return null;
}
public boolean validateToken(String token) {
try {
Jws<Claims> claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token);
if (claims.getBody().getExpiration().before(new Date())) {
return false;
}
return true;
} catch (JwtException | IllegalArgumentException e) {
throw new InvalidJwtAuthenticationException("Expired or invalid JWT token");
}
}
}
Given your context, the controller is responsible for issuing a new token (after validating credentials) while the filter is responsible for authenticating the user against the given token. The controller should not populate the security context (authenticate user), it is the filter's responsibility.
To better understand the two phases:
Spring uses two filters to authenticate and log in a user.
See UsernamePasswordAuthenticationFilter and SecurityContextPersistenceFilter in a "username/password" scenario, from the Spring Security project: the first one processes an authentication attempt (username/password) while the latter populates the security context from a SecurityContextRepository (from a session in general).

How can I resolve 'Unresolved compilation problems for authenticationFailureHandler' in Spring Security?

I am using Spring Security to build an authentication entry point for my web app. Now mr registration works well aside for the fact that a user is unable to login due to a compilation error resulting from my successHandler() and failureHandler() method.
The error logged is: java.lang.Error: Unresolved compilation problems:
successHandler cannot be resolved to a variable
authenticationFailureHandler cannot be resolved to a variable
I am not sure what I am doing wrong. I am pasting the security configuration code of my spring boot app. Where do I need to add the required variable or parameters (if any) in order to resolve this?
I've tried to create 2 variables with private modifiers that denote to the same parameters of the Handler which still doesn't work
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
#Autowired
private DataSource dataSource;
#Value("${spring.queries.users-query}")
private String usersQuery;
#Value("${spring.queries.roles-query}")
private String rolesQuery;
#Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.
jdbcAuthentication()
.usersByUsernameQuery(usersQuery)
.authoritiesByUsernameQuery(rolesQuery)
.dataSource(dataSource)
.passwordEncoder(bCryptPasswordEncoder);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/login").permitAll()
.antMatchers("/signup_employer").permitAll()
.antMatchers("/registrations").permitAll()
.antMatchers("/admin/**").hasAuthority("ADMIN").anyRequest()
.authenticated().and().csrf().disable()
.formLogin()
.loginPage("/login").failureUrl("/login?error=true")
.defaultSuccessUrl("/admin")
.usernameParameter("email")
.passwordParameter("password")
.successHandler(successHandler)
.failureHandler(authenticationFailureHandler)
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/logout").deleteCookies("JSESSIONID").deleteCookies("my-rememberme")
.logoutSuccessHandler(logoutSuccessHandler())
.and().rememberMe()
.tokenRepository(persistentTokenRepository())
.and()
// .exceptionHandling().accessDeniedHandler(accessDeniedHandler())
//.and()
.headers().cacheControl().disable()
.and().sessionManagement()
.sessionFixation().migrateSession()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.invalidSessionUrl("/invalidSession")
.maximumSessions(1)
.expiredUrl("/invalidSession");
}
#Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl tokenRepositoryImpl = new JdbcTokenRepositoryImpl();
tokenRepositoryImpl.setDataSource(dataSource);
return tokenRepositoryImpl;
}
#Bean
public LogoutSuccessHandler logoutSuccessHandler() {
return new CustomLogoutSuccessHandler();
}
#Bean
public AccessDeniedHandler accessDeniedHandler() {
return new CustomAccessDeniedHandler();
}
#Bean
public AuthenticationEntryPoint unauthorizedEntryPoint() {
return (request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**", "/static/**", "/css/**", "/email_templates/**", "/error/**", "/font-awesome/**", "/fonts/**", "/res/**", "/vendor/**", "/js/**", "/img/**");
}
#Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
}
login success handler:
public class MySimpleUrlAuthenticationSuccessHandler implements
AuthenticationSuccessHandler {
protected final Log logger = LogFactory.getLog(this.getClass());
protected int SessionTimeout = 1 * 60;
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
public MySimpleUrlAuthenticationSuccessHandler() {
super();
}
// API
#Override
public void onAuthenticationSuccess(final HttpServletRequest request, final
HttpServletResponse response, final Authentication authentication) throws
IOException {
handle(request, response, authentication);
clearAuthenticationAttributes(request);
}
// IMPL
protected void handle(final HttpServletRequest request, final
HttpServletResponse response, final Authentication authentication) throws
IOException {
final String targetUrl = determineTargetUrl(authentication);
if (response.isCommitted()) {
logger.debug("Response has already been committed. Unable to
redirect to " + targetUrl);
return;
}
redirectStrategy.sendRedirect(request, response, targetUrl);
}
protected String determineTargetUrl(final Authentication authentication) {
boolean isUser = false;
boolean isAdmin = false;
final Collection<? extends GrantedAuthority> authorities =
authentication.getAuthorities();
for (final GrantedAuthority grantedAuthority : authorities) {
if (grantedAuthority.getAuthority().equals("USER")) {
isUser = true;
break;
} else if (grantedAuthority.getAuthority().equals("ADMIN")) {
isAdmin = true;
break;
}
}
if (isUser) {
return "/homepage.html";
} else if (isAdmin) {
return "/admin";
} else {
throw new IllegalStateException();
}
}
/**
* Removes temporary authentication-related data which may have been stored
in the session
* during the authentication process.
*/
protected final void clearAuthenticationAttributes(final HttpServletRequest
request) {
final HttpSession session = request.getSession(false);
if (session == null) {
return;
}
session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
}
protected RedirectStrategy getRedirectStrategy() {
return redirectStrategy;
}
public void setRedirectStrategy(final RedirectStrategy redirectStrategy) {
this.redirectStrategy = redirectStrategy;
}
}
Those two lines inside the configure(HttpSecurity) method refers to properties/variables which do not seem to exist.
.successHandler(successHandler)
.failureHandler(authenticationFailureHandler)
I see you've created your MySimpleUrlAuthenticationSuccessHandler. Provide an instance of that class to successHandler. And do the same with failureHandler with an instance of your custom/bundled AuthenticationFailureHandler.
I suppose that the warning you mention requires defining the AuthenticationSuccessHandler as Bean.
#Configuration
class MyConfigurationClass {
...
#Bean
AuthenticationSuccessHandler myAuthenticationSuccessHandler() {
return new MyCustomOrBundledAuthenticationSuccessHandler();
}
}
You can then
.successHandler(myAuthenticationSuccessHandler())

spring auth error No AuthenticationProvider

I'm trying to build auth system with Spring Boot Security.
So I have custom auth provider (without #Component annotation)
public class CustomAuthProvider extends DaoAuthenticationProvider {
#Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
String name = authentication.getName();
String password = authentication.getCredentials().toString();
if (authentication.isAuthenticated()) {
return authentication;
}
if ("user".equals(name) && "password".equals(password)) {
return new UsernamePasswordAuthenticationToken(
name, password, new ArrayList<GrantedAuthority>(Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"))));
} else {
return null;
}
}
#Override
public boolean supports(Class<?> authentication) {
return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}
}
which declared as a bean
#Bean
public DaoAuthenticationProvider authProvider() {
final CustomAuthProvider authProvider = new CustomAuthProvider();
authProvider.setUserDetailsService(userDetailsService);
return authProvider;
}
Here is configuration:
#Override
protected void configure(
AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/anonymous*").anonymous()
.antMatchers("/login*").permitAll()
.antMatchers("/user/registration*").permitAll()
.anyRequest().authenticated()
.and().formLogin()
.loginProcessingUrl("/login");
}
When I'm trying post query to localhost:8080/login I'm getting sign-in form with message
No AuthenticationProvider found for
org.springframework.security.authentication.UsernamePasswordAuthenticationToken
spring's default request parameters for authentication are: username and password
you are sending email instead of username so it throws an exception somewhere in your authentication manager.
if you want to override the default values you can simply specify that in your HTTP security configs:
.formLogin()
.loginProcessingUrl("/login")
.usernameParameter("email")
.passwordParameter("password")

Angular 6 Basic Auth returns 401 from client

So I've looked around for the answer to my problem for quite a while now and tried many suggestions but I can't seem to find an answer.
The problem is, when I use Postman to check if basic auth works I get a 200 code back and it's all good, but as soon as I try to authenticate using my Login Component I get the code 401 back and says "Full authentication is required to access this resource".
I'm fairly new to Angular and completely new to using Basic Auth so I have no idea why does it work with Postman and why doesn't it work from the app.
Any help is appreciated
Below are the relevant codes
log-in.component.ts:
onLogin(form: NgForm) {
/* ... */
let headers = new Headers();
let userCredentials = user.userName + ":" + user.password;
headers.append("Origin", "http://localhost:8080");
headers.append("Authorization", "Basic " + btoa(userCredentials));
return this.http.post('http://localhost:8080/api/users/login', headers).subscribe(
(response) => {
/* ... */
},
(error) => {
console.log(error);
}
);
}
Endpoint on the server side:
#PostMapping(LOG_IN)
public ResponseEntity<User> login() {
return ResponseEntity.ok().build();
}
WebSecurityConfig:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf().disable()
.authorizeRequests()
.antMatchers("/h2/**").permitAll()
.anyRequest().authenticated()
.and()
.httpBasic()
.authenticationEntryPoint(getBasicAuthEntryPoint())
.and()
.headers()
.frameOptions().disable()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Autowired
protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("admin").password("1234").roles("ADMIN");
}
#Autowired
private UserDetailsService userDetailsService;
#Autowired
protected void configureAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
#Bean
public CustomBasicAuthenticationEntryPoint getBasicAuthEntryPoint(){
return new CustomBasicAuthenticationEntryPoint();
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
CustomBasicAuthenticationEntryPoint:
public class CustomBasicAuthenticationEntryPoint extends BasicAuthenticationEntryPoint {
#Override
public void commence(final HttpServletRequest request,
final HttpServletResponse response,
final AuthenticationException authException) throws IOException, ServletException {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.addHeader("WWW-Authenticate", "Basic realm=" + getRealmName() + "");
PrintWriter writer = response.getWriter();
writer.println("HTTP Status 401 : " + authException.getMessage());
}
#Override
public void afterPropertiesSet() throws Exception {
setRealmName("MY REALM");
super.afterPropertiesSet();
}
}
MyUserDetailsService:
#Service
public class MyUserDetailsService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Autowired
private AuthenticatedUser authenticatedUser;
#Override
#Transactional(readOnly = true)
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Optional<User> oUser = userRepository.findByUserName(username);
if (!oUser.isPresent()) {
throw new UsernameNotFoundException(username);
}
User user = oUser.get();
authenticatedUser.setUser(user);
Set<GrantedAuthority> grantedAuthorities = new HashSet<>();
grantedAuthorities.add(new SimpleGrantedAuthority(user.getRole().toString()));
return new org.springframework.security.core.userdetails.User(user.getUserName(), user.getPassword(), grantedAuthorities);
}
}
You need to pass the headers as 3rd parameter for the post method. The 2nd one is the body
return this.http.post('http://localhost:8080/api/users/login', {}, {headers}).subscribe(
(response) => {
If you are using angular 6, you should really be using the new HttpClient class, the old Http class being deprecated
This is because the browser send OPTION method to the server before send your request, , try to update your security configuration by allowing OPTION method. like this
protected void configure(HttpSecurity http) throws Exception
{
http
.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS,"/path/to/allow").permitAll()//allow CORS option calls
.antMatchers("/resources/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.httpBasic();
}

Spring Security Active Directory LDAP Authentication without full name

Using Spring Security 3.2 I have configured ActiveDirectoryLdapAuthenticationProvider. I am able to authenticate using full name example sharon#mydomain.com but when I try to authenticate with just username 'sharon' I get the below error
2015-12-21_17:07:00.752 DEBUG o.s.s.l.a.a.ActiveDirectoryLdapAuthenticationProvider - authenticate - Processing authentication request for user: sharon
2015-12-21_17:07:00.793 DEBUG o.s.s.l.SpringSecurityLdapTemplate - searchForSingleEntryInternal - Searching for entry under DN '', base = 'dc=mydomain,dc=com', filter = '(&(objectClass=user)(userPrincipalName={0}))'
2015-12-21_17:07:00.793 INFO o.s.s.l.SpringSecurityLdapTemplate - searchForSingleEntryInternal - Ignoring PartialResultException
2015-12-21_17:07:00.794 DEBUG o.s.s.l.a.LdapAuthenticationProvider - authenticate - Processing authentication request for user: gdcadmin
2015-12-21_17:07:00.796 DEBUG o.s.s.l.a.BindAuthenticator - bindWithDn - Attempting to bind as cn=gdcadmin,cn=Users,dc=mydomain,dc=com,dc=springframework,dc=org
2015-12-21_17:07:00.796 DEBUG o.s.s.l.DefaultSpringSecurityContextSource - setupEnvironment - Removing pooling flag for user cn=gdcadmin,cn=Users,dc=mydomain,dc=com,dc=springframework,dc=org
2015-12-21_17:07:00.858 DEBUG o.a.m.f.codec.ProtocolCodecFilter - messageReceived - Processing a MESSAGE_RECEIVED for session 1
2015-12-21_17:07:00.859 DEBUG o.a.d.shared.asn1.ber.Asn1Decoder - decode - >>>==========================================
.....
.....
.....
015-12-21_17:07:00.905 DEBUG o.s.s.l.a.BindAuthenticator - handleBindException - Failed to bind as cn=gdcadmin,CN=Users,DC=mydomain,DC=com: org.springframework.ldap.AuthenticationException: [LDAP: error code 49 - cannot bind the principalDn.]; nested exception is javax.naming.AuthenticationException: [LDAP: error code 49 - cannot bind the principalDn.]
As per spring security document :
A user named "Sharon", for example, would then be able to authenticate
by entering either the username sharon or the full Active Directory
userPrincipalName, namely sharon#mydomain.com
my configuration
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(activeDirectoryLdapAuthenticationProvider());
auth.eraseCredentials(false);
auth.ldapAuthentication().userDnPatterns("cn={0},CN=Users,DC=mydomain,DC=com");
}
#Bean
public ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider(env.getProperty("mydomain.com"),
env.getProperty("ldap://hmidir01.mydomain.com:389/"));
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
provider.setUserDetailsContextMapper(userDetailsContextMapper);
return provider;
}
What is the mistake in my configuration.
You can implement in given ways :
1) Without persisting data into our database
WebSecurityConfig .java
#Configuration
#EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
public ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider(<ldap-domain>,<ldap-url>);
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
return provider;
}
#Bean
public LoggerListener loggerListener() {
return new LoggerListener();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(activeDirectoryLdapAuthenticationProvider());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/admin/**").hasAnyAuthority("ADMIN")
.antMatchers("/user/**").hasAnyAuthority("ADMIN", "USER")
.antMatchers("/rest/**", "/css/**", "/fonts/**", "/images/**", "/js/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/").failureUrl("/?error").successHandler("/home").permitAll()
.usernameParameter("emailId").passwordParameter("password")
.and()
.logout()
.logoutUrl("/logout").logoutSuccessUrl("/").permitAll()
.and()
.exceptionHandling().accessDeniedPage("/home")
.and()
.csrf()
.and()
.httpBasic();
}
}
2) With persisting data into our database
WebSecurityConfig.java
#Configuration
#EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/admin/**").hasAnyAuthority("ADMIN")
.antMatchers("/user/**").hasAnyAuthority("ADMIN", "USER")
.antMatchers("/rest/**", "/css/**", "/fonts/**", "/images/**", "/js/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/").failureUrl("/?error").successHandler("/home").permitAll()
.usernameParameter("emailId").passwordParameter("password")
.and()
.logout()
.logoutUrl("/logout").logoutSuccessUrl("/").permitAll()
.and()
.exceptionHandling().accessDeniedPage("/home")
.and()
.csrf()
.and()
.httpBasic();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)throws Exception {
auth
.authenticationProvider(activeDirectoryLdapAuthenticationProvider());
}
#Bean
public ActiveDirectoryLdapAuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider = new ActiveDirectoryLdapAuthenticationProvider(<ldap-domain>(null), <ldap-url>);
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
provider.setUserDetailsContextMapper(userDetailsContextMapper());
return provider;
}
#Bean
public UserDetailsContextMapper userDetailsContextMapper() {
return new AttributesLDAPUserDetailsContextMapper();
}
}
AttributesLDAPUserDetailsContextMapper.java
public class AttributesLDAPUserDetailsContextMapper implements UserDetailsContextMapper {
#Autowired
private UserService service;
private InetOrgPersonContextMapper ldapUserDetailsMapper = new InetOrgPersonContextMapper();
#Override
public UserDetails mapUserFromContext(DirContextOperations dirContextOperations, String userName, Collection<? extends GrantedAuthority> collection) {
InetOrgPerson userLdap = (InetOrgPerson) ldapUserDetailsMapper.mapUserFromContext(dirContextOperations, userName, collection);
User user = service.findOne(userLdap.getUsername());
if (user == null) {
user = new Usere();
user.setName(StringUtils.defaultString(userLdap.getDisplayName()).trim());
user.setEmailId(StringUtils.defaultString(userLdap.getUsername()).trim());
user.setdescription(StringUtils.defaultString(userLdap.getDescription()).trim());
user.setIsAdmin(false);
user.setIsEmployee(true);
service.save(user);
}
return new LdapSecuredUser(user);
}
#Override
public void mapUserToContext(UserDetails userDetails, DirContextAdapter dirContextAdapter) {
ldapUserDetailsMapper.mapUserToContext(userDetails, dirContextAdapter);
}
}
LdapSecuredUser.java
public class LdapSecuredUser extends User implements LdapUserDetails {
private static final long serialVersionUID = -8997460180274787521L;
public LdapSecuredUser(User user) {
if (user != null) {
this.setId(user.getId());
this.setEmailId(user.getEmailId());
this.setName(user.getName());
this.setdescription(user.getDescription());
this.setIsAdmin(user.getIsAdmin());
this.setIsEmployee(user.getIsEmployee());
}
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("USER"));
if(super.getIsAdmin())
authorities.add(new SimpleGrantedAuthority("ADMIN"));
return authorities;
}
#Override
public String getUsername() {
return super.getEmailId();
}
#Override
public String getPassword() {
return null;
}
#Override
public String getDn() {
return null;
}
#Override
public boolean isAccountNonExpired() {
return false;
}
#Override
public boolean isAccountNonLocked() {
return false;
}
#Override
public boolean isCredentialsNonExpired() {
return false;
}
#Override
public boolean isEnabled() {
return false;
}
}
With spring security 5.2.1:
You can use the setSearchFilter() function.
The LDAP auth process has two main steps: binding, which uses the domain parameter (the first) from ActiveDirectoryLdapAuthenticationProvider()to form the username like this: myUser#sub.domain.com
given credentials: username= myUser; password myPassword.
If this is is not correct, you'll get bad credential error (AcceptSecurityContext error, data 52e).
Then the next step is to finding your user in the ldap directory.
If your user doesn't have an attribute named username which is = myUser#sub.domain.com, the ldap server will give you back a not found error (this will pop up in your log as Ignoring PartialResultException from an UsernameNotFoundException: User myUser not found in directory. exception. For this you can use the searchFilter option.
The provided username will be inserted at the {1} point.
#Configuration
#EnableWebSecurity
#AllArgsConstructor
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception{
http.cors().and().csrf().disable().authorizeRequests()
.anyRequest().fullyAuthenticated().and().httpBasic(); //this will invoke an auth popup in browser
}
#Bean
public AuthenticationManager authenticationManager() {
return new ProviderManager(activeDirectoryLdapAuthenticationProvider());
}
#Bean
public AuthenticationProvider activeDirectoryLdapAuthenticationProvider() {
ActiveDirectoryLdapAuthenticationProvider provider = new
ActiveDirectoryLdapAuthenticationProvider("sub.domain.com","ldap://url");
provider.setSearchFilter("mailNickname={1}"); //here is the trick
provider.setConvertSubErrorCodesToExceptions(true);
provider.setUseAuthenticationRequestCredentials(true);
return provider;
}
}

Categories

Resources