整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

web开发源代码案例2-注册页面

web开发源代码案例2-注册页面


果图

1、整体的框架用了表格标签,可以让内容排列整齐

2、列表标签<ul><li>

3、表单标签

<input type="radio">单选

<input type="checkbox">多选

<input type="text">文本填写

<textarea>文本框

<select>里面一定要有一个<option>下拉选项

属性:checked="checked" 默认

. 概述

在这篇文章中,我们将使用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 registerUserAccount

public 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<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();
    }
}

现在让我们在我们的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<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());
    }

}

现在,将@IsPasswordMatching注解应用到我们的UserDto对象。

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

4.4 检查该账户是否已经存在

我们要实现的第四个检查是验证该电子邮件帐户在数据库中是否已经存在。

这是在表单被验证后进行的,我们把这项验证放在了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层,那么只要做增删改,就要提交一次事务,那么事务的特性就发挥不出来,尤其是事务的一致性。当出现并发问题的时候,用户从数据库查到的数据都会有所偏差。

一般的时候,我们的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<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配置文件。至此我们完成了一个由Spring Boot实现的基本的用户注册过程。

作者:翊君
链接:https://juejin.cn/post/7051279571341017101
来源:稀土掘金

读提示:本文纯干货,无任何废话,SDS申请步骤需反复阅读,建议现在收藏本文章,避免日后迷路!

一.SDS介绍

根据IRCC描述,SDS(Student Direct Stream)是申请加拿大学习签证快速通道,主要针对包括中国在内的一些发展中国家的学生,要求本人递交申请时需要在国内,且录取学校在DLI列表中(Designated learning institutions官方认可的学校)。SDS获批率为75%-90%,申请费用150CAD。

二.SDS申请资格

以下条件必须全部满足:

(1).申请人必须在中国境内递交申请

(2).持有DLI列表中院校的offer

(3).缴纳首年学费证明

(4).GIC证明(至少1万CAD GIC存款证明)

(5).无犯罪记录证明

(6).最近的中学或大学成绩单

(7).满足语言成绩(雅思6.0分、托福ibt 83分、CAEL60分、PTE Academic 60分以及法语TEF和TCF考试成绩)

点击查看官网原文IRCC Student Direct Stream

三.SDS材料准备

*以下所有材料均提供pdf电子版

  1. Offer Letter
  2. 学费缴费证明
  3. GIC证明
  4. 预体检回执
  5. 成绩单/毕业证/学位证
  6. 英语成绩
  7. 无犯罪记录
  8. 身份证明
  9. 护照
  10. 签证照
  11. 学习计划
  12. 其它资料

3.1 Offer Letter

加拿大DLI院校出具的正式offer,提交PDF电子版。点击查询DLI学校列表。

如果录取院校不在列表中,则不能申请SDS快速通道,只能申请普通学签。

3.2 学费缴费证明

第一年学费的缴纳证明可选以下方式之一,提交英文PDF电子版。

  • DLI出具的学费缴纳缴费回执
  • 学费缴纳确认letter
  • 缴纳学费银行出具的收据回执
  • 本人在DLI院校的学生账户余额证明

如果学校未要求预先缴纳学费,可以发邮件联系学校说明办理SDS签证需要提前缴纳首年学费,大部分学校都会接受提前缴纳学费,并提供缴费证明。通常缴费信息都会在学校网站登录学生账户后进行查看和下载。

3.3 GIC证明

GIC(Guaranteed Investment Certificate)银行开具1万+的一年期存款证明,SDS支持多家银行的GIC证明,推荐加拿大五大行GIC。一方面是因为入境加拿大后需要开本地银行卡和信用卡,融入本地生活积累信用。另一方面是因为这些银行在加拿大网点众多,办理业务非常方便。

根据个人经验,推荐办理加拿大五大行的CIBC(加拿大帝国商业银行)的GIC。CIBC办理GIC的费用和其它银行相当,但是处理速度很快,快点的话7天就能办好。CIBC在加拿大可以预约中文柜员和在线客服,网点分布非常广泛,涵盖绝大多数偏远地区。

这是我写的GIC办理流程,点击查看(GIC办理流程)。

3.4 预体检回执

申请加拿大学签需要进行体检,体检中心可以点击此处查看IRCC认可的中国地区体检中心,我是在北京,选择的崇文门体检中心,费用是1670。体检结束后体检中心会打印几张盖章的回执,将回执扫描为电子版PDF。

这是我写的体检流程,点击查看(体检流程)。

3.5 成绩单/毕业证/学位证

如果是高中毕业申请本科/大专,需要提交高中成绩单。

如果是专科、本科或硕博毕业,需要提交所有大学期间的成绩单,不需要高中成绩单。

成绩单需提供中英双版,如果没有英文版,可以TB或PDD搜索盖章翻译。盖章翻译后合并为一个PDF文件。

建议一并提供毕业证学位证的中英文版,推荐提供WES电子报告。WES申请:World Education Services: International Credential Evaluation for U.S.World Education Services: International Credential Evaluation for U.S.

3.6 英语成绩

上传符合SDS最低要求的英语测试成绩单

最低要求:雅思6.0分、托福ibt 83分、CAEL60分、PTE Academic 60分以及法语TEF和TCF考试成绩

以雅思为例,上传雅思成绩单带个人照片页的pdf扫描电子版。

3.7 无犯罪记录

在户籍所在地或工作地派出所开具近10年或18岁后的无犯罪记录证明,开具后需办理公证。然后扫描无犯罪记录证明原件和公正件,合并成PDF电子版。

大部分地区都能网上申请办理,以北京天津为例,可以在京通小程序、津心办APP在线申请,最快当天就能获取电子版无犯罪记录证明。

3.8 身份证明

提交时需要提交本人公民身份材料,强烈建议办理出生公证、户口本公证和身份证公证。如果没有《出生医学证明》需联系户籍所在地开具出生证明,均需公证处公证。将原件扫描和公正书扫描件合并成一个pdf文件提交。

如果公证办理有任何疑问,可以找我索要我当时的官方办理机构,费用低、要求低。

涉及毕业后毕业工签或后期申请加拿大永居,或者涉及配偶工签或父母未来办理探亲签等情况,办理以上公证后可以省去后期再跨国办理的麻烦,也为未来其它申请办理铺好路。

3.9 护照

扫描个人新旧护照扫描件,需扫描首页以及所有盖章或签证页,合并成pdf电子版,无需翻译。

3.10签证照片

可以选择周边打印店或快照店拍照,拍照时告知加拿大签证照,一般都会知道签证照拍摄要求。

照片尺寸至少35x45mm,纯白背景,近6个月内拍照。

3.10学习计划

3.10其它可选材料

以下材料如有建议尽可能一并提供:(需盖章翻译/公证)

增加资金证明可以提供:存款证明、工资流水、纳税证明、社保证明、房产证明

提升回国约束力可以提供:结婚证公证、在职证明

四、注册GCKey账户

个人申请加拿大签证首先需要在IRCC官网注册申请个人账号,用于管理自己的申请,官网申请入口:https://www.canada.ca/en/immigration-refugees-citizenship/services/application/account.html

注册GCKey请参考我另一篇文章。

五、填写表格

六、提交申请

点赞+关注,查看后续...