I'm using thymeleaf-extras-springsecurity4 with spring security on my project. The problem is I cannot get user's extra fields (which means user information on database except username, password, enabled, etc. given by UserDetails) by using <span sec:authentication="principal.something" />.
Heres are my simple codes:
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@EqualsAndHashCode
@Entity
@Table(name = "users", schema = "myschema")
public class UserEntity implements UserDetails {
@Id
@GeneratedValue
@Column(name = "id", nullable = false)
private int id;
@Basic
@Column(name = "username", nullable = false, unique = true, length = 64)
private String username;
@Basic
@Column(name = "password", nullable = false, columnDefinition = "TEXT")
private String password;
@Basic
@Column(name = "enabled", nullable = false, columnDefinition = "BIT")
private boolean enabled;
@Basic
@Column(name = "phone", nullable = false, length = 16)
private String phone;
@OneToMany(mappedBy = "user", fetch = FetchType.EAGER)
private List<AuthorityEntity> authorities;
@Override
public boolean isAccountNonExpired() {
return enabled;
}
@Override
public boolean isAccountNonLocked() {
return enabled;
}
@Override
public boolean isCredentialsNonExpired() {
return enabled;
}
@Override
public String toString() {
return username;
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "authorities", schema = "myschema",
uniqueConstraints = @UniqueConstraint(columnNames = {"user_id", "authority"}))
public class AuthorityEntity implements GrantedAuthority {
@Id
@GeneratedValue
@Column(name = "id", nullable = false)
private int id;
@Basic
@Column(name = "authority", nullable = false, length = 24)
private String authority;
@ManyToOne
@JoinColumn(name = "user_id", referencedColumnName = "id", nullable = false)
private UserEntity user;
}
@Repository
public interface UserRepository extends JpaRepository<UserEntity, Integer> {
UserEntity findOneByUsernameAndEnabledTrue(String username);
}
@Service
public class UserService {
private UserRepository userRepository;
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public UserEntity loadUserByUsername(String username) {
return userRepository.findOneByUsernameAndEnabledTrue(username);
}
}
@Service
public class SecurityService implements UserDetailsService {
private UserService userService;
@Autowired
public SecurityService(UserService userService) {
this.userService = userService;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserDetails user = userService.loadUserByUsername(username);
if (user == null) {
throw new UsernameNotFoundException(username);
}
return user;
}
}
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private SecurityService securityService;
@Autowired
public SecurityConfig(SecurityService securityService) {
this.securityService = securityService;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/user/login").anonymous()
.antMatchers("/**").hasAnyRole("ADMIN", "USER")
.and()
.formLogin()
.loginPage("/user/login")
.defaultSuccessUrl("/")
.and()
.logout()
.logoutUrl("/user/logout")
.logoutSuccessUrl("/")
.and()
.exceptionHandling()
.accessDeniedPage("/error/403");
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
auth.userDetailsService(securityService).passwordEncoder(passwordEncoder);
}
}
<!DOCTYPE html>
<html lang="ko"
xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4"
layout:decorator="layout/base">
<th:block layout:fragment="content">
<h1>Main Page</h1>
<p sec:authentication="principal.username">Username</p>
<p sec:authentication="principal.phone">Phone</p>
</th:block>
In index.html, sec:authentication="principal.username" works as expected, but sec:authentication="principal.phone" does not despite my UserDetailsService implementation stores UserEntry which implements UserDetails with extra field phone.
sec:authentication="principal.phone" work well? (or "princiapl.getPhone()" respectively)UserEntry object without plugging model explicitly for instance through mav of each controller method? Does AOP deal with this?(Additional) In many other examples applying spring security, they don't implement UserDetails on UserEntry (or similar classes), but make a new UserDetails instance in their UserDetailService implementation like
@Override
public UserDetails loadUserByUsername(String userName)
throws UsernameNotFoundException {
UserInfo activeUserInfo = userInfoDAO.getActiveUser(userName);
GrantedAuthority authority = new SimpleGrantedAuthority(activeUserInfo.getRole());
UserDetails userDetails = (UserDetails)new User(activeUserInfo.getUserName(),
activeUserInfo.getPassword(), Arrays.asList(authority));
return userDetails;
}
(from here). I think my structure is not a good design but I don't know exactly why. Is there any comment for my class design?
If my questions are too vague, let me know so then I would update this more concrete.
In order to use additional fields contained in your user's data in Thymeleaf, you must go through the next steps.
loadUserByUsername, so that it returns your custom user.${#authentication.getPrincipal()}, instead of sec.STEP 1
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import java.util.Collection;
// Our own implementation of the Spring Security User.
public class MyUser extends User {
// Here we add the extra fields of our users.
private String phone;
private static final long serialVersionUID = 1L;
public MyUser(String username,
String password,
Collection<GrantedAuthority> authorities,
String phone) {
super(username, password, authorities);
this.phone = phone;
}
public String getPhone() {
return realName;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
STEP 2
@Override
public MyUser loadUserByUsername(String userName)
throws AuthenticationException {
// Fetch the user.
UserDetails user = userService.loadUserByUsername(username);
// For each user's authority, add it into our authorities' collection.
Collection<GrantedAuthority> grantedAuthorities = new LinkedList<GrantedAuthority>();
if (user.getAuthorities().size() > 0){
for (Authority authority : user.getAuthorities()) {
// Add a new GrantedAuthority for each user's authorities.
grantedAuthorities.add(new SimpleGrantedAuthority(authority.getAuthority()));
}
}
return new MyUser(user.getUsername(), user.getPassword(), grantedAuthorities, user.getPhone());
}
STEP 3
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
</dependency>
STEP 4
<th:block th:with="auth=${#authentication.getPrincipal()}">
<p th:text="${auth ? auth.phone : 'NULL'}">Phone</p>
</th:block>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With