项目准备
熟悉静态页面
查看真实在线的途牛旅游项目
其实也是一个商城而已,重点项目搭建,核心模块:登录
前言
(1)前言
为了巩固web基础知识,提升综合运用能力,故而讲解此案例。
要求,每位同学能够独立完成此案例。
(2)项目演示
01-静态页面
让客户可以直观的看到项目完成的效果
(3)复制到项目
新建web-app
复制到项目
启动
三层架构
(1)三层架构
!
技术选型
(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进行封装
项目搭建
项目初始化,目录结构
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>
创建数据库
登录功能
最终实现截图
实现登录功能
实现登录功能
- 创建测试类
//测试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"?>
- 配置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"/>
- 创建获取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);
}
}
- 运行测试类,测试逻辑是否有问题
测试结果,测试成功
Preparing: select uid,username,password,name,birthday,sex,telephone,email,status,code from tab_user where username = ? and password = ?
- 编写随机生成验证码的业务逻辑类
/*
- 生成验证码,并将验证码转化为图片的业务层
- */
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() {
}
}
- 编写页面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>
- aJax实现异步登录,并判断输入是否合法,防止非法注入
- 运行结果
登陆成功