From 9fe3d2761ea7db32e69b9476ac257544735c78fd Mon Sep 17 00:00:00 2001 From: Chris Oberle Date: Sun, 14 Jan 2018 08:19:17 -0500 Subject: [PATCH 1/9] BAEL-1418 - spring security with extra login fields --- spring-5/pom.xml | 512 +++++++++--------- ...gSecurity5ExtraLoginFieldsApplication.java | 15 + .../CustomAuthFailureHandler.java | 20 + .../CustomAuthenticationFilter.java | 53 ++ .../CustomUserDetailsService.java | 32 ++ .../CustomUserRepository.java | 26 + .../securityextrafields/SecurityConfig.java | 63 +++ .../baeldung/securityextrafields/User.java | 23 + .../securityextrafields/UserRepository.java | 7 + .../securityextrafields/WebController.java | 51 ++ .../src/main/resources/static/css/main.css | 18 + .../src/main/resources/templates/index.html | 24 + .../src/main/resources/templates/login.html | 23 + .../main/resources/templates/user/index.html | 13 + .../SecurityExtraFieldsTest.java | 108 ++++ 15 files changed, 738 insertions(+), 250 deletions(-) create mode 100644 spring-5/src/main/java/com/baeldung/SpringSecurity5ExtraLoginFieldsApplication.java create mode 100644 spring-5/src/main/java/com/baeldung/securityextrafields/CustomAuthFailureHandler.java create mode 100644 spring-5/src/main/java/com/baeldung/securityextrafields/CustomAuthenticationFilter.java create mode 100644 spring-5/src/main/java/com/baeldung/securityextrafields/CustomUserDetailsService.java create mode 100644 spring-5/src/main/java/com/baeldung/securityextrafields/CustomUserRepository.java create mode 100644 spring-5/src/main/java/com/baeldung/securityextrafields/SecurityConfig.java create mode 100644 spring-5/src/main/java/com/baeldung/securityextrafields/User.java create mode 100644 spring-5/src/main/java/com/baeldung/securityextrafields/UserRepository.java create mode 100644 spring-5/src/main/java/com/baeldung/securityextrafields/WebController.java create mode 100644 spring-5/src/main/resources/static/css/main.css create mode 100644 spring-5/src/main/resources/templates/index.html create mode 100644 spring-5/src/main/resources/templates/login.html create mode 100644 spring-5/src/main/resources/templates/user/index.html create mode 100644 spring-5/src/test/java/com/baeldung/securityextrafields/SecurityExtraFieldsTest.java diff --git a/spring-5/pom.xml b/spring-5/pom.xml index 19dd65d78f..71f355f41a 100644 --- a/spring-5/pom.xml +++ b/spring-5/pom.xml @@ -1,250 +1,262 @@ - - - 4.0.0 - - com.baeldung - spring-5 - 0.0.1-SNAPSHOT - jar - - spring-5 - spring 5 sample project about new features - - - org.springframework.boot - spring-boot-starter-parent - 2.0.0.M7 - - - - - - org.springframework.boot - spring-boot-starter-data-jpa - - - org.springframework.boot - spring-boot-starter-security - - - org.springframework.boot - spring-boot-starter-validation - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-webflux - - - org.springframework.boot - spring-boot-starter-hateoas - - - org.projectreactor - reactor-spring - ${reactor-spring.version} - - - javax.json.bind - javax.json.bind-api - ${jsonb-api.version} - - - - - - - - - - - - - - - org.apache.geronimo.specs - geronimo-json_1.1_spec - ${geronimo-json_1.1_spec.version} - - - org.apache.johnzon - johnzon-jsonb - ${johnzon.version} - - - - org.apache.commons - commons-lang3 - - - - - - org.springframework.boot - spring-boot-devtools - runtime - - - com.h2database - h2 - runtime - - - - org.springframework - spring-test - - - org.springframework.boot - spring-boot-starter-test - test - - - org.springframework.security - spring-security-test - test - - - - org.apache.commons - commons-collections4 - 4.1 - test - - - - org.junit.jupiter - junit-jupiter-api - ${junit.jupiter.version} - - - org.junit.jupiter - junit-jupiter-engine - ${junit.jupiter.version} - test - - - org.junit.platform - junit-platform-surefire-provider - ${junit.platform.version} - test - - - org.junit.platform - junit-platform-runner - ${junit.platform.version} - test - - - - org.springframework.restdocs - spring-restdocs-mockmvc - test - - - org.springframework.restdocs - spring-restdocs-webtestclient - test - - - org.springframework.restdocs - spring-restdocs-restassured - test - - - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - com.baeldung.Spring5Application - JAR - - - - - org.apache.maven.plugins - maven-surefire-plugin - - 3 - true - methods - true - - **/*IntegrationTest.java - **/*LiveTest.java - - - - - org.asciidoctor - asciidoctor-maven-plugin - ${asciidoctor-plugin.version} - - - generate-docs - package - - process-asciidoc - - - html - book - - ${snippetsDirectory} - - src/docs/asciidocs - target/generated-docs - - - - - - - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - false - - - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - false - - - - - - UTF-8 - UTF-8 - 1.8 - 1.0.0 - 5.0.0 - 2.20 - 5.0.2.RELEASE - 1.0.1.RELEASE - 1.1.3 - 1.0 - 1.0 - 1.5.6 - ${project.build.directory}/generated-snippets - - - + + + 4.0.0 + + com.baeldung + spring-5 + 0.0.1-SNAPSHOT + jar + + spring-5 + spring 5 sample project about new features + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.M7 + + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.thymeleaf.extras + thymeleaf-extras-springsecurity4 + + + org.springframework.boot + spring-boot-starter-webflux + + + org.springframework.boot + spring-boot-starter-hateoas + + + org.projectreactor + reactor-spring + ${reactor-spring.version} + + + javax.json.bind + javax.json.bind-api + ${jsonb-api.version} + + + + + + + + + + + + + + + org.apache.geronimo.specs + geronimo-json_1.1_spec + ${geronimo-json_1.1_spec.version} + + + org.apache.johnzon + johnzon-jsonb + ${johnzon.version} + + + + org.apache.commons + commons-lang3 + + + + + + org.springframework.boot + spring-boot-devtools + runtime + + + com.h2database + h2 + runtime + + + + org.springframework + spring-test + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.security + spring-security-test + test + + + + org.apache.commons + commons-collections4 + 4.1 + test + + + + org.junit.jupiter + junit-jupiter-api + ${junit.jupiter.version} + + + org.junit.jupiter + junit-jupiter-engine + ${junit.jupiter.version} + test + + + org.junit.platform + junit-platform-surefire-provider + ${junit.platform.version} + test + + + org.junit.platform + junit-platform-runner + ${junit.platform.version} + test + + + + org.springframework.restdocs + spring-restdocs-mockmvc + test + + + org.springframework.restdocs + spring-restdocs-webtestclient + test + + + org.springframework.restdocs + spring-restdocs-restassured + test + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + com.baeldung.Spring5Application + JAR + + + + + org.apache.maven.plugins + maven-surefire-plugin + + 3 + true + methods + true + + **/*IntegrationTest.java + **/*LiveTest.java + + + + + org.asciidoctor + asciidoctor-maven-plugin + ${asciidoctor-plugin.version} + + + generate-docs + package + + process-asciidoc + + + html + book + + ${snippetsDirectory} + + src/docs/asciidocs + target/generated-docs + + + + + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + UTF-8 + UTF-8 + 1.8 + 1.0.0 + 5.0.0 + 2.20 + 5.0.2.RELEASE + 1.0.1.RELEASE + 1.1.3 + 1.0 + 1.0 + 1.5.6 + ${project.build.directory}/generated-snippets + + + diff --git a/spring-5/src/main/java/com/baeldung/SpringSecurity5ExtraLoginFieldsApplication.java b/spring-5/src/main/java/com/baeldung/SpringSecurity5ExtraLoginFieldsApplication.java new file mode 100644 index 0000000000..b6285dfc71 --- /dev/null +++ b/spring-5/src/main/java/com/baeldung/SpringSecurity5ExtraLoginFieldsApplication.java @@ -0,0 +1,15 @@ +package com.baeldung; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; + +@ComponentScan(basePackages = {"com.baeldung.securityextrafields"}) +@SpringBootApplication +public class SpringSecurity5ExtraLoginFieldsApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringSecurity5ExtraLoginFieldsApplication.class, args); + } + +} diff --git a/spring-5/src/main/java/com/baeldung/securityextrafields/CustomAuthFailureHandler.java b/spring-5/src/main/java/com/baeldung/securityextrafields/CustomAuthFailureHandler.java new file mode 100644 index 0000000000..ef99ef55ce --- /dev/null +++ b/spring-5/src/main/java/com/baeldung/securityextrafields/CustomAuthFailureHandler.java @@ -0,0 +1,20 @@ +package com.baeldung.securityextrafields; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; + +public class CustomAuthFailureHandler extends SimpleUrlAuthenticationFailureHandler { + + @Override + public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) + throws IOException, ServletException { + getRedirectStrategy().sendRedirect(request, response, "/login?error=true"); + } + +} diff --git a/spring-5/src/main/java/com/baeldung/securityextrafields/CustomAuthenticationFilter.java b/spring-5/src/main/java/com/baeldung/securityextrafields/CustomAuthenticationFilter.java new file mode 100644 index 0000000000..5a54552dfd --- /dev/null +++ b/spring-5/src/main/java/com/baeldung/securityextrafields/CustomAuthenticationFilter.java @@ -0,0 +1,53 @@ +package com.baeldung.securityextrafields; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.security.authentication.AuthenticationServiceException; +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; + +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()); + } + + UsernamePasswordAuthenticationToken authRequest = getAuthRequest(request); + setDetails(request, authRequest); + return this.getAuthenticationManager() + .authenticate(authRequest); + } + + private UsernamePasswordAuthenticationToken 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 = ""; + } + + username = username.trim(); + return new UsernamePasswordAuthenticationToken(username + ":" + domain, password); + } + + private String obtainDomain(HttpServletRequest request) { + return request.getParameter(SPRING_SECURITY_FORM_DOMAIN_KEY); + } +} diff --git a/spring-5/src/main/java/com/baeldung/securityextrafields/CustomUserDetailsService.java b/spring-5/src/main/java/com/baeldung/securityextrafields/CustomUserDetailsService.java new file mode 100644 index 0000000000..0e2858c68f --- /dev/null +++ b/spring-5/src/main/java/com/baeldung/securityextrafields/CustomUserDetailsService.java @@ -0,0 +1,32 @@ +package com.baeldung.securityextrafields; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +@Service("userDetailsService") +public class CustomUserDetailsService implements UserDetailsService { + + private final UserRepository userRepository; + + public CustomUserDetailsService(UserRepository userRepository) { + this.userRepository = userRepository; + } + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + String[] usernameAndDomain = StringUtils.split(username, ":"); + if (usernameAndDomain == null || usernameAndDomain.length != 2) { + throw new UsernameNotFoundException("Username and domain must be provided"); + } + User user = userRepository.findUser(usernameAndDomain[0], usernameAndDomain[1]); + if (user == null) { + throw new UsernameNotFoundException( + String.format("Username not found for domain, username=%s, domain=%s", + usernameAndDomain[0], usernameAndDomain[1])); + } + return user; + } +} diff --git a/spring-5/src/main/java/com/baeldung/securityextrafields/CustomUserRepository.java b/spring-5/src/main/java/com/baeldung/securityextrafields/CustomUserRepository.java new file mode 100644 index 0000000000..c86769b016 --- /dev/null +++ b/spring-5/src/main/java/com/baeldung/securityextrafields/CustomUserRepository.java @@ -0,0 +1,26 @@ +package com.baeldung.securityextrafields; + +import java.util.ArrayList; +import java.util.Collection; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.stereotype.Repository; + +@Repository("userRepository") +public class CustomUserRepository implements UserRepository { + + @Override + public User findUser(String username, String domain) { + if (StringUtils.isAnyBlank(username, domain)) { + return null; + } else { + Collection authorities = new ArrayList<>(); + User user = new User(username, domain, + "$2a$10$U3GhSMpsMSOE8Kqsbn58/edxDBKlVuYMh7qk/7ErApYFjJzi2VG5K", true, + true, true, true, authorities); + return user; + } + } + +} diff --git a/spring-5/src/main/java/com/baeldung/securityextrafields/SecurityConfig.java b/spring-5/src/main/java/com/baeldung/securityextrafields/SecurityConfig.java new file mode 100644 index 0000000000..becb4631f2 --- /dev/null +++ b/spring-5/src/main/java/com/baeldung/securityextrafields/SecurityConfig.java @@ -0,0 +1,63 @@ +package com.baeldung.securityextrafields; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +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.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +@EnableWebSecurity +public class SecurityConfig extends WebSecurityConfigurerAdapter { + + @Autowired + private UserDetailsService 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() { + DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); + provider.setUserDetailsService(userDetailsService); + provider.setPasswordEncoder(passwordEncoder()); + return provider; + } + + + public CustomAuthFailureHandler failureHandler() { + return new CustomAuthFailureHandler(); + } + + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } +} diff --git a/spring-5/src/main/java/com/baeldung/securityextrafields/User.java b/spring-5/src/main/java/com/baeldung/securityextrafields/User.java new file mode 100644 index 0000000000..a5b3a434ae --- /dev/null +++ b/spring-5/src/main/java/com/baeldung/securityextrafields/User.java @@ -0,0 +1,23 @@ +package com.baeldung.securityextrafields; + +import java.util.Collection; + +import org.springframework.security.core.GrantedAuthority; + +public class User extends org.springframework.security.core.userdetails.User { + + private static final long serialVersionUID = 1L; + + private final String domain; + + public User(String username, String domain, String password, boolean enabled, + boolean accountNonExpired, boolean credentialsNonExpired, + boolean accountNonLocked, Collection authorities) { + super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities); + this.domain = domain; + } + + public String getDomain() { + return domain; + } +} diff --git a/spring-5/src/main/java/com/baeldung/securityextrafields/UserRepository.java b/spring-5/src/main/java/com/baeldung/securityextrafields/UserRepository.java new file mode 100644 index 0000000000..4ca65b13d5 --- /dev/null +++ b/spring-5/src/main/java/com/baeldung/securityextrafields/UserRepository.java @@ -0,0 +1,7 @@ +package com.baeldung.securityextrafields; + +public interface UserRepository { + + public User findUser(String username, String domain); + +} diff --git a/spring-5/src/main/java/com/baeldung/securityextrafields/WebController.java b/spring-5/src/main/java/com/baeldung/securityextrafields/WebController.java new file mode 100644 index 0000000000..4a8abb4a83 --- /dev/null +++ b/spring-5/src/main/java/com/baeldung/securityextrafields/WebController.java @@ -0,0 +1,51 @@ +package com.baeldung.securityextrafields; + +import java.util.Optional; + +import org.springframework.security.authentication.AnonymousAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; + +@Controller +public class WebController { + + @RequestMapping("/") + public String root() { + return "redirect:/index"; + } + + @RequestMapping("/index") + public String index(Model model) { + getDomain().ifPresent(d -> { + model.addAttribute("domain", d); + }); + return "index"; + } + + @RequestMapping("/user/index") + public String userIndex(Model model) { + getDomain().ifPresent(d -> { + model.addAttribute("domain", d); + }); + return "user/index"; + } + + @RequestMapping("/login") + public String login() { + return "login"; + } + + private Optional getDomain() { + Authentication auth = SecurityContextHolder.getContext() + .getAuthentication(); + String domain = null; + if (auth != null && !auth.getClass().equals(AnonymousAuthenticationToken.class)) { + User user = (User) auth.getPrincipal(); + domain = user.getDomain(); + } + return Optional.ofNullable(domain); + } +} diff --git a/spring-5/src/main/resources/static/css/main.css b/spring-5/src/main/resources/static/css/main.css new file mode 100644 index 0000000000..9299ee6158 --- /dev/null +++ b/spring-5/src/main/resources/static/css/main.css @@ -0,0 +1,18 @@ +body { + font-family: sans; + font-size: 1em; +} + +p.error { + font-weight: bold; + color: red; +} + +div.logout { + float: right; +} + +.formfield { + margin: 0.5em; + padding: 0.3em; +} \ No newline at end of file diff --git a/spring-5/src/main/resources/templates/index.html b/spring-5/src/main/resources/templates/index.html new file mode 100644 index 0000000000..52f6224dfb --- /dev/null +++ b/spring-5/src/main/resources/templates/index.html @@ -0,0 +1,24 @@ + + + + Spring Security - Login With Extra Fields + + + + +
+ Logged in user: | + domain: Some Domain +
+
+ +
+
+
+

Hello Spring Security

+

This is an unsecured page, but you can access the secured pages after authenticating.

+ + + diff --git a/spring-5/src/main/resources/templates/login.html b/spring-5/src/main/resources/templates/login.html new file mode 100644 index 0000000000..cafec89c15 --- /dev/null +++ b/spring-5/src/main/resources/templates/login.html @@ -0,0 +1,23 @@ + + + + Login page + + + + +

Login page

+

Example: user / domain / password

+

Invalid user, password, or domain

+
+ : +
+ : +
+ : +
+ +
+

Back to home page

+ + diff --git a/spring-5/src/main/resources/templates/user/index.html b/spring-5/src/main/resources/templates/user/index.html new file mode 100644 index 0000000000..a4c1535100 --- /dev/null +++ b/spring-5/src/main/resources/templates/user/index.html @@ -0,0 +1,13 @@ + + + + Spring Security - Login With Extra Fields + + + + +
+

This is a secured page!

+

Back to home page

+ + diff --git a/spring-5/src/test/java/com/baeldung/securityextrafields/SecurityExtraFieldsTest.java b/spring-5/src/test/java/com/baeldung/securityextrafields/SecurityExtraFieldsTest.java new file mode 100644 index 0000000000..09ee0e66a0 --- /dev/null +++ b/spring-5/src/test/java/com/baeldung/securityextrafields/SecurityExtraFieldsTest.java @@ -0,0 +1,108 @@ +package com.baeldung.securityextrafields; + +import static org.junit.Assert.assertEquals; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.util.ArrayList; +import java.util.Collection; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.mock.web.MockHttpSession; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.web.FilterChainProxy; +import org.springframework.security.web.context.HttpSessionSecurityContextRepository; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +import com.baeldung.SpringSecurity5ExtraLoginFieldsApplication; + +@WebAppConfiguration +@SpringJUnitWebConfig +@RunWith(SpringRunner.class) +@ContextConfiguration(classes = SpringSecurity5ExtraLoginFieldsApplication.class) +public class SecurityExtraFieldsTest { + + @Autowired + private FilterChainProxy springSecurityFilterChain; + + private MockMvc mockMvc; + + @BeforeEach + public void setup(WebApplicationContext wac) { + this.mockMvc = MockMvcBuilders.webAppContextSetup(wac) + .apply(springSecurity(springSecurityFilterChain)).build(); + } + + @DisplayName("Access of root path redirects to index") + @Test + public void givenRootPathAccess_thenRedirectToIndex() throws Exception { + this.mockMvc.perform(get("/")) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrlPattern("/index*")); + } + + @DisplayName("Unauthenticated access of secured resource redirects to login page") + @Test + public void givenSecuredResource_whenAccessUnauthenticated_thenRequiresAuthentication() throws Exception { + this.mockMvc.perform(get("/user/index")) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrlPattern("**/login")); + } + + @DisplayName("Succesfull auth on login page redirects and extra field exists") + @Test + public void givenAccessSecuredResource_whenAuthenticated_thenAuthHasExtraFields() throws Exception { + MockHttpServletRequestBuilder securedResourceAccess = get("/user/index"); + MvcResult unauthenticatedResult = mockMvc.perform(securedResourceAccess) + .andExpect(status().is3xxRedirection()) + .andReturn(); + + MockHttpSession session = (MockHttpSession) unauthenticatedResult.getRequest() + .getSession(); + String loginUrl = unauthenticatedResult.getResponse() + .getRedirectedUrl(); + + User user = getUser(); + + mockMvc.perform(post(loginUrl) + .param("username", user.getUsername()) + .param("password", user.getPassword()) + .param("domain", user.getDomain()) + .session(session) + .with(csrf())) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrlPattern("**/user/index")) + .andReturn(); + + mockMvc.perform(securedResourceAccess.session(session)) + .andExpect(status().isOk()); + + SecurityContext securityContext + = (SecurityContext) session.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY); + Authentication auth = securityContext.getAuthentication(); + assertEquals(((User)auth.getPrincipal()).getDomain(), user.getDomain()); + } + + private User getUser() { + Collection authorities = new ArrayList<>(); + return new User("myusername", "mydomain", "password", true, true, true, true, authorities); + } +} From f0175dd4f5d1062123245543de6fe33f943f77d0 Mon Sep 17 00:00:00 2001 From: Chris Oberle Date: Sun, 14 Jan 2018 09:05:52 -0500 Subject: [PATCH 2/9] change delimeter for username/domain concatenation --- .../securityextrafields/CustomAuthenticationFilter.java | 5 +++-- .../securityextrafields/CustomUserDetailsService.java | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/spring-5/src/main/java/com/baeldung/securityextrafields/CustomAuthenticationFilter.java b/spring-5/src/main/java/com/baeldung/securityextrafields/CustomAuthenticationFilter.java index 5a54552dfd..b5d628628d 100644 --- a/spring-5/src/main/java/com/baeldung/securityextrafields/CustomAuthenticationFilter.java +++ b/spring-5/src/main/java/com/baeldung/securityextrafields/CustomAuthenticationFilter.java @@ -43,8 +43,9 @@ public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFi domain = ""; } - username = username.trim(); - return new UsernamePasswordAuthenticationToken(username + ":" + domain, password); + String usernameDomain = String.format("%s%s%s", username.trim(), + String.valueOf(Character.LINE_SEPARATOR), domain); + return new UsernamePasswordAuthenticationToken(usernameDomain, password); } private String obtainDomain(HttpServletRequest request) { diff --git a/spring-5/src/main/java/com/baeldung/securityextrafields/CustomUserDetailsService.java b/spring-5/src/main/java/com/baeldung/securityextrafields/CustomUserDetailsService.java index 0e2858c68f..be02834852 100644 --- a/spring-5/src/main/java/com/baeldung/securityextrafields/CustomUserDetailsService.java +++ b/spring-5/src/main/java/com/baeldung/securityextrafields/CustomUserDetailsService.java @@ -17,7 +17,7 @@ public class CustomUserDetailsService implements UserDetailsService { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { - String[] usernameAndDomain = StringUtils.split(username, ":"); + String[] usernameAndDomain = StringUtils.split(username, String.valueOf(Character.LINE_SEPARATOR)); if (usernameAndDomain == null || usernameAndDomain.length != 2) { throw new UsernameNotFoundException("Username and domain must be provided"); } From 2b78cdbafa800694f7b5314a8c89a66c8d49a0cf Mon Sep 17 00:00:00 2001 From: Chris Oberle Date: Mon, 15 Jan 2018 18:34:55 -0500 Subject: [PATCH 3/9] remove unnecessary class --- .../CustomAuthFailureHandler.java | 20 ------------------- .../securityextrafields/SecurityConfig.java | 6 +++--- 2 files changed, 3 insertions(+), 23 deletions(-) delete mode 100644 spring-5/src/main/java/com/baeldung/securityextrafields/CustomAuthFailureHandler.java diff --git a/spring-5/src/main/java/com/baeldung/securityextrafields/CustomAuthFailureHandler.java b/spring-5/src/main/java/com/baeldung/securityextrafields/CustomAuthFailureHandler.java deleted file mode 100644 index ef99ef55ce..0000000000 --- a/spring-5/src/main/java/com/baeldung/securityextrafields/CustomAuthFailureHandler.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.baeldung.securityextrafields; - -import java.io.IOException; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; - -public class CustomAuthFailureHandler extends SimpleUrlAuthenticationFailureHandler { - - @Override - public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) - throws IOException, ServletException { - getRedirectStrategy().sendRedirect(request, response, "/login?error=true"); - } - -} diff --git a/spring-5/src/main/java/com/baeldung/securityextrafields/SecurityConfig.java b/spring-5/src/main/java/com/baeldung/securityextrafields/SecurityConfig.java index becb4631f2..e8e8567773 100644 --- a/spring-5/src/main/java/com/baeldung/securityextrafields/SecurityConfig.java +++ b/spring-5/src/main/java/com/baeldung/securityextrafields/SecurityConfig.java @@ -10,6 +10,7 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur import org.springframework.security.core.userdetails.UserDetailsService; 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 @@ -51,10 +52,9 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { provider.setPasswordEncoder(passwordEncoder()); return provider; } - - public CustomAuthFailureHandler failureHandler() { - return new CustomAuthFailureHandler(); + public SimpleUrlAuthenticationFailureHandler failureHandler() { + return new SimpleUrlAuthenticationFailureHandler("/login?error=true"); } public PasswordEncoder passwordEncoder() { From a5f6f5e03518100b572c2924efe5f09cae1baf63 Mon Sep 17 00:00:00 2001 From: Chris Oberle Date: Tue, 16 Jan 2018 16:59:57 -0500 Subject: [PATCH 4/9] move source to spring-5-security module --- spring-5-security/pom.xml | 203 ++++++++++-------- .../CustomAuthenticationFilter.java | 54 +++++ .../CustomUserDetailsService.java | 32 +++ .../CustomUserRepository.java | 26 +++ .../securityextrafields/SecurityConfig.java | 65 ++++++ .../SpringExtraLoginFieldsApplication.java | 13 ++ .../baeldung/securityextrafields/User.java | 23 ++ .../securityextrafields/UserRepository.java | 7 + .../securityextrafields/WebController.java | 51 +++++ .../application-extrafields.properties | 1 + .../src/main/resources/static/css/main.css | 18 ++ .../resources/templatesextrafields/index.html | 24 +++ .../resources/templatesextrafields/login.html | 23 ++ .../templatesextrafields/user/index.html | 13 ++ 14 files changed, 459 insertions(+), 94 deletions(-) create mode 100644 spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomAuthenticationFilter.java create mode 100644 spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomUserDetailsService.java create mode 100644 spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomUserRepository.java create mode 100644 spring-5-security/src/main/java/com/baeldung/securityextrafields/SecurityConfig.java create mode 100644 spring-5-security/src/main/java/com/baeldung/securityextrafields/SpringExtraLoginFieldsApplication.java create mode 100644 spring-5-security/src/main/java/com/baeldung/securityextrafields/User.java create mode 100644 spring-5-security/src/main/java/com/baeldung/securityextrafields/UserRepository.java create mode 100644 spring-5-security/src/main/java/com/baeldung/securityextrafields/WebController.java create mode 100644 spring-5-security/src/main/resources/application-extrafields.properties create mode 100644 spring-5-security/src/main/resources/static/css/main.css create mode 100644 spring-5-security/src/main/resources/templatesextrafields/index.html create mode 100644 spring-5-security/src/main/resources/templatesextrafields/login.html create mode 100644 spring-5-security/src/main/resources/templatesextrafields/user/index.html diff --git a/spring-5-security/pom.xml b/spring-5-security/pom.xml index c0f73b1bdd..c94e921a03 100644 --- a/spring-5-security/pom.xml +++ b/spring-5-security/pom.xml @@ -1,95 +1,110 @@ - - 4.0.0 - com.baeldung - spring-5-security - 0.0.1-SNAPSHOT - jar - - spring-5-security - spring 5 security sample project - - - org.springframework.boot - spring-boot-starter-parent - 2.0.0.M7 - - - - - - - org.springframework.boot - spring-boot-starter-security - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-thymeleaf - - - - - org.springframework.security - spring-security-oauth2-client - - - org.springframework.security - spring-security-oauth2-jose - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - org.apache.maven.plugins - maven-surefire-plugin - - 3 - true - methods - true - - **/*IntegrationTest.java - **/*LiveTest.java - - - - - - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - false - - - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - false - - - - - - UTF-8 - UTF-8 - 1.8 - + + 4.0.0 + com.baeldung + spring-5-security + 0.0.1-SNAPSHOT + jar + + spring-5-security + spring 5 security sample project + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.M7 + + + + + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.thymeleaf.extras + thymeleaf-extras-springsecurity4 + + + + + org.springframework.security + spring-security-oauth2-client + + + org.springframework.security + spring-security-oauth2-jose + + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.security + spring-security-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.apache.maven.plugins + maven-surefire-plugin + + 3 + true + methods + true + + **/*IntegrationTest.java + **/*LiveTest.java + + + + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + UTF-8 + UTF-8 + 1.8 + \ No newline at end of file diff --git a/spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomAuthenticationFilter.java b/spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomAuthenticationFilter.java new file mode 100644 index 0000000000..b5d628628d --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomAuthenticationFilter.java @@ -0,0 +1,54 @@ +package com.baeldung.securityextrafields; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.security.authentication.AuthenticationServiceException; +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; + +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()); + } + + UsernamePasswordAuthenticationToken authRequest = getAuthRequest(request); + setDetails(request, authRequest); + return this.getAuthenticationManager() + .authenticate(authRequest); + } + + private UsernamePasswordAuthenticationToken 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 = ""; + } + + String usernameDomain = String.format("%s%s%s", username.trim(), + String.valueOf(Character.LINE_SEPARATOR), domain); + return new UsernamePasswordAuthenticationToken(usernameDomain, password); + } + + private String obtainDomain(HttpServletRequest request) { + return request.getParameter(SPRING_SECURITY_FORM_DOMAIN_KEY); + } +} diff --git a/spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomUserDetailsService.java b/spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomUserDetailsService.java new file mode 100644 index 0000000000..be02834852 --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomUserDetailsService.java @@ -0,0 +1,32 @@ +package com.baeldung.securityextrafields; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +@Service("userDetailsService") +public class CustomUserDetailsService implements UserDetailsService { + + private final UserRepository userRepository; + + public CustomUserDetailsService(UserRepository userRepository) { + this.userRepository = userRepository; + } + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + String[] usernameAndDomain = StringUtils.split(username, String.valueOf(Character.LINE_SEPARATOR)); + if (usernameAndDomain == null || usernameAndDomain.length != 2) { + throw new UsernameNotFoundException("Username and domain must be provided"); + } + User user = userRepository.findUser(usernameAndDomain[0], usernameAndDomain[1]); + if (user == null) { + throw new UsernameNotFoundException( + String.format("Username not found for domain, username=%s, domain=%s", + usernameAndDomain[0], usernameAndDomain[1])); + } + return user; + } +} diff --git a/spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomUserRepository.java b/spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomUserRepository.java new file mode 100644 index 0000000000..c86769b016 --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomUserRepository.java @@ -0,0 +1,26 @@ +package com.baeldung.securityextrafields; + +import java.util.ArrayList; +import java.util.Collection; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.stereotype.Repository; + +@Repository("userRepository") +public class CustomUserRepository implements UserRepository { + + @Override + public User findUser(String username, String domain) { + if (StringUtils.isAnyBlank(username, domain)) { + return null; + } else { + Collection authorities = new ArrayList<>(); + User user = new User(username, domain, + "$2a$10$U3GhSMpsMSOE8Kqsbn58/edxDBKlVuYMh7qk/7ErApYFjJzi2VG5K", true, + true, true, true, authorities); + return user; + } + } + +} diff --git a/spring-5-security/src/main/java/com/baeldung/securityextrafields/SecurityConfig.java b/spring-5-security/src/main/java/com/baeldung/securityextrafields/SecurityConfig.java new file mode 100644 index 0000000000..257cc42692 --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/securityextrafields/SecurityConfig.java @@ -0,0 +1,65 @@ +package com.baeldung.securityextrafields; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.PropertySource; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +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.core.userdetails.UserDetailsService; +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("application-extrafields.properties") +public class SecurityConfig extends WebSecurityConfigurerAdapter { + + @Autowired + private UserDetailsService 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() { + DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); + provider.setUserDetailsService(userDetailsService); + provider.setPasswordEncoder(passwordEncoder()); + return provider; + } + + public SimpleUrlAuthenticationFailureHandler failureHandler() { + return new SimpleUrlAuthenticationFailureHandler("/login?error=true"); + } + + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } +} diff --git a/spring-5-security/src/main/java/com/baeldung/securityextrafields/SpringExtraLoginFieldsApplication.java b/spring-5-security/src/main/java/com/baeldung/securityextrafields/SpringExtraLoginFieldsApplication.java new file mode 100644 index 0000000000..a779acc75e --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/securityextrafields/SpringExtraLoginFieldsApplication.java @@ -0,0 +1,13 @@ +package com.baeldung.securityextrafields; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringExtraLoginFieldsApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringExtraLoginFieldsApplication.class, args); + } + +} diff --git a/spring-5-security/src/main/java/com/baeldung/securityextrafields/User.java b/spring-5-security/src/main/java/com/baeldung/securityextrafields/User.java new file mode 100644 index 0000000000..a5b3a434ae --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/securityextrafields/User.java @@ -0,0 +1,23 @@ +package com.baeldung.securityextrafields; + +import java.util.Collection; + +import org.springframework.security.core.GrantedAuthority; + +public class User extends org.springframework.security.core.userdetails.User { + + private static final long serialVersionUID = 1L; + + private final String domain; + + public User(String username, String domain, String password, boolean enabled, + boolean accountNonExpired, boolean credentialsNonExpired, + boolean accountNonLocked, Collection authorities) { + super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities); + this.domain = domain; + } + + public String getDomain() { + return domain; + } +} diff --git a/spring-5-security/src/main/java/com/baeldung/securityextrafields/UserRepository.java b/spring-5-security/src/main/java/com/baeldung/securityextrafields/UserRepository.java new file mode 100644 index 0000000000..4ca65b13d5 --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/securityextrafields/UserRepository.java @@ -0,0 +1,7 @@ +package com.baeldung.securityextrafields; + +public interface UserRepository { + + public User findUser(String username, String domain); + +} diff --git a/spring-5-security/src/main/java/com/baeldung/securityextrafields/WebController.java b/spring-5-security/src/main/java/com/baeldung/securityextrafields/WebController.java new file mode 100644 index 0000000000..4a8abb4a83 --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/securityextrafields/WebController.java @@ -0,0 +1,51 @@ +package com.baeldung.securityextrafields; + +import java.util.Optional; + +import org.springframework.security.authentication.AnonymousAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; + +@Controller +public class WebController { + + @RequestMapping("/") + public String root() { + return "redirect:/index"; + } + + @RequestMapping("/index") + public String index(Model model) { + getDomain().ifPresent(d -> { + model.addAttribute("domain", d); + }); + return "index"; + } + + @RequestMapping("/user/index") + public String userIndex(Model model) { + getDomain().ifPresent(d -> { + model.addAttribute("domain", d); + }); + return "user/index"; + } + + @RequestMapping("/login") + public String login() { + return "login"; + } + + private Optional getDomain() { + Authentication auth = SecurityContextHolder.getContext() + .getAuthentication(); + String domain = null; + if (auth != null && !auth.getClass().equals(AnonymousAuthenticationToken.class)) { + User user = (User) auth.getPrincipal(); + domain = user.getDomain(); + } + return Optional.ofNullable(domain); + } +} diff --git a/spring-5-security/src/main/resources/application-extrafields.properties b/spring-5-security/src/main/resources/application-extrafields.properties new file mode 100644 index 0000000000..ab4134ce3e --- /dev/null +++ b/spring-5-security/src/main/resources/application-extrafields.properties @@ -0,0 +1 @@ +spring.thymeleaf.prefix = classpath:/templatesextrafields/ \ No newline at end of file diff --git a/spring-5-security/src/main/resources/static/css/main.css b/spring-5-security/src/main/resources/static/css/main.css new file mode 100644 index 0000000000..9299ee6158 --- /dev/null +++ b/spring-5-security/src/main/resources/static/css/main.css @@ -0,0 +1,18 @@ +body { + font-family: sans; + font-size: 1em; +} + +p.error { + font-weight: bold; + color: red; +} + +div.logout { + float: right; +} + +.formfield { + margin: 0.5em; + padding: 0.3em; +} \ No newline at end of file diff --git a/spring-5-security/src/main/resources/templatesextrafields/index.html b/spring-5-security/src/main/resources/templatesextrafields/index.html new file mode 100644 index 0000000000..52f6224dfb --- /dev/null +++ b/spring-5-security/src/main/resources/templatesextrafields/index.html @@ -0,0 +1,24 @@ + + + + Spring Security - Login With Extra Fields + + + + +
+ Logged in user: | + domain: Some Domain +
+
+ +
+
+
+

Hello Spring Security

+

This is an unsecured page, but you can access the secured pages after authenticating.

+ + + diff --git a/spring-5-security/src/main/resources/templatesextrafields/login.html b/spring-5-security/src/main/resources/templatesextrafields/login.html new file mode 100644 index 0000000000..cafec89c15 --- /dev/null +++ b/spring-5-security/src/main/resources/templatesextrafields/login.html @@ -0,0 +1,23 @@ + + + + Login page + + + + +

Login page

+

Example: user / domain / password

+

Invalid user, password, or domain

+
+ : +
+ : +
+ : +
+ +
+

Back to home page

+ + diff --git a/spring-5-security/src/main/resources/templatesextrafields/user/index.html b/spring-5-security/src/main/resources/templatesextrafields/user/index.html new file mode 100644 index 0000000000..a4c1535100 --- /dev/null +++ b/spring-5-security/src/main/resources/templatesextrafields/user/index.html @@ -0,0 +1,13 @@ + + + + Spring Security - Login With Extra Fields + + + + +
+

This is a secured page!

+

Back to home page

+ + From 99887d2f1b2a2ef5257b3a3dbe06eb7dd195c939 Mon Sep 17 00:00:00 2001 From: Chris Oberle Date: Tue, 16 Jan 2018 18:26:43 -0500 Subject: [PATCH 5/9] finish moving example code to spring-5-security module --- spring-5-security/pom.xml | 222 ++++---- .../securityextrafields/SecurityConfig.java | 2 +- .../SecurityExtraFieldsTest.java | 25 +- spring-5/pom.xml | 512 +++++++++--------- ...gSecurity5ExtraLoginFieldsApplication.java | 15 - .../CustomAuthenticationFilter.java | 54 -- .../CustomUserDetailsService.java | 32 -- .../CustomUserRepository.java | 26 - .../securityextrafields/SecurityConfig.java | 63 --- .../baeldung/securityextrafields/User.java | 23 - .../securityextrafields/UserRepository.java | 7 - .../securityextrafields/WebController.java | 51 -- .../src/main/resources/static/css/main.css | 18 - .../src/main/resources/templates/index.html | 24 - .../src/main/resources/templates/login.html | 23 - .../main/resources/templates/user/index.html | 13 - 16 files changed, 374 insertions(+), 736 deletions(-) rename {spring-5 => spring-5-security}/src/test/java/com/baeldung/securityextrafields/SecurityExtraFieldsTest.java (83%) delete mode 100644 spring-5/src/main/java/com/baeldung/SpringSecurity5ExtraLoginFieldsApplication.java delete mode 100644 spring-5/src/main/java/com/baeldung/securityextrafields/CustomAuthenticationFilter.java delete mode 100644 spring-5/src/main/java/com/baeldung/securityextrafields/CustomUserDetailsService.java delete mode 100644 spring-5/src/main/java/com/baeldung/securityextrafields/CustomUserRepository.java delete mode 100644 spring-5/src/main/java/com/baeldung/securityextrafields/SecurityConfig.java delete mode 100644 spring-5/src/main/java/com/baeldung/securityextrafields/User.java delete mode 100644 spring-5/src/main/java/com/baeldung/securityextrafields/UserRepository.java delete mode 100644 spring-5/src/main/java/com/baeldung/securityextrafields/WebController.java delete mode 100644 spring-5/src/main/resources/static/css/main.css delete mode 100644 spring-5/src/main/resources/templates/index.html delete mode 100644 spring-5/src/main/resources/templates/login.html delete mode 100644 spring-5/src/main/resources/templates/user/index.html diff --git a/spring-5-security/pom.xml b/spring-5-security/pom.xml index c94e921a03..9300882459 100644 --- a/spring-5-security/pom.xml +++ b/spring-5-security/pom.xml @@ -1,110 +1,114 @@ - - 4.0.0 - com.baeldung - spring-5-security - 0.0.1-SNAPSHOT - jar - - spring-5-security - spring 5 security sample project - - - org.springframework.boot - spring-boot-starter-parent - 2.0.0.M7 - - - - - - - org.springframework.boot - spring-boot-starter-security - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-thymeleaf - - - org.thymeleaf.extras - thymeleaf-extras-springsecurity4 - - - - - org.springframework.security - spring-security-oauth2-client - - - org.springframework.security - spring-security-oauth2-jose - - - - org.springframework.boot - spring-boot-starter-test - test - - - org.springframework.security - spring-security-test - test - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - org.apache.maven.plugins - maven-surefire-plugin - - 3 - true - methods - true - - **/*IntegrationTest.java - **/*LiveTest.java - - - - - - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - false - - - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - false - - - - - - UTF-8 - UTF-8 - 1.8 - + + 4.0.0 + com.baeldung + spring-5-security + 0.0.1-SNAPSHOT + jar + + spring-5-security + spring 5 security sample project + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.M7 + + + + + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.thymeleaf.extras + thymeleaf-extras-springsecurity4 + + + + + org.springframework.security + spring-security-oauth2-client + + + org.springframework.security + spring-security-oauth2-jose + + + + org.springframework + spring-test + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.security + spring-security-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.apache.maven.plugins + maven-surefire-plugin + + 3 + true + methods + true + + **/*IntegrationTest.java + **/*LiveTest.java + + + + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + UTF-8 + UTF-8 + 1.8 + \ No newline at end of file diff --git a/spring-5-security/src/main/java/com/baeldung/securityextrafields/SecurityConfig.java b/spring-5-security/src/main/java/com/baeldung/securityextrafields/SecurityConfig.java index 257cc42692..affb6d436d 100644 --- a/spring-5-security/src/main/java/com/baeldung/securityextrafields/SecurityConfig.java +++ b/spring-5-security/src/main/java/com/baeldung/securityextrafields/SecurityConfig.java @@ -15,7 +15,7 @@ import org.springframework.security.web.authentication.SimpleUrlAuthenticationFa import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @EnableWebSecurity -@PropertySource("application-extrafields.properties") +@PropertySource("classpath:/application-extrafields.properties") public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired diff --git a/spring-5/src/test/java/com/baeldung/securityextrafields/SecurityExtraFieldsTest.java b/spring-5-security/src/test/java/com/baeldung/securityextrafields/SecurityExtraFieldsTest.java similarity index 83% rename from spring-5/src/test/java/com/baeldung/securityextrafields/SecurityExtraFieldsTest.java rename to spring-5-security/src/test/java/com/baeldung/securityextrafields/SecurityExtraFieldsTest.java index 09ee0e66a0..cf0701708d 100644 --- a/spring-5/src/test/java/com/baeldung/securityextrafields/SecurityExtraFieldsTest.java +++ b/spring-5-security/src/test/java/com/baeldung/securityextrafields/SecurityExtraFieldsTest.java @@ -11,47 +11,44 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import java.util.ArrayList; import java.util.Collection; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; +import org.junit.Before; +import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; import org.springframework.mock.web.MockHttpSession; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.web.FilterChainProxy; import org.springframework.security.web.context.HttpSessionSecurityContextRepository; -import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig; import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; -import com.baeldung.SpringSecurity5ExtraLoginFieldsApplication; - -@WebAppConfiguration -@SpringJUnitWebConfig @RunWith(SpringRunner.class) -@ContextConfiguration(classes = SpringSecurity5ExtraLoginFieldsApplication.class) +@SpringJUnitWebConfig +@SpringBootTest(classes = SpringExtraLoginFieldsApplication.class) public class SecurityExtraFieldsTest { @Autowired private FilterChainProxy springSecurityFilterChain; + @Autowired + private WebApplicationContext wac; + private MockMvc mockMvc; - @BeforeEach - public void setup(WebApplicationContext wac) { + @Before + public void setup() { this.mockMvc = MockMvcBuilders.webAppContextSetup(wac) .apply(springSecurity(springSecurityFilterChain)).build(); } - @DisplayName("Access of root path redirects to index") @Test public void givenRootPathAccess_thenRedirectToIndex() throws Exception { this.mockMvc.perform(get("/")) @@ -59,7 +56,6 @@ public class SecurityExtraFieldsTest { .andExpect(redirectedUrlPattern("/index*")); } - @DisplayName("Unauthenticated access of secured resource redirects to login page") @Test public void givenSecuredResource_whenAccessUnauthenticated_thenRequiresAuthentication() throws Exception { this.mockMvc.perform(get("/user/index")) @@ -67,7 +63,6 @@ public class SecurityExtraFieldsTest { .andExpect(redirectedUrlPattern("**/login")); } - @DisplayName("Succesfull auth on login page redirects and extra field exists") @Test public void givenAccessSecuredResource_whenAuthenticated_thenAuthHasExtraFields() throws Exception { MockHttpServletRequestBuilder securedResourceAccess = get("/user/index"); diff --git a/spring-5/pom.xml b/spring-5/pom.xml index 71f355f41a..19dd65d78f 100644 --- a/spring-5/pom.xml +++ b/spring-5/pom.xml @@ -1,262 +1,250 @@ - - - 4.0.0 - - com.baeldung - spring-5 - 0.0.1-SNAPSHOT - jar - - spring-5 - spring 5 sample project about new features - - - org.springframework.boot - spring-boot-starter-parent - 2.0.0.M7 - - - - - - org.springframework.boot - spring-boot-starter-data-jpa - - - org.springframework.boot - spring-boot-starter-security - - - org.springframework.boot - spring-boot-starter-validation - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-thymeleaf - - - org.thymeleaf.extras - thymeleaf-extras-springsecurity4 - - - org.springframework.boot - spring-boot-starter-webflux - - - org.springframework.boot - spring-boot-starter-hateoas - - - org.projectreactor - reactor-spring - ${reactor-spring.version} - - - javax.json.bind - javax.json.bind-api - ${jsonb-api.version} - - - - - - - - - - - - - - - org.apache.geronimo.specs - geronimo-json_1.1_spec - ${geronimo-json_1.1_spec.version} - - - org.apache.johnzon - johnzon-jsonb - ${johnzon.version} - - - - org.apache.commons - commons-lang3 - - - - - - org.springframework.boot - spring-boot-devtools - runtime - - - com.h2database - h2 - runtime - - - - org.springframework - spring-test - - - org.springframework.boot - spring-boot-starter-test - test - - - org.springframework.security - spring-security-test - test - - - - org.apache.commons - commons-collections4 - 4.1 - test - - - - org.junit.jupiter - junit-jupiter-api - ${junit.jupiter.version} - - - org.junit.jupiter - junit-jupiter-engine - ${junit.jupiter.version} - test - - - org.junit.platform - junit-platform-surefire-provider - ${junit.platform.version} - test - - - org.junit.platform - junit-platform-runner - ${junit.platform.version} - test - - - - org.springframework.restdocs - spring-restdocs-mockmvc - test - - - org.springframework.restdocs - spring-restdocs-webtestclient - test - - - org.springframework.restdocs - spring-restdocs-restassured - test - - - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - com.baeldung.Spring5Application - JAR - - - - - org.apache.maven.plugins - maven-surefire-plugin - - 3 - true - methods - true - - **/*IntegrationTest.java - **/*LiveTest.java - - - - - org.asciidoctor - asciidoctor-maven-plugin - ${asciidoctor-plugin.version} - - - generate-docs - package - - process-asciidoc - - - html - book - - ${snippetsDirectory} - - src/docs/asciidocs - target/generated-docs - - - - - - - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - false - - - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - false - - - - - - UTF-8 - UTF-8 - 1.8 - 1.0.0 - 5.0.0 - 2.20 - 5.0.2.RELEASE - 1.0.1.RELEASE - 1.1.3 - 1.0 - 1.0 - 1.5.6 - ${project.build.directory}/generated-snippets - - - + + + 4.0.0 + + com.baeldung + spring-5 + 0.0.1-SNAPSHOT + jar + + spring-5 + spring 5 sample project about new features + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.M7 + + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-webflux + + + org.springframework.boot + spring-boot-starter-hateoas + + + org.projectreactor + reactor-spring + ${reactor-spring.version} + + + javax.json.bind + javax.json.bind-api + ${jsonb-api.version} + + + + + + + + + + + + + + + org.apache.geronimo.specs + geronimo-json_1.1_spec + ${geronimo-json_1.1_spec.version} + + + org.apache.johnzon + johnzon-jsonb + ${johnzon.version} + + + + org.apache.commons + commons-lang3 + + + + + + org.springframework.boot + spring-boot-devtools + runtime + + + com.h2database + h2 + runtime + + + + org.springframework + spring-test + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.security + spring-security-test + test + + + + org.apache.commons + commons-collections4 + 4.1 + test + + + + org.junit.jupiter + junit-jupiter-api + ${junit.jupiter.version} + + + org.junit.jupiter + junit-jupiter-engine + ${junit.jupiter.version} + test + + + org.junit.platform + junit-platform-surefire-provider + ${junit.platform.version} + test + + + org.junit.platform + junit-platform-runner + ${junit.platform.version} + test + + + + org.springframework.restdocs + spring-restdocs-mockmvc + test + + + org.springframework.restdocs + spring-restdocs-webtestclient + test + + + org.springframework.restdocs + spring-restdocs-restassured + test + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + com.baeldung.Spring5Application + JAR + + + + + org.apache.maven.plugins + maven-surefire-plugin + + 3 + true + methods + true + + **/*IntegrationTest.java + **/*LiveTest.java + + + + + org.asciidoctor + asciidoctor-maven-plugin + ${asciidoctor-plugin.version} + + + generate-docs + package + + process-asciidoc + + + html + book + + ${snippetsDirectory} + + src/docs/asciidocs + target/generated-docs + + + + + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + spring-milestones + Spring Milestones + https://repo.spring.io/milestone + + false + + + + + + UTF-8 + UTF-8 + 1.8 + 1.0.0 + 5.0.0 + 2.20 + 5.0.2.RELEASE + 1.0.1.RELEASE + 1.1.3 + 1.0 + 1.0 + 1.5.6 + ${project.build.directory}/generated-snippets + + + diff --git a/spring-5/src/main/java/com/baeldung/SpringSecurity5ExtraLoginFieldsApplication.java b/spring-5/src/main/java/com/baeldung/SpringSecurity5ExtraLoginFieldsApplication.java deleted file mode 100644 index b6285dfc71..0000000000 --- a/spring-5/src/main/java/com/baeldung/SpringSecurity5ExtraLoginFieldsApplication.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.baeldung; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.ComponentScan; - -@ComponentScan(basePackages = {"com.baeldung.securityextrafields"}) -@SpringBootApplication -public class SpringSecurity5ExtraLoginFieldsApplication { - - public static void main(String[] args) { - SpringApplication.run(SpringSecurity5ExtraLoginFieldsApplication.class, args); - } - -} diff --git a/spring-5/src/main/java/com/baeldung/securityextrafields/CustomAuthenticationFilter.java b/spring-5/src/main/java/com/baeldung/securityextrafields/CustomAuthenticationFilter.java deleted file mode 100644 index b5d628628d..0000000000 --- a/spring-5/src/main/java/com/baeldung/securityextrafields/CustomAuthenticationFilter.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.baeldung.securityextrafields; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.security.authentication.AuthenticationServiceException; -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; - -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()); - } - - UsernamePasswordAuthenticationToken authRequest = getAuthRequest(request); - setDetails(request, authRequest); - return this.getAuthenticationManager() - .authenticate(authRequest); - } - - private UsernamePasswordAuthenticationToken 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 = ""; - } - - String usernameDomain = String.format("%s%s%s", username.trim(), - String.valueOf(Character.LINE_SEPARATOR), domain); - return new UsernamePasswordAuthenticationToken(usernameDomain, password); - } - - private String obtainDomain(HttpServletRequest request) { - return request.getParameter(SPRING_SECURITY_FORM_DOMAIN_KEY); - } -} diff --git a/spring-5/src/main/java/com/baeldung/securityextrafields/CustomUserDetailsService.java b/spring-5/src/main/java/com/baeldung/securityextrafields/CustomUserDetailsService.java deleted file mode 100644 index be02834852..0000000000 --- a/spring-5/src/main/java/com/baeldung/securityextrafields/CustomUserDetailsService.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.baeldung.securityextrafields; - -import org.apache.commons.lang3.StringUtils; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.stereotype.Service; - -@Service("userDetailsService") -public class CustomUserDetailsService implements UserDetailsService { - - private final UserRepository userRepository; - - public CustomUserDetailsService(UserRepository userRepository) { - this.userRepository = userRepository; - } - - @Override - public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { - String[] usernameAndDomain = StringUtils.split(username, String.valueOf(Character.LINE_SEPARATOR)); - if (usernameAndDomain == null || usernameAndDomain.length != 2) { - throw new UsernameNotFoundException("Username and domain must be provided"); - } - User user = userRepository.findUser(usernameAndDomain[0], usernameAndDomain[1]); - if (user == null) { - throw new UsernameNotFoundException( - String.format("Username not found for domain, username=%s, domain=%s", - usernameAndDomain[0], usernameAndDomain[1])); - } - return user; - } -} diff --git a/spring-5/src/main/java/com/baeldung/securityextrafields/CustomUserRepository.java b/spring-5/src/main/java/com/baeldung/securityextrafields/CustomUserRepository.java deleted file mode 100644 index c86769b016..0000000000 --- a/spring-5/src/main/java/com/baeldung/securityextrafields/CustomUserRepository.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.baeldung.securityextrafields; - -import java.util.ArrayList; -import java.util.Collection; - -import org.apache.commons.lang3.StringUtils; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.stereotype.Repository; - -@Repository("userRepository") -public class CustomUserRepository implements UserRepository { - - @Override - public User findUser(String username, String domain) { - if (StringUtils.isAnyBlank(username, domain)) { - return null; - } else { - Collection authorities = new ArrayList<>(); - User user = new User(username, domain, - "$2a$10$U3GhSMpsMSOE8Kqsbn58/edxDBKlVuYMh7qk/7ErApYFjJzi2VG5K", true, - true, true, true, authorities); - return user; - } - } - -} diff --git a/spring-5/src/main/java/com/baeldung/securityextrafields/SecurityConfig.java b/spring-5/src/main/java/com/baeldung/securityextrafields/SecurityConfig.java deleted file mode 100644 index e8e8567773..0000000000 --- a/spring-5/src/main/java/com/baeldung/securityextrafields/SecurityConfig.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.baeldung.securityextrafields; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.authentication.AuthenticationProvider; -import org.springframework.security.authentication.dao.DaoAuthenticationProvider; -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.core.userdetails.UserDetailsService; -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 -public class SecurityConfig extends WebSecurityConfigurerAdapter { - - @Autowired - private UserDetailsService 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() { - DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); - provider.setUserDetailsService(userDetailsService); - provider.setPasswordEncoder(passwordEncoder()); - return provider; - } - - public SimpleUrlAuthenticationFailureHandler failureHandler() { - return new SimpleUrlAuthenticationFailureHandler("/login?error=true"); - } - - public PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); - } -} diff --git a/spring-5/src/main/java/com/baeldung/securityextrafields/User.java b/spring-5/src/main/java/com/baeldung/securityextrafields/User.java deleted file mode 100644 index a5b3a434ae..0000000000 --- a/spring-5/src/main/java/com/baeldung/securityextrafields/User.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.baeldung.securityextrafields; - -import java.util.Collection; - -import org.springframework.security.core.GrantedAuthority; - -public class User extends org.springframework.security.core.userdetails.User { - - private static final long serialVersionUID = 1L; - - private final String domain; - - public User(String username, String domain, String password, boolean enabled, - boolean accountNonExpired, boolean credentialsNonExpired, - boolean accountNonLocked, Collection authorities) { - super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities); - this.domain = domain; - } - - public String getDomain() { - return domain; - } -} diff --git a/spring-5/src/main/java/com/baeldung/securityextrafields/UserRepository.java b/spring-5/src/main/java/com/baeldung/securityextrafields/UserRepository.java deleted file mode 100644 index 4ca65b13d5..0000000000 --- a/spring-5/src/main/java/com/baeldung/securityextrafields/UserRepository.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.baeldung.securityextrafields; - -public interface UserRepository { - - public User findUser(String username, String domain); - -} diff --git a/spring-5/src/main/java/com/baeldung/securityextrafields/WebController.java b/spring-5/src/main/java/com/baeldung/securityextrafields/WebController.java deleted file mode 100644 index 4a8abb4a83..0000000000 --- a/spring-5/src/main/java/com/baeldung/securityextrafields/WebController.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.baeldung.securityextrafields; - -import java.util.Optional; - -import org.springframework.security.authentication.AnonymousAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.RequestMapping; - -@Controller -public class WebController { - - @RequestMapping("/") - public String root() { - return "redirect:/index"; - } - - @RequestMapping("/index") - public String index(Model model) { - getDomain().ifPresent(d -> { - model.addAttribute("domain", d); - }); - return "index"; - } - - @RequestMapping("/user/index") - public String userIndex(Model model) { - getDomain().ifPresent(d -> { - model.addAttribute("domain", d); - }); - return "user/index"; - } - - @RequestMapping("/login") - public String login() { - return "login"; - } - - private Optional getDomain() { - Authentication auth = SecurityContextHolder.getContext() - .getAuthentication(); - String domain = null; - if (auth != null && !auth.getClass().equals(AnonymousAuthenticationToken.class)) { - User user = (User) auth.getPrincipal(); - domain = user.getDomain(); - } - return Optional.ofNullable(domain); - } -} diff --git a/spring-5/src/main/resources/static/css/main.css b/spring-5/src/main/resources/static/css/main.css deleted file mode 100644 index 9299ee6158..0000000000 --- a/spring-5/src/main/resources/static/css/main.css +++ /dev/null @@ -1,18 +0,0 @@ -body { - font-family: sans; - font-size: 1em; -} - -p.error { - font-weight: bold; - color: red; -} - -div.logout { - float: right; -} - -.formfield { - margin: 0.5em; - padding: 0.3em; -} \ No newline at end of file diff --git a/spring-5/src/main/resources/templates/index.html b/spring-5/src/main/resources/templates/index.html deleted file mode 100644 index 52f6224dfb..0000000000 --- a/spring-5/src/main/resources/templates/index.html +++ /dev/null @@ -1,24 +0,0 @@ - - - - Spring Security - Login With Extra Fields - - - - -
- Logged in user: | - domain: Some Domain -
-
- -
-
-
-

Hello Spring Security

-

This is an unsecured page, but you can access the secured pages after authenticating.

- - - diff --git a/spring-5/src/main/resources/templates/login.html b/spring-5/src/main/resources/templates/login.html deleted file mode 100644 index cafec89c15..0000000000 --- a/spring-5/src/main/resources/templates/login.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - Login page - - - - -

Login page

-

Example: user / domain / password

-

Invalid user, password, or domain

-
- : -
- : -
- : -
- -
-

Back to home page

- - diff --git a/spring-5/src/main/resources/templates/user/index.html b/spring-5/src/main/resources/templates/user/index.html deleted file mode 100644 index a4c1535100..0000000000 --- a/spring-5/src/main/resources/templates/user/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - Spring Security - Login With Extra Fields - - - - -
-

This is a secured page!

-

Back to home page

- - From 5a0899ff25c1f54f890566e84840f668d660b444 Mon Sep 17 00:00:00 2001 From: Chris Oberle Date: Tue, 16 Jan 2018 18:31:42 -0500 Subject: [PATCH 6/9] fix formatting in pom --- spring-5-security/pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spring-5-security/pom.xml b/spring-5-security/pom.xml index 9300882459..0a1d1f5df0 100644 --- a/spring-5-security/pom.xml +++ b/spring-5-security/pom.xml @@ -30,10 +30,10 @@ org.springframework.boot spring-boot-starter-thymeleaf - - org.thymeleaf.extras - thymeleaf-extras-springsecurity4 - + + org.thymeleaf.extras + thymeleaf-extras-springsecurity4 + From 29abf338f0aec14770562204fa558088f7b213d0 Mon Sep 17 00:00:00 2001 From: Chris Oberle Date: Tue, 16 Jan 2018 20:39:53 -0500 Subject: [PATCH 7/9] adjust spacing --- .../java/com/baeldung/securityextrafields/SecurityConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-5-security/src/main/java/com/baeldung/securityextrafields/SecurityConfig.java b/spring-5-security/src/main/java/com/baeldung/securityextrafields/SecurityConfig.java index affb6d436d..429f6df972 100644 --- a/spring-5-security/src/main/java/com/baeldung/securityextrafields/SecurityConfig.java +++ b/spring-5-security/src/main/java/com/baeldung/securityextrafields/SecurityConfig.java @@ -45,7 +45,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { - auth.authenticationProvider(authProvider()); + auth.authenticationProvider(authProvider()); } public AuthenticationProvider authProvider() { From a20a4c9c23a4ef5636c4dca1862321d98a865cdb Mon Sep 17 00:00:00 2001 From: Chris Oberle Date: Sun, 21 Jan 2018 08:56:59 -0500 Subject: [PATCH 8/9] BAEL-1418 Spring Security with Extra Login Fields * added additional custom example * refactored and added tests --- .../CustomAuthenticationFilter.java | 50 ++++++++++ .../CustomAuthenticationToken.java | 30 ++++++ ...stomUserDetailsAuthenticationProvider.java | 92 +++++++++++++++++++ .../CustomUserDetailsService.java | 10 ++ .../CustomUserDetailsServiceImpl.java | 30 ++++++ .../CustomUserRepository.java | 2 +- .../ExtraLoginFieldsApplication.java} | 6 +- .../SecurityConfig.java | 62 +++++++++++++ .../User.java | 2 +- .../UserRepository.java | 2 +- .../WebController.java | 2 +- .../ExtraLoginFieldsApplication.java | 13 +++ .../SecurityConfig.java | 6 +- .../SimpleAuthenticationFilter.java} | 4 +- .../SimpleUserDetailsService.java} | 6 +- .../SimpleUserRepository.java | 26 ++++++ .../baeldung/loginextrafieldssimple/User.java | 23 +++++ .../UserRepository.java | 7 ++ .../loginextrafieldssimple/WebController.java | 51 ++++++++++ .../src/main/resources/static/css/main.css | 12 +-- .../resources/templatesextrafields/index.html | 22 +++-- .../resources/templatesextrafields/login.html | 49 ++++++---- .../templatesextrafields/user/index.html | 29 +++--- .../AbstractExtraLoginFieldsTest.java | 46 ++++++++++ .../LoginFieldsFullTest.java} | 45 ++------- .../LoginFieldsSimpleTest.java | 72 +++++++++++++++ 26 files changed, 599 insertions(+), 100 deletions(-) create mode 100644 spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomAuthenticationFilter.java create mode 100644 spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomAuthenticationToken.java create mode 100644 spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsAuthenticationProvider.java create mode 100644 spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsService.java create mode 100644 spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsServiceImpl.java rename spring-5-security/src/main/java/com/baeldung/{securityextrafields => loginextrafieldscustom}/CustomUserRepository.java (91%) rename spring-5-security/src/main/java/com/baeldung/{securityextrafields/SpringExtraLoginFieldsApplication.java => loginextrafieldscustom/ExtraLoginFieldsApplication.java} (54%) create mode 100644 spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/SecurityConfig.java rename spring-5-security/src/main/java/com/baeldung/{securityextrafields => loginextrafieldscustom}/User.java (91%) rename spring-5-security/src/main/java/com/baeldung/{securityextrafields => loginextrafieldscustom}/UserRepository.java (66%) rename spring-5-security/src/main/java/com/baeldung/{securityextrafields => loginextrafieldscustom}/WebController.java (93%) create mode 100644 spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/ExtraLoginFieldsApplication.java rename spring-5-security/src/main/java/com/baeldung/{securityextrafields => loginextrafieldssimple}/SecurityConfig.java (91%) rename spring-5-security/src/main/java/com/baeldung/{securityextrafields/CustomAuthenticationFilter.java => loginextrafieldssimple/SimpleAuthenticationFilter.java} (92%) rename spring-5-security/src/main/java/com/baeldung/{securityextrafields/CustomUserDetailsService.java => loginextrafieldssimple/SimpleUserDetailsService.java} (85%) create mode 100644 spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleUserRepository.java create mode 100644 spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/User.java create mode 100644 spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/UserRepository.java create mode 100644 spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/WebController.java create mode 100644 spring-5-security/src/test/java/com/baeldung/loginextrafields/AbstractExtraLoginFieldsTest.java rename spring-5-security/src/test/java/com/baeldung/{securityextrafields/SecurityExtraFieldsTest.java => loginextrafields/LoginFieldsFullTest.java} (65%) create mode 100644 spring-5-security/src/test/java/com/baeldung/loginextrafields/LoginFieldsSimpleTest.java diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomAuthenticationFilter.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomAuthenticationFilter.java new file mode 100644 index 0000000000..2a5c5f0368 --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomAuthenticationFilter.java @@ -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); + } +} diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomAuthenticationToken.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomAuthenticationToken.java new file mode 100644 index 0000000000..6de842f2e0 --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomAuthenticationToken.java @@ -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 authorities) { + super(principal, credentials, authorities); + this.domain = domain; + super.setAuthenticated(true); // must use super, as we override + } + + public String getDomain() { + return this.domain; + } +} diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsAuthenticationProvider.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsAuthenticationProvider.java new file mode 100644 index 0000000000..97a589077e --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsAuthenticationProvider.java @@ -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; + } + +} diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsService.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsService.java new file mode 100644 index 0000000000..358129173d --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsService.java @@ -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; + +} diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsServiceImpl.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsServiceImpl.java new file mode 100644 index 0000000000..bf53170205 --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsServiceImpl.java @@ -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; + } +} diff --git a/spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomUserRepository.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserRepository.java similarity index 91% rename from spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomUserRepository.java rename to spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserRepository.java index c86769b016..428c8bf532 100644 --- a/spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomUserRepository.java +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserRepository.java @@ -1,4 +1,4 @@ -package com.baeldung.securityextrafields; +package com.baeldung.loginextrafieldscustom; import java.util.ArrayList; import java.util.Collection; diff --git a/spring-5-security/src/main/java/com/baeldung/securityextrafields/SpringExtraLoginFieldsApplication.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/ExtraLoginFieldsApplication.java similarity index 54% rename from spring-5-security/src/main/java/com/baeldung/securityextrafields/SpringExtraLoginFieldsApplication.java rename to spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/ExtraLoginFieldsApplication.java index a779acc75e..0cf934f288 100644 --- a/spring-5-security/src/main/java/com/baeldung/securityextrafields/SpringExtraLoginFieldsApplication.java +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/ExtraLoginFieldsApplication.java @@ -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); } } diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/SecurityConfig.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/SecurityConfig.java new file mode 100644 index 0000000000..def85ab978 --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/SecurityConfig.java @@ -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(); + } +} diff --git a/spring-5-security/src/main/java/com/baeldung/securityextrafields/User.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/User.java similarity index 91% rename from spring-5-security/src/main/java/com/baeldung/securityextrafields/User.java rename to spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/User.java index a5b3a434ae..2d684d96e4 100644 --- a/spring-5-security/src/main/java/com/baeldung/securityextrafields/User.java +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/User.java @@ -1,4 +1,4 @@ -package com.baeldung.securityextrafields; +package com.baeldung.loginextrafieldscustom; import java.util.Collection; diff --git a/spring-5-security/src/main/java/com/baeldung/securityextrafields/UserRepository.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/UserRepository.java similarity index 66% rename from spring-5-security/src/main/java/com/baeldung/securityextrafields/UserRepository.java rename to spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/UserRepository.java index 4ca65b13d5..e2358e055b 100644 --- a/spring-5-security/src/main/java/com/baeldung/securityextrafields/UserRepository.java +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/UserRepository.java @@ -1,4 +1,4 @@ -package com.baeldung.securityextrafields; +package com.baeldung.loginextrafieldscustom; public interface UserRepository { diff --git a/spring-5-security/src/main/java/com/baeldung/securityextrafields/WebController.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/WebController.java similarity index 93% rename from spring-5-security/src/main/java/com/baeldung/securityextrafields/WebController.java rename to spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/WebController.java index 4a8abb4a83..b5e0b511ac 100644 --- a/spring-5-security/src/main/java/com/baeldung/securityextrafields/WebController.java +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/WebController.java @@ -1,4 +1,4 @@ -package com.baeldung.securityextrafields; +package com.baeldung.loginextrafieldscustom; import java.util.Optional; diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/ExtraLoginFieldsApplication.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/ExtraLoginFieldsApplication.java new file mode 100644 index 0000000000..c82a13de1a --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/ExtraLoginFieldsApplication.java @@ -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); + } + +} diff --git a/spring-5-security/src/main/java/com/baeldung/securityextrafields/SecurityConfig.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SecurityConfig.java similarity index 91% rename from spring-5-security/src/main/java/com/baeldung/securityextrafields/SecurityConfig.java rename to spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SecurityConfig.java index 429f6df972..d8c5ea8147 100644 --- a/spring-5-security/src/main/java/com/baeldung/securityextrafields/SecurityConfig.java +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SecurityConfig.java @@ -1,4 +1,4 @@ -package com.baeldung.securityextrafields; +package com.baeldung.loginextrafieldssimple; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.PropertySource; @@ -36,8 +36,8 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { .logoutUrl("/logout"); } - public CustomAuthenticationFilter authenticationFilter() throws Exception { - CustomAuthenticationFilter filter = new CustomAuthenticationFilter(); + public SimpleAuthenticationFilter authenticationFilter() throws Exception { + SimpleAuthenticationFilter filter = new SimpleAuthenticationFilter(); filter.setAuthenticationManager(authenticationManagerBean()); filter.setAuthenticationFailureHandler(failureHandler()); return filter; diff --git a/spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomAuthenticationFilter.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleAuthenticationFilter.java similarity index 92% rename from spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomAuthenticationFilter.java rename to spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleAuthenticationFilter.java index b5d628628d..9dcb524157 100644 --- a/spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomAuthenticationFilter.java +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleAuthenticationFilter.java @@ -1,4 +1,4 @@ -package com.baeldung.securityextrafields; +package com.baeldung.loginextrafieldssimple; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -9,7 +9,7 @@ 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 class SimpleAuthenticationFilter extends UsernamePasswordAuthenticationFilter { public static final String SPRING_SECURITY_FORM_DOMAIN_KEY = "domain"; diff --git a/spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomUserDetailsService.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleUserDetailsService.java similarity index 85% rename from spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomUserDetailsService.java rename to spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleUserDetailsService.java index be02834852..0b20c350b9 100644 --- a/spring-5-security/src/main/java/com/baeldung/securityextrafields/CustomUserDetailsService.java +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleUserDetailsService.java @@ -1,4 +1,4 @@ -package com.baeldung.securityextrafields; +package com.baeldung.loginextrafieldssimple; import org.apache.commons.lang3.StringUtils; import org.springframework.security.core.userdetails.UserDetails; @@ -7,11 +7,11 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; @Service("userDetailsService") -public class CustomUserDetailsService implements UserDetailsService { +public class SimpleUserDetailsService implements UserDetailsService { private final UserRepository userRepository; - public CustomUserDetailsService(UserRepository userRepository) { + public SimpleUserDetailsService(UserRepository userRepository) { this.userRepository = userRepository; } diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleUserRepository.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleUserRepository.java new file mode 100644 index 0000000000..e8aaa774a1 --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleUserRepository.java @@ -0,0 +1,26 @@ +package com.baeldung.loginextrafieldssimple; + +import java.util.ArrayList; +import java.util.Collection; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.stereotype.Repository; + +@Repository("userRepository") +public class SimpleUserRepository implements UserRepository { + + @Override + public User findUser(String username, String domain) { + if (StringUtils.isAnyBlank(username, domain)) { + return null; + } else { + Collection authorities = new ArrayList<>(); + User user = new User(username, domain, + "$2a$10$U3GhSMpsMSOE8Kqsbn58/edxDBKlVuYMh7qk/7ErApYFjJzi2VG5K", true, + true, true, true, authorities); + return user; + } + } + +} diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/User.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/User.java new file mode 100644 index 0000000000..97b81f85e5 --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/User.java @@ -0,0 +1,23 @@ +package com.baeldung.loginextrafieldssimple; + +import java.util.Collection; + +import org.springframework.security.core.GrantedAuthority; + +public class User extends org.springframework.security.core.userdetails.User { + + private static final long serialVersionUID = 1L; + + private final String domain; + + public User(String username, String domain, String password, boolean enabled, + boolean accountNonExpired, boolean credentialsNonExpired, + boolean accountNonLocked, Collection authorities) { + super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities); + this.domain = domain; + } + + public String getDomain() { + return domain; + } +} diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/UserRepository.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/UserRepository.java new file mode 100644 index 0000000000..919e611b9c --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/UserRepository.java @@ -0,0 +1,7 @@ +package com.baeldung.loginextrafieldssimple; + +public interface UserRepository { + + public User findUser(String username, String domain); + +} diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/WebController.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/WebController.java new file mode 100644 index 0000000000..1b17de7bec --- /dev/null +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/WebController.java @@ -0,0 +1,51 @@ +package com.baeldung.loginextrafieldssimple; + +import java.util.Optional; + +import org.springframework.security.authentication.AnonymousAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; + +@Controller +public class WebController { + + @RequestMapping("/") + public String root() { + return "redirect:/index"; + } + + @RequestMapping("/index") + public String index(Model model) { + getDomain().ifPresent(d -> { + model.addAttribute("domain", d); + }); + return "index"; + } + + @RequestMapping("/user/index") + public String userIndex(Model model) { + getDomain().ifPresent(d -> { + model.addAttribute("domain", d); + }); + return "user/index"; + } + + @RequestMapping("/login") + public String login() { + return "login"; + } + + private Optional getDomain() { + Authentication auth = SecurityContextHolder.getContext() + .getAuthentication(); + String domain = null; + if (auth != null && !auth.getClass().equals(AnonymousAuthenticationToken.class)) { + User user = (User) auth.getPrincipal(); + domain = user.getDomain(); + } + return Optional.ofNullable(domain); + } +} diff --git a/spring-5-security/src/main/resources/static/css/main.css b/spring-5-security/src/main/resources/static/css/main.css index 9299ee6158..febc353af7 100644 --- a/spring-5-security/src/main/resources/static/css/main.css +++ b/spring-5-security/src/main/resources/static/css/main.css @@ -1,18 +1,8 @@ -body { - font-family: sans; - font-size: 1em; -} - p.error { font-weight: bold; color: red; } div.logout { - float: right; + margin-right: 2em;; } - -.formfield { - margin: 0.5em; - padding: 0.3em; -} \ No newline at end of file diff --git a/spring-5-security/src/main/resources/templatesextrafields/index.html b/spring-5-security/src/main/resources/templatesextrafields/index.html index 52f6224dfb..37833ff0d2 100644 --- a/spring-5-security/src/main/resources/templatesextrafields/index.html +++ b/spring-5-security/src/main/resources/templatesextrafields/index.html @@ -1,24 +1,32 @@ - + - Spring Security - Login With Extra Fields + Spring Security with Extra Fields + + + + + -
- Logged in user: | - domain: Some Domain +
+
+

Logged in: | Some Domain +

- +
-

Hello Spring Security

+ +

Hello Spring Security

This is an unsecured page, but you can access the secured pages after authenticating.

+
diff --git a/spring-5-security/src/main/resources/templatesextrafields/login.html b/spring-5-security/src/main/resources/templatesextrafields/login.html index cafec89c15..5c51ea3b2c 100644 --- a/spring-5-security/src/main/resources/templatesextrafields/login.html +++ b/spring-5-security/src/main/resources/templatesextrafields/login.html @@ -1,23 +1,36 @@ - - - Login page - - - - -

Login page

+ + + Login page + + + + + + + + + +
+ +

+ + +

+

+ + +

+

+ + +

+

Back to home page

- + +
+ diff --git a/spring-5-security/src/main/resources/templatesextrafields/user/index.html b/spring-5-security/src/main/resources/templatesextrafields/user/index.html index a4c1535100..9c41f0e78c 100644 --- a/spring-5-security/src/main/resources/templatesextrafields/user/index.html +++ b/spring-5-security/src/main/resources/templatesextrafields/user/index.html @@ -1,13 +1,20 @@ - - - Spring Security - Login With Extra Fields - - - - -
-

This is a secured page!

-

Back to home page

- + + + Secured Page + + + + + + + + + +
+
+

This is a secured page!

+

Back to home page

+
+ diff --git a/spring-5-security/src/test/java/com/baeldung/loginextrafields/AbstractExtraLoginFieldsTest.java b/spring-5-security/src/test/java/com/baeldung/loginextrafields/AbstractExtraLoginFieldsTest.java new file mode 100644 index 0000000000..30b869714f --- /dev/null +++ b/spring-5-security/src/test/java/com/baeldung/loginextrafields/AbstractExtraLoginFieldsTest.java @@ -0,0 +1,46 @@ +package com.baeldung.loginextrafields; + +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.web.FilterChainProxy; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +public abstract class AbstractExtraLoginFieldsTest { + + @Autowired + private FilterChainProxy springSecurityFilterChain; + + @Autowired + private WebApplicationContext wac; + + protected MockMvc mockMvc; + + @Before + public void setup() { + this.mockMvc = MockMvcBuilders.webAppContextSetup(wac) + .apply(springSecurity(springSecurityFilterChain)) + .build(); + } + + @Test + public void givenRootPathAccess_thenRedirectToIndex() throws Exception { + this.mockMvc.perform(get("/")) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrlPattern("/index*")); + } + + @Test + public void givenSecuredResource_whenAccessUnauthenticated_thenRequiresAuthentication() throws Exception { + this.mockMvc.perform(get("/user/index")) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrlPattern("**/login")); + } +} diff --git a/spring-5-security/src/test/java/com/baeldung/securityextrafields/SecurityExtraFieldsTest.java b/spring-5-security/src/test/java/com/baeldung/loginextrafields/LoginFieldsFullTest.java similarity index 65% rename from spring-5-security/src/test/java/com/baeldung/securityextrafields/SecurityExtraFieldsTest.java rename to spring-5-security/src/test/java/com/baeldung/loginextrafields/LoginFieldsFullTest.java index cf0701708d..38c219cb5e 100644 --- a/spring-5-security/src/test/java/com/baeldung/securityextrafields/SecurityExtraFieldsTest.java +++ b/spring-5-security/src/test/java/com/baeldung/loginextrafields/LoginFieldsFullTest.java @@ -1,8 +1,7 @@ -package com.baeldung.securityextrafields; +package com.baeldung.loginextrafields; import static org.junit.Assert.assertEquals; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; -import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern; @@ -11,57 +10,26 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import java.util.ArrayList; import java.util.Collection; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.mock.web.MockHttpSession; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.web.FilterChainProxy; import org.springframework.security.web.context.HttpSessionSecurityContextRepository; import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig; import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import org.springframework.web.context.WebApplicationContext; + +import com.baeldung.loginextrafieldscustom.ExtraLoginFieldsApplication; +import com.baeldung.loginextrafieldscustom.User; @RunWith(SpringRunner.class) @SpringJUnitWebConfig -@SpringBootTest(classes = SpringExtraLoginFieldsApplication.class) -public class SecurityExtraFieldsTest { - - @Autowired - private FilterChainProxy springSecurityFilterChain; - - @Autowired - private WebApplicationContext wac; - - private MockMvc mockMvc; - - @Before - public void setup() { - this.mockMvc = MockMvcBuilders.webAppContextSetup(wac) - .apply(springSecurity(springSecurityFilterChain)).build(); - } - - @Test - public void givenRootPathAccess_thenRedirectToIndex() throws Exception { - this.mockMvc.perform(get("/")) - .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrlPattern("/index*")); - } - - @Test - public void givenSecuredResource_whenAccessUnauthenticated_thenRequiresAuthentication() throws Exception { - this.mockMvc.perform(get("/user/index")) - .andExpect(status().is3xxRedirection()) - .andExpect(redirectedUrlPattern("**/login")); - } +@SpringBootTest(classes = ExtraLoginFieldsApplication.class) +public class LoginFieldsFullTest extends AbstractExtraLoginFieldsTest { @Test public void givenAccessSecuredResource_whenAuthenticated_thenAuthHasExtraFields() throws Exception { @@ -100,4 +68,5 @@ public class SecurityExtraFieldsTest { Collection authorities = new ArrayList<>(); return new User("myusername", "mydomain", "password", true, true, true, true, authorities); } + } diff --git a/spring-5-security/src/test/java/com/baeldung/loginextrafields/LoginFieldsSimpleTest.java b/spring-5-security/src/test/java/com/baeldung/loginextrafields/LoginFieldsSimpleTest.java new file mode 100644 index 0000000000..5c0d462772 --- /dev/null +++ b/spring-5-security/src/test/java/com/baeldung/loginextrafields/LoginFieldsSimpleTest.java @@ -0,0 +1,72 @@ +package com.baeldung.loginextrafields; + +import static org.junit.Assert.assertEquals; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrlPattern; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.util.ArrayList; +import java.util.Collection; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.mock.web.MockHttpSession; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.web.context.HttpSessionSecurityContextRepository; +import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; + +import com.baeldung.loginextrafieldssimple.ExtraLoginFieldsApplication; +import com.baeldung.loginextrafieldssimple.User; + +@RunWith(SpringRunner.class) +@SpringJUnitWebConfig +@SpringBootTest(classes = ExtraLoginFieldsApplication.class) +public class LoginFieldsSimpleTest extends AbstractExtraLoginFieldsTest { + + @Test + public void givenAccessSecuredResource_whenAuthenticated_thenAuthHasExtraFields() throws Exception { + MockHttpServletRequestBuilder securedResourceAccess = get("/user/index"); + MvcResult unauthenticatedResult = mockMvc.perform(securedResourceAccess) + .andExpect(status().is3xxRedirection()) + .andReturn(); + + MockHttpSession session = (MockHttpSession) unauthenticatedResult.getRequest() + .getSession(); + String loginUrl = unauthenticatedResult.getResponse() + .getRedirectedUrl(); + + User user = getUser(); + + mockMvc.perform(post(loginUrl) + .param("username", user.getUsername()) + .param("password", user.getPassword()) + .param("domain", user.getDomain()) + .session(session) + .with(csrf())) + .andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrlPattern("**/user/index")) + .andReturn(); + + mockMvc.perform(securedResourceAccess.session(session)) + .andExpect(status().isOk()); + + SecurityContext securityContext + = (SecurityContext) session.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY); + Authentication auth = securityContext.getAuthentication(); + assertEquals(((User)auth.getPrincipal()).getDomain(), user.getDomain()); + } + + private User getUser() { + Collection authorities = new ArrayList<>(); + return new User("myusername", "mydomain", "password", true, true, true, true, authorities); + } + +} From 8e321ce2779943c88a466b17b11b9916cde3b326 Mon Sep 17 00:00:00 2001 From: Chris Oberle Date: Sun, 21 Jan 2018 11:38:16 -0500 Subject: [PATCH 9/9] remove final keywords and serialVersionUID constants --- .../loginextrafieldscustom/CustomAuthenticationToken.java | 4 +--- .../CustomUserDetailsAuthenticationProvider.java | 4 ++-- .../loginextrafieldscustom/CustomUserDetailsServiceImpl.java | 2 +- .../main/java/com/baeldung/loginextrafieldscustom/User.java | 2 +- .../loginextrafieldssimple/SimpleUserDetailsService.java | 2 +- .../main/java/com/baeldung/loginextrafieldssimple/User.java | 4 +--- 6 files changed, 7 insertions(+), 11 deletions(-) diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomAuthenticationToken.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomAuthenticationToken.java index 6de842f2e0..50995169a1 100644 --- a/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomAuthenticationToken.java +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomAuthenticationToken.java @@ -7,9 +7,7 @@ import org.springframework.security.core.GrantedAuthority; public class CustomAuthenticationToken extends UsernamePasswordAuthenticationToken { - private static final long serialVersionUID = 1L; - - private final String domain; + private String domain; public CustomAuthenticationToken(Object principal, Object credentials, String domain) { super(principal, credentials); diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsAuthenticationProvider.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsAuthenticationProvider.java index 97a589077e..693900d843 100644 --- a/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsAuthenticationProvider.java +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsAuthenticationProvider.java @@ -19,8 +19,8 @@ public class CustomUserDetailsAuthenticationProvider extends AbstractUserDetails */ private static final String USER_NOT_FOUND_PASSWORD = "userNotFoundPassword"; - private final PasswordEncoder passwordEncoder; - private final CustomUserDetailsService userDetailsService; + private PasswordEncoder passwordEncoder; + private CustomUserDetailsService userDetailsService; /** * The password used to perform diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsServiceImpl.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsServiceImpl.java index bf53170205..ea979e2fab 100644 --- a/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsServiceImpl.java +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/CustomUserDetailsServiceImpl.java @@ -8,7 +8,7 @@ import org.springframework.stereotype.Service; @Service("userDetailsService") public class CustomUserDetailsServiceImpl implements CustomUserDetailsService { - private final UserRepository userRepository; + private UserRepository userRepository; public CustomUserDetailsServiceImpl(UserRepository userRepository) { this.userRepository = userRepository; diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/User.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/User.java index 2d684d96e4..aa03f15b6a 100644 --- a/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/User.java +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldscustom/User.java @@ -8,7 +8,7 @@ public class User extends org.springframework.security.core.userdetails.User { private static final long serialVersionUID = 1L; - private final String domain; + private String domain; public User(String username, String domain, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleUserDetailsService.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleUserDetailsService.java index 0b20c350b9..2fad50ad01 100644 --- a/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleUserDetailsService.java +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/SimpleUserDetailsService.java @@ -9,7 +9,7 @@ import org.springframework.stereotype.Service; @Service("userDetailsService") public class SimpleUserDetailsService implements UserDetailsService { - private final UserRepository userRepository; + private UserRepository userRepository; public SimpleUserDetailsService(UserRepository userRepository) { this.userRepository = userRepository; diff --git a/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/User.java b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/User.java index 97b81f85e5..b76da65638 100644 --- a/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/User.java +++ b/spring-5-security/src/main/java/com/baeldung/loginextrafieldssimple/User.java @@ -6,9 +6,7 @@ import org.springframework.security.core.GrantedAuthority; public class User extends org.springframework.security.core.userdetails.User { - private static final long serialVersionUID = 1L; - - private final String domain; + private String domain; public User(String username, String domain, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired,