用户模块
一、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中打印"已注册"或"注册成功";