- Добавил миграции Flyway.

- Добавил профиль пользователя и возможность его редактирования.
https://www.youtube.com/watch?v=ArM7nCys4hY&list=PLU2ftbIeotGpAYRP9Iv2KLIwK36-o_qYk&index=12
This commit is contained in:
Leonid
2025-02-16 14:46:28 +03:00
parent 2736f3ddf6
commit 9a41c4caae
10 changed files with 179 additions and 25 deletions

View File

@@ -69,6 +69,14 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-mysql</artifactId>
</dependency>
</dependencies>
<build>

View File

@@ -2,36 +2,37 @@ package net.sytes.kashey.sweater.controller;
import net.sytes.kashey.sweater.domain.Role;
import net.sytes.kashey.sweater.domain.User;
import net.sytes.kashey.sweater.repository.UserRepository;
import net.sytes.kashey.sweater.security.CustomUserDetails;
import net.sytes.kashey.sweater.service.CustomUserDetailsService;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@Controller
@RequestMapping("/users")
@PreAuthorize("hasAuthority('ADMIN')")
public class UserController {
private final UserRepository userRepository;
private final CustomUserDetailsService customUserDetailsService;
public UserController(UserRepository userRepository) {
public UserController(CustomUserDetailsService customUserDetailsService) {
this.userRepository = userRepository;
this.customUserDetailsService = customUserDetailsService;
}
@PreAuthorize("hasAuthority('ADMIN')")
@GetMapping
public String getAllUsers(Model model) {
model.addAttribute("users", userRepository.findAll());
model.addAttribute("users", customUserDetailsService.findAll());
return "userList";
}
@PreAuthorize("hasAuthority('ADMIN')")
@GetMapping("/{user}")
public String editUser(@PathVariable User user, Model model) {
@@ -40,20 +41,31 @@ public class UserController {
return "userEdit";
}
@PreAuthorize("hasAuthority('ADMIN')")
@PostMapping
public String updateUser(@RequestParam String username,
@RequestParam Map<String, String> form,
@RequestParam("userId") User user) {
user.setUsername(username);
user.getRoles().clear();
Set<String> roles = Arrays.stream(Role.values()).map(Enum::name).collect(Collectors.toSet());
for (String role : form.keySet()) {
if (roles.contains(role)) {
user.getRoles().add(Role.valueOf(role));
}
}
userRepository.save(user);
customUserDetailsService.updateUser(username, form, user);
return "redirect:/users";
}
@GetMapping("/profile")
public String getProfile(Model model, @AuthenticationPrincipal CustomUserDetails customUserDetails) {
model.addAttribute("username", customUserDetails.getUsername());
model.addAttribute("email", customUserDetails.getEmail());
model.addAttribute("password", customUserDetails.getPassword());
customUserDetails.getPassword();
return "profile";
}
@PostMapping("/profile")
public String updateProfile(@AuthenticationPrincipal CustomUserDetails customUserDetails,
@RequestParam String password,
@RequestParam String email) {
customUserDetailsService.updateProfile(customUserDetails.getUser(), password, email);
return "redirect:/users/profile";
}
}

View File

@@ -6,7 +6,7 @@ import jakarta.persistence.*;
public class Message {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private Long id;
private String text;
private String tag;
@@ -25,11 +25,11 @@ public class Message {
this.author = author;
}
public Integer getId() {
public Long getId() {
return id;
}
public void setId(Integer id) {
public void setId(Long id) {
this.id = id;
}

View File

@@ -61,4 +61,8 @@ public class CustomUserDetails implements UserDetails {
public boolean isAdmin() {
return user.getRoles().contains(Role.ADMIN);
}
public String getEmail() {
return user.getEmail();
}
}

View File

@@ -11,8 +11,8 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import java.util.Collections;
import java.util.UUID;
import java.util.*;
import java.util.stream.Collectors;
@Service
public class CustomUserDetailsService implements UserDetailsService {
@@ -53,6 +53,11 @@ public class CustomUserDetailsService implements UserDetailsService {
user.setPassword("{noop}" + user.getPassword()); //пока не введено шифрование паролей
userRepository.save(user);
sendMessage(user);
return "";
}
private void sendMessage(User user) {
if (!ObjectUtils.isEmpty(user.getEmail())) {
String message = String.format(
@@ -65,7 +70,6 @@ public class CustomUserDetailsService implements UserDetailsService {
emailService.send(user.getEmail(), "Activation code", message);
}
return "";
}
public boolean activateUser(String activationCode) {
@@ -80,4 +84,47 @@ public class CustomUserDetailsService implements UserDetailsService {
userRepository.save(user);
return true;
}
public void updateUser(String username, Map<String, String> form, User user) {
user.setUsername(username);
user.getRoles().clear();
Set<String> roles = Arrays.stream(Role.values()).map(Enum::name).collect(Collectors.toSet());
for (String role : form.keySet()) {
if (roles.contains(role)) {
user.getRoles().add(Role.valueOf(role));
}
}
userRepository.save(user);
}
public void updateProfile(User user, String password, String email) {
String userEmail = user.getEmail();
boolean isEmailChanged = (email != null && !email.equals(userEmail)) ||
(userEmail != null && !userEmail.equals(email));
if (isEmailChanged) {
user.setEmail(email);
if (!ObjectUtils.isEmpty(email)) {
user.setActivationCode(UUID.randomUUID().toString());
}
}
if (!ObjectUtils.isEmpty(password)) {
user.setPassword("{noop}" + password);
}
userRepository.save(user);
if (isEmailChanged) {
sendMessage(user);
}
}
public List<User> findAll() {
return userRepository.findAll();
}
}

View File

@@ -1,4 +1,5 @@
spring.jpa.hibernate.ddl-auto=update
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.show-sql=true
spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/sweater_db
spring.datasource.username=user
spring.datasource.password=123456

View File

@@ -0,0 +1,48 @@
create table message
(
id bigint not null,
user_id bigint,
filename varchar(255),
tag varchar(255),
text varchar(2048) not null,
primary key (id)
) engine = InnoDB;
create table message_seq
(
next_val bigint
) engine = InnoDB;
insert into message_seq
values (1);
create table user_role
(
user_id bigint not null,
roles enum ('ADMIN','USER')
) engine = InnoDB;
create table usr
(
enabled bit not null,
id bigint not null,
activation_code varchar(255),
email varchar(255),
password varchar(255) not null,
username varchar(255) not null,
primary key (id)
) engine = InnoDB;
create table usr_seq
(
next_val bigint
) engine = InnoDB;
insert into usr_seq
values (1);
alter table message
add constraint message_user_fk foreign key (user_id) references usr (id);
alter table user_role
add constraint user_role_user_fk foreign key (user_id) references usr (id);

View File

@@ -0,0 +1,5 @@
insert into usr (id, username, password, enabled)
values (1, 'admin', '{noop}admin', true);
insert into user_role (user_id, roles)
values (1, 'USER'), (1, 'ADMIN');

View File

@@ -22,6 +22,11 @@
<a class="nav-link" aria-current="page" href="/users">Users</a>
</li>
</#if>
<#if user??>
<li class="nav-item">
<a class="nav-link" aria-current="page" href="/users/profile">Profile</a>
</li>
</#if>
</ul>
</div>
<div class="navbar-text mr-3" >${name}</div>

View File

@@ -0,0 +1,24 @@
<#import "parts/common.ftl" as com>
<@com.page>
<h5>${username}</h5>
<#if message??>
<div class="mb-3"> ${message} </div>
</#if>
<form method="post" xmlns="http://www.w3.org/1999/html">
<div class="row mb-3">
<label for="inputPassword3" class="col-sm-2 col-form-label"> Password: </label>
<div class="col-sm-6">
<input type="password" class="form-control" name="password" value="${password}">
</div>
</div>
<div class="row mb-3">
<label class="col-sm-2 col-form-label"> E-mail:</label>
<div class="col-sm-6">
<input type="email" class="form-control" name="email" value="${email!''}"/>
</div>
</div>
<input class="btn btn-primary" type="submit" value="Save"/>
<input type="hidden" name="_csrf" value="${_csrf.token}"/>
</form>
</@com.page>