forked from eugenp/tutorials
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
BAEL-1418 Spring Security with Extra Login Fields
* added additional custom example * refactored and added tests
- Loading branch information
1 parent
6dc3ab1
commit a20a4c9
Showing
26 changed files
with
599 additions
and
100 deletions.
There are no files selected for viewing
50 changes: 50 additions & 0 deletions
50
...ecurity/src/main/java/com/baeldung/loginextrafieldscustom/CustomAuthenticationFilter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package com.baeldung.loginextrafieldscustom; | ||
|
||
import javax.servlet.http.HttpServletRequest; | ||
import javax.servlet.http.HttpServletResponse; | ||
|
||
import org.springframework.security.authentication.AuthenticationServiceException; | ||
import org.springframework.security.core.Authentication; | ||
import org.springframework.security.core.AuthenticationException; | ||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; | ||
|
||
public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter { | ||
|
||
public static final String SPRING_SECURITY_FORM_DOMAIN_KEY = "domain"; | ||
|
||
@Override | ||
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) | ||
throws AuthenticationException { | ||
|
||
if (!request.getMethod().equals("POST")) { | ||
throw new AuthenticationServiceException("Authentication method not supported: " | ||
+ request.getMethod()); | ||
} | ||
|
||
CustomAuthenticationToken authRequest = getAuthRequest(request); | ||
setDetails(request, authRequest); | ||
return this.getAuthenticationManager().authenticate(authRequest); | ||
} | ||
|
||
private CustomAuthenticationToken getAuthRequest(HttpServletRequest request) { | ||
String username = obtainUsername(request); | ||
String password = obtainPassword(request); | ||
String domain = obtainDomain(request); | ||
|
||
if (username == null) { | ||
username = ""; | ||
} | ||
if (password == null) { | ||
password = ""; | ||
} | ||
if (domain == null) { | ||
domain = ""; | ||
} | ||
|
||
return new CustomAuthenticationToken(username, password, domain); | ||
} | ||
|
||
private String obtainDomain(HttpServletRequest request) { | ||
return request.getParameter(SPRING_SECURITY_FORM_DOMAIN_KEY); | ||
} | ||
} |
30 changes: 30 additions & 0 deletions
30
...security/src/main/java/com/baeldung/loginextrafieldscustom/CustomAuthenticationToken.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package com.baeldung.loginextrafieldscustom; | ||
|
||
import java.util.Collection; | ||
|
||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | ||
import org.springframework.security.core.GrantedAuthority; | ||
|
||
public class CustomAuthenticationToken extends UsernamePasswordAuthenticationToken { | ||
|
||
private static final long serialVersionUID = 1L; | ||
|
||
private final String domain; | ||
|
||
public CustomAuthenticationToken(Object principal, Object credentials, String domain) { | ||
super(principal, credentials); | ||
this.domain = domain; | ||
super.setAuthenticated(false); | ||
} | ||
|
||
public CustomAuthenticationToken(Object principal, Object credentials, String domain, | ||
Collection<? extends GrantedAuthority> authorities) { | ||
super(principal, credentials, authorities); | ||
this.domain = domain; | ||
super.setAuthenticated(true); // must use super, as we override | ||
} | ||
|
||
public String getDomain() { | ||
return this.domain; | ||
} | ||
} |
92 changes: 92 additions & 0 deletions
92
...ain/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsAuthenticationProvider.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
package com.baeldung.loginextrafieldscustom; | ||
|
||
import org.springframework.security.authentication.BadCredentialsException; | ||
import org.springframework.security.authentication.InternalAuthenticationServiceException; | ||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | ||
import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider; | ||
import org.springframework.security.core.AuthenticationException; | ||
import org.springframework.security.core.userdetails.UserDetails; | ||
import org.springframework.security.core.userdetails.UsernameNotFoundException; | ||
import org.springframework.security.crypto.password.PasswordEncoder; | ||
import org.springframework.util.Assert; | ||
|
||
public class CustomUserDetailsAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider { | ||
|
||
/** | ||
* The plaintext password used to perform | ||
* PasswordEncoder#matches(CharSequence, String)} on when the user is | ||
* not found to avoid SEC-2056. | ||
*/ | ||
private static final String USER_NOT_FOUND_PASSWORD = "userNotFoundPassword"; | ||
|
||
private final PasswordEncoder passwordEncoder; | ||
private final CustomUserDetailsService userDetailsService; | ||
|
||
/** | ||
* The password used to perform | ||
* {@link PasswordEncoder#matches(CharSequence, String)} on when the user is | ||
* not found to avoid SEC-2056. This is necessary, because some | ||
* {@link PasswordEncoder} implementations will short circuit if the password is not | ||
* in a valid format. | ||
*/ | ||
private String userNotFoundEncodedPassword; | ||
|
||
public CustomUserDetailsAuthenticationProvider(PasswordEncoder passwordEncoder, CustomUserDetailsService userDetailsService) { | ||
this.passwordEncoder = passwordEncoder; | ||
this.userDetailsService = userDetailsService; | ||
} | ||
|
||
@Override | ||
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) | ||
throws AuthenticationException { | ||
|
||
if (authentication.getCredentials() == null) { | ||
logger.debug("Authentication failed: no credentials provided"); | ||
throw new BadCredentialsException( | ||
messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); | ||
} | ||
|
||
String presentedPassword = authentication.getCredentials() | ||
.toString(); | ||
|
||
if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) { | ||
logger.debug("Authentication failed: password does not match stored value"); | ||
throw new BadCredentialsException( | ||
messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); | ||
} | ||
} | ||
|
||
@Override | ||
protected void doAfterPropertiesSet() throws Exception { | ||
Assert.notNull(this.userDetailsService, "A UserDetailsService must be set"); | ||
this.userNotFoundEncodedPassword = this.passwordEncoder.encode(USER_NOT_FOUND_PASSWORD); | ||
} | ||
|
||
@Override | ||
protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) | ||
throws AuthenticationException { | ||
CustomAuthenticationToken auth = (CustomAuthenticationToken) authentication; | ||
UserDetails loadedUser; | ||
|
||
try { | ||
loadedUser = this.userDetailsService.loadUserByUsernameAndDomain(auth.getPrincipal() | ||
.toString(), auth.getDomain()); | ||
} catch (UsernameNotFoundException notFound) { | ||
if (authentication.getCredentials() != null) { | ||
String presentedPassword = authentication.getCredentials() | ||
.toString(); | ||
passwordEncoder.matches(presentedPassword, userNotFoundEncodedPassword); | ||
} | ||
throw notFound; | ||
} catch (Exception repositoryProblem) { | ||
throw new InternalAuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem); | ||
} | ||
|
||
if (loadedUser == null) { | ||
throw new InternalAuthenticationServiceException("UserDetailsService returned null, " | ||
+ "which is an interface contract violation"); | ||
} | ||
return loadedUser; | ||
} | ||
|
||
} |
10 changes: 10 additions & 0 deletions
10
...-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package com.baeldung.loginextrafieldscustom; | ||
|
||
import org.springframework.security.core.userdetails.UserDetails; | ||
import org.springframework.security.core.userdetails.UsernameNotFoundException; | ||
|
||
public interface CustomUserDetailsService { | ||
|
||
UserDetails loadUserByUsernameAndDomain(String username, String domain) throws UsernameNotFoundException; | ||
|
||
} |
30 changes: 30 additions & 0 deletions
30
...urity/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsServiceImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package com.baeldung.loginextrafieldscustom; | ||
|
||
import org.apache.commons.lang3.StringUtils; | ||
import org.springframework.security.core.userdetails.UserDetails; | ||
import org.springframework.security.core.userdetails.UsernameNotFoundException; | ||
import org.springframework.stereotype.Service; | ||
|
||
@Service("userDetailsService") | ||
public class CustomUserDetailsServiceImpl implements CustomUserDetailsService { | ||
|
||
private final UserRepository userRepository; | ||
|
||
public CustomUserDetailsServiceImpl(UserRepository userRepository) { | ||
this.userRepository = userRepository; | ||
} | ||
|
||
@Override | ||
public UserDetails loadUserByUsernameAndDomain(String username, String domain) throws UsernameNotFoundException { | ||
if (StringUtils.isAnyBlank(username, domain)) { | ||
throw new UsernameNotFoundException("Username and domain must be provided"); | ||
} | ||
User user = userRepository.findUser(username, domain); | ||
if (user == null) { | ||
throw new UsernameNotFoundException( | ||
String.format("Username not found for domain, username=%s, domain=%s", | ||
username, domain)); | ||
} | ||
return user; | ||
} | ||
} |
2 changes: 1 addition & 1 deletion
2
...rityextrafields/CustomUserRepository.java → ...trafieldscustom/CustomUserRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
6 changes: 3 additions & 3 deletions
6
...ds/SpringExtraLoginFieldsApplication.java → ...dscustom/ExtraLoginFieldsApplication.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,13 @@ | ||
package com.baeldung.securityextrafields; | ||
package com.baeldung.loginextrafieldscustom; | ||
|
||
import org.springframework.boot.SpringApplication; | ||
import org.springframework.boot.autoconfigure.SpringBootApplication; | ||
|
||
@SpringBootApplication | ||
public class SpringExtraLoginFieldsApplication { | ||
public class ExtraLoginFieldsApplication { | ||
|
||
public static void main(String[] args) { | ||
SpringApplication.run(SpringExtraLoginFieldsApplication.class, args); | ||
SpringApplication.run(ExtraLoginFieldsApplication.class, args); | ||
} | ||
|
||
} |
62 changes: 62 additions & 0 deletions
62
spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/SecurityConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package com.baeldung.loginextrafieldscustom; | ||
|
||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.context.annotation.PropertySource; | ||
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.crypto.bcrypt.BCryptPasswordEncoder; | ||
import org.springframework.security.crypto.password.PasswordEncoder; | ||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; | ||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; | ||
|
||
@EnableWebSecurity | ||
@PropertySource("classpath:/application-extrafields.properties") | ||
public class SecurityConfig extends WebSecurityConfigurerAdapter { | ||
|
||
@Autowired | ||
private CustomUserDetailsService userDetailsService; | ||
|
||
@Override | ||
protected void configure(HttpSecurity http) throws Exception { | ||
|
||
http | ||
.addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class) | ||
.authorizeRequests() | ||
.antMatchers("/css/**", "/index").permitAll() | ||
.antMatchers("/user/**").authenticated() | ||
.and() | ||
.formLogin().loginPage("/login") | ||
.and() | ||
.logout() | ||
.logoutUrl("/logout"); | ||
} | ||
|
||
public CustomAuthenticationFilter authenticationFilter() throws Exception { | ||
CustomAuthenticationFilter filter = new CustomAuthenticationFilter(); | ||
filter.setAuthenticationManager(authenticationManagerBean()); | ||
filter.setAuthenticationFailureHandler(failureHandler()); | ||
return filter; | ||
} | ||
|
||
@Autowired | ||
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { | ||
auth.authenticationProvider(authProvider()); | ||
} | ||
|
||
public AuthenticationProvider authProvider() { | ||
CustomUserDetailsAuthenticationProvider provider | ||
= new CustomUserDetailsAuthenticationProvider(passwordEncoder(), userDetailsService); | ||
return provider; | ||
} | ||
|
||
public SimpleUrlAuthenticationFailureHandler failureHandler() { | ||
return new SimpleUrlAuthenticationFailureHandler("/login?error=true"); | ||
} | ||
|
||
public PasswordEncoder passwordEncoder() { | ||
return new BCryptPasswordEncoder(); | ||
} | ||
} |
2 changes: 1 addition & 1 deletion
2
...om/baeldung/securityextrafields/User.java → ...baeldung/loginextrafieldscustom/User.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
...g/securityextrafields/UserRepository.java → ...oginextrafieldscustom/UserRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
...ng/securityextrafields/WebController.java → ...loginextrafieldscustom/WebController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
13 changes: 13 additions & 0 deletions
13
...curity/src/main/java/com/baeldung/loginextrafieldssimple/ExtraLoginFieldsApplication.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package com.baeldung.loginextrafieldssimple; | ||
|
||
import org.springframework.boot.SpringApplication; | ||
import org.springframework.boot.autoconfigure.SpringBootApplication; | ||
|
||
@SpringBootApplication | ||
public class ExtraLoginFieldsApplication { | ||
|
||
public static void main(String[] args) { | ||
SpringApplication.run(ExtraLoginFieldsApplication.class, args); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.