简体   繁体   中英

How can I get the currently logged in user's data in Spring Boot Thymeleaf?

I want my logged in user to be able to see their own user profile and even modify that if they want to.

My two entities that store my user's data:

@Entity
@Table(name = "users")
public class User {

    @Id
    @GeneratedValue
    private Long id;

    @Column(unique = true, nullable = false)
    private String email;

    @Column(nullable = false)
    private String password;

    private String fullName;

    private String activation;

    private Boolean enabled;

    @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinTable(name = "users_roles", joinColumns = { @JoinColumn(name = "user_id") }, inverseJoinColumns = {
            @JoinColumn(name = "role_id") })
    private Set<Role> roles = new HashSet<Role>();

    @OneToOne(mappedBy = "user", cascade = CascadeType.ALL)
    private UserProfile userProfile;
//constructors, getters and setters

and:

@Entity
@Table(name="profiles")
public class UserProfile {

    
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Id
    private Long id;

    @Column(length = 20)
    private String activation;

    @Column(length = 64)
    private String address;

    @Column(length = 32)
    private String phoneNumber;

    @Column(length = 60)
    private String resetToken;

    @OneToOne
    private User user;
    
//constructors, getters and setters

I wrote this in my UserController:

    @GetMapping("/profile")
    public String showUserProfile (Model model){
        //Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        //String currentPrincipalName = authentication.getName();
        //User user = userRepository.findByEmail(authentication.getPrincipal());
        //model.addAttribute("currentUser", user);
        return "profile";
    }

My findByEmail method in UserRepository and UserService:

User findByEmail(String email);

STS wants to modify this method into a (object principal) or something like that, but I can't change it since that method is already used.

java.lang.Error: Unresolved compilation problem: 
    The method findByEmail(String) in the type UserRepository is not applicable for the arguments (Object)

I'm sorry, I'm still new to Spring Boot. How do I get the current user's data? I've already made a users CRUD, I want to do the same just with the current user and without DELETE and CREATE.

In order to use spring-security you have to implement UserDetails like:

public class UserPrincipal implements UserDetails {

    private User user;
    private String name;
    private long uid;

    public long getUid() {
        return uid;
    }

    public void setUid(long uid) {
        this.uid = uid;
    }

    public UserPrincipal(User user) {
        this.user = user;
        this.name = user.getName();
        this.uid = user.getUid();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<GrantedAuthority> authorities = new ArrayList<>();
        authorities.add(new GrantedAuthority() {
            @Override
            public String getAuthority() {
                return "ROLE_USER";
            }
        });
        return authorities;
    }

    @Override
    public String getPassword() {
        return user.getPassword();
    }

    @Override
    public String getUsername() {
        return user.getEmail();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

And a service like:

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
        User user = userRepository.findByEmail(email);
        if(user == null) {
            throw new UsernameNotFoundException("User Not found");
        }
        return new UserPrincipal(user);
    }
}

Then add to get the Current logged-in user:

private UserPrincipal getLoggedInUser() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        return (UserPrincipal)authentication.getPrincipal();
    }

In-side UserPricipal object store Profile and access it in thymeleaf.

To work with Spring Security and Thymeleaf, you need to write a class that implements UserDetails . This class is the "bridge" between your application and Spring Security. It could look something like this:

public class ApplicationUserDetails implements UserDetails {
    
    public ApplicationUserDetails(User user) {
        this.username = user.getEmail();
        this.password = user.getPassword(); //<.>
        this.authorities = user.getRoles().stream()
                               .map(userRole -> new SimpleGrantedAuthority("ROLE_" + userRole.name()))
                               .collect(Collectors.toSet());
    }
}

Next to that, you need to write an implementation of UserDetailsService . This class will be called by Spring Security to know what user object corresponds to a given username (or email if you use emails instead of user names). So something like this:

@Service
@Transactional(readOnly = true)
public class DatabaseUserDetailsService implements UserDetailsService {

    private final UserRepository userRepository;

    @Autowired
    public DatabaseUserDetailsService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByEmail(username)
                                  .orElseThrow(() -> new UsernameNotFoundException(
                                          format("User with email %s could not be found", username)));

        return new ApplicationUserDetails(user);
    }
}

You also need to configure Spring Security correctly by having a WebSecurityConfigurerAdapter implementation:

@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
    private final PasswordEncoder passwordEncoder;
    private final UserDetailsService userDetailsService;

    public WebSecurityConfiguration(PasswordEncoder passwordEncoder,
                                    UserDetailsService userDetailsService) {
        this.passwordEncoder = passwordEncoder;
        this.userDetailsService = userDetailsService;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService)
            .passwordEncoder(passwordEncoder);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
            .antMatchers("/img/*").permitAll()
            .anyRequest().authenticated()
            .and()
            .formLogin()
            .loginPage("/login")
            .permitAll()
            .and()
            .logout().permitAll();
    }
    
}

You can now use the Spring Security Thymeleaf integration to show information on the logged in user.

<span sec:authentication="name">Bob</span>

See https://www.thymeleaf.org/doc/articles/springsecurity.html for the docs or https://github.com/wimdeblauwe/taming-thymeleaf-sources/tree/main/chapter14 for a full Spring Boot project that shows all this in action.

I think your findByEmail method is correct. You only need to update how to fetch the current user email. That you can get by modifying how to get the user email from security context like below

SecurityContextHolder.getContext().getAuthentication().getPrincipal.toString()

Because in this way, It will return the string value of principal that will be email in your case. But right now it returns Principal Object and that is the reason STS complains about to modify the method signature to (object principal) as you have mentioned.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM