title: Get current user's extra information by thymeleaf sec tag working with spring security
body: <p>I'm using <strong>thymeleaf-extras-springsecurity4</strong> with spring security on my project. The problem is I cannot get user's extra fields (which means user information on database except <code>username</code>, <code>password</code>, <code>enabled</code>, etc. given by <code>UserDetails</code>) by using <code>&lt;span sec:authentication="principal.something" /&gt;</code>.</p>
  
  <p>Heres are my simple codes:</p>
  
  <h1>UserEntity (implements UserDetails)</h1>
  
  <pre><code>@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&lt;AuthorityEntity&gt; 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;
    }
  }
  </code></pre>
  
  <h1>AuthorityEntity (implements GrantedAuthority)</h1>
  
  <pre><code>@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;
  }
  </code></pre>
  
  <h1>UserRepository</h1>
  
  <pre><code>@Repository
  public interface UserRepository extends JpaRepository&lt;UserEntity, Integer&gt; {
    UserEntity findOneByUsernameAndEnabledTrue(String username);
  }
  </code></pre>
  
  <h1>UserService</h1>
  
  <pre><code>@Service
  public class UserService {
    private UserRepository userRepository;
  
    @Autowired
    public UserService(UserRepository userRepository) {
      this.userRepository = userRepository;
    }
  
    public UserEntity loadUserByUsername(String username) {
      return userRepository.findOneByUsernameAndEnabledTrue(username);
    }
  }
  </code></pre>
  
  <h1>SecurityService (extends UserDetailService)</h1>
  
  <pre><code>@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;
    }
  }
  </code></pre>
  
  <h1>SecurityConfig (extends WebSecurityConfigurerAdapter)</h1>
  
  <pre><code>@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);
    }
  }
  </code></pre>
  
  <h1>index.html (using thymeleaf-extras-springsecurity)</h1>
  
  <pre><code>&lt;!DOCTYPE html&gt;
  &lt;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"&gt;
  
  &lt;th:block layout:fragment="content"&gt;
      &lt;h1&gt;Main Page&lt;/h1&gt;
      &lt;p sec:authentication="principal.username"&gt;Username&lt;/p&gt;
      &lt;p sec:authentication="principal.phone"&gt;Phone&lt;/p&gt;
  &lt;/th:block&gt;
  </code></pre>
  
  <h1>The Problem</h1>
  
  <p>In <code>index.html</code>, <code>sec:authentication="principal.username"</code> works as expected, but <code>sec:authentication="principal.phone"</code> does not despite my <code>UserDetailsService</code> implementation stores <code>UserEntry</code> which implements <code>UserDetails</code> with extra field <code>phone</code>.</p>
  
  <h1>Questions</h1>
  
  <ul>
  <li>Is there any way to make <code>sec:authentication="principal.phone"</code> work well? (or <code>"princiapl.getPhone()"</code> respectively)</li>
  <li>If not, can I get current user's phone number in my thymeleaf without passing it through controller?</li>
  <li>If not, how can I pass current user's <code>UserEntry</code> object without plugging model explicitly for instance through <code>mav</code> of each controller method? Does AOP deal with this?</li>
  </ul>
  
  <p><strong>(Additional)</strong> In many other examples applying spring security, they don't implement <code>UserDetails</code> on <code>UserEntry</code> (or similar classes), but make a new <code>UserDetails</code> instance in their <code>UserDetailService</code> implementation like</p>
  
  <pre><code>@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;
  }
  </code></pre>
  
  <p>(<a href="http://www.concretepage.com/spring-boot/spring-boot-mvc-security-custom-login-and-logout-thymeleaf-csrf-mysql-database-jpa-hibernate-example#security" rel="nofollow noreferrer">from here</a>). I think my structure is not a good design but I don't know exactly why. Is there any comment for my class design?</p>
  
  <h1>Thanks!</h1>
  
  <p>If my questions are too vague, let me know so then I would update this more concrete.</p>