范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

SpringBoot实现用户注册验证全过程

  1. 概述
  在这篇文章中,我们将使用Spring Boot实现一个基本的邮箱注册账户以及验证的过程。
  我们的目标是添加一个完整的注册过程,允许用户注册,验证,并持久化用户数据。2. 创建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的区别
  @NotNull: 适用于CharSequence, Collection, Map 和 Array 对象,不能是null,但可以是空集(size = 0)。
  @NotEmpty: 适用于CharSequence, Collection, Map 和 Array 对象,不能是null并且相关对象的size大于0。
  @NotBlank: 该注解只能作用于String类型。String非null且去除两端空白字符后的长度(trimmed length)大于0。
  在下面的章节里,我们还将自定义注解来验证电子邮件地址的格式以及确认二次密码。3. 实现一个注册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时,它创建了新的UserDto对象,将其绑定在Model上,并返回了注册页面registration.html。Model 对象负责在控制器Controller和展现数据的视图View之间传递数据。
  实际上,放到 Model 属性中的数据将会复制到 Servlet Response 的属性中,这样视图就能在这里找到它们了。
  从广义上来说,Model 指的是 MVC框架 中的 M,即 Model(模型)。从狭义上讲,Model 就是个 key-value 集合。4. 验证注册数据
  接下来,让我们看看控制器在注册新账户时将执行的验证:所有必须填写的字段都已填写且没有空字段该电子邮件地址是有效的密码确认字段与密码字段相符该账户不存在4.1 内置的验证
  对于简单的检查,我们将使用@NotBlank来验证DTO对象。
  为了触发验证过程,我们将在Controller中用@Valid注解来验证对象。
  例4.1 registerUserAccountpublic ModelAndView registerUserAccount(@ModelAttribute("user") @Valid UserDto userDto,   HttpServletRequest request, Errors errors) {     //... }4.2 自定义验证以检查电子邮件的有效性
  下一步,让我们验证电子邮件地址,以保证它的格式是正确的。我们将为此建立一个自定义验证器,以及一个自定义验证注解--IsEmailValid。
  下面是电子邮件验证注解IsEmailValid和自定义验证器EmailValidator:为什么不使用Hibernate内置的@Email?
  因为Hibernate中的@Email会验证通过XXX@XXX之类的邮箱,其实这是不符合规定的。
  感兴趣的读者朋友可以移步此处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 {     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,})#34;;     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();     } }
  现在让我们在我们的UserDto实现上使用新注解。@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对象来执行验证。
  例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 {      @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());     }  }
  现在,将@IsPasswordMatching注解应用到我们的UserDto对象。@IsPasswordMatching public class UserDto {     //... }4.4 检查该账户是否已经存在
  我们要实现的第四个检查是验证该电子邮件帐户在数据库中是否已经存在。
  这是在表单被验证后进行的,我们把这项验证放在了UserService。
  例4.4.1 UserServicepackage 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层,那么只要做增删改,就要提交一次事务,那么事务的特性就发挥不出来,尤其是事务的一致性。当出现并发问题的时候,用户从数据库查到的数据都会有所偏差。
  一般的时候,我们的Service层可以调用多个DAO层,我们只需要在Service层加一个事务注解@Transactional,这样我们就可以一个事务处理多个请求,事务的特性也会充分地发挥出来。
  UserService依靠UserRepository类来检查数据库中是否已存在拥有相同邮箱的用户账户。当然在本文中我们不会涉及到UserRepository的实现。5. 持久化处理
  然后我们继续实现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); }
  在上面的代码中我们可以发现:
  1、我们创建了ModelAndView对象,该对象既可以保存数据也可以返回一个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);
  2、在注册的过程中如果产生任何报错,将会返回到注册页面。6. 安全登录
  在本节内容中,我们将实现一个自定义的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 getAuthorities (List roles) {         List 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配置文件。至此我们完成了一个由Spring Boot实现的基本的用户注册过程。
  作者:翊君
  链接:https://juejin.cn/post/7051279571341017101

别小瞧比亚迪,下半年几款重磅新车一旦到来,有机会够到一线门槛刚进9月,比亚迪就急戳戳公布了8月份销量,没出意外,当月数据依然能打,特别是新能源车板块,超6万辆成绩笑傲一众国产车品牌。比亚迪新能源确实很出彩儿,但总体上还是跟一线自主存在一定差比特币等虚拟货币的去中心化只是一种骗局点亮好奇心大家都知道比特币等区块链的货币,也知道区块链宣传一种去中心化。但是大家不知道,其实任何虚拟货币还是集中在少数人手中。大家经常会看到一些报道说谁是比特币的大佬,虚拟货币的大五天爆红的Loot到底是什么?又引起一波NFT浪潮最近各种关于Loot的信息刷屏,毫无征兆。社交媒体网络Vine联合创始人DomHofmann的NFT项目Lootforadventures仅用了5天时间,就吸引了4600万美元的销中国第1个万亿市值车企将诞生,新能源车大战,丰田已输掉比赛?不得了哈,中国首个万亿市值车企或将诞生,8月4日,比亚迪市值已狂飙至8815亿元,离万亿市值,只差最后的临门一脚。一个曾经被众多国人看不起的汽车品牌,为啥现在这么生猛呢?三个字新能原来2。0T只是开胃菜,纯电动大G亮相,奔驰发布EQG概念车当传统豪华品牌开始发力纯电动车型,你不会想到他们会有多彻底,G级作为一款硬派越野车,在诞生的几十年里一直都凭借优秀的可靠性和越野性能作为卖点,而在推出2。0T的版本虽然有争议,但是Iphone的刘海屏,库克的刘海头。乔帮主留下了一代传奇iphone4,然后库克又在iphone上做了什么改变呢,iphoneiphone612,库克在每一代产品都进行了或多或少的变动。比如iphone5增加了屏幕尺马斯克为什么要砸钱做机器人,将成为全球最便宜劳动力?埃隆马斯克的工业机器人,有一天会不会把人类从,令人厌烦的体力劳动中解放出来,他当然是这么想的,而且在这个过程中,这位特斯拉的超级大佬认为,他可以解决人工智能中的一些新问题,尤其是如买手机选6GB运存还是8GB?12GB到底有没有必要?手机运存是一项非常关键的配置参数,尤其是对于安卓手机而言。一般来说,手机运存越大,也就意味着手机可以同时运行更多的软件,并且不会影响到手机的流畅性。图片来源于网络如此一来,机友们或又见套捐!大量公益组织在薅羊毛?中国社会福利基金会寒了谁的心作者古原,独立经济学者1hr又见套捐。9月5日,腾讯公益拿出一亿元来配套社会组织捐赠。中国社会福利基金会旗下的中国家庭儿童预防烧烫伤干预项目的一位负责人,公然在网上发动网友套捐,打手机多久一换最合适?懂行老师傅说得很直白由于手机行业每个月基本都有新机发布,那我们很多机友也时时刻刻在遭遇着换机的诱惑。新手机的性能外观以及黑科技实在太能打了,分分钟就想让我们换掉手里的老手机。然而心有余力不足,咱们的钱开学季,适合学生党的几款手机第一款红米Note10pro月魄色康宁大猩猩保护玻璃Victus目前只在顶级旗舰小米11三星S21上使用,搭配LCD屏幕,将耐摔进行到底!67W快充5000mAh电池,42分钟充到
电脑显示器能改装成电视吗?家里闲置的电脑显示器只有显示功能,通过改造是可以用来作为小电视使用的,作为餐厅或者卧室电视是不错的选择。有两种方式,方式1使用机顶盒连接,方式2连接MTVBox相当于给显示器连接了可以推荐中学生用的性价比高的笔记本电脑吗?1。戴尔XPS13戴尔再次提高游戏水平,并提供世界上最好的学生笔记本电脑配置CPU第8代IntelCorei5i7显示卡IntelUHDGraphics620内存4GB16GB屏幕1978年他用4200元买下一台电脑,43年后价格涨到960万大家相信吗?1978年,一位加拿大人用约4200元的价格买下一台电脑,然而43年后,这台电脑的价格居然涨到了960万,听起来可能有些匪夷所思,然而,这事在现实生活中却确确实实地发生华为将推出墨水屏平板电脑功耗低可护眼据报道,微博上有相当多的数码博主爆料华为将要推出一款墨水屏平板电脑,而联系到9月华为公开招聘测试人员,参加用眼及电子阅读器吐槽分享大会,以及前不久的墨水屏阅读器线下体验活动,引得众电脑组装好后,店家说加399安装一个正版系统,能不能干?现在组装电脑的价格也相对比较透明了,装机店遇到比较懂行的人组装电脑一般都没有多大利润,最多也就赚个装机辛苦费而已,但是运气好,要是碰到不太懂电脑的人,那装机店可就要吃肉了,硬件差价预算3000左右组一台电脑,固态512,内存16,不玩游戏办公用,求大佬推荐一下配置?怎么配比较好?文小伊评科技3000左右的预算,基础需求是512GB的固态以及16G的内存,这个预算和需求还是很靠谱的,准确的把握到了办公电脑的真实痛点,看来题主还是很懂得。3000元这个预算对于阿里巴巴国际站运营经验总结在分享之前,说几个我自己的观点运营永远都是选择大于努力,其实做那么久的运营,才真的发现选择比努力重要多了。在我们选择进入一个行业,又或者运营找工作的时候,好的行业,确实事半功倍,而Go实现Nginx加权轮询算法一,Nginx负载均衡的轮询(roundrobin)在说加权轮询之前我们先来简单的说一下轮询转自httpssegmentfault。coma1190000039799210整理地鼠这个新思路你知道吗?javascript中的多种进制与进制转换进制介绍JavaScript中提供的进制表示方法有四种十进制二进制十六进制八进制。对于数值字面量,主要使用不同的前缀来区分十进制(Decimal)取值数字09不用前缀。二进制(BiLinux平台Makefile文件的编写基础篇目的基本掌握了make的用法,能在Linux系统上编程。环境Linux系统,或者有一台Linux服务器,通过终端连接。一句话有Linux编译环境。准备准备三个文件file1。c,f用微信语音取代打电话,真的合适吗?从使用比例来看,使用微信语音的占比越来越高,电话越来越低。但是从目前的形势来看,没有微信语音可能取代电话的迹象存在。从大的方面说,电话是只有运营商才允许插手的,大家熟悉的三大运营商