• 【JavaWeb项目】一个众筹网站的开发(四)后台用户注册功能


    重点:

    • 密码加密存储
    • 使用jQuery插件做校验和错误提示等

    密码不能明文存储,在数据库中是加密存储的

    • 可逆加密:通过密文使用解密算法得到明文 DES AES
    • 不可逆加密:通过密文,得不到明文 MD5 SHA-1 SHA-2 

    MD5优点:压缩性,长度固定;容易计算;抗修改性;强抗碰撞;MD5加盐值加密码

    一、公司的工具类中加入MD5

    project-commons下

     创建MD5Util.java

    package com.atguigu.project;
    
    import java.security.MessageDigest;
    
    public class MD5Util {
        public static String digest16(String inStr) {  
            return digest(inStr, 16);
        }
        public static String digest(String inStr) {  
            return digest(inStr, 32);
        }  
        private static String digest(String inStr, int rang) {  
            MessageDigest md5 = null;  
            if ( MyStringUtils.isEmpty(inStr) ) {
                return "";
            }
          
            try {
                md5 = MessageDigest.getInstance("MD5");  
            } catch (Exception e) {  
                e.printStackTrace();  
                return "";  
            }
            
            char[] charArray = inStr.toCharArray();  
            byte[] byteArray = new byte[charArray.length];  
          
            for (int i = 0; i < charArray.length; i++) {
                byteArray[i] = (byte) charArray[i]; 
            }
            
            byte[] md5Bytes = md5.digest(byteArray);  
            
            StringBuilder hexValue = new StringBuilder();  
          
            for (int i = 0; i < md5Bytes.length; i++) {  
                int val = ((int) md5Bytes[i]) & 0xff;  
                if (val < 16)  
                    hexValue.append("0");  
                hexValue.append(Integer.toHexString(val));  
            }  
            if ( rang == 32 ) {
                return hexValue.toString(); 
            } else {
                return hexValue.toString().substring(8, 24);
            }
        }  
    
        public static void main(String args[]) {  
            String s = new String("admin");
            System.out.println(digest(s));
        }  
    }

    MyStringUtils

    package com.atguigu.project;
    
    public class MyStringUtils {
    
        public static boolean isEmpty(String str) {
            if (str == null) {
                return true;
            }
            if (str.trim().equals("")) {
                return true;
            }
            return false;
        }
    }
    业务中使用MD5加密:
    1.将用户输入的用户密码拿到
    2.去数据库查询用户,使用用户带来的用户名和加密后的密码进行查询

    二、使用jQuery插件做校验和错误提示

    jQuery Validate 插件为表单提供了强大的验证功能,让客户端表单验证变得更简单,同时提供了大量的定制选项,满足应用程序各种需求。该插件捆绑了一套有用的验证方法,包括 URL 和电子邮件验证,同时提供了一个用来编写用户自定义方法的 API。所有的捆绑方法默认使用英语作为错误信息,且已翻译成其他 37 种语言。

    该插件是由 Jörn Zaefferer 编写和维护的,他是 jQuery 团队的一名成员,是 jQuery UI 团队的主要开发人员,是 QUnit 的维护人员。该插件在 2006 年 jQuery 早期的时候就已经开始出现,并一直更新至今。目前版本是 1.14.0

    访问 jQuery Validate 官网,下载最新版的 jQuery Validate 插件

    使用:引入jQuery和jQuery Validate
    <script type="application/javascript" src="yourpath/jquery-3.2.1.min.js"></script> 
    <script type="application/javascript" src="yourpath/jquery.validate.js"></script>

    rules指定校验规则
    messages指定报错信息

    举例:
    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8" />
            <title></title>
            <script type="application/javascript" src="js/jquery-3.2.1.min.js"></script>
            <script type="application/javascript" src="plugin/jquery-validation-1.13.1/dist/jquery.validate.js"></script>
        </head>
        <body>
            <form class="cmxform" id="commentForm" method="get" action="">
                <fieldset>
                    <legend>Please provide your name, email address (won't be published) and a comment</legend>
                    <p>
                        <label for="cname">Name (required, at least 2 characters)</label>
                        <input id="cname" name="name" minlength="2" type="text" required>
                    </p>
                    <p>
                        <label for="cemail">E-Mail (required)</label>
                        <input id="cemail" type="email" name="email" required>
                    </p>
                    <p>
                        <label for="curl">URL (optional)</label>
                        <input id="curl" type="url" name="url">
                    </p>
                    <p>
                        <label for="ccomment">Your comment (required)</label>
                        <textarea id="ccomment" name="comment" required></textarea>
                    </p>
                    <p>
                        <input class="submit" type="submit" value="Submit">
                    </p>
                </fieldset>
            </form>
        </body>
            <script>
        $.validator.setDefaults({
            submitHandler: function() {
                alert("submitted!");
            }
        });
        
        $().ready(function() {
        
            $("#commentForm").validate({
                rules: {
                    name: "required",
    
                    email: {
                        required: true,
                        email: true
                    }
                },
                messages: {
                        name:{
                            required:"名字是必须的",
                            },
                        email: {
                            required: "邮箱是必须的",
                            email: "邮箱格式必须是合法的"
                        }
                    
                }
            });
    
        });
        </script>
    </html>

    rules指定规则,name: "required"意为name是必须的,当有多条规则的时候使用大括号,email字段是必须的,且要检查email格式

    rules: {
            name: "required",
    
            email: {
                    required: true,
                    email: true
                   }
           }

    messages是错误信息,与上面的rules相对应

    messages: {
         name:{
               required:"名字是必须的",
              },
         email: {
                  required: "邮箱是必须的",
                  email: "邮箱格式必须是合法的"
                }    
             }

     

    三、用户登陆和注册界面

    将css和js的引入统一放到WEB-INF的includes文件夹下,所有页面共用的css和js聚集在一起,各页面独有的则存放在各个页面

    js-file.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <script src="${ctp}/jquery/jquery-2.1.1.min.js"></script>
    <script src="${ctp}/plugin/bootstrap-3.3.7/js/bootstrap.min.js"></script>
    <script src="${ctp}/script/docs.min.js"></script>
    <script src="${ctp}/script/back-to-top.js"></script>
    <!-- --------随着项目不断增加 -->

      <script src="${ctp}/plugin/jquery-validation-1.13.1/dist/jquery.validate.min.js"></script>

    css-file.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <link rel="stylesheet" href="${ctp}/plugin/bootstrap-3.3.7/css/bootstrap.min.css">
    <link rel="stylesheet" href="${ctp}/css/font-awesome.min.css">
    <link rel="stylesheet" href="${ctp}/css/login.css">
    <link rel="stylesheet" href="${ctp}/css/main.css">

    在各页面需要引入js和css的地方分别使用:
    <%@include file="/WEB-INF/includes/js-file.jsp" %>
    <%@include file="/WEB-INF/includes/css-file.jsp" %>

    JSP指令(directive)是为JSP引擎而设计的,它们并不直接产生任何可见输出,而只是告诉引擎如何处理JSP页面中的其余部分。
    JSP指令的基本语法格式:
    <%@ 指令 属性名="值" %>

    include指令
    include指令用于通知JSP引擎在翻译当前JSP页面时将其他文件中的内容合并进当前JSP页面转换成的Servlet源文件中,这种在源文件级别进行引入的方式称之为静态引入,当前JSP页面与静态引入的页面紧密结合为一个Servlet。
    语法:
    <%@ include file="relativeURL"%>
    其中的file属性用于指定被引入文件的相对路径。

    在index.jsp中配置登陆和注册页面的url

    <ul class="nav navbar-nav navbar-right">
        <li><a href="${ctp}/login.jsp">登录</a></li>
        <li><a href="${ctp}/reg.jsp">注册</a></li>
    </ul>

    同时也配置login.jsp和reg.jsp中的相关链接

    四、管理员用户注册

    注册的流程:

    来到注册页面--->输入表单内容--->js插件校验--->来到控制器处理注册请求--->调用UserService的注册方法--->UserMapper给数据库中保存东西--->注册完跳转到管理员控制台

    注册页面找到注册表单

    表单有action指向

    method是post

     <form id="regForm" class="form-signin" role="form" action="${ctp }/permission/user/reg" method="post">
           ...
            </div>
            <a id="submitBtn" class="btn btn-lg btn-success btn-block" > 注册</a>
          </form>
        </div>
      <%@include file="/WEB-INF/includes/js-file.jsp" %>
      <script type="text/javascript">
      $("#submitBtn").click(function () {
        $("#regForm").submit();
        return false;
    });
      
      </script>

    action 属性规定当提交表单时,向何处发送表单数据

    action="${ctp }/permission/user/reg",因此点击提交后会将表单数据发送给/manager-web/permission/user/reg

    在web控制器里

    分层,后台有很多功能

     controller中根据功能分包

    权限

    业务审核

    业务管理

    参数管理

    我们将处理上面注册请求的功能放在permission下,接收并响应上面的请求

    处理注册请求

    转载新闻 超详细 Spring @RequestMapping 注解使用技巧

    项目部属对应的path

    从上面可知webapp在根路径下

    @RequestMapping("/permission/user") 正是上面发送数据的地址

    package com.atguigu.scw.manager.controller.permission;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    import com.atguigu.scw.manager.bean.TUser;
    
    //处理所有/permission/user下的请求
    @RequestMapping("/permission/user")
    @Controller
    public class UserController {
    
        private String manager = "manager/";
        
        /**
         * 
         */
        // /permission/user/reg
        @RequestMapping("/reg")
        public String reg(TUser user) {
            System.out.println("用户注册...");
            // 1.注册成功后来到控制面板页面
            return "manager/main";
        }
    }

    return "manager/main" 

    实际访问的是/manager-web/WEB-INF/jsps/manager/main.jsp

    为什么能访问到呢?因为我们在SpringMVC中配置了解析器

    <!--2、视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
         <property name="prefix" value="/WEB-INF/jsps/"></property>
         <property name="suffix" value=".jsp"></property>
    </bean>

    controller方法返回字符串可以指定逻辑视图名,通过视图解析器解析为物理视图地址。因此,根据上面的前后缀配置,manager/main解析为/manager-web/WEB-INF/jsps/manager/main.jsp

    控制台页面不是直接能够访问的,不放在webapp,放在WEB-INF的jsps下的manager中

    manger存放管理员的页面

    下面又包含permission、audi、serviceman、system文件夹,分别对应权限管理、业务审核、业务管理、参数管理

    将main.jsp放在manager下

     五、表单校验

    设置下注册页面的表单

    校验插件拷贝到plugin文件夹下

     修改reg.jsp

    ... 
    <div class="container">
    
          <form id="regForm" class="form-signin" role="form" action="${ctp }/permission/user/reg" method="post">
            <h2 class="form-signin-heading"><i class="glyphicon glyphicon-log-in"></i> 用户注册</h2>
              <div class="form-group has-success has-feedback">
                <input type="text" name="loginacct" class="form-control" 
                id="loginacct_input" 
                placeholder="请输入登录账号" autofocus>
                <span class="glyphicon glyphicon-user form-control-feedback"></span>
              </div>
              <div class="form-group has-success has-feedback">
                <input type="password" name="userpswd" class="form-control" 
                id="userpswd_input" 
                placeholder="请输入登录密码" style="margin-top:10px;">
                <span class="glyphicon glyphicon-lock form-control-feedback"></span>
              </div>
              <div class="form-group has-success has-feedback">
                <input type="text" name="email" class="form-control" 
                id="email_input" placeholder="请输入邮箱地址" style="margin-top:10px;">
                <span class="glyphicon glyphicon glyphicon-envelope form-control-feedback"></span>
              </div>
              <div class="form-group has-success has-feedback">
                <select class="form-control" >
                    <option>会员</option>
                    <option>管理</option>
                </select>
              </div>
            <div class="checkbox">
              <label>
                忘记密码
              </label>
              <label style="float:right">
                <a href="${ctp}/login.jsp">我有账号</a>
              </label>
            </div>
            <a id="submitBtn" class="btn btn-lg btn-success btn-block" > 注册</a>
          </form>
        </div>
      <%@include file="/WEB-INF/includes/js-file.jsp" %>
      <script type="text/javascript">
      $("#submitBtn").click(function () {
        $("#regForm").submit();
        return false;
    });
      
      $("#regForm").validate({
          rules:{
              loginacct:{
                  required: true,
                  minlength: 6
              },
              userpswd: {
                  required: true,
                  minlength: 6
              },
              email: {
                  required: true,
                  email: true
              }
          },
          messages:{
              loginacct:{
                  required: "请填写用户账号",
                  minlength: "登陆账号必须是6位以上"
              },
              userpswd: {
                  required: "请填写密码",
                  minlength: "密码必须是6位以上"
              },
              email: {
                  required: "请填写邮箱",
                  email: "请输入正确的邮箱"
              }
          }
      });
      
      </script>
    ...

    运行

     

     可以发现图标因为显示提示信息位置有点错乱

    可以想办法把提示信息放在图标的下边

    自定义错误显示

     <script type="text/javascript">
      
    //给校验器设置一些策略
        $.validator.setDefaults({
            showErrors:function(map, list) {
                //console.log(map);
                //console.log(list);
                //将每个元素自己的错误信息显示在自己的图标下
                //先清除所有错误;清状态
                $(".errorinfo").empty();
                $(".form-group").removeClass("has-success has-error has-warning")
                
                $.each(list,function(){
                    //当前发生错误的详细信息;
                    //element当前错误元素
                    //错误信息
                    $(this.element).nextAll(".errorinfo").text(this.message);
                    //改变input的状态;
                    $(this.element).parent("div.form-group").addClass("has-error")
                    
                    
                })
            }
        });
    ...

    showErrors:function(map, list) 

    map和list

    map封装了每个字段的错误信息 

    element是错误信息的元素

    在图标下面加一个span用来显示错误信息

    <span class="errorinfo" style="color: red"></span>

    bootstrap 中的 表单

          <form>
           
              <div class="form-group has-success has-feedback">
                <input type="text" class="form-control" name="loginaccount" id="login_account" placeholder="请输入登录账号" autofocus>
                <span class="glyphicon glyphicon-user form-control-feedback"></span>
                  <span class="errorinfo" style="color: red"></span>
              </div>
              <div class="form-group has-success has-feedback">
                <input type="text" class="form-control" name="loginpassword" id="login_password" placeholder="请输入登录密码" style="margin-top:10px;">
                <span class="glyphicon glyphicon-lock form-control-feedback"></span>
                  <span class="errorinfo" style="color: red"></span>
              </div>
              <div class="form-group has-success has-feedback">
                <input type="text" class="form-control" name="loginemail" id="login_email" placeholder="请输入邮箱地址" style="margin-top:10px;">
                <span class="glyphicon glyphicon glyphicon-envelope form-control-feedback"></span>
                  <span class="errorinfo" style="color: red"></span>
              </div>
          </form>

    1.Textual form controls—like <input>s, <select>s, and <textarea>s—are styled with the .form-control class.

    2.表单状态验证

    Bootstrap 对表单控件的校验状态,如 error、warning 和 success 状态,都定义了样式。
    使用时,添加 .has-warning.has-error 或 .has-success 类到这些控件的父元素即可。
    任何包含在此元素之内的 .control-label.form-control 和 .help-block 元素都将接受这些校验状态的样式。

    3.has-error 优先级高于has-warning高于has-success

    4.has-feedback

    Feedback icons only work with textual <input class="form-control"> elements

    类名"has-feedback"要与"has-error"、"has-warning"、"has-success"配合使用。
    使用 span 设置图标
     <span class="glyphicon glyphicon-user form-control-feedback"></span>

    注册分会员注册和管理员注册

    修改下reg.jsp中提交时的js

     $("#submitBtn").click(function () {
        var loginType = $("select.form-control").val();
        if(loginType == "管理"){
            $("#regForm").submit();
        }else{
            alert("此功能尚未开通")
        } 
        return false;
    });

    在表单提交的UserController中编写注册的逻辑

    //处理所有/permission/user下的请求
    @RequestMapping("/permission/user")
    @Controller
    public class UserController {
    
        private String manager = "manager/";
        @Autowired
        UserService userService;
    
        /**
         * 
         */
        // /permission/user/reg
        @RequestMapping("/reg")
        public String reg(TUser user, Model model, HttpSession session) {
            System.out.println("用户注册..." + user);
            // 1.注册成功后来到控制面板页面
            // 1.注册用户
            boolean flag = userService.register(user);
            if (flag == true) {
                // 1.注册成功,来到控制面板
                // 用户保存的数据在user对象中,id是数据库生成的自增主键
                // 需要修改mybatis的mapper文件,让其使用自动生成的id
                session.setAttribute(Constants.LOGIN_USER, user);
    
                return "manager/main";
            } else {
                // 2.注册失败
                model.addAttribute("regError", "用户名已经被使用");
                // 3.来到页面要回显user之前输入的内容
                // 4.user先从隐含模型中拿的;只要pojo,确定完值以后会自动放在隐含模型中
                // 用的key是类名首字母小写${tUser.loginacct }
    // 由于bean包中的类名前两个字母大写,比较特殊,这里实际使用的是TUser
    return "forward:/reg.jsp"; } } }

    注册失败好要回显,在reg.jsp的表格中设置用户名和邮箱的value,value="${TUser.loginacct }

    <form id="regForm" class="form-signin" role="form" action="${ctp }/permission/user/reg" method="post">
            <h2 class="form-signin-heading"><i class="glyphicon glyphicon-log-in"></i> 用户注册</h2>
              <div class="form-group has-success has-feedback">
                <input type="text" name="loginacct" class="form-control" 
                id="loginacct_input" 
                placeholder="请输入登录账号" value="${TUser.loginacct }" autofocus>
                <span class="glyphicon glyphicon-user form-control-feedback"></span>
                <span class="errorinfo" style="color: red"></span>
              </div>
              <div class="form-group has-success has-feedback">
                <input type="password" name="userpswd" class="form-control" 
                id="userpswd_input" 
                placeholder="请输入登录密码" style="margin-top:10px;">
                <span class="glyphicon glyphicon-lock form-control-feedback"></span>
                <span class="errorinfo" style="color: red"></span>
              </div>
              <div class="form-group has-success has-feedback">
                <input type="text" name="email" class="form-control" 
                id="email_input" placeholder="请输入邮箱地址" value="${TUser.email }" style="margin-top:10px;">
                <span class="glyphicon glyphicon glyphicon-envelope form-control-feedback"></span>
                <span class="errorinfo" style="color: red"></span>
              </div>

    低级错误

        @RequestMapping("reg")
        public String reg(TUser user, Model model, HttpSession session){
    
            System.out.println("用户注册");
            UserService userService = new UserServiceImpl();
            Boolean flag = userService.register(user);
            if (flag == true){
                session.setAttribute(Constants.LOGIN_USER, user);
                return "manager/main";
            }else {
                model.addAttribute("regError", "注册失败");
                return "forward:/reg.jsp";
            }
        }

    自己新建对象,然后调用方法,而register中使用了 @Autowired 注解,这回导致方法调用失败,报空指针异常,方法中的bean为null

    自己新建对象,Spring不会管理,无法调用Spring容器中的实例,所以为null

    还有一种bean为null的情况

    静态方法调用了bean,但是由于spring的加载机制,是加载静态代码-》构造函数-》注入bean-》postconstuct的顺序执行,因此静态方法里调用的bean,并没有注入相应的bean,因此为空,所以会出现bean为null的情况

    由于bean起名的时候前两个字母都是大写,因此页面中要和类名相同,即TUser,而不是常用的tUser

    上面用到了UserService的register方法和Constants.LOGIN_USER常量

    编写UserService接口和实现类

    package com.atguigu.scw.manager.service;
    
    import com.atguigu.scw.manager.bean.TUser;
    
    public interface UserService {
    
        public boolean register(TUser user);
    }
    package com.atguigu.scw.manager.service.impl;
    
    import java.util.Date;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import com.atguigu.project.MD5Util;
    import com.atguigu.project.MyStringUtils;
    import com.atguigu.scw.manager.bean.TUser;
    import com.atguigu.scw.manager.dao.TUserMapper;
    import com.atguigu.scw.manager.service.UserService;
    
    @Service
    public class UserServiceImpl implements UserService {
    
        @Autowired
        TUserMapper userMapper;
    
        public boolean register(TUser user) {
            // 1.拿到用户信息,先加密密码
            // 加密的密码 盐,这里使用了用户名
            // 盐不能超过8位,因此本打算用用户名,但容易出错,这里取部分用户名
            // String loginacct = user.getLoginacct();
            // String salt = loginacct.substring(0, 6) + "HA";
            // String crypt = Md5Crypt.md5Crypt(user.getUserpswd().getBytes());
            String diget = MD5Util.digest(user.getUserpswd());
            user.setUserpswd(diget);
            // 2.在将用户的其他信息设置默认值
            // 设置昵称 创建时间
            user.setUsername(user.getLoginacct());
            user.setCreatetime(MyStringUtils.formatSimpleDate(new Date()));
            // 3.去数据库保存用户,登陆账号不能重复
            int i;
            try {
                i = userMapper.insertSelective(user);
            } catch (Exception e) {
                // 保存失败的原因就是用户重复,数据库设置了Loginacct不可重复
                return false;
            }
            // i影响多少行
            return i == 1 ? true : false;
        }
    
    }

    用到了MyStringUtils.formatSimpleDate(new Date())

     MyStringUtils中添加方法

     public static String formatSimpleDate(Date date) {
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
            return format.format(date);
        }

    Constants.LOGIN_USER常量

     

    package com.atguigu.scw.manager.constant;
    
    
    public class Constants {
    
        public static final String LOGIN_USER = "loginUser";
    }

    修改下/manager-dao/src/main/resources/mybatis/mapper/TUserMapper.xml

    <insert id="insertSelective" parameterType="com.atguigu.scw.manager.bean.TUser" useGeneratedKeys="true" keyProperty="id">

    数据库里密码长度改为64

    登陆后的控制面板,右上角显示用户名

    从session中取,用loginUser

    main.jsp中

    <div class="btn-group">
                      <button type="button" class="btn btn-default btn-success dropdown-toggle" data-toggle="dropdown">
                        <i class="glyphicon glyphicon-user"></i> 
                       ${loginUser.username }
                      <span class="caret"></span>
                      </button>
                          <ul class="dropdown-menu" role="menu">
                            <li><a href="#"><i class="glyphicon glyphicon-cog"></i> 个人设置</a></li>
                            <li><a href="#"><i class="glyphicon glyphicon-comment"></i> 消息</a></li>
                            <li class="divider"></li>
                            <li><a href="index.html"><i class="glyphicon glyphicon-off"></i> 退出系统</a></li>
                          </ul>
                    </div>
  • 相关阅读:
    随想13:论“善”字
    Nginx做前端Proxy时TIME_WAIT过多的问题
    HTTP的长连接和短连接
    nginx长连接的问题
    Tomcat性能参数设置
    Nginx1.1.4+ 对后端机器的长连接特性
    HTTP长连接200万尝试及调优方法
    NGINX轻松管理10万长连接 --- 基于2GB内存的CentOS 6.5 x86-64
    CRtmpServer转推流到Nginx Rtmp及SRS(SimpleRtmpServer)的经历
    rtmp流媒体编程相关整理2013(crtmpserver,rtmpdump,x264,faac)
  • 原文地址:https://www.cnblogs.com/aidata/p/11470712.html
Copyright © 2020-2023  润新知