Rumah  >  Artikel  >  Java  >  Apakah teknik untuk pengesahan parameter SpringBoot?

Apakah teknik untuk pengesahan parameter SpringBoot?

WBOY
WBOYke hadapan
2023-05-12 08:19:241410semak imbas

1. Gunakan anotasi pengesahan

Spring Boot menyediakan anotasi pengesahan terbina dalam yang boleh membantu anda mengesahkan medan input dengan mudah dan cepat, seperti menyemak medan kosong atau kosong, menguatkuasakan had panjang dan menggunakan biasa ekspresi Corak pengesahan ungkapan serta mengesahkan alamat e-mel.

Beberapa anotasi pengesahan yang paling biasa digunakan termasuk:

  • @NotNull: Medan yang dinyatakan tidak boleh kosong.

  • @NotEmpty: Medan senarai yang ditentukan tidak boleh kosong.

  • @NotBlank: Medan rentetan yang ditentukan tidak boleh kosong atau mengandungi ruang sahaja.

  • @Min dan @Max: Tentukan nilai minimum dan maksimum untuk medan angka.

  • @Pattern: Menentukan corak ungkapan biasa yang mesti dipadankan oleh medan rentetan.

  • @Email: Menentukan bahawa medan rentetan mestilah alamat e-mel yang sah.

Untuk penggunaan khusus, sila rujuk contoh berikut:

public class User {
    @NotNull
    private Long id;

    @NotBlank
    @Size(min = 2, max = 50)
    private String firstName;

    @NotBlank
    @Size(min = 2, max = 50)
    private String lastName;

    @Email
    private String email;

    @NotNull
    @Min(18)
    @Max(99)
    private Integer age;

    @NotEmpty
    private List<String> hobbies;

    @Pattern(regexp = "[A-Z]{2}\d{4}")
    private String employeeId;

2. Gunakan anotasi pengesahan tersuai

Walaupun anotasi pengesahan Spring Boot terbina dalam adalah berguna, mereka Mungkin tidak meliputi semua situasi. Jika anda mempunyai senario pengesahan parameter khas, anda boleh menggunakan rangka kerja pengesahan JSR 303 Spring untuk membuat anotasi pengesahan tersuai. Anotasi tersuai boleh menjadikan logik pengesahan anda lebih boleh digunakan semula dan diselenggara.

Andaikan kami mempunyai aplikasi yang membolehkan pengguna membuat siaran. Setiap siaran hendaklah mempunyai tajuk dan isi, dan tajuk itu hendaklah unik merentas semua siaran. Walaupun Spring Boot menyediakan anotasi pengesahan terbina dalam untuk menyemak sama ada medan kosong, ia tidak menyediakan anotasi pengesahan terbina dalam untuk menyemak keunikan. Dalam kes ini, kami boleh membuat anotasi pengesahan tersuai untuk mengendalikan situasi ini.

Pertama, kami mencipta anotasi kekangan tersuai UniqueTitle:

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = UniqueTitleValidator.class)
public @interface UniqueTitle {
    String message() default "Title must be unique";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

Seterusnya, kami mencipta antara muka PostRepository dengan tujuan mendapatkan semula siaran daripada pangkalan data:

public interface PostRepository extends JpaRepository<Post, Long> {
    Post findByTitle(String title);
}

Kemudian kita perlu mentakrifkan kelas pengesah UniqueTitleValidator seperti berikut:

@Component
public class UniqueTitleValidator implements ConstraintValidator<UniqueTitle, String> {

    @Autowired
    private PostRepository postRepository;

    @Override
    public boolean isValid(String title, ConstraintValidatorContext context) {
        if (title == null) {
            return true;
        }
        return Objects.isNull(postRepository.findByTitle(title));
    }
}

UniqueTitleValidator melaksanakan antara muka ConstraintValidator, yang mempunyai dua jenis generik: yang pertama ialah anotasi tersuai UniqueTitle, kedua ialah jenis medan yang sedang disahkan (dalam kes ini String kami juga mengautomasikan kelas PostRepository untuk mendapatkan semula siaran daripada pangkalan data. Kaedah

isValid() menyemak sama ada PostRepository adalah batal atau ia unik dengan menanyakannya. Jika title adalah batal atau unik, pengesahan berjaya dan benar dikembalikan. title

Dengan anotasi pengesahan tersuai dan kelas pengesah ditakrifkan, kami kini boleh menggunakannya untuk mengesahkan tajuk siaran dalam aplikasi Spring Boot kami:

public class Post {
    @UniqueTitle
    private String title;

    @NotNull
    private String body;
}

Kami telah menggunakan anotasi

pada @UniqueTitle pembolehubah dalam kelas Post. Apabila mengesahkan medan ini, ini akan mencetuskan logik pengesahan yang ditakrifkan dalam kelas title. UniqueTitleValidator

3. Pengesahan di bahagian pelayan

Selain pengesahan di bahagian hadapan atau klien, adalah penting untuk mengesahkan input di bahagian pelayan. Ia memastikan bahawa mana-mana data berniat jahat atau salah bentuk ditangkap sebelum ia diproses atau disimpan, yang penting untuk keselamatan dan kestabilan aplikasi.

Andaikan kita mempunyai

titik akhir yang membolehkan pengguna membuat akaun baharu. Titik akhir menjangkakan badan permintaan JSON yang mengandungi nama pengguna dan kata laluan pengguna. Untuk memastikan bahawa input adalah sah, kami boleh mencipta kelas DTO (Data Transfer Object) dan menggunakan anotasi pengesahan pada medannya: REST

public class UserDTO {

    @NotBlank
    private String username;

    @NotBlank
    private String password;
}

Kami menggunakan anotasi

untuk memastikan @NotBlank dan username medan tidak Kosong atau batal. password

Seterusnya, kami boleh mencipta kaedah pengawal untuk mengendalikan permintaan HTTP POST dan mengesahkan input sebelum mencipta pengguna baharu:

@RestController
@RequestMapping("/users")
@Validated
public class UserController {
    @Autowired
    private UserService userService;

    @PostMapping
    public ResponseEntity<String> createUser(@Valid @RequestBody UserDTO userDto) {
        userService.createUser(userDto);
        return ResponseEntity.status(HttpStatus.CREATED).body("User created successfully");
    }
}

Kami menggunakan anotasi Spring

untuk mendayakan pengesahan tahap kaedah , kami juga menggunakan anotasi @Validated pada parameter @Valid untuk mencetuskan proses pengesahan. userDto

4. Berikan mesej ralat yang bermakna

Apabila pengesahan gagal, mesej ralat yang jelas dan ringkas mesti disediakan yang menerangkan perkara yang salah dan cara membetulkannya.

Ini adalah contoh jika kita mempunyai

yang membolehkan pengguna mencipta pengguna baharu. Kami memastikan bahawa medan nama dan alamat e-mel tidak kosong dan berumur antara 18 dan 99. Selain medan ini, kami juga menyediakan mesej ralat atau "alamat e-mel" yang jelas jika pengguna cuba membuat akaun dengan "nama pengguna" pendua. RESTful API

Untuk melakukan ini, kami boleh menentukan Pengguna kelas model dengan anotasi pengesahan yang diperlukan seperti berikut:

public class User {

    @NotBlank(message = "用户名不能为空")
    private String name;

    @NotBlank(message = "Email不能为空")
    @Email(message = "无效的Emaild地址")
    private String email;

    @NotNull(message = "年龄不能为空")
    @Min(value = 18, message = "年龄必须大于18")
    @Max(value = 99, message = "年龄必须小于99")
    private Integer age;
}

Kami menyediakan mesej ralat tersuai untuk setiap anotasi pengesahan menggunakan atribut mesej.

Seterusnya, dalam pengawal Spring kami, kami boleh mengendalikan penyerahan borang dan mengesahkan input pengguna menggunakan anotasi

: @Valid

@RestController
@RequestMapping("/users")
public class UserController {
    @Autowired
    private UserService userService;

    @PostMapping
    public ResponseEntity<String> createUser(@Valid @RequestBody User user, BindingResult result) {
        if (result.hasErrors()) {
            List<String> errorMessages = result.getAllErrors().stream()
                    .map(DefaultMessageSourceResolvable::getDefaultMessage)
                    .collect(Collectors.toList());
            return ResponseEntity.badRequest().body(errorMessages.toString());
        }

        // save the user to the database using UserService
        userService.saveUser(user);

        return ResponseEntity.status(HttpStatus.CREATED).body("User created successfully");
    }
}

Kami menggunakan anotasi

untuk mencetuskan @Valid Pengesahan objek dan gunakan objek User untuk menangkap sebarang ralat pengesahan. BindingResult

5.将 i18n 用于错误消息

如果你的应用程序支持多种语言,则必须使用国际化 (i18n) 以用户首选语言显示错误消息。

以下是在 Spring Boot 应用程序中使用 i18n 处理错误消息的示例

首先,在资源目录下创建一个包含默认错误消息的 messages.properties 文件

# messages.properties
user.name.required=Name is required.
user.email.invalid=Invalid email format.
user.age.invalid=Age must be a number between 18 and 99.

接下来,为每种支持的语言创建一个 messages_xx.properties 文件,例如,中文的 messages_zh_CN.properties

user.name.required=名称不能为空.
user.email.invalid=无效的email格式.
user.age.invalid=年龄必须在18到99岁之间.

然后,更新您的验证注释以使用本地化的错误消息

public class User {
    @NotNull(message = "{user.id.required}")
    private Long id;

    @NotBlank(message = "{user.name.required}")
    private String name;

    @Email(message = "{user.email.invalid}")
    private String email;

    @NotNull(message = "{user.age.required}")
    @Min(value = 18, message = "{user.age.invalid}")
    @Max(value = 99, message = "{user.age.invalid}")
    private Integer age;
}

最后,在 Spring 配置文件中配置 MessageSource bean 以加载 i18n 消息文件

@Configuration
public class AppConfig {
    @Bean
    public MessageSource messageSource() {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        messageSource.setBasename("messages");
        messageSource.setDefaultEncoding("UTF-8");
        return messageSource;
    }

    @Bean
    public LocalValidatorFactoryBean validator() {
        LocalValidatorFactoryBean validatorFactoryBean = new LocalValidatorFactoryBean();
        validatorFactoryBean.setValidationMessageSource(messageSource());
        return validatorFactoryBean;
    }
}

现在,当发生验证错误时,错误消息将根据随请求发送的“Accept-Language”标头以用户的首选语言显示。

6.使用分组验证

验证组是 Spring Boot 验证框架的一个强大功能,允许您根据其他输入值或应用程序状态应用条件验证规则。

现在有一个包含三个字段的User类的情况下:firstNamelastNameemail。我们要确保如果 email 字段为空,则 firstNamelastName 字段必须非空。否则,所有三个字段都应该正常验证。

为此,我们将定义两个验证组:EmailNotEmptyDefaultEmailNotEmpty 组将包含当 email 字段不为空时的验证规则,而 Default 组将包含所有三个字段的正常验证规则。

创建带有验证组的 User

public class User {
    @NotBlank(groups = Default.class)
    private String firstName;

    @NotBlank(groups = Default.class)
    private String lastName;

    @Email(groups = EmailNotEmpty.class)
    private String email;

    // getters and setters omitted for brevity
    public interface EmailNotEmpty {}
    public interface Default {}
}

请注意,我们在User类中定义了两个接口,EmailNotEmptyDefault。这些将作为我们的验证组。

接下来,我们更新Controller使用这些验证组

@RestController
@RequestMapping("/users")
@Validated
public class UserController {
    public ResponseEntity<String> createUser(
            @Validated({org.example.model.ex6.User.EmailNotEmpty.class}) @RequestBody User userWithEmail,
            @Validated({User.Default.class}) @RequestBody User userWithoutEmail)
    {
        // Create the user and return a success response
       
    }
}

我们已将@Validated注释添加到我们的控制器,表明我们想要使用验证组。我们还更新了 createUser 方法,将两个 User 对象作为输入,一个在 email 字段不为空时使用,另一个在它为空时使用。

@Validated 注释用于指定将哪个验证组应用于每个 User 对象。对于 userWithEmail 参数,我们指定了 EmailNotEmpty 组,而对于 userWithoutEmail 参数,我们指定了 Default 组。

进行这些更改后,现在将根据“电子邮件”字段是否为空对“用户”类进行不同的验证。如果为空,则 firstNamelastName 字段必须非空。否则,所有三个字段都将正常验证。

7.对复杂逻辑使用跨域验证

如果需要验证跨多个字段的复杂输入规则,可以使用跨字段验证来保持验证逻辑的组织性和可维护性。跨字段验证可确保所有输入值均有效且彼此一致,从而防止出现意外行为。

假设我们有一个表单,用户可以在其中输入任务的开始日期和结束日期,并且我们希望确保结束日期不早于开始日期。我们可以使用跨域验证来实现这一点。

首先,我们定义一个自定义验证注解EndDateAfterStartDate

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = EndDateAfterStartDateValidator.class)
public @interface EndDateAfterStartDate {
    String message() default "End date must be after start date";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

然后,我们创建验证器EndDateAfterStartDateValidator

public class EndDateAfterStartDateValidator implements ConstraintValidator<EndDateAfterStartDate, TaskForm> {
    @Override
    public boolean isValid(TaskForm taskForm, ConstraintValidatorContext context) {
        if (taskForm.getStartDate() == null || taskForm.getEndDate() == null) {
            return true;
        }

        return taskForm.getEndDate().isAfter(taskForm.getStartDate());
    }
}

最后,我们将EndDateAfterStartDate注释应用于我们的表单对象TaskForm

@EndDateAfterStartDate
public class TaskForm {
    @NotNull
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate startDate;

    @NotNull
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private LocalDate endDate;
}

现在,当用户提交表单时,验证框架将自动检查结束日期是否晚于开始日期,如果不是,则提供有意义的错误消息。

8.对验证错误使用异常处理

可以使用异常处理ExceptionHandler来统一捕获和处理验证错误。

以下是如何在 Spring Boot 中使用异常处理来处理验证错误的示例:

@RestControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex,
                                                                  HttpHeaders headers, HttpStatus status,
                                                                  WebRequest request) {
        Map<String, Object> body = new LinkedHashMap<>();
        body.put("timestamp", LocalDateTime.now());
        body.put("status", status.value());

        // Get all errors
        List<String> errors = ex.getBindingResult()
                .getFieldErrors()
                .stream()
                .map(x -> x.getDefaultMessage())
                .collect(Collectors.toList());

        body.put("errors", errors);

        return new ResponseEntity<>(body, headers, status);
    }
}

在这里,我们创建了一个用 @RestControllerAdvice 注解的 RestExceptionHandler 类来处理我们的 REST API 抛出的异常。然后我们创建一个用 @ExceptionHandler 注解的方法来处理在验证失败时抛出的 MethodArgumentNotValidException

在处理程序方法中,我们创建了一个 Map 对象来保存错误响应的详细信息,包括时间戳、HTTP 状态代码和错误消息列表。我们使用 MethodArgumentNotValidException 对象的 getBindingResult() 方法获取所有验证错误并将它们添加到错误消息列表中。

最后,我们返回一个包含错误响应详细信息的ResponseEntity对象,包括作为响应主体的错误消息列表、HTTP 标头和 HTTP 状态代码。

有了这个异常处理代码,我们的 REST API 抛出的任何验证错误都将被捕获并以结构化和有意义的格式返回给用户,从而更容易理解和解决问题。

9.测试你的验证逻辑

需要为你的验证逻辑编写单元测试,以帮助确保它正常工作。

@DataJpaTest
public class UserValidationTest {

    @Autowired
    private TestEntityManager entityManager;

    @Autowired
    private Validator validator;

    @Test
    public void testValidation() {
        User user = new User();
        user.setFirstName("John");
        user.setLastName("Doe");
        user.setEmail("invalid email");

        Set<ConstraintViolation<User>> violations = validator.validate(user);

        assertEquals(1, violations.size());
        assertEquals("must be a well-formed email address", violations.iterator().next().getMessage());
    }
}

我们使用 JUnit 5 编写一个测试来验证具有无效电子邮件地址的“用户”对象。然后我们使用 Validator 接口来验证 User 对象并检查是否返回了预期的验证错误。

10.考虑客户端验证

客户端验证可以通过向用户提供即时反馈并减少对服务器的请求数量来改善用户体验。但是,不应依赖它作为验证输入的唯一方法。客户端验证很容易被绕过或操纵,因此必须在服务器端验证输入,以确保安全性和数据完整性。

Atas ialah kandungan terperinci Apakah teknik untuk pengesahan parameter SpringBoot?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:yisu.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam