最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring?Boot用戶注冊驗證的實現(xiàn)全過程記錄

 更新時間:2022年01月18日 11:08:28   作者:翊君  
最近在設計自己的博客系統(tǒng),涉及到用戶注冊與登錄驗證,所以下面這篇文章主要給大家介紹了關于Spring?Boot用戶注冊驗證的實現(xiàn)全過程,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下

1. 概述

在這篇文章中,我們將使用Spring Boot實現(xiàn)一個基本的郵箱注冊賬戶以及驗證的過程。

我們的目標是添加一個完整的注冊過程,允許用戶注冊,驗證,并持久化用戶數(shù)據(jù)。

2. 創(chuàng)建User DTO Object

首先,我們需要一個DTO來囊括用戶的注冊信息。這個對象應該包含我們在注冊和驗證過程中所需要的基本信息。

例2.1 UserDto的定義

package com.savagegarden.web.dto;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;

public class UserDto {

    @NotBlank
    private String username;

    @NotBlank
    private String password;

    @NotBlank
    private String repeatedPassword;

    @NotBlank
    private String email;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getRepeatedPassword() {
        return repeatedPassword;
    }

    public void setRepeatedPassword(String repeatedPassword) {
        this.repeatedPassword = repeatedPassword;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

請注意我們在DTO對象的字段上使用了標準的javax.validation注解——@NotBlank。

@NotBlank、@NotEmpty、@NotNull的區(qū)別

@NotNull: 適用于CharSequence, Collection, Map 和 Array 對象,不能是null,但可以是空集(size = 0)。

@NotEmpty: 適用于CharSequence, Collection, Map 和 Array 對象,不能是null并且相關對象的size大于0。
@NotBlank: 該注解只能作用于String類型。String非null且去除兩端空白字符后的長度(trimmed length)大于0。

在下面的章節(jié)里,我們還將自定義注解來驗證電子郵件地址的格式以及確認二次密碼。

3. 實現(xiàn)一個注冊Controller

登錄頁面上的注冊鏈接將用戶帶到注冊頁面:

例3.1 RegistrationController的定義

package com.savagegarden.web.controller;

import com.savagegarden.web.dto.UserDto;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class RegistrationController {

    @GetMapping("/user/registration")
    public String showRegistrationForm(Model model) {
        model.addAttribute("user", new UserDto());
        return "registration";
    }

}

當RegistrationController收到請求/user/registration時,它創(chuàng)建了新的UserDto對象,將其綁定在Model上,并返回了注冊頁面registration.html。

Model 對象負責在控制器Controller和展現(xiàn)數(shù)據(jù)的視圖View之間傳遞數(shù)據(jù)。

實際上,放到 Model 屬性中的數(shù)據(jù)將會復制到 Servlet Response 的屬性中,這樣視圖就能在這里找到它們了。

從廣義上來說,Model 指的是 MVC框架 中的 M,即 Model(模型)。從狹義上講,Model 就是個 key-value 集合。

4. 驗證注冊數(shù)據(jù)

接下來,讓我們看看控制器在注冊新賬戶時將執(zhí)行的驗證:

  • 所有必須填寫的字段都已填寫且沒有空字段
  • 該電子郵件地址是有效的
  • 密碼確認字段與密碼字段相符
  • 該賬戶不存在

4.1 內(nèi)置的驗證

對于簡單的檢查,我們將使用@NotBlank來驗證DTO對象。

為了觸發(fā)驗證過程,我們將在Controller中用@Valid注解來驗證對象。

例4.1 registerUserAccount

public ModelAndView registerUserAccount(@ModelAttribute("user") @Valid UserDto userDto,
  HttpServletRequest request, Errors errors) {
    //...
}

4.2 自定義驗證以檢查電子郵件的有效性

下一步,讓我們驗證電子郵件地址,以保證它的格式是正確的。我們將為此建立一個自定義驗證器,以及一個自定義驗證注解--IsEmailValid。

下面是電子郵件驗證注解IsEmailValid和自定義驗證器EmailValidator:

為什么不使用Hibernate內(nèi)置的@Email?

因為Hibernate中的@Email會驗證通過XXX@XXX之類的郵箱,其實這是不符合規(guī)定的。

感興趣的讀者朋友可以移步此處Hibernate validator: @Email accepts ask@stackoverflow as valid?。

例4.2.1 IsEmailVaild注解的定義

package com.savagegarden.validation;

import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.Payload;

@Target({ TYPE, FIELD, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = EmailValidator.class)
@Documented
public @interface IsEmailVaild {

    String message() default "Invalid Email";

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

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

@Target的作用是說明了該注解所修飾的對象范圍

@Retention的作用是說明了被它所注解的注解保留多久

@Constraint的作用是說明自定義注解的方法

@Documented的作用是說明了被這個注解修飾的注解可以被例如javadoc此類的工具文檔化

關于如何自定義一個Java Annotation,感興趣的朋友可以看看我的另一篇文章。

例4.2.2 EmailValidator的定義

package com.savagegarden.validation;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class EmailValidator implements ConstraintValidator<IsEmailVaild, String> {
    private static final String EMAIL_PATTERN = "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@" + "[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";
    private static final Pattern PATTERN = Pattern.compile(EMAIL_PATTERN);

    @Override
    public void initialize(IsEmailVaild constraintAnnotation) {
    }

    @Override
    public boolean isValid(final String username, final ConstraintValidatorContext context) {
        return (validateEmail(username));
    }

    private boolean validateEmail(final String email) {
        Matcher matcher = PATTERN.matcher(email);
        return matcher.matches();
    }
}

現(xiàn)在讓我們在我們的UserDto實現(xiàn)上使用新注解。

@NotBlank
@IsEmailVaild
private String email;

4.3 使用自定義驗證來確認密碼

我們還需要一個自定義注解和驗證器,以確保UserDto中的password和repeatedPassword字段相匹配。

例4.3.1 IsPasswordMatching注解的定義

package com.savagegarden.validation;

import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.Payload;

@Target({ TYPE, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = PasswordMatchingValidator.class)
@Documented
public @interface IsPasswordMatching {

    String message() default "Passwords don't match";

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

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

}

請注意,@Target注解表明這是一個Type級別的注解。這是因為我們需要整個UserDto對象來執(zhí)行驗證。

例4.3.2 PasswordMatchingValidator的定義

package com.savagegarden.validation;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

import com.savagegarden.web.dto.UserDto;

public class PasswordMatchingValidator implements ConstraintValidator<IsPasswordMatching, Object> {

    @Override
    public void initialize(final IsPasswordMatching constraintAnnotation) {
        //
    }

    @Override
    public boolean isValid(final Object obj, final ConstraintValidatorContext context) {
        final UserDto user = (UserDto) obj;
        return user.getPassword().equals(user.getRepeatedPassword());
    }

}

現(xiàn)在,將@IsPasswordMatching注解應用到我們的UserDto對象。

@IsPasswordMatching
public class UserDto {
    //...
}

4.4 檢查該賬戶是否已經(jīng)存在

我們要實現(xiàn)的第四個檢查是驗證該電子郵件帳戶在數(shù)據(jù)庫中是否已經(jīng)存在。

這是在表單被驗證后進行的,我們把這項驗證放在了UserService。

例4.4.1 UserService

package com.savagegarden.service.impl;

import com.savagegarden.error.user.UserExistException;
import com.savagegarden.persistence.dao.UserRepository;
import com.savagegarden.persistence.model.User;
import com.savagegarden.service.IUserService;
import com.savagegarden.web.dto.UserDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;

@Service
@Transactional
public class UserService implements IUserService {
    @Autowired
    private UserRepository userRepository;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public User registerNewUserAccount(UserDto userDto) throws UserExistException {
        if (hasEmailExisted(userDto.getEmail())) {
            throw new UserExistException("The email has already existed: "
                    + userDto.getEmail());
        }

        User user = new User();
        user.setUsername(userDto.getUsername());
        user.setPassword(passwordEncoder.encode(userDto.getPassword()));
        user.setEmail(userDto.getEmail());
        return userRepository.save(user);
    }
    private boolean hasEmailExisted(String email) {
        return userRepository.findByEmail(email) != null;
    }
}

使用@Transactional開啟事務注解,至于為什么@Transactional加在Service層而不是DAO層?

如果我們的事務注解@Transactional加在DAO層,那么只要做增刪改,就要提交一次事務,那么事務的特性就發(fā)揮不出來,尤其是事務的一致性。當出現(xiàn)并發(fā)問題的時候,用戶從數(shù)據(jù)庫查到的數(shù)據(jù)都會有所偏差。

一般的時候,我們的Service層可以調(diào)用多個DAO層,我們只需要在Service層加一個事務注解@Transactional,這樣我們就可以一個事務處理多個請求,事務的特性也會充分地發(fā)揮出來。

UserService依靠UserRepository類來檢查數(shù)據(jù)庫中是否已存在擁有相同郵箱的用戶賬戶。當然在本文中我們不會涉及到UserRepository的實現(xiàn)。

5. 持久化處理

然后我們繼續(xù)實現(xiàn)RegistrationController中的持久化邏輯。

@PostMapping("/user/registration")
public ModelAndView registerUserAccount(
        @ModelAttribute("user") @Valid UserDto userDto,
        HttpServletRequest request,
        Errors errors) {

    try {
        User registered = userService.registerNewUserAccount(userDto);
    } catch (UserExistException uaeEx) {
        ModelAndView mav = new ModelAndView();
        mav.addObject("message", "An account for that username/email already exists.");
        return mav;
    }

     return new ModelAndView("successRegister", "user", userDto);
}

在上面的代碼中我們可以發(fā)現(xiàn):

  • 我們創(chuàng)建了ModelAndView對象,該對象既可以保存數(shù)據(jù)也可以返回一個View。

常見的ModelAndView的三種用法

(1) new ModelAndView(String viewName, String attributeName, Object attributeValue);

(2) mav.setViewName(String viewName);

mav.addObejct(String attributeName, Object attributeValue);

(3) new ModelAndView(String viewName);

  • 在注冊的過程中如果產(chǎn)生任何報錯,將會返回到注冊頁面。

6. 安全登錄

在本節(jié)內(nèi)容中,我們將實現(xiàn)一個自定義的UserDetailsService,從持久層檢查登錄的憑證。

6.1 自定義UserDetailsService

讓我們從自定義UserDetailsService開始。

例6.1.1 MyUserDetailsService

@Service
@Transactional
public class MyUserDetailsService 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("No user found with username: " + email);
        }
        boolean enabled = true;
        boolean accountNonExpired = true;
        boolean credentialsNonExpired = true;
        boolean accountNonLocked = true;

        return new org.springframework.security.core.userdetails.User(
                user.getEmail(), user.getPassword().toLowerCase(), enabled, accountNonExpired,
                credentialsNonExpired, accountNonLocked, getAuthorities(user.getRoles()));
    }

    private static List<GrantedAuthority> getAuthorities (List<String> roles) {
        List<GrantedAuthority> authorities = new ArrayList<>();
        for (String role : roles) {
            authorities.add(new SimpleGrantedAuthority(role));
        }
        return authorities;
    }
}

6.2 開啟New Authentication Provider

然后,為了真正地能夠開啟自定義的MyUserDetailsService,我們還需要在SecurityConfig配置文件中加入以下代碼:

@Override
    protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(authProvider());
    }
復制代碼

限于篇幅,我們就不在這里詳細展開SecurityConfig配置文件。

7. 結語

至此我們完成了一個由Spring Boot實現(xiàn)的基本的用戶注冊過程。

到此這篇關于Spring Boot用戶注冊驗證實現(xiàn)的文章就介紹到這了,更多相關Spring Boot用戶注冊驗證內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Java中的CountDownLatch源碼解析

    Java中的CountDownLatch源碼解析

    這篇文章主要介紹了Java中的CountDownLatch源碼解析,CountDownLatch類是一個同步輔助裝置,允許一個或多個線程去等待直到另外的線程完成了一組操作,需要的朋友可以參考下
    2023-12-12
  • Java緩沖字符流的實現(xiàn)示例

    Java緩沖字符流的實現(xiàn)示例

    本文詳細介紹了Java緩沖字符流的使用,通過使用BufferedReader和BufferedWriter,可以更高效地處理文本數(shù)據(jù),提高讀寫效率,感興趣的可以了解一下
    2024-12-12
  • Aspectj框架實戰(zhàn)案例詳解

    Aspectj框架實戰(zhàn)案例詳解

    這篇文章主要介紹了Aspectj框架實戰(zhàn),結合具體案例形式詳細分析了Aspectj框架具體配置、使用、編譯等相關操作技巧,需要的朋友可以參考下
    2020-01-01
  • RestTemplate發(fā)送HTTP?GET請求使用方法詳解

    RestTemplate發(fā)送HTTP?GET請求使用方法詳解

    這篇文章主要為大家介紹了關于RestTemplate發(fā)送HTTP?GET請求的使用方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家<BR>33+多多進步
    2022-03-03
  • 如何使用@Slf4j和logback-spring.xml搭建日志框架

    如何使用@Slf4j和logback-spring.xml搭建日志框架

    這篇文章主要介紹了如何使用@Slf4j和logback-spring.xml搭建日志框架問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-06-06
  • java xml轉為json的n種方法

    java xml轉為json的n種方法

    本文給大家分享java xml轉為json的兩種方法,每種方法通過實例代碼給大家介紹的非常詳細,小編感覺第一種方法要比第二種方法好些,具體實現(xiàn)代碼跟隨小編一起看看吧
    2021-08-08
  • Java實現(xiàn)文件檢索系統(tǒng)的示例代碼

    Java實現(xiàn)文件檢索系統(tǒng)的示例代碼

    這篇文章主要為大家詳細介紹了如何劉Java語言實現(xiàn)簡易的文件檢索系統(tǒng),文中的示例代碼講解詳細,對我們學習Java開發(fā)有一定的幫助,需要的可以參考一下
    2022-07-07
  • 詳解MyBatis延遲加載是如何實現(xiàn)的

    詳解MyBatis延遲加載是如何實現(xiàn)的

    MyBatis 的延遲加載(懶加載)特性允許在需要使用關聯(lián)對象數(shù)據(jù)時才進行加載,而不是在執(zhí)行主查詢時就加載所有相關數(shù)據(jù),我們將通過以下幾個方面來深入了解MyBatis的延遲加載實現(xiàn)機制,需要的朋友可以參考下
    2024-07-07
  • SpringBoot實現(xiàn)任意位置獲取HttpServletRequest對象

    SpringBoot實現(xiàn)任意位置獲取HttpServletRequest對象

    這篇文章主要介紹了SpringBoot實現(xiàn)任意位置獲取HttpServletRequest對象,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Java 創(chuàng)建URL的常見問題及解決方案

    Java 創(chuàng)建URL的常見問題及解決方案

    這篇文章主要介紹了Java 創(chuàng)建URL的常見問題及解決方案的相關資料,需要的朋友可以參考下
    2016-10-10

最新評論

石景山区| 昭苏县| 会东县| 贞丰县| 东乡族自治县| 泰兴市| 祁阳县| 牡丹江市| 张北县| 阿荣旗| 凭祥市| 佳木斯市| 抚顺市| 兴安盟| 孟州市| 榆社县| 雷波县| 临潭县| 云龙县| 信宜市| 五寨县| 阿克陶县| 红河县| 乐都县| 彰武县| 廉江市| 马关县| 济南市| 五寨县| 荆州市| 商都县| 广汉市| 宁夏| 读书| 土默特右旗| 西丰县| 岳阳县| 库伦旗| 类乌齐县| 英山县| 鄄城县|