【途牛旅游项目】02 - 登录功能实现 - 验证码功能 - 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. 运行测试类,测试逻辑是否有问题

测试结果,测试成功
>Preparing: select uid,username,password,name,birthday,sex,telephone,email,status,code from tab_user where username = ? and password = ?

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. 运行结果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
登录成功:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_40542534/article/details/108944617