项目准备

熟悉静态页面
查看真实在线的途牛旅游项目
其实也是一个商城而已,重点项目搭建,核心模块:登录

前言

(1)前言
为了巩固web基础知识,提升综合运用能力,故而讲解此案例。
要求,每位同学能够独立完成此案例。
(2)项目演示
01-静态页面
让客户可以直观的看到项目完成的效果
(3)复制到项目
新建web-app
复制到项目
启动

三层架构

(1)三层架构

!

途牛组织架构 途牛公司的组织架构_xml

技术选型

(1)Web层
a)Servlet:前端控制器
b)html:视图
c)Filter:过滤器
d)BeanUtils:数据封装
e)Jackson:json序列化工具
(2)Service层
f)Javamail:java发送邮件工具
g)Redis:nosql内存数据库
h)Jedis:java的redis客户端
(3)Dao层
i)Mysql:数据库
j)Mybatis:对jdbc进行封装

项目搭建

项目初始化,目录结构

途牛组织架构 途牛公司的组织架构_java_02


bean包:存放java实体类

dao包:数据操作层

   Mysql:数据库(Mybatis:对jdbc进行封装,MySQL:数据库)

Service包:业务逻辑层

   主要包括三大类:(Javamail:java发送邮件工具,Redis:nosql内存数据库,Jedis:java的redis客户端)

util包:存放工具类

Web包

Servlet:前端控制器
Filter:过滤器
listener:监听器

导入pom.xml依赖

<?xml version="1.0" encoding="UTF-8"?>

4.0.0

<groupId>com.hjy</groupId>
<artifactId>Travel</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
    <module>MyTravel</module>
    <module>MyTravelTest01</module>
</modules>

<name>Travel</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
</properties>

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
        <scope>test</scope>
    </dependency>
    <!--servlet-->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <scope>provided</scope>
    </dependency>

    <!-- MySql -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.32</version>
    </dependency>
    <!-- Mybatis -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.4.5</version>
    </dependency>
    <!--日志包-->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.25</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>

    <!--beanUtils-->
    <dependency>
        <groupId>commons-beanutils</groupId>
        <artifactId>commons-beanutils</artifactId>
        <version>1.9.2</version>
        <scope>compile</scope>
    </dependency>
    <!--jackson-->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.3.3</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>2.3.3</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-annotations</artifactId>
        <version>2.3.3</version>
    </dependency>


    <!--javaMail-->
    <dependency>
        <groupId>javax.mail</groupId>
        <artifactId>javax.mail-api</artifactId>
        <version>1.5.6</version>
    </dependency>
    <dependency>
        <groupId>com.sun.mail</groupId>
        <artifactId>javax.mail</artifactId>
        <version>1.5.3</version>
    </dependency>
    <!--jedis-->
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>2.7.0</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.12</version>
    </dependency>

    <dependency>
        <groupId>jstl</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>

</dependencies>

<build>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
        <plugins>
            <!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
            <plugin>
                <artifactId>maven-clean-plugin</artifactId>
                <version>3.1.0</version>
            </plugin>
            <!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
            <plugin>
                <artifactId>maven-resources-plugin</artifactId>
                <version>3.0.2</version>
            </plugin>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
            </plugin>
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.22.1</version>
            </plugin>
            <plugin>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.0.2</version>
            </plugin>
            <plugin>
                <artifactId>maven-install-plugin</artifactId>
                <version>2.5.2</version>
            </plugin>
            <plugin>
                <artifactId>maven-deploy-plugin</artifactId>
                <version>2.8.2</version>
            </plugin>
            <!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
            <plugin>
                <artifactId>maven-site-plugin</artifactId>
                <version>3.7.1</version>
            </plugin>
            <plugin>
                <artifactId>maven-project-info-reports-plugin</artifactId>
                <version>3.0.0</version>
            </plugin>
        </plugins>
    </pluginManagement>
</build>

创建数据库

途牛组织架构 途牛公司的组织架构_maven_03

登录功能

途牛组织架构 途牛公司的组织架构_java_04

最终实现截图

途牛组织架构 途牛公司的组织架构_bc_05

实现登录功能

实现登录功能

  1. 创建测试类
    //测试user业务层
    public class TestUserService {
    //测试登录
    @Test
    public void test01(){
    UserService userService = new UserService();
//创建user数据
 User user = new User();
 user.setUsername("861221293");
 user.setPassword("ABC123456");
 //user.setStatus("Y");//用户激活

 //测试登录
 int code = userService.login(user);
 if(code == 1){
     System.out.println("登录成功");
 }else if(code == -1){
     System.out.println("用户名或密码错误");
 }else if(code == -2){
     System.out.println("用户未激活");
 }
}
 }1. 创建业务层UserService
 //user业务层
 public class UserService {
private static UserDao userDao = null;
//登录用户
 public int login(User user) {
 userDao = GetDaoUtils.getMapper(UserDao.class);
User u = userDao.getUserByUsernamePassword(user);
 if(u == null){
     return -1;  //账号密码匹配找不到
 }else {
     //判断用户user是否激活
     if(u.getStatus().equals("Y")){
         return 1;   //账号密码正确,且已经激活
     }else {
         return -2;  //账号未激活
     }
 }
}
 }1. 创建实体类 - User用户类和Msg错误提示类
 //User类
 public class User {
 private int uid;
 private String username; //登录用户名
 private String password; //密码
 private Date birthday; //生日
 private String name; //昵称
 private String sex; //性别
 private String telephone; //电话号码
 private String email; //邮箱
 private String status; //激活状态
 private String code; //激活码(UUID)
 //省略getter/setter方法
 }//Msg信息提示类
 public class Msg {
 private int code;
 private Object data;
 //省略getter/setter方法
 }4. 创建dao层
 public interface UserDao {
 // 通过usernmae和password查询用户
 public User getUserByUsernamePassword(User user);
 }5. 创建dao的映射文件
 <?xml version="1.0" encoding="UTF-8" ?> select uid,username,password,name,birthday,sex,telephone,email,status,code from tab_user where username = #{username} and password = #{password} 
6. 创建属性文件,连接数据库db.properties
 #mysql jdbc 属性文件里面不能有分号
 jdbc.driver = com.mysql.jdbc.Driver
 jdbc.url = jdbc:mysql://localhost:3306/travelnetwork?useUnicode=true&characterEncoding=UTF-8&useSSL=false
 jdbc.username = root
 jdbc.password = 8612212937. 编写日志配置log4j2.xml
 <?xml version="1.0" encoding="UTF-8"?>


  1. 配置Spring核心配置文件applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>

<!--1. 引入jdbc的属性文件,在配置中通过占位使用 -->
<context:property-placeholder location="classpath*:db.properties" />

<!--2. <context:component-scan>扫描包中注解所标注的类(@Component、@Service、@Controller、@Repository) -->
<context:component-scan base-package="com.xgf"/>

<!--3. 由spring管理    配置数据源数据库连接(从jdbc属性文件中读取参数) -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="driverClassName" value="${jdbc.driver}"/>
</bean>

<!--  通过spring来管理Mybatis的sqlSessionFactory对象创建,将MyBatis的二级缓存配置configLocation粘入  -->
<!--4. 通过完全限定名匹配查找  创建SqlSessionFactoryBean  -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
</bean>

<!-- 5. mybatis提供的一个注解扫描标签(搜索映射器 Mapper 接口),通过自动扫描注解的机制,创建每个dao接口定义的bean  -->
<mybatis:scan base-package="com.xgf.dao"/>
  1. 创建获取class类的工具类GetDaoUtils
1.  // 获取bean工具类
 public class GetDaoUtils {
private static ApplicationContext applicationContext = null;
//静态代码块 只加载一次
 static {
 //加载配置文件
 applicationContext = new ClassPathXmlApplicationContext(“com/xgf/config/applicationContext.xml”);
 }
public static T getMapper(Class classFile) {
 return (T) applicationContext.getBean(classFile);
 }
 }
  1. 运行测试类,测试逻辑是否有问题
    测试结果,测试成功

Preparing: select uid,username,password,name,birthday,sex,telephone,email,status,code from tab_user where username = ? and password = ?

  1. 编写随机生成验证码的业务逻辑类

/*

  • 生成验证码,并将验证码转化为图片的业务层
  • */
    public class VerificationCodeService {
    public VerificationCodeService() {
    }
    // 生成验证码字符串 – 随机数
    public String createRandomCode() {
    String str = “ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789”;//所有随机数字符串集合
    //Random类 产生指定范围内的随机数
    Random random = new Random();
StringBuilder sb = new StringBuilder();
  //随机截取4个字符
  for(int i =0 ;i < 4; i++){//4个随机数
      //包括开头不包括结尾 从0到str.length()-1里面随机产生一个整数
      int index = random.nextInt(str.length());//从0到str.length()不包括最后一个 右边开区间
      char randomStr = str.charAt(index);//安装随机产生的值获取字符
      sb.append(randomStr);//StringBuilder拼接字符串

  }
  return sb.toString();
}
// 将生成的随机字符串验证码转化为图片
 public BufferedImage changeStringToImage(String code) {
 Random rd = new Random();
 //创建一个画布
 int width = 80;
 int height = 30;
 BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
 //创建画笔
 Graphics g = image.getGraphics();
 //给画笔设置颜色(绘制随机验证码的时候的验证码颜色)
 g.setColor(new Color(240,240,240)); //#00000 FFFFFF
 //设置验证码的 背景色
 g.fillRect(0, 0, width, height);
 // 设置字体
 g.setFont(new Font(“宋体”,Font.BOLD,16));
g.setColor(new Color(0,0,0));  //#00000   FFFFFF
  // g.drawString(checkCodeStr, 20, 20);
  for (int i = 0; i <4 ; i++) {
      //画字符
      g.setColor(new Color(rd.nextInt(120),rd.nextInt(120),rd.nextInt(120)));
      g.drawString(code.charAt(i)+"", 16*i + rd.nextInt(16), 15 + rd.nextInt(10) );
      if(i % 2 == 0) {//画线
          g.setColor(new Color(rd.nextInt(120), rd.nextInt(120), rd.nextInt(120)));
          g.drawLine(rd.nextInt(75), rd.nextInt(28), rd.nextInt(75), rd.nextInt(28));
      }
  }
  return image;
}
 }12. 编写图片验证码的servlet
// 验证码-生成验证码显示成图片的servlet
 @WebServlet("/checkCodeServlet")
 public class CheckCodeServlet extends HttpServlet {
 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 doGet(request,response);
 }
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    //服务器通知浏览器不要缓存
    response.setHeader("pragma","no-cache");
    response.setHeader("cache-control","no-cache");
    response.setHeader("expires","0");
    //1:创建一个验证码的业务
    VerificationCodeService vcs = new VerificationCodeService();
    //2:生产一个随机的4个字符组成的字符串
    String verificationCode =  vcs.createRandomCode();
    System.out.println(verificationCode);

    //将验证码保存到session中
    HttpSession session = request.getSession();
    session.setAttribute("verificationCode",verificationCode);

// request.setAttribute(“verificationCode”,verificationCode);

//3:将字符串转成图片
    //BufferedImage类将图片生成到内存中,然后直接发送给浏览器
    BufferedImage image =  vcs.changeStringToImage(verificationCode);

    //4:使用OutputStream写到浏览器

// System.out.println(image);
//获取输出流
ServletOutputStream outputStream = response.getOutputStream();

ImageIO.write(image,"jpeg",outputStream);//参1,内存中的图片  参2,格式  参3,字节输出流
    outputStream.flush();
    outputStream.close();//关流
// request.getRequestDispatcher("/").forward(request,response);
 }
 }13. 登录servlet
 @WebServlet("/LoginServlet")
 public class LoginServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 doGet(request, response);
 }
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
UserService userService = new UserService();
HttpSession session = request.getSession();//获取session对象,里面主要是验证码和当前user
Msg msg = new Msg();//提示信息

//用户输入的验证码
String inputCheckCode = request.getParameter("inputCheckCode");
//从session中获取系统当前生成的验证码
String verificationCode = (String) session.getAttribute("verificationCode");
System.out.println("inputCheckCode : " + inputCheckCode + " ;\t verificationCode : " + verificationCode);

//inputCheckCode 与 verificationCode
//相同表示验证码不正确,将提示信息写到页面的错误提示
if (inputCheckCode == null || !inputCheckCode.equalsIgnoreCase(verificationCode)) {
    //验证码不看大小写
    msg.setCode(-3);
    msg.setData("验证码输入出错,请重新输入验证码");

    //将字符串转换为json数据格式返回给浏览器
    String json = new ObjectMapper().writeValueAsString(msg);
    response.getWriter().println(json);
    return; //返回不继续执行
}


//获取请求参数
Map<String, String[]> map = request.getParameterMap();
//当前登录用户
User loginUser = new User();
try {
    //参1 javaBean 参2 map 封装bean对象
    BeanUtils.populate(loginUser, map);//将map里面所有的参数赋值给javaBean(login就是输入的username和password)
} catch (IllegalAccessException e) {
    e.printStackTrace();
} catch (InvocationTargetException e) {
    e.printStackTrace();
}

//调用service处理参数,查询当前登录username和password是否存在
int code = userService.login(loginUser);
//响应给浏览器 ajax 是响应json给浏览器就可以
msg.setCode(code);//设置code

if (code == 1) {
    msg.setData("登录成功,欢迎您的使用Login Success");
    //将登录user保存到session中
    session.setAttribute("user", loginUser);
    //判断是否开启免登陆
    //登录成功且开启了十天免登陆  就要保存/覆盖cookie
    String ssh = request.getParameter("ssh");
    //从session中删除生成的验证码 不移除的话可能会覆盖新的验证码
    session.removeAttribute("verificationCode");
} else if (code == -1) {
    msg.setData("您输入的用户名或密码错误,请重新输入Incorrect user name or password");

} else if (code == -2) {
    msg.setData("您的账号还没有激活,请前往激活The account is activated");
}

//将字符串转成json数据格式,返回给浏览器显示提示信息msg
String json = new ObjectMapper().writeValueAsString(msg);
response.getWriter().println(json);
}
 }13. 编写filter,全局编码解决
 //解决全站乱码问题,处理所有的请求,拦截所有设置编码
 @WebFilter("/*")
 public class CharchaterFilter implements Filter {
 public void init(FilterConfig filterConfig) throws ServletException {
 }
@Override
 public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
 System.out.println(“CharchaterFilter”);
 //设置请求编码
 req.setCharacterEncoding(“UTF-8”);
 //设置响应编码
 resp.setContentType(“text/html;charset=UTF-8”);
 //放行
 chain.doFilter(req, resp);
 }
public void destroy() {
}
 }
  1. 编写页面jsp,点击验证码进行图片更换
</div>
<div class="login-box">
    <div class="title">
        <img src="images/login_logo.png" alt="">
        <span>欢迎登录途牛旅游账户</span>
    </div>
    <div class="login_inner">

        <!--登录错误提示消息-->
        <div id="errorMsg" class="alert alert-danger" ></div>
        <form id="loginForm" action="" method="post" accept-charset="utf-8">
            <input type="hidden" name="action" value="login"/>
            <input id="username" name="username" type="text" placeholder="请输入账号">
            <input id="password" name="password" type="password" placeholder="请输入密码" autocomplete="off">
            <div class="verify">
                <input name="inputCheckCode" type="text" placeholder="请输入验证码" autocomplete="off">
                <span><img src="checkCodeServlet" alt="" onclick="changeCheckCode(this)"></span>
                <script type="text/javascript">
                    //图片点击事件
                    function changeCheckCode(img) {
                        img.src="checkCodeServlet?"+new Date().getTime();//添加时间戳
                    }
                </script>
            </div>

            <div class="submit_btn" >
                <button id="btn_login" type="button">登录</button>
                <div class="auto_login">
                    <%-- ssh : 免密码登录 --%>
                    <input type="checkbox" name="ssh" value="ssh" class="checkbox" style="margin-left: 10px">
                    <span >十天免登陆</span>
                </div>
            </div>
        </form>
        <div class="reg" >没有账户?<a href="javascript:;">立即注册</a></div>
    </div>
</div>


  1. aJax实现异步登录,并判断输入是否合法,防止非法注入
  2. 运行结果

    登陆成功