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.