【途牛旅游项目】02 - 登录功能实现 - 验证码功能 - ajax实现登录
文章目录
- 登录功能分析图解
- 最终实现截图
- 实现登录功能
- 创建测试类
- 创建业务层UserService
- 创建实体类 - User用户类和Msg错误提示类
- 创建dao层
- 创建dao的映射文件
- 创建属性文件,连接数据库db.properties
- 编写日志配置log4j2.xml
- 配置Spring核心配置文件applicationContext.xml
- 创建获取class类的工具类GetDaoUtils
- 运行测试类,测试逻辑是否有问题
- 编写随机生成验证码的业务逻辑类
- 编写图片验证码的servlet
- 登录servlet
- 编写filter,全局编码解决
- 编写页面jsp,点击验证码进行图片更换
- aJax实现异步登录,并判断输入是否合法,防止非法注入
- 运行结果
登录功能分析图解
最终实现截图
实现登录功能
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("用户未激活");
}
}
}
2. 创建业务层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; //账号未激活
}
}
}
}
3. 创建实体类 - 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" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xgf.dao.UserDao">
<select id="getUserByUsernamePassword" parameterType="com.xgf.bean.User" resultType="com.xgf.bean.User">
select uid,username,password,name,birthday,sex,telephone,email,status,code
from tab_user
where username = #{username} and password = #{password}
</select>
</mapper>
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 = 861221293
7. 编写日志配置log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- 日志文件 -->
<Configuration status="WARN">
<Appenders>
<!--Console向控制台追加日志 SYSTEM_OUT系统输出控制台 -->
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern=" 日志log: %d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n "/>
</Console>
</Appenders>
<!-- 记录哪些类的日志信息 -->
<Loggers>
<!--DEBUG 是日志级别 调试信息也会记录 -->
<Logger name="com.xgf.dao" level="DEBUG"/>
<Root level="error">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
8. 配置Spring核心配置文件applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mybatis="http://mybatis.org/schema/mybatis-spring" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--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"/>
</beans>
9. 创建获取class类的工具类GetDaoUtils
// 获取bean工具类
public class GetDaoUtils {
private static ApplicationContext applicationContext = null;
//静态代码块 只加载一次
static {
//加载配置文件
applicationContext = new ClassPathXmlApplicationContext("com/xgf/config/applicationContext.xml");
}
public static <T> T getMapper(Class classFile) {
return (T) applicationContext.getBean(classFile);
}
}
10. 运行测试类,测试逻辑是否有问题
测试结果,测试成功
11. 编写随机生成验证码的业务逻辑类
/* * 生成验证码,并将验证码转化为图片的业务层 * */
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);
}
}
14. 编写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() {
}
}
15. 编写页面jsp,点击验证码进行图片更换
<section id="login_wrap">
<div class="fullscreen-bg" style="background: url(images/login_bg.png);height: 532px;">
</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>
</section>
<!--引入尾部-->
<div id="footer">
</div>
16. aJax实现异步登录,并判断输入是否合法,防止非法注入
<script type="text/javascript" >
//检查用户名是否规范
function checkUserName(){
//获取输入框的值
var username = $("#username").val();
//正则表达式 定义一个规则,执行test方法,符合规则返回true,否则返回false
var reg = /^\w{8,20}$/ ;
var flag = reg.test(username); //判断
//如果符合要求,设置输入框边框是正常,否则设置红色
if(flag){
$("#username").css("border","");
}else{
$("#username").css("border","1px solid red");
}
//alert(flag)
return flag;
}
//检查密码是否规范
function checkPassword(){
//判断密码输入框的值是否合法
var username = $("#password").val();
var reg = /^\w{8,20}$/ ;
var flag = reg.test(username); //判断
if(flag){
$("#password").css("border","");//无色框
}else{
$("#password").css("border","1px solid red");//红框
}
//alert(flag)
return flag;
}
//页面加载执行函数
$(function () {
$("#errorMsg").html("");
// 判断两个输入框架的是否格式正确
$("#username").blur(checkUserName);//输入框失去焦点
// 如果正确,使用ajax发送请求到servlet
$("#password").blur(checkPassword);
//登录判断,username、password是否符合注册规范,如果不符合报错(防止直接注入)
$("#btn_login").click(function () {
//alert("点击btn_login")
//要求两个值正确,我们才做提交
if(checkUserName()&&checkPassword()){
var username = $("#username").val()
var password = $("#password").val()
var inputCheckCode = $("#inputCheckCode").val()
//alert(username+password+inputCheckCode)
//写提交
$.ajax({
url:"LoginServlet",
async:true,
data:$("#loginForm").serialize(),
type:"post",
dataType:"json",
success:function (data) {
// alert(data) {"code":1,"data":"登录成功"}
if(1 == data.code){
//跳转到主页 index.jsp
$("#errorMsg").html("");
window.location="index.jsp"
}else{
//显示在界面上
$("#errorMsg").html(data.data);
}
},
error:function () {
alert("服务器发生了错误Error")//比如找不到servlet
}
});
}else{
$("#errorMsg").html("用户名或密码输入不规范,用户名密码错误");
}
})
})
</script>
17. 运行结果
登录成功:
还没有评论,来说两句吧...