- Добавил миграции Flyway.
- Добавил профиль пользователя и возможность его редактирования. https://www.youtube.com/watch?v=ArM7nCys4hY&list=PLU2ftbIeotGpAYRP9Iv2KLIwK36-o_qYk&index=12
This commit is contained in:
8
pom.xml
8
pom.xml
@@ -69,6 +69,14 @@
|
|||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-mail</artifactId>
|
<artifactId>spring-boot-starter-mail</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.flywaydb</groupId>
|
||||||
|
<artifactId>flyway-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.flywaydb</groupId>
|
||||||
|
<artifactId>flyway-mysql</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
@@ -2,36 +2,37 @@ package net.sytes.kashey.sweater.controller;
|
|||||||
|
|
||||||
import net.sytes.kashey.sweater.domain.Role;
|
import net.sytes.kashey.sweater.domain.Role;
|
||||||
import net.sytes.kashey.sweater.domain.User;
|
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.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
@RequestMapping("/users")
|
@RequestMapping("/users")
|
||||||
@PreAuthorize("hasAuthority('ADMIN')")
|
|
||||||
public class UserController {
|
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
|
@GetMapping
|
||||||
public String getAllUsers(Model model) {
|
public String getAllUsers(Model model) {
|
||||||
|
|
||||||
model.addAttribute("users", userRepository.findAll());
|
model.addAttribute("users", customUserDetailsService.findAll());
|
||||||
return "userList";
|
return "userList";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("hasAuthority('ADMIN')")
|
||||||
@GetMapping("/{user}")
|
@GetMapping("/{user}")
|
||||||
public String editUser(@PathVariable User user, Model model) {
|
public String editUser(@PathVariable User user, Model model) {
|
||||||
|
|
||||||
@@ -40,20 +41,31 @@ public class UserController {
|
|||||||
return "userEdit";
|
return "userEdit";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PreAuthorize("hasAuthority('ADMIN')")
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public String updateUser(@RequestParam String username,
|
public String updateUser(@RequestParam String username,
|
||||||
@RequestParam Map<String, String> form,
|
@RequestParam Map<String, String> form,
|
||||||
@RequestParam("userId") User user) {
|
@RequestParam("userId") User user) {
|
||||||
|
|
||||||
user.setUsername(username);
|
customUserDetailsService.updateUser(username, form, user);
|
||||||
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);
|
|
||||||
return "redirect:/users";
|
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";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -6,7 +6,7 @@ import jakarta.persistence.*;
|
|||||||
public class Message {
|
public class Message {
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||||
private Integer id;
|
private Long id;
|
||||||
private String text;
|
private String text;
|
||||||
private String tag;
|
private String tag;
|
||||||
|
|
||||||
@@ -25,11 +25,11 @@ public class Message {
|
|||||||
this.author = author;
|
this.author = author;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Integer getId() {
|
public Long getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setId(Integer id) {
|
public void setId(Long id) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,4 +61,8 @@ public class CustomUserDetails implements UserDetails {
|
|||||||
public boolean isAdmin() {
|
public boolean isAdmin() {
|
||||||
return user.getRoles().contains(Role.ADMIN);
|
return user.getRoles().contains(Role.ADMIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getEmail() {
|
||||||
|
return user.getEmail();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -11,8 +11,8 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.*;
|
||||||
import java.util.UUID;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class CustomUserDetailsService implements UserDetailsService {
|
public class CustomUserDetailsService implements UserDetailsService {
|
||||||
@@ -53,6 +53,11 @@ public class CustomUserDetailsService implements UserDetailsService {
|
|||||||
user.setPassword("{noop}" + user.getPassword()); //пока не введено шифрование паролей
|
user.setPassword("{noop}" + user.getPassword()); //пока не введено шифрование паролей
|
||||||
userRepository.save(user);
|
userRepository.save(user);
|
||||||
|
|
||||||
|
sendMessage(user);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendMessage(User user) {
|
||||||
if (!ObjectUtils.isEmpty(user.getEmail())) {
|
if (!ObjectUtils.isEmpty(user.getEmail())) {
|
||||||
|
|
||||||
String message = String.format(
|
String message = String.format(
|
||||||
@@ -65,7 +70,6 @@ public class CustomUserDetailsService implements UserDetailsService {
|
|||||||
|
|
||||||
emailService.send(user.getEmail(), "Activation code", message);
|
emailService.send(user.getEmail(), "Activation code", message);
|
||||||
}
|
}
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean activateUser(String activationCode) {
|
public boolean activateUser(String activationCode) {
|
||||||
@@ -80,4 +84,47 @@ public class CustomUserDetailsService implements UserDetailsService {
|
|||||||
userRepository.save(user);
|
userRepository.save(user);
|
||||||
return true;
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/sweater_db
|
||||||
spring.datasource.username=user
|
spring.datasource.username=user
|
||||||
spring.datasource.password=123456
|
spring.datasource.password=123456
|
||||||
|
|||||||
48
src/main/resources/db/migration/V1__Init_DB.sql
Normal file
48
src/main/resources/db/migration/V1__Init_DB.sql
Normal 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);
|
||||||
5
src/main/resources/db/migration/V2__Add_admin.sql
Normal file
5
src/main/resources/db/migration/V2__Add_admin.sql
Normal 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');
|
||||||
@@ -22,6 +22,11 @@
|
|||||||
<a class="nav-link" aria-current="page" href="/users">Users</a>
|
<a class="nav-link" aria-current="page" href="/users">Users</a>
|
||||||
</li>
|
</li>
|
||||||
</#if>
|
</#if>
|
||||||
|
<#if user??>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" aria-current="page" href="/users/profile">Profile</a>
|
||||||
|
</li>
|
||||||
|
</#if>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="navbar-text mr-3" >${name}</div>
|
<div class="navbar-text mr-3" >${name}</div>
|
||||||
|
|||||||
24
src/main/resources/templates/profile.ftl
Normal file
24
src/main/resources/templates/profile.ftl
Normal 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>
|
||||||
Reference in New Issue
Block a user