项目1在线交流平台-2.开发交流社区注册登录模块-2.SpringBoot开发注册功能模块

参考牛客网高级项目教程

整体功能实现

  • 对于业务需求比较大的网页,可以按照功能进行拆分设计
    • 也是按照请求来拆解设计

在这里插入图片描述

1. 访问注册页面

功能需求:

  • 点击顶部区域内的链接,打开注册页面。
  • 注册页面头部和尾部复用主页的页面
  • 点击首页,能够再次跳转回首页
在这里插入图片描述

功能实现

  • 只是为了显示表单信息,只需在视图层处理即可,没有modle需要封装

  • 需要处理以下信息:

  • Controller处理点击访问url,直接返回模板页面,因此可以设定为GET请求

    @Controller
    public class LoginController {
        // 注册页面的显示
        @RequestMapping(path = "/register", method = RequestMethod.GET)
        public String getRegisterPage() {
            return "/site/register";
        }
    }
    
    
th:href="@{/index}
th:fragment=“header”
th:replace=“index::header”
  • View处理: 将主页的头部和尾部复用,使用th引擎处理
    • url点击位置在index主页上,因此需要处理主页页面的url链接,用th控制
      • th:href="@{/index}
      • th:href="@{/register}"
    • 定义复用片段的别名:th:fragment=“header”
    • 替换要复用的片段:th:replace=“index::header”

2. 提交注册页面

功能需求

  • 用户通过表单提交数据。
  • 服务端验证账号是否已存在、邮箱是否已注册。
    • 注册成功后,
      • 将用户信息添加进数据库
      • 注册成功后,服务端向用户注册邮箱发送激活邮件
    • 注册失败:
      • 将错误信息封装,并在注册表单页面上显示错误提示信息

功能实现

2.1 Service层处理用户提交的表单数据
环境、工具类准备
commons-lang3包导入-StringUtils.isBlank(key)
  • 提供判断字符串、集合常用数据结构空值等其他情况

    • null,空字符串、空格均被判为空值
    <!--提供判断字符串、集合常用数据结构空值情况-->
    <dependency>
       <groupId>org.apache.commons</groupId>
       <artifactId>commons-lang3</artifactId>
       <version>3.9</version>
    </dependency>
    
配置域名
  • 注册邮箱时,需要向用户邮箱发送激活码,激活码中要拼接带有用户id,激活码信息

    • 因此需要将当前项目域名、项目名、功能名拼接进去
    • 提前在配置类中配置好,以便复用
    # community,域名
    community.path.domain=http://localhost:8080
    # url中的项目名
    server.servlet.context-path=/community
    
设计注册工具类-UUID,MD5

可以定义成静态方法,可以直接使用,无需交给SpringIOC容器管理

  • 1.生成随机字符串

    • 将"-" 替换成""

       // 生成随机字符串
          public static String generateUUID() {
              return UUID.randomUUID().toString().replace("-", "");
          }
      
  • 2.MD5加密

    • MD5实质上只能加密,不能解密,

      • 但简单字符串加密后可以破解,是查询了MD5密码库
      • 所以,为了增加安全性,将用户密码拼接上一个salt(随机字符串),再MD5加密,库中就查不到了
    • 调用Spring中自带的MD5加密16进制字符串方法

      • 不过,接受的参数为byte数组类型,需将字符串类型转换
      // MD5加密
      // hello -> abc123def456
      // hello + 3e4a8 -> abc123def456abc
      public static String md5(String key) {
          // 判空
          if(StringUtils.isBlank(key)) {
              return null;
          }
          // 调用Spring的工具类
          return DigestUtils.md5DigestAsHex(key.getBytes())
      }
      
server层处理业务逻辑
  1. 数据属性注入
  • 发邮件工具类

  • 模板引擎

  • url的域名、项目名,注意变量名不要用关键字context

    @Autowired
    private MailClient mailClient;
    
    @Autowired
    private TemplateEngine templateEngine;
    
    @Value("${community.path.domain}")
    private String domain;
    
    @Value("${server.servlet.context-path}")
    private String contextPth;	// 
    
  1. 注册业务方法
  • 返回值:user,用户信息

  • 接受参数:返回的注册错误信息

    /**
     * 注册业务
     * @param user,用户信息
     * @return  返回的注册错误信息
     */
    public Map<String, Object> register(User user) {
        Map<String, Object> falseMap = new HashMap<>();
        ...
        return falseMap;
    }
    
  • 内部逻辑:

2.1.验证信息
  • 空值处理
    • 账户、密码、邮箱
  • 与数据库比对验证
    • 账户不能重复
    • 邮箱不能重复
Map<String, Object> falseMap = new HashMap<>();
//        1.验证用户输入内容是否有问题
        // 用户对象不能为null
        if(user == null) {
            throw new IllegalArgumentException("注册用户不能为空");
        }

        // 内容不能为空
        // 用户名
        if(StringUtils.isBlank(user.getUsername())) {
            falseMap.put("userNameMsg", "用户名不能为空!");
            return falseMap;
        }
        // 密码
        if(StringUtils.isBlank(user.getPassword())) {
            falseMap.put("passwordMsg", "密码不能为空!");
            return falseMap;
        }
        // 邮箱
        if(StringUtils.isBlank(user.getEmail())) {
            falseMap.put("emailMsg", "邮箱不能为空!");
            return falseMap;
        }
2.2 信息入库
  • 验证无误,将用户信息入库,并向用户发送激活链接
// 先将密码进行MD5加密
String salt = CommunityUtil.generateUUID().substring(0, 5);
user.setSalt(salt);
user.setPassword(CommunityUtil.md5(user.getPassword() + salt));
// 其他状态设置
user.setType(0); // 普通用户
user.setStatus(0); // 未激活
String code = CommunityUtil.generateUUID();
user.setActivationCode(code); // 激活码
user.setCreateTime(new Date()); // 创建时间
user.setHeaderUrl(String.format(    // 用户头像,随机生成
        "http://images.nowcoder.com/head/%dt.png", new Random().nextInt(1000)));
// 入库
userMapper.insertUser(user);
2.3 发送激活邮件
  • 向注册用户邮箱发邮件
// 创建封装数据的Context,封装数据
Context context = new Context();    // thymeleaf模板中的Context,相当于model
context.setVariable("toMsg", user.getEmail());
// 拼接激活链接url,http://localhost:8080/community/activation/101/code
String url = domain + contextPth + "/activate" + "/" + user.getId() + "/" + code;
context.setVariable("urlMsg", url);

// 发送HTML邮件
// 利用模板生成动态网页,需将模板视图传过去
// 模板引擎自动识别context传入的参数,然后动态加载到网页中,
// 将网页的动态变量进行替换,并将网页内容加载到content中
String content = templateEngine.process("/mail/activation", context);
mailClient.sendMail(user.getEmail(), "交流网用户注册激活链接", content);
  • 处理激活邮件模板页面
  • 渲染链接:
    • 1.th:href="@{${urlMsg}}"
    • 2.th:href="${urlMsg}" 都行,但通过Controller传过去数据只能用第二种
<div>
   <p>
      <b th:text="${toMsg}">xxx@xxx.com</b>, 您好!
   </p>
   <p>
      您正在注册交流网, 这是一封激活邮件, 请点击
      <a th:href="@{${urlMsg}}">此链接</a>,
      激活您的社交账号!
   </p>
</div>

2.2 Controller层处理注册表单提交与信息反馈

请求方式、接受参数
  • 提交表单数据,用Post请求,路径与GET请求一样,但,请求方式不同,处理方式不同

  • 直接用User类接收表单数据

    • 属性名相同,Spring会自动将表单中对应属性值userName,password,email等属性值注入到user类中
     // 注册页面表单提交请求
        @RequestMapping(path = "/register", method = RequestMethod.POST)
        public String register(Model model, User user) {
            ...
            return "";
        }
    
对请求的接收与响应

接收提交表单数据,交给service处理后,返回给表单

  • 接收的user类中表单输入信息交给service层处理后,获取返回的map信息

  • 处理map信息,

    • 成功,封装激活中间页面需要的model信息:
      • 页面显示的提示信息和最终跳转的链接:
        • msg:提示注册成功,已经向用户发送了激活邮件,提醒用户尽快激活账户
        • target:一定时间后,自动跳转或立即手动跳转的指定链接
      • 跳转到激活中间页面
    • 失败,封装map错误信息,
      • 重新返回注册页面,并显示错误提示信息
    // 注册页面表单提交请求
    @RequestMapping(path = "/register", method = RequestMethod.POST)
    public String register(Model model, User user) {
        // 将接收的user类中表单输入信息交给service层处理后,获取返回的map信息
        Map<String, Object> falseMap = userService.register(user);
        
        // 前端显示处理这些信息
        if(falseMap == null || falseMap.isEmpty()) {
            return "/site/operate-result"; // 注册成功跳转到激活提示激活页面
        } else {
            model.addAttribute("userNameMsg", falseMap.get("userNameMsg"));
            model.addAttribute("passwordMsg", falseMap.get("passwordMsg"));
            model.addAttribute("emailMsg", falseMap.get("emailMsg"));
            return "/site/register"; // 返回注册表单页面
        }
    }
    

2.3 View视图模板页面处理

中间提示跳转页面模板
th:href="@{${target}}
  • thymeleaf自动识别并动态填入链接的url值

    <div class="jumbotron">
       <p class="lead" th:text="${msg}">您的账号已经激活成功,可以正常使用了!</p>
       <hr class="my-4">
       <p>
          系统会在 <span id="seconds" class="text-danger">8</span> 秒后自动跳转,
          您也可以点此 <a id="target" th:href="@{${target}}" class="text-primary">链接</a>, 手动跳转!
       </p>
    </div>
    
注册表单页面模板
th:action="@{/register}
  • 处理表头:method=“post” th:action="@{/register}

    <form class="mt-5" method="post" th:action="@{/register}">
    
name=“userName”
  • 处理表单中的每行标签内容:html中Input标签中,属性名一定要和User类中的属性名一致
    • 这样,Spring才能自动识别并注入数据到类中
th:text="${usernameMsg}"
is-invalid
  • 显示错误提示信息,将controller返回的model封装的错误信息渲染处理

  • 如果没有msg错误信息,则不显示,Bootstrap前端框架处理是在input标签的class选择器中处理

    • 因此需要"|静态 动态|"拼接,显示样式均交给css处理,放在class选择器中
    • Bootstrap中判断:is-invalid,输入内容非法,就将错误提示框显示出来,反之不显示
    <input type="text"
          th:class="|form-control ${usernameMsg != null ? 'is-invalid' : ''}|"
          th:value="${user != null ? user.userName : ''}"
          id="username" name="userName" placeholder="请输入您的账号!" required>
    <div class="invalid-feedback" th:text="${userNameMsg}">
       该账号已存在!
    </div>
    
th:value=""
  • 默认值的显示,如果反馈注册信息,将之前填入的值自动填入

  • 要动态判断,如果第一次访问没有默认值

    • user为null,再调用user的属性方法,会异常,故要判空处理
    th:value="${user != null ? user.userName : ''}"
    
邮件模板页面
  • 用thymeleaf模板引擎处理的,因此在service层处理完业务后,就处理模板页面,免得遗忘了

    <div>
       <p>
          <b th:text="${toMsg}">xxx@xxx.com</b>, 您好!
       </p>
       <p>
          您正在注册交流网, 这是一封激活邮件, 请点击
          <a th:href="@{${urlMsg}}">此链接</a>,
          激活您的社交账号!
       </p>
    </div>
    

测试

  • 结果显示
在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

3. 激活注册用户

功能需求

  • 点击邮件中的链接,访问服务端的激活服务
  • 将用户激活状态改变,并在页面显示激活的结果

功能实现

3.1 service层处理激活业务
设计静态常量接口
  • 设置项目中的静态常量接口,可以直接使用静态常量来表示激活信号

    public interface CommunityConstant {
        /**
         * 激活成功
         */
        int ACTIVATION_SUCCESS = 0;
    
        /**
         * 重复激活
         */
        int ACTIVATION_REPEAT = 1;
    
        /**
         * 激活失败
         */
        int ACTIVATION_FAILURE = 2;
    }
    
设计激活业务
  • 将从controller接收的用户id从库中查询指定用户

  • 根据指定用户的激活码与传入的激活码比对,处理不同结果

    • 激活成功:返回成功信号,并将指定用户的状态更新为已激活状态,static=1
    • 激活失败:
      • 用户已经激活,重复激活
      • 激活码不对,不能激活
    /**
     * 处理用户激活业务
     * @param userId 接收Controller解析url中的userID,定位指定用户
     * @param code  接收Controller解析url中的激活码
     * @return      返回激活状态,用静态变量展示
     */
    public int activation(int userId, String code) {
        // controller接收的用户id从库中查询指定用户
        User user = userMapper.selectById(userId); 
        // 指定用户的激活码与传入的激活码比对
        if (user.getStatus() == 1) {   // 重复激活
            return ACTIVATION_REPEAT;
        } else if (user.getActivationCode().equals(code)) { // 激活码匹配成功
            userMapper.updateStatus(userId, 1); // 更新用户状态,激活成功
            return ACTIVATION_SUCCESS;
        } else {    // 激活码不匹配,不能激活
            return ACTIVATION_FAILURE;
        }
    }
    
3.2 Controller层接收和响应请求
path = “/activate/{userId}/{code}”
  • 请求路径:为之前发送到邮件的激活链接url,RestFul风格拼接的字符串
  • 请求方式为Get请求,浏览器直接访问POST请求不被支持
@PathVariable(“userId”) int userId
  • 解析RestFul风格拼接的字符串中后面拼接的路径名的值

    // http://localhost:8080/community/activation/101/code
    @RequestMapping(path = "/activate/{userId}/{code}", method = RequestMethod.GET)
    public String activation(Model model, 
                             @PathVariable("userId") int userId, 
                             @PathVariable("code") String code) {
        ...
        return "/site/operate-result";
    }
    
请求接收响应处理逻辑
  • 接收用户传入的url,并将路径解析

    • 将userId,code解析出来后交给service处理
  • 接收service反馈的信息

    • 将信息转为跳转中间页面需要的msg、target封装,交给中间页面渲染显示给用户浏览器
    // http://localhost:8080/community/activation/101/code
    @RequestMapping(path = "/activate/{userId}/{code}", method = RequestMethod.GET)
    public String activation(Model model, 
                             @PathVariable("userId") int userId, 
                             @PathVariable("code") String code) {
        // 将userId,code解析出来后交给service处理,
        // 接收service反馈的信息
        int result = userService.activation(userId, code);
        if (result == ACTIVATION_SUCCESS) { // 激活成功,跳转到登录页面
            model.addAttribute("msg", "激活成功,您的账号已经可以正常使用了,请登录!");
            model.addAttribute("target", "/login");
        } else if (result == ACTIVATION_REPEAT) { // 重复激活
            model.addAttribute("msg", "无效操作,该账号已经激活过了!");
            model.addAttribute("target", "/index");
        } else {
            model.addAttribute("msg", "激活失败,您提供的激活码不正确!");
            model.addAttribute("target", "/index");
        }
        return "/site/operate-result";
    }
    
3.2 View层处理模板页面
  • 主要处理激活成功后要跳转的登录模板页面
    • 加上thymeleaf模板引用即可
  • 中间跳转模板页面,前面注册时已经处理好了

测试:

image-20220303115726302 image-20220303115746372
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值