- Добавил reCaptcha
- Добавил поддержку rememberMe и хранение сессий в БД. https://www.youtube.com/watch?v=7cDpbAbhyjc&list=PLU2ftbIeotGpAYRP9Iv2KLIwK36-o_qYk&index=14
This commit is contained in:
4
pom.xml
4
pom.xml
@@ -81,6 +81,10 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.session</groupId>
|
||||
<artifactId>spring-session-jdbc</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package net.sytes.kashey.sweater.config;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
@@ -12,6 +14,11 @@ public class MvcConfig implements WebMvcConfigurer {
|
||||
@Value("${upload.path}")
|
||||
private String uploadPath;
|
||||
|
||||
@Bean
|
||||
public RestTemplate restTemplate() {
|
||||
return new RestTemplate();
|
||||
}
|
||||
|
||||
public void addViewControllers(ViewControllerRegistry registry) {
|
||||
|
||||
registry.addViewController("/login").setViewName("login");
|
||||
|
||||
@@ -2,6 +2,7 @@ package net.sytes.kashey.sweater.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
@@ -30,7 +31,7 @@ public class WebSecurityConfig {
|
||||
.loginPage("/login")
|
||||
.defaultSuccessUrl("/main", true)
|
||||
.permitAll()
|
||||
)
|
||||
).rememberMe(Customizer.withDefaults())
|
||||
.logout(LogoutConfigurer::permitAll);
|
||||
|
||||
return http.build();
|
||||
|
||||
@@ -2,21 +2,33 @@ package net.sytes.kashey.sweater.controller;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import net.sytes.kashey.sweater.domain.User;
|
||||
import net.sytes.kashey.sweater.domain.dto.CaptchaResponseDto;
|
||||
import net.sytes.kashey.sweater.service.CustomUserDetailsService;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
|
||||
@Controller
|
||||
public class RegistrationController {
|
||||
|
||||
private final static String CAPTCHA_URL = "https://www.google.com/recaptcha/api/siteverify?secret=%s&response=%s";
|
||||
private final CustomUserDetailsService customUserDetailsService;
|
||||
|
||||
public RegistrationController(CustomUserDetailsService customUserDetailsService) {
|
||||
@Value("${recaptcha.secret}")
|
||||
private String recaptchaSecret;
|
||||
|
||||
private final RestTemplate restTemplate;
|
||||
|
||||
public RegistrationController(CustomUserDetailsService customUserDetailsService, RestTemplate restTemplate) {
|
||||
this.customUserDetailsService = customUserDetailsService;
|
||||
this.restTemplate = restTemplate;
|
||||
}
|
||||
|
||||
@GetMapping("/registration")
|
||||
@@ -26,13 +38,31 @@ public class RegistrationController {
|
||||
}
|
||||
|
||||
@PostMapping("/registration")
|
||||
public String addUser(@ModelAttribute @Valid User user, BindingResult bindingResult, Model model) {
|
||||
public String addUser(@RequestParam(name = "g-recaptcha-response") String captchaResponse,
|
||||
@RequestParam(name = "password2") String passwordConfirmation,
|
||||
@ModelAttribute @Valid User user,
|
||||
BindingResult bindingResult,
|
||||
Model model) {
|
||||
String url = String.format(CAPTCHA_URL, recaptchaSecret, captchaResponse);
|
||||
CaptchaResponseDto responseDto = restTemplate.postForObject(url, Collections.EMPTY_LIST, CaptchaResponseDto.class);
|
||||
|
||||
if (user.getPassword() != null && !user.getPassword().equals(user.getPassword2())) {
|
||||
boolean isCaptchaEmpty = !Objects.requireNonNull(responseDto).isSuccess();
|
||||
|
||||
if (isCaptchaEmpty) {
|
||||
model.addAttribute("captchaError", "Fill the captcha");
|
||||
}
|
||||
|
||||
if (user.getPassword() != null && !user.getPassword().equals(passwordConfirmation)) {
|
||||
model.addAttribute("passwordError", "Passwords do not match!");
|
||||
}
|
||||
|
||||
if (bindingResult.hasErrors()) {
|
||||
boolean isConfirmationEmpty = ObjectUtils.isEmpty(passwordConfirmation);
|
||||
|
||||
if (isConfirmationEmpty) {
|
||||
model.addAttribute("password2Error", "Password confirmation must not be empty.");
|
||||
}
|
||||
|
||||
if (bindingResult.hasErrors() || isConfirmationEmpty || isCaptchaEmpty) {
|
||||
model.mergeAttributes(ControllerUtils.getErrors(bindingResult));
|
||||
return "registration";
|
||||
}
|
||||
@@ -50,8 +80,10 @@ public class RegistrationController {
|
||||
public String activateAccount(Model model, @PathVariable String activateCode) {
|
||||
|
||||
if (customUserDetailsService.activateUser(activateCode)) {
|
||||
model.addAttribute("messageType", "success");
|
||||
model.addAttribute("message", "User successfully activated!");
|
||||
} else {
|
||||
model.addAttribute("messageType", "danger");
|
||||
model.addAttribute("message", "User activation is failed!");
|
||||
}
|
||||
|
||||
|
||||
@@ -4,11 +4,15 @@ import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Set;
|
||||
|
||||
@Entity
|
||||
@Table(name = "usr")
|
||||
public class User {
|
||||
public class User implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
@@ -18,9 +22,6 @@ public class User {
|
||||
@NotBlank(message = "Password must not be empty.")
|
||||
private String password;
|
||||
|
||||
@Transient
|
||||
@NotBlank(message = "Password confirmation must not be empty.")
|
||||
private String password2;
|
||||
private boolean enabled;
|
||||
|
||||
@Email(message = "E-mail is not valid.")
|
||||
@@ -100,12 +101,4 @@ public class User {
|
||||
public void setActivationCode(String activationCode) {
|
||||
this.activationCode = activationCode;
|
||||
}
|
||||
|
||||
public String getPassword2() {
|
||||
return password2;
|
||||
}
|
||||
|
||||
public void setPassword2(String password2) {
|
||||
this.password2 = password2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package net.sytes.kashey.sweater.domain.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAlias;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class CaptchaResponseDto {
|
||||
|
||||
private boolean success;
|
||||
|
||||
@JsonAlias("error-codes")
|
||||
private Set<String> errorCodes;
|
||||
|
||||
public boolean isSuccess() {
|
||||
return success;
|
||||
}
|
||||
|
||||
public void setSuccess(boolean success) {
|
||||
this.success = success;
|
||||
}
|
||||
|
||||
public Set<String> getErrorCodes() {
|
||||
return errorCodes;
|
||||
}
|
||||
|
||||
public void setErrorCodes(Set<String> errorCodes) {
|
||||
this.errorCodes = errorCodes;
|
||||
}
|
||||
}
|
||||
@@ -4,14 +4,22 @@ spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/sweater_db
|
||||
spring.datasource.username=user
|
||||
spring.datasource.password=123456
|
||||
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
|
||||
|
||||
spring.freemarker.expose-request-attributes=true
|
||||
spring.freemarker.expose-session-attributes=true
|
||||
spring.freemarker.suffix=.ftl
|
||||
|
||||
upload.path=/home/kashey/soft/sweater/uploads
|
||||
hosting.path=http://localhost:8080
|
||||
|
||||
spring.mail.host=smtp.yandex.ru
|
||||
spring.mail.username=username@yandex.ru
|
||||
spring.mail.password=pa$$word
|
||||
spring.mail.username=kasheyne@yandex.ru
|
||||
spring.mail.password=xyyfvjuoiapkerqr
|
||||
spring.mail.port=465
|
||||
spring.mail.protocol=smtps
|
||||
mail.debug=true
|
||||
|
||||
recaptcha.secret=6LcB49sqAAAAALtmKoXaCFXIr4MqgQyoKHwPiMQO
|
||||
spring.session.jdbc.serialize=java
|
||||
spring.session.jdbc.initialize-schema=always
|
||||
spring.session.jdbc.table-name=SPRING_SESSION
|
||||
|
||||
@@ -2,9 +2,14 @@
|
||||
<#import "parts/login.ftl" as l>
|
||||
|
||||
<@com.page>
|
||||
<#if Session?? && Session.SPRING_SECURITY_LAST_EXCEPTION??>
|
||||
<#if SPRING_SECURITY_LAST_EXCEPTION??>
|
||||
<div class="alert alert-danger" role="alert">
|
||||
${Session.SPRING_SECURITY_LAST_EXCEPTION.message}
|
||||
${SPRING_SECURITY_LAST_EXCEPTION.message}
|
||||
</div>
|
||||
</#if>
|
||||
<#if message??>
|
||||
<div <#if messageType??>class="alert alert-${messageType}" role="alert"</#if> >
|
||||
${message}
|
||||
</div>
|
||||
</#if>
|
||||
<@l.login "/login" false/>
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.1.3/dist/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
|
||||
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -53,6 +53,14 @@
|
||||
</#if>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="g-recaptcha" data-sitekey="6LcB49sqAAAAAIvXz46_hEGqtYAzFYYGuY_Y9Rlg"></div>
|
||||
<#if captchaError??>
|
||||
<div class="alert alert-danger" role="alert">
|
||||
${captchaError}
|
||||
</div>
|
||||
</#if>
|
||||
</div>
|
||||
</#if>
|
||||
<#if !isRegistrationForm>
|
||||
<a href="/registration" class="btn btn-primary">Add new user</a>
|
||||
|
||||
Reference in New Issue
Block a user