【左京淳的JAVA WEB学习笔记】第十二章 用户管理

如果用户在登入画面成功登入,则在session创建用户对象及其购物车对象。
若失败则返回登入页面,提示重新登入。

新建LoginSvl

@WebServlet("/LoginSvl")
public class LoginSvl extends HttpServlet {
  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    request.getRequestDispatcher("/WEB-INF/main/login.jsp").forward(request, response);
  }
}

从首页的登入按钮触发以下连结,呼叫LoginSvl的doGet()方法,转发到login.jsp

localhost:8080/BookShop/LoginSvl

如果用户在login.jsp输入帐号密码并送出,则会触发doPost()方法。
编辑doPost()方法

  protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String uname = request.getParameter("uname");
    String pwd = request.getParameter("pwd");
    if(uname != null && pwd != null && !uname.equals("") && !pwd.equals("")) {
      UserBiz biz = new UserBiz();
      try {
        TUser user = biz.login(uname,pwd);
        if(user != null) {
          //登入成功
          request.getSession().setAttribute("user", user);
          Map<String,Integer> shopCar = new HashMap<String,Integer>();
          request.getSession().setAttribute("shopCar", shopCar);
          request.getRequestDispatcher("/MainSvl").forward(request, response);
        }else {
          //登入失败
          request.setAttribute("msg", "用户名或密码错误");
          request.getRequestDispatcher("/WEB-INF/main/login.jsp").forward(request, response);
        }
      }catch(InputNullException e) {
        request.setAttribute("msg", e.getMessage());
        request.getRequestDispatcher("/WEB-INF/main/login.jsp").forward(request, response);
      }catch(Exception e) {
        Log.logger.error(e.getMessage(), e);
        request.setAttribute("msg", e.getMessage());
        request.getRequestDispatcher("/WEB-INF/main/login.jsp").forward(request, response);
      }
    }else {
      request.setAttribute("msg", "用户名或密码不能为空");
      request.getRequestDispatcher("/WEB-INF/main/login.jsp").forward(request, response);
    }
  }

新增UserBiz

  public TUser login(String uname, String pwd) throws Exception{
    TUser user = null;
    if(uname == null || pwd == null || uname.equals("") || pwd.equals("")) {
      throw new InputNullException("用户名或密码不能为空");
    }
    IUserDao dao = new UserDaoMysql();
    try {
      user = dao.login(uname,pwd);
    }finally{
      dao.closeConnection();
    }
    return user;
  }
}

新增UserDaoMysql

public class UserDaoMysql extends BaseDao {
  public TUser login(String uname,String pwd) throws Exception{
    TUser user = null;
    String sql = "select * from tuser where name = ? and pwd = ?";
    this.openConnection();
    PreparedStatement ps = this.connection.prepareStatement(sql);
    ps.setString(1, uname);
    ps.setString(2, pwd);
    ResultSet rs = ps.executeQuery();
    if(rs != null) {
      while(rs.next()) {
        user = new TUser();
        user.setUname(uname);
        user.setPwd(pwd);
        user.setAccount(rs.getDouble("account"));
        user.setRole(Integer.parseInt(rs.getString("role")));
        break;
      }
    }
    rs.close();
    ps.close();
    return user;
  }
}

用户登出

当用户点击登出钮,清空session并重定向至首页。

新增LogoutSvl
需为已登入用户才能登出,因此连结网址前缀需为/user/*,让过滤器进行检查。

@WebServlet("/user/LogoutSvl")
public class LogoutSvl extends HttpServlet {
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
      request.getSession().invalidate();
      //request.getRequestDispatcher("/WEB-INF/main/main.jsp").forward(request, response);
      String path = request.getContextPath();
      String basePath = request.getScheme() + "://" + request.getServerName()  
      + ":" + request.getServerPort() + path + "/";
      response.sendRedirect(basePath + "MainSvl");
    }
}

记忆重点

  • 使用request.getSession().invalidate();让session失效。
  • 使用response.sendRedirect(url);进行重定向。不使用转发是为了让url列更新。进行重定向需先准备好绝对路径。

用户注册

在用户注册页面,需校验用户名是否重复
使用AJAX搭配UnameCheckSvl进行校验

新增regist.jsp页面
使用input标签的onkeyup方法触发校验函式。

<table>
  <tr>
    <td width="107" height="36">用户名:</td>
    <td width="524">
      <input type="text" id="uname" name="uname" maxlength="16" onkeyup="unameValid()">
      <span id="unameAlert" style="color:red;font-size:8px"></span>
    </td>
  </tr>
  <tr>
    <td>密码</td>
    <td><input type="password" name="pwd"></td>
  </tr>
  <tr>
    <td>密码确认</td>
    <td><input type="password" name="pwd2"></td>
  </tr>
  <tr>
    <td><input type="submit" value="送出"></td>
    <td><a href="<%=basePath%>MainSvl>">返回</a></td>
  </tr>
</table>

编写校验函式

<script src="http://ajax.microsoft.com/ajax/jQuery/jquery-1.4.4.min.js"></script>
<script  type="text/javascript">
  function unameValid(){
    var uname = $('#uname').val();
    if(uname != ""){
      var destUrl = "<%=basePath%>UnameCheckSvl?uname=" + uname;
      $.ajax({
        type:"GET",
        url:destUrl,
        dataType:"text",
        timeout:3000,
        success:function(msg){
          if(msg == 0){
            $('#unameAlert').html('用户名可用');
          }else if(msg == 1){
            $('#unameAlert').html('用户名重复');
          }else if(msg == 2){
            $('#unameAlert').html('系统异常');
          }else{}
        },
        error:function(){
          alert("连线逾时");
        }
      });
    }
  }
</script>

记忆重点

  • 使用$('#uname').val();取得要校验的资料
  • 事先准备好目标Servlet的url,并把要校验的资料也加进去。
  • 使用jquery的 $.ajax({});招唤AJAX。

新增UnameCheckSvl

@WebServlet("/UnameCheckSvl")
public class UnameCheckSvl extends HttpServlet {    
  public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
      String uname = request.getParameter("uname");
      UserBiz biz = new UserBiz();
      PrintWriter out = response.getWriter();
      try {
        boolean bRet = biz.isHaveUserName(uname);
        if(bRet) {
          out.print("1"); //用户名重复
        }else {
          out.print("2"); //用户名可用
        }
      }catch(Exception e) {
        out.print("-1");
      }
      out.flush();
      out.close();
    }
}

校验後使用response.getWriter();写出结果。

在UserBiz内新增isHaveUserName(String uname)

  public boolean isHaveUserName(String uname) throws Exception{
    if(uname == null || uname.trim().equals("")) {
      throw new Exception("用户名不能为空");
    }
    IUserDao dao = new UserDaoMysql();
    try {
      return dao.isHaveUserName(uname);
    }finally{
      dao.closeConnection();
    }
  }

在UserDaoMysql内新增isHaveUserName(String uname)

  public boolean isHaveUserName(String uname) throws Exception{
    boolean bFind = false;
    String sql = "select uname from tuser where uname = ?";
    this.openConnection();
    PreparedStatement ps = this.connection.prepareStatement(sql);
    ps.setString(1, uname);
    ResultSet rs = ps.executeQuery();
    while(rs.next()) {
      bFind = true;
      break;
    }
    rs.close();
    ps.close();
    return bFind;
  }

记忆重点

  • ResultSet在这边没有进行null判定。查了一下发现即便返回0件资料,也可以用while(rs.next())来确认,不会报空指针错误。
  • 进行null判定是当有处理要放在回圈外时使用,例如new ArrayList<>()之类的。

注册

校验完成後即可进入注册程序
修改regist.jsp

  <form action="<%=basePath%>RegistSvl" id="myform" onsubmit="return checkUserInfo()" method="post">
    <table border="0" cellpadding="0" cellspacing="0" align="center">
      <tr><td height="100"></td></tr>
      <tr>
        <td width="107" height="36">用户名:</td>
        <td width="524"><input type="text" id="uname" name="uname"
          maxlength="16" onkeyup="unameValid()"> <span
          id="unameAlert" style="color: red; font-size: 8px"></span></td>
      </tr>
      <tr>
        <td width="107" height="36">密码:</td>
        <td width="524"><input type="password" id="pwd" name="pwd"></td>
      </tr>
      <tr>
        <td width="107" height="36">确认密码:</td>
        <td width="524"><input type="password" id="pwd2" name="pwd2"></td>
      </tr>
      <tr>
        <td colspan="2">
          &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
          <input type="submit" value="送出">&nbsp;
          <a href="<%=basePath%>MainSvl>">返回</a>
        </td>
      </tr>
      <tr>
        <td colspan="2">
          <span style="color:red;font-size:8px">${msg }</span>
        </td>
      </tr>
    </table>
  </form>

记忆重点

  • 使用标签来建立HTML表单
  • 表单可指定目标url
  • 表单可使用onsubmit()在送出前进行资料检查
  • 使用来设定表单格式
    虽然不使用和也可以送出资料,不过正式的格式还是应该套用这些标签。

新增js脚本checkUserInfo()

  function checkUserInfo(){
    var uname = $(#'uname').val();
    var pwd = $(#'pwd').val();
    var pwd2 = $(#'pwd2').val();
    if(uname == ""){
      alert("用户名不能为空!");
      return false;
    }
    if(pwd == ""){
      alert("密码不能为空!");
      return false;
    }
    if(pwd2 != pwd){
      alert("密码不一致!");
      return false;
    }
  }

新建RegistSvl

  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    request.getRequestDispatcher("/WEB-INF/main/regist.jsp").forward(request, response);
  }

  protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String uname = request.getParameter("uname");
    String pwd = request.getParameter("pwd");
    UserBiz biz = new UserBiz();
    TUser user = new TUser();
    user.setUname(uname);
    user.setPwd(pwd);
    user.setRole(IRole.CUSER);
    try {
      biz.addUser(user);
      request.setAttribute("msg", "注册成功,请重新登入");
      request.getRequestDispatcher("/WEB-INF/main/login.jsp").forward(request, response);
    }catch(java.sql.SQLIntegrityConstraintViolationException e){
      request.setAttribute("msg", "用户名冲突,请重新输入");
      request.getRequestDispatcher("/WEB-INF/main/regist.jsp").forward(request, response);
    }catch(Exception e) {
      request.setAttribute("msg", "系统忙碌中,请稍後再试");
      request.getRequestDispatcher("/WEB-INF/error.jsp").forward(request, response);
    }
  }

记忆重点,Servlet负责:

  • 取得表单参数
  • 叫服务层出来干活
  • 依据处理结果将用户导向不同网页

修改UserBiz,新增addUser()

  public void addUser(TUser user) throws Exception{
    IUser dao = new UserDaoMysql();
    try {
      dao.addUser(user);
    }finally {
      dao.closeConnection();
    }
  }

记忆重点,Biz负责:

  • 叫DAO层出来干活
  • 活做完关connection
  • 出问题使用throws Exception回报给Servlet

修改UserDaoMysql,新增addUser()

  public void addUser(TUser user) throws Exception{
    String sql = "insert into tuser values(?,?,?,?)";
    this.openConnection();
    PreparedStatement ps = this.connection.prepareStatement(sql);
    ps.setString(1, user.getUname());
    ps.setString(2, user.getPwd());
    ps.setDouble(3, user.getAccount());
    ps.setInt(4, IRole.CUSER);
    ps.executeUpdate();
    ps.close();
  }

记忆重点,Dao负责:

  • 准备好sql文
  • openConnection()
  • prepareStatement(sql)
  • 依照资料型态使用setString、setInt等方法将资料写入prepareStatement物件
  • executeUpdate(),记得查询(executeQuery)与编辑资料(executeUpdate)是不同指令。
  • close()

<<:  使用WSL2在Windows下快速打造Linux开发环境(含Docker)

>>:  进击的软件工程师之路-软件战斗营 第六周

[第二十二天]从0开始的UnityAR手机游戏开发-APP内撷取画面

在玩AR APP时都有一个拍照功能,本次章节会介绍如何撰写Unity的截图功能 先在Project新...

Day 27 - 建立自己的K线资料库 (中)

本篇重点 透过Pandas读取资料及做OHLC转换 DataFrame.resample中的Left...

C# 9 record 并非简单属性 POCO 的语法糖

C# 9 record 并非简单属性 POCO 的语法糖 今天继续升级专案到大统一 .NET 5 并...

D21: 工程师太师了: 第11话

工程师太师了: 第11话 杂记: 前阵子跟朋友讨论到绿色金刚战士。 小时候在看金刚战士的时候, 有个...

Day 18 ( 中级 ) 地球绕着太阳转

地球绕着太阳转 教学原文参考:地球绕着太阳转 这篇文章会介绍,如何在 Scratch 3 里使用重复...