BAEL-5638: Added display logged-in user details in Thymeleaf tutorial (#12524)

* BAEL-5638: Added display logged-in user details in Thymeleaf tutorial

* BAEL-5638: Removed unneeded security config
This commit is contained in:
Adrian Bob 2022-08-04 06:54:49 +03:00 committed by GitHub
parent 8ec8c01a67
commit 81b05fb3ac
9 changed files with 328 additions and 0 deletions

View File

@ -0,0 +1,94 @@
package com.baeldung.customuserdetails;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import java.util.Collection;
import java.util.Objects;
public class CustomUserDetails extends User {
private final String firstName;
private final String lastName;
private final String email;
private CustomUserDetails(Builder builder) {
super(builder.username, builder.password, builder.authorities);
this.firstName = builder.firstName;
this.lastName = builder.lastName;
this.email = builder.email;
}
public String getLastName() {
return this.lastName;
}
public String getFirstName() {
return this.firstName;
}
public String getEmail() {
return email;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
if (!super.equals(o))
return false;
CustomUserDetails that = (CustomUserDetails) o;
return firstName.equals(that.firstName) && lastName.equals(that.lastName) && email.equals(that.email);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), firstName, lastName, email);
}
public static class Builder {
private String firstName;
private String lastName;
private String email;
private String username;
private String password;
private Collection<? extends GrantedAuthority> authorities;
public Builder withFirstName(String firstName) {
this.firstName = firstName;
return this;
}
public Builder withLastName(String lastName) {
this.lastName = lastName;
return this;
}
public Builder withEmail(String email) {
this.email = email;
return this;
}
public Builder withUsername(String username) {
this.username = username;
return this;
}
public Builder withPassword(String password) {
this.password = password;
return this;
}
public Builder withAuthorities(Collection<? extends GrantedAuthority> authorities) {
this.authorities = authorities;
return this;
}
public CustomUserDetails build() {
return new CustomUserDetails(this);
}
}
}

View File

@ -0,0 +1,52 @@
package com.baeldung.customuserdetails;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@Service
public class CustomUserDetailsService implements UserDetailsService {
private final PasswordEncoder passwordEncoder;
private final Map<String, CustomUserDetails> userRegistry = new HashMap<>();
public CustomUserDetailsService(PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}
@PostConstruct
public void init() {
userRegistry.put("user", new CustomUserDetails.Builder().withFirstName("Mark")
.withLastName("Johnson")
.withEmail("mark.johnson@email.com")
.withUsername("user")
.withPassword(passwordEncoder.encode("password"))
.withAuthorities(Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")))
.build());
userRegistry.put("admin", new CustomUserDetails.Builder().withFirstName("James")
.withLastName("Davis")
.withEmail("james.davis@email.com")
.withUsername("admin")
.withPassword(passwordEncoder.encode("admin"))
.withAuthorities(Collections.singletonList(new SimpleGrantedAuthority("ROLE_ADMIN")))
.build());
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
final CustomUserDetails userDetails = userRegistry.get(username);
if (userDetails == null) {
throw new UsernameNotFoundException(username);
}
return userDetails;
}
}

View File

@ -0,0 +1,15 @@
package com.baeldung.customuserdetails;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class PasswordEncoderConfiguration {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

View File

@ -0,0 +1,38 @@
package com.baeldung.customuserdetails;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final UserDetailsService userDetailsService;
public SecurityConfiguration(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.userDetailsService(userDetailsService)
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.successForwardUrl("/index")
.and()
.logout()
.permitAll()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/login");
}
}

View File

@ -0,0 +1,12 @@
package com.baeldung.customuserdetails;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringSecurityThymeleafApplication {
public static void main(String[] args) {
SpringApplication.run(SpringSecurityThymeleafApplication.class, args);
}
}

View File

@ -0,0 +1,21 @@
package com.baeldung.customuserdetails;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class ViewController {
@RequestMapping("/login")
public String login() {
return "login";
}
@RequestMapping({ "/index", "/" })
public String index() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return "userdetails";
}
}

View File

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<title>Welcome to Spring Security Thymeleaf tutorial</title>
</head>
<body>
<h2>Welcome</h2>
<p>Spring Security Thymeleaf tutorial</p>
<div sec:authorize="hasRole('USER')">Text visible to user.</div>
<div sec:authorize="hasRole('ADMIN')">Text visible to admin.</div>
<div sec:authorize="isAuthenticated()">Text visible only to
authenticated users.</div>
Authenticated username:
<div sec:authentication="name"></div>
Authenticated user's firstName:
<div sec:authentication="principal.firstName"></div>
Authenticated user's lastName:
<div sec:authentication="principal.lastName"></div>
Authenticated user's email:
<div sec:authentication="principal.email"></div>
Authenticated user roles:
<div sec:authentication="principal.authorities"></div>
</body>
</html>

View File

@ -0,0 +1,29 @@
package com.baeldung.customuserdetails;
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.test.context.junit4.SpringRunner;
import org.springframework.web.context.WebApplicationContext;
import static org.junit.Assert.assertNotNull;
import com.baeldung.customuserdetails.ViewController;
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringSecurityThymeleafApplicationIntegrationTest {
@Autowired
ViewController viewController;
@Autowired
WebApplicationContext wac;
@Test
public void whenConfigured_thenLoadsContext() {
assertNotNull(viewController);
assertNotNull(wac);
}
}

View File

@ -0,0 +1,42 @@
package com.baeldung.customuserdetails;
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.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
import com.baeldung.customuserdetails.PasswordEncoderConfiguration;
@RunWith(SpringRunner.class)
@WebMvcTest
@Import(PasswordEncoderConfiguration.class)
public class ViewControllerIntegrationTest {
@Autowired
private WebApplicationContext context;
MockMvc mvc;
@Before
public void setup() {
mvc = MockMvcBuilders
.webAppContextSetup(context)
.build();
}
@Test
public void givenUser_whenPerformingGet_thenReturnsIndex() throws Exception {
mvc.perform(get("/index").with(user("user").password("password"))).andExpect(status().isOk()).andExpect(view().name("userdetails"));
}
}