用户模块

一、BaseServlet优化

返回值处理

每个模块处理完都需要进行跳转,跳转方式有重定向,转发及返回字符串;在每个模块中进行不同跳转非常麻烦。

处理方案:可以在BaseServlet中统一处理,模块功能中统一返回字符串即可;如果不需要跳转的,那么返回值类型为void即可,例如:验证码展示

@WebServlet("/user")   //http://localhost:8080/user?action=login
public class UserController extends BaseServlet {
    public String login(HttpServletRequest request,HttpServletResponse response){
        System.out.println("登录功能");
        //登录完成后,进行重定向
        return "redirect:/show.jsp";
    }
    public String register(HttpServletRequest request,HttpServletResponse response){
        System.out.println("注册功能..");
        //登录完成后,返回字符串
        return "注册成功";
    }
}
@WebServlet("/product")
public class ProductController extends BaseServlet {
    public String show(HttpServletRequest request,HttpServletResponse response){
        System.out.println("产品展示");
        //产品展示完成,进行转发
        return "forward:/show.jsp";
    }
    public void search(HttpServletRequest request,HttpServletResponse response){
        System.out.println("产品搜索");
        //匹配条件搜索后,无需进行跳转  例如:验证码展示
    }
}

BaseServlet中统一处理返回值:

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String action = req.getParameter("action");
    //反射应用:
    try{
        Class clazz = this.getClass(); //获取反射对象  哪个模块访问的service,则反射对象代表谁
        Method method = clazz.getMethod(action,HttpServletRequest.class,HttpServletResponse.class);
        Object o = method.invoke(this,req,resp); //调用指定功能
        if(o!=null){ //不为null,则是统一的String类型
            String res = (String) o;
            //处理跳转方式
            if(res.contains("redirect:")){//重定向  redirect:容易写错,可提取静态常量
                String path = res.split(":")[1]; //redirect:/show.jsp
                resp.sendRedirect(req.getContextPath()+path);
            }else if(res.contains("forward:")){//转发
                String path = res.split(":")[1]; //forward:/show.jsp
                req.getRequestDispatcher(path).forward(req,resp);
            }else{ //返回字符串
                resp.getWriter().write(res);
            }
        }
    }catch (Exception e){
        System.out.println("映射路径的访问出现异常");
        e.printStackTrace();
    }
}

编码过滤

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    System.out.println("进入编码处理");
    request.setCharacterEncoding("utf-8");
    response.setContentType("text/html;charset=utf-8");

    chain.doFilter(request,response);
}

优化处理

将重定向及转发的标记提取状态值,这样可读性更强,且维护性更好,且不容易写错

public class ConstUtils { //工具类
    public static  final String FOR = "forward:";  //转发标记
    public static final  String RED = "redirect:";  //重定向标记
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String action = req.getParameter("action");
    if(action==null){ //如果action写错了,则进入首页
        action = "index";  //http://localhost:8080/user?acti=xx
    }
    //反射应用:
    try{
        Class clazz = this.getClass(); //获取反射对象  哪个模块访问的service,则反射对象代表谁
        Method method = clazz.getMethod(action,HttpServletRequest.class,HttpServletResponse.class);
        Object o = method.invoke(this,req,resp); //调用指定功能
        if(o!=null){ //不为null,则是统一的String类型
            String res = (String) o;
            //处理跳转方式
            if(res.startsWith(ConstUtils.RED)){//重定向  redirect:容易写错,可提取静态常量
                String path = res.split(":")[1]; //redirect:/show.jsp
                resp.sendRedirect(req.getContextPath()+path);
            }else if(res.startsWith(ConstUtils.FOR)){//转发
                String path = res.split(":")[1]; //forward:/show.jsp
                req.getRequestDispatcher(path).forward(req,resp);
            }else{ //返回字符串
                resp.getWriter().write(res);
            }
        }
    }catch (Exception e){
        System.out.println("映射路径的访问出现异常");
        e.printStackTrace();
    }
}

public String index(HttpServletRequest request,HttpServletResponse response){
    System.out.println("参数异常,进入首页...");
    return ConstUtils.RED+"/index.jsp";
}

二、UserDao测试

项目开发中,最先开始操作的是用户模块,可以先在DAO层开始测试数据库的连接

用户实体类创建:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
  private int u_id;    //用户ID
  private String u_name;  //用户名
  private String u_password;  //密码
  private String u_email;    //邮箱
  private String u_sex;      //性别
  private int u_status;     //用户激活状态
  private String u_code;     //激活码
  private int u_role;       //用户角色
}

DAO测试:

public interface UserDao { //用户模块DAO
    //根据用户名匹配返回对象,业务层对应登录和注册校验功能
    public User selectByName(String name) throws SQLException;
    public int  addUser(User user) throws SQLException;  //添加--业务层对应注册
}
public class UserDaoImpl implements UserDao {
    //传入C3P0数据源
    private QueryRunner runner = new QueryRunner(C3P0Utils.getDataSource());
    @Override
    public User selectByName(String name) throws SQLException {
        String sql = "select * from user where u_name=?";
        return runner.query(sql,new BeanHandler<>(User.class),name);
    }
    @Override
    public int addUser(User u) throws SQLException {
        String sql = "insert into user(u_name,u_password,u_email,u_sex," +
                "u_status,u_code,u_role) values(?,?,?,?,?,?,?)";
        return runner.update(sql,u.getU_name(),u.getU_password(),u.getU_email(),
                u.getU_sex(),u.getU_status(),u.getU_code(),u.getU_role());
    }
    public static void main(String[] args) throws SQLException {
/*        User user = new User(0,"zs","123","xxx","男",1,"xxxdd",0);
        int res = new UserDaoImpl().addUser(user);
        System.out.println(res);*/

        User user = new UserDaoImpl().selectByName("zs");
        System.out.println(user);
    }
}

三、用户注册

校验用户名

在注册前,需要先校验用户名是否成功;

//在register.jsp中发送异步请求,校验用户名
$("#username").change(function(){
    //使用ajax 做username 的异步验证user?action=check&username=zsf
    $.get("/user","action=check&username="+this.value,function(data){
        if(data==1){
            $("#usernameMsg").html("用户名已经存在").css("color","red");
            $("#registerBtn").prop("disabled",true);
        }else{
            $("#usernameMsg").html("用户名可用").css("color","green");
            $("#registerBtn").removeAttr("disabled");
        }
    })
});
//在控制层进行用户名校验:
public String check(HttpServletRequest request,HttpServletResponse response){
    //获取参数,交给业务层处理
    String username = request.getParameter("username");
    System.out.println("用户名:"+username);
    User user = userService.checkUser(username);
    System.out.println(user);
    if(user==null){
        return "0";    //0表示恭喜可用  1表示已注册
    }else{
        return "1";
    }
}

注册功能

填充所有前端注册数据,添加到数据库

前端注册的参数特别多,在控制层需要每一项都进行getParameter取参数,非常麻烦

方案:可使用第三方工具,进行注入实体中;注意,传的参数名必须要和实体属性名一致

//UserController模块进行注册
public String register(HttpServletRequest request,HttpServletResponse response) throws InvocationTargetException, IllegalAccessException {
    //{u_name:zs,u_password:123...}
    Map<String, String[]> map = request.getParameterMap(); //获取参数的集合
    User u = new User();
    //确保map的属性名和实体的属性名一直  问题:第三方如何完成注入的?
    BeanUtils.populate(u,map);  //将map集合注入到实体属性中
    u.setU_code(RandomUtils.createActive()); //创建激活码
    u.setU_role(0);  //用户角色  0-会员  1-管理员
    u.setU_status(0); //激活状态 0-未激活  1-已激活
    u.setU_password(MD5Utils.md5(u.getU_password()));  //密码加密
    int res = userService.register(u);
    System.out.println("注册:"+res);
    //登录完成后,返回字符串
    return ConstUtils.RED+"/registerSuccess.jsp";
}

四、邮箱校验

为了确认账户的合法性及唯一性,可以对邮箱进行绑定校验。

发送邮件

在注册成功后,发送邮件,并将User对象传到邮箱工具类,并生成邮件发送出去

//在注册成功时,发送邮件
int res = userService.register(u);
if(res==1){ //注册成功,则发送邮件
    EmailUtils.sendEmail(u);  //将user对象传出
}
//邮箱工具类中进行发送邮件
message.setRecipient(MimeMessage.RecipientType.TO, new InternetAddress(user.getU_email(), user.getU_name(), "utf-8"));
//3.3生成邮件主题
message.setSubject("小米商城账号激活邮件","utf-8");
String ip = Inet4Address.getLocalHost().getHostAddress();
//链接中的激活码进行了编码,在商城服务器中需要解码
String url = "http://"+ip+":8080/user?action=active&c="+Base64Utils.encode(user.getU_code());
//设置邮件正文 setContent 可以使用html标签
message.setContent(user.getU_name()+",你好<br>欢迎注册小米商城! 请点击链接进行激活:<a href='"+url+"'>"+url+"</a>","text/html;charset=utf-8");

激活邮箱

打开邮箱,并点击激活链接,发请求到商城项目,根据激活码匹配后,改激活状态

public String active(HttpServletRequest request,HttpServletResponse response){
    String c = request.getParameter("c"); //获取激活码
    String code = Base64Utils.decode(c);  //进行解码
    User user = userService.selectByCode(code); //根据激活码返回对象
    if(user==null){
        request.setAttribute("msg","激活失败");

    }else{
        if(user.getU_status()==1){
            request.setAttribute("msg","请不要重复激活");
        }else{
            int res = userService.updateStatus(user.getU_id());
            System.out.println("修改激活状态:"+res);
            request.setAttribute("msg","激活成功");
        }
    }
    return ConstUtils.FOR+"/message.jsp";
}

五、用户登录

登录功能

在用户模块的登录功能中,根据用户名和密码的匹配,返回登录状态

public String login(HttpServletRequest request,HttpServletResponse response){
    //获取参数信息
    String username = request.getParameter("username");
    String password = request.getParameter("password");
    User user = userService.login(username,password);
    if(user==null){
        request.setAttribute("msg","用户名或密码失败");
        return ConstUtils.FOR +"/login.jsp";  //重定向到登录页面
    }else{ //登录成功,则到达首页
        if(user.getU_status()==1) {
            //设置登录凭证
            request.getSession().setAttribute(ConstUtils.LOGIN, user);
            return ConstUtils.RED + "/index.jsp";
        }else{ //激活不成功
            request.setAttribute("msg","用户未激活");
            return ConstUtils.FOR +"/login.jsp";  //重定向到登录页面
        }
    }
}
//业务层的判断
public User login(String username, String password) {
    try {
        User user = userDao.selectByName(username);
        //如果用户名匹配,再匹配密码,密码都需要加密进行判断
        if(user!=null&&user.getU_password().equals(MD5Utils.md5(password))){
            return user;
        }
    } catch (SQLException e) {
        e.printStackTrace();
    }
    return null;
}

登录凭证

在登录成功后,设置凭证,在首页即可显示登录用户

//设置状态值
public static  final  String LOGIN="loginUser"; //登录凭证
//设置登录凭证
request.getSession().setAttribute(ConstUtils.LOGIN,user);
//在header.jsp中显示凭证
<c:if test="${not empty loginUser}">
   <a href="userAddress?flag=show" id="a_top">${loginUser.u_name}</a>
...    

六、总结与作业

总结

1.BaseServlet优化
返回值处理-统一在BaseServlet中处理跳转
编码过滤,优化处理异常-状态值提取
2.UserDao测试(重点)
测试数据库连接是否有问题,JDBC操作是否成功
3.用户注册(重点)
校验用户名-根据用户名匹配,返回状态
注册功能-填充参数,使用BeanUtils注入
4.邮箱校验
发送邮件-发送方发送邮件到注册用户
激活邮箱-从邮箱中发送请求到商城,携带激活码
5.用户登录(重点)
登录功能-用户名和密码匹配;登录成功则设置凭证

作业

1.完成今天的所有用户模块功能
2.完成验证码校验功能(交)
扩展作业:(选做)
记住账户功能--cookie的设置与获取

晨考

手写注册功能的校验:
在JS中发异步请求到服务器,控制层接收参数,只需完成控制层的调用操作(无需写业务层和DAO层);
调用完成,返回1或0的状态,在js中打印"已注册"或"注册成功";