笔记5:登录功能实现

用户登录:

首先前面已经实现了跳转到登录页面

登录业务需求

1)用户名和密码不能为空

2)不能登录:①用户名和密码错误,②用户已过期,③用户状态被锁定, ④ip受限(安全性问题)

(user表中有过期时间,用户状态,用户常用IP地址

3)登录成功之后,所有业务页面显示当前用户的名称

4)十天记住密码

5)登录成功后,跳转到业务页面,如果错误输出提示信息

流程分析:

起始:客户端浏览器发请求

1)用户输入账号密码 ——> 用户点击登录按钮 / 按回车键

2)向后台发请求 ——> 后台写一个Controller接受请求并处理

到底创建几个Controller类,看响应信息是返回到哪个页面,如果页面占一个独立的资源目录,则写一个新的Controller。

响应信息还是返回原来的login.jsp页面,看是否成功再决定是否跳转

所以不用创建新的Controller,还是在UserController中添加方法

遇到没学过的知识:

同步请求:全局刷新

异步请求:局部刷新

异步请求:根据返回条件判断,都有可能

 所以这里用异步请求

这里需要提供的参数:用户名密码

知识:

是否提交参数,提交哪些参数,看下一步Controller中需要什么

 这里UserController中需要验证用户名,密码判断是否需要记住密码,并决定是否写Cookie(Cookie也没学过)

参数:账号,密码,是否记住密码
loginAct, loginPwd, isRemPwd

现在流程推进到UserController

复习Controller代码的三个职责:

1. 接收请求,获取参数,需要则封装参数(SpringMVC在方法上定义形参即可接收参数)

这里把参数封装到map中

2. 处理业务,即处理数据。一般调用Service层去具体处理业务,Controller是控制流程的,决定具体调用哪个Service类。

1)这里到数据库中查询账号密码的数据,并判断是否过期,是否锁定等

2)这里创建UserService类,Service类的创建与表相关(需要访问用户表),不看前台页面。

3)在UserService中编写方法来处理业务:queryUserByLoginActAndPwd(map),参数是UserController中封装好的map。

4)Service不能真正访问数据库,根据代码分层的原理,dao / mapper 层访问数据,本层也由表决定。

写个UserMapper类,编写方法selectUserByLoginActAndPwd(map),本项目通过mybatis来访问数据库,执行SQL语句。

5)查询的记录封装为实体类对象User来返回,这里因为账号不重复,所以只返回一个实体类对象user

6)Service再将user返回给Controller

3. 根据处理的结果返回响应信息

将响应信息返回给前台

又是没学的Ajax:

前面异步请求是Ajax发的,现在将响应信息返回给Ajax,Ajax能解析是是json数据

所以这里返回的是json字符串,字符串里包含什么数据由前台的需要决定

(联系前面的:前台发送请求所包含的数据由Controller的需要决定)

怎么知道前台需要的数据?      看需求文档

前台只需要知道成功或失败(,失败的原因)

成功则跳转

不成功则提示信息

所以将这两个数据放在json字符串中返回,返回给Ajax的回调函数,来解析json

表示成功或失败
code: 1 成功  0 失败
提示信息
message: xxxxx 失败原因

解析json信息并继续流程 ——>  渲染页面(很好又一个没学)

-------------------------------------------------------流程分析结束-------------------------------------------------------

mybatis逆向工程:

1. 简介:根据表生成mapper层三部分代码:①实体类,②mapper接口,③映射文件

2. 使用mybatis逆向工程:

1)创建工程:crm-mybatis-generator

笔记5:登录功能实现_第1张图片

2)添加插件:这里的jar包做成了插件。

一个小知识点:

依赖只能被别的程序调用,插件是可以独立运行的

去文档里复制

3)添加配置文件,告诉工程一些信息:

数据库连接信息

代码保存的目录

表的信息

去文档里复制配置文件(P27注意配置文件的含义)

笔记5:登录功能实现_第2张图片 配置并执行

笔记5:登录功能实现_第3张图片 成功

 --------------------------------------------------------开始写代码---------------------------------------------------------

按照前面的分析,在UserMapper接口中写方法查询用户信息:

/**
     * 根据账号和密码查询用户
     * @param map
     * @return
     */
    User selectUserByLoginActAndPwd(Map map);

然后在UserMapper.xml中写SQL语句:

这里直接根据用户名和密码,查询表中所有字段信息。

注意在配置文件中开启扫描,这里不太懂开启什么扫描,好像并没有@MapperScan这个注解啊,也找不到basePackage在哪里。

笔记5:登录功能实现_第4张图片

 现在mapper层做好了,开始写service层来调用mapper层。先写UserService接口,里面写方法调用mapper层方法的方法:

public interface UserService {
    User queryUserByLoginActAndPwd(Map map);
}

然后实现类中写方法的具体实现:

//创建bean对象
@Service("userService")
public class UserServiceImpl implements UserService {

    //这里创建mapper层的对象,用来调用mapper层的方法
    //自动注入
    @Autowired
    private UserMapper userMapper;

    @Override
    public User queryUserByLoginActAndPwd(Map map) {
        return userMapper.selectUserByLoginActAndPwd(map);
    }
}

这个也要记得开启注解扫描:

笔记5:登录功能实现_第5张图片

 然后开始写controller层,调用service层处理业务:

一些点:

1)响应结果应该返回json,这里将结果封装在一个Java实体类中返回

2)先封装参数:封装在map中

3)然后调用service层

4)然后处理响应信息:根据文档上的需求逐个判断

5)写一个Java实体类ReturnObject来封装相应信息,放在公共包中

这里有点疑问:

service应该是业务层,但是这里的service层的方法中只是调用了mapper层中的crud,响应信息的判断都在controller中,感觉controller层中的内容有点多。

或许因为是响应信息的处理,所以在controller层中。有弹幕说service只写crud的内容。

一些知识:

1. @ResponseBody注解,和json有关,没学过

2. HttpServletRequest request 请求信息可以直接在方法参数中注入

/**
     * 这里应该返回json,
     * 把数据封装在Java对象中返回
     * 这里直接写成Object,无论哪种Java对象都能返回
     * 最后会把实际返回的对象转为json,按子类转,不是按Object
     */
    @RequestMapping("/settings/qx/user/login.do")
    //响应信息返回给哪个页面,url就和哪个页面的资源路径保持一致,资源的名称和方法名一样

    //因为要返回json,所以写一个@ResponseBody注解,这里没学过json不了解
    public @ResponseBody Object login(String loginAct, String loginPwd, String isRemPwd, HttpServletRequest request) {
        //封装参数
        Map map = new HashMap<>();
        map.put("loginAct", loginAct);
        map.put("loginPwd", loginPwd);
        //isRemPwd不需要封装进去,因为后面用不到这个参数

        //调用service层方法来查询用户,得到user对象
        User user = userService.queryUserByLoginActAndPwd(map);

        //根据查询结果生成响应信息
        ReturnObject returnObject = new ReturnObject();
        if(user == null) {
            //没查到用户名和密码
            returnObject.setCode("0");
            returnObject.setMessage("用户名或密码错误");
        } else {
            //查出来了,但是不一定成功

            //拿到过期时间,和当前时间相比。这里都转换成字符串比较
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String nowStr = sdf.format(new Date());
            if(nowStr.compareTo(user.getExpireTime()) > 0) {
                //当前时间大,账号已过期,登录失败
                returnObject.setCode("0");
                returnObject.setMessage("账号已过期");
            } else if("0".equals(user.getLockState())) {
                //0 状态被锁定,登录失败
                returnObject.setCode("0");
                returnObject.setMessage("账号被锁定");
            } else if( ! user.getAllowIps().contains(request.getRemoteAddr())) {
                //请求里带有ip地址,得到后判断是否是用户常用ip地址
                //request可以在方法参数里注入
                //ip地址受限,登录失败
                returnObject.setCode("0");
                returnObject.setMessage("ip受限制");
            } else {
                //登录成功
                returnObject.setCode("1");
            }
        }

        //返回封装了信息的对象
        return returnObject;
    }

------------------------------------------------后台内容结束,开始前台页面处理响应信息------------------------------------------------

前台页面login.jsp收到响应后处理

登录按钮本来是submit类型,但是submit发送的是同步请求,会使整个页面刷新,所以改为button

然后添加jQuery的入口函数来给登录按钮添加单击事件(jQuery忘完了,仿佛没学过)

写一个入口函数:

昨天自己写的不行,点击登录毫无反应,用户名密码不能为空的提示也没有,应该是入口函数完全没用。找bug找了很久,没找到问题,然后发现复制老师的就可以,但是自己的跟老师一样。今天自己写的又行了。。。。。。

笔记5:登录功能实现_第6张图片

登录成功,中间部分目前还是404。

然后是一些代码优化

1. 日期的格式化可以写一个工具类

2. 字符串代表登录成功,可以写成常量封装在一个类里

/**
 * 对Date类型数据进行处理的工具类
 */
public class DateUtils {

    /**
     * 对指定的date对象进行格式化yyyy-MM-dd HH:mm:ss
     * 静态方法方便调用
     * @param date
     * @return
     */
    public static String formatDateTime(Date date) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateStr = sdf.format(date);
        return dateStr;
    }

    /**
     * 对指定的date对象进行格式化yyyy-MM-dd
     * 静态方法方便调用
     * @param date
     * @return
     */
    public static String formatDate(Date date) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        String dateStr = sdf.format(date);
        return dateStr;
    }

    /**
     * 对指定的date对象进行格式化HH:mm:ss
     * 静态方法方便调用
     * @param date
     * @return
     */
    public static String formatTime(Date date) {
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
        String dateStr = sdf.format(date);
        return dateStr;
    }
}
public class Constant {
    //保存ReturnObject类中的code值
    public static final String RETURN_OBJECT_CODE_SUCCESS = "1";//成功
    public static final String RETURN_OBJECT_CODE_FAIL = "0";//失败
}

优化后的UserController中的login方法:

一个小细节:

下载的数据库表中账号过期时间是2018年,所以无法登录,应该把数据库表中的过期时间改掉。但是发现数据库表改不了(遗留问题),所以把代码里改成在过期时间之后的可以登录了。

/**
     * 这里应该返回json,
     * 把数据封装在Java对象中返回
     * 这里直接写成Object,无论哪种Java对象都能返回
     * 最后会把实际返回的对象转为json,按子类转,不是按Object
     */
    @RequestMapping("/settings/qx/user/login.do")
    //响应信息返回给哪个页面,url就和哪个页面的资源路径保持一致,资源的名称和方法名一样

    //因为要返回json,所以写一个@ResponseBody注解,这里没学过json不了解
    public @ResponseBody Object login(String loginAct, String loginPwd, String isRemPwd, HttpServletRequest request) {
        //封装参数
        Map map = new HashMap<>();
        map.put("loginAct", loginAct);
        map.put("loginPwd", loginPwd);
        //isRemPwd不需要封装进去,因为后面用不到这个参数

        //调用service层方法来查询用户,得到user对象
        User user = userService.queryUserByLoginActAndPwd(map);

        //根据查询结果生成响应信息
        ReturnObject returnObject = new ReturnObject();
        if(user == null) {
            //没查到用户名和密码
            returnObject.setCode(Constant.RETURN_OBJECT_CODE_FAIL);
            returnObject.setMessage("用户名或密码错误");
        } else {
            //查出来了,但是不一定成功

            //拿到过期时间,和当前时间相比。这里都转换成字符串比较
//            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//            String nowStr = sdf.format(new Date());
            if(DateUtils.formatDateTime(new Date()).compareTo(user.getExpireTime()) < 0) {
                //当前时间大,账号已过期,登录失败
                returnObject.setCode(Constant.RETURN_OBJECT_CODE_FAIL);
                returnObject.setMessage("账号已过期");
            } else if("0".equals(user.getLockState())) {
                //0 状态被锁定,登录失败
                returnObject.setCode(Constant.RETURN_OBJECT_CODE_FAIL);
                returnObject.setMessage("账号被锁定");
            } else if( ! user.getAllowIps().contains(request.getRemoteAddr())) {
                //请求里带有ip地址,得到后判断是否是用户常用ip地址
                //request可以在方法参数里注入
                //ip地址受限,登录失败
                returnObject.setCode(Constant.RETURN_OBJECT_CODE_FAIL);
                returnObject.setMessage("ip受限制");
            } else {
                //登录成功
                returnObject.setCode(Constant.RETURN_OBJECT_CODE_SUCCESS);
            }
        }

        //返回
        return returnObject;
    }

想起来应该去复习一下枚举类

登录完成!

你可能感兴趣的:(动力节点crm项目笔记,java,servlet,spring,tomcat,maven)