通过使用 spring security 的 thymeleaf sec 标签获取当前用户的额外信息 [英] Get current user's extra information by thymeleaf sec tag working with spring security

查看:84
本文介绍了通过使用 spring security 的 thymeleaf sec 标签获取当前用户的额外信息的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在我的项目中使用 thymeleaf-extras-springsecurity4 和 spring security.问题是我无法获取用户的额外字段(这意味着除了 usernamepasswordenabled 等由 给出的数据库上的用户信息>UserDetails) 使用 .

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" />.

这是我的简单代码:

@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;
  }
}

AuthorityEntity(实现 GrantedAuthority)

@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);
  }
}

SecurityService(扩展 UserDetailService)

@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;
  }
}

SecurityConfig(扩展 WebSecurityConfigurerAdapter)

@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);
  }
}

index.html(使用 thymeleaf-extras-springsecurity)

<!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>

问题

index.html 中,sec:authentication="principal.username" 按预期工作,但 sec:authentication="principal.phone"UserDetailsS​​ervice 实现存储了 UserEntry,但 code> 并没有实现,它实现了带有额外字段 phoneUserDetails.

The Problem

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" 正常工作?(或分别为 "princiapl.getPhone()")
  • 如果没有,我可以在不通过控制器的情况下在 thymeleaf 中获取当前用户的电话号码吗?
  • 如果没有,我如何传递当前用户的 UserEntry 对象而不显式插入模型,例如通过每个控制器方法的 mav ?AOP 会处理这个问题吗?
  • Is there any way to make sec:authentication="principal.phone" work well? (or "princiapl.getPhone()" respectively)
  • If not, can I get current user's phone number in my thymeleaf without passing it through controller?
  • If not, how can I pass current user's UserEntry object without plugging model explicitly for instance through mav of each controller method? Does AOP deal with this?

(附加) 在许多其他应用 Spring Security 的示例中,他们没有在 UserEntry(或类似的类)上实现 UserDetails,但是在他们的 UserDetailService 实现中创建一个新的 UserDetails 实例,如

(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.

推荐答案

为了使用 Thymeleaf 中用户数据中包含的其他字段,您必须执行后续步骤.

In order to use additional fields contained in your user's data in Thymeleaf, you must go through the next steps.

  1. 实现您自己的 Spring Security 用户.
  2. 覆盖 loadUserByUsername,使其返回您的自定义用户.
  3. 添加 Spring Security 的 Thymeleaf Extras 依赖项.
  4. 使用 ${#authentication.getPrincipal()},而不是 sec.
  1. Implement your own Spring Security's user.
  2. Override loadUserByUsername, so that it returns your custom user.
  3. Add the Spring Security's Thymeleaf Extras dependencies.
  4. Use ${#authentication.getPrincipal()}, instead of sec.

第一步

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;
    }

}

第 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());
}

第 3 步

<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity4</artifactId>
</dependency>

第 4 步

<th:block th:with="auth=${#authentication.getPrincipal()}">
    <p th:text="${auth ? auth.phone : 'NULL'}">Phone</p>
</th:block>

这篇关于通过使用 spring security 的 thymeleaf sec 标签获取当前用户的额外信息的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆