【实战】5-2 用户登录相关功能开发

来源:互联网 发布:淘宝的卖家中心在哪里 编辑:程序博客网 时间:2024/06/04 01:11

前言

接下来就要正式开始往之前搭建的结构中填充代码了,这些代码也不是一次完成的,我要纠结一下怎么来把这个学习过程明白的描写出来。可能有些类增加了多少就把那一部分写出来吧, 我大部分的上课笔记还是会写在代码的注释中,这样方便了记笔记,却让读者的思路顺序有些乱了,尽量表达的清楚一点吧~

以这种形式把上课学习内容写出来还真是有些别扭,以后可能我就只能挑着有意义的部分写了。

(20171025结束了不少校招面试,回来补项目经历,哈哈)最后我是把用户相关操作都做完了才来把这篇博客补全,没有更多的精力来把增量内容一点一点写出来了。这里不得不说一下,这里的MD5加密部分不严密,大家看看就行,不要学这里,我以后自己也要修改这一部分。

高可用服务响应对象

还是先贴代码,在common包下建立,同时还新建了ResponseCode枚举类,把一些状态码的列举放进去。
这样不管是底层service在数据库中查询出什么结果,都可以通过这个通用的对象返回,包括状态码status,一些提示消息msg,还有数据对象。这个对象返回给controller层后,也会修改或不修改的返回前端(浏览器)。

package top.winxblast.happymall.common;import org.codehaus.jackson.annotate.JsonIgnore;import org.codehaus.jackson.map.annotate.JsonSerialize;import java.io.Serializable;/** * 高可用服务响应对象 * JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)意思是 * 值为null时不要序列化,因为在生成错误对象时没有data,这时候就不要把data * 序列化进json了(有时候msg也为null)(保证序列化json的时候,如果是null的 * 对象,key也会消失) * * @author winxblast * @create 2017/10/19 **/@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)public class ServerResponse<T> implements Serializable {    //这些都是在5-1接口设计中返回内容提到过的    /**     * 状态码     */    private int status;    /**     * 返回的消息     */    private String msg;    /**     * 返回数据,可以是一些类的json格式     * 泛型的好处:这样在返回的时候可以指定泛型里面的内容,也可以不指定泛型里面的强制类型     */    private T data;    //私有构造器,这里老师提到一种问题,我的理解就是泛型T万一是String类型    //那么构造方法会选择2,3中的哪一个?这个问题需要在后面public方法中考虑    //所以下面公开的方法要区分有message和data,不过调用构造方法是不会错的了    private ServerResponse(int status) {        this.status = status;    }    private ServerResponse(int status, T date) {        this.status = status;        this.data = date;    }    private ServerResponse(int status, String msg) {        this.status = status;        this.msg = msg;    }    private ServerResponse(int status, String msg, T data) {        this.status = status;        this.msg = msg;        this.data = data;    }    /**     * 根据状态码返回是否成功     * JsonIgnore使被注解内容不在json序列化结果当中     * 大概看了一下,JsonSerialize是通过getter方法来获取要序列化的内容的     * 所以哪个内容不想被包含,就加上jasonignore注解     * @return     */    @JsonIgnore    public boolean isSuccess() {        return this.status == ResponseCode.SUCCESS.getCode();    }    public int getStatus() {        return status;    }    public String getMsg() {        return msg;    }    public T getData() {        return data;    }    /**     * 通过该公开方法直接获取一个成功状态码的本对象     * @param <T>     * @return     */    public static <T> ServerResponse<T> createBySuccess() {        return new ServerResponse<T>(ResponseCode.SUCCESS.getCode());    }    public static <T> ServerResponse<T> createBySuccessMessage(String msg) {        return new ServerResponse<T>(ResponseCode.SUCCESS.getCode(), msg);    }    public static <T> ServerResponse<T> createBySuccess(T data) {        return new ServerResponse<T>(ResponseCode.SUCCESS.getCode(), data);    }    public static <T> ServerResponse<T> createBySuccess(String msg, T data) {        return new ServerResponse<T>(ResponseCode.SUCCESS.getCode(), msg, data);    }    /**     * 获取错误对象     * @param <T>     * @return     */    public static <T> ServerResponse<T> createByError() {        return new ServerResponse<T>(ResponseCode.ERROR.getCode(), ResponseCode.ERROR.getDesc());    }    public static <T> ServerResponse<T> createByErrorMessage(String errorMessage) {        return new ServerResponse<T>(ResponseCode.ERROR.getCode(), errorMessage);    }    public static <T> ServerResponse<T> createByErrorCodeMessage(int errorCode, String errorMessage) {        return new ServerResponse<T>(errorCode, errorMessage);    }}

响应编码的枚举类,与ServerResponse类配合使用

package top.winxblast.happymall.common;/** * 响应编码的枚举类 * * @author winxblast * @create 2017/10/19 **/public enum ResponseCode {    //以后想要扩展的时候修改下面这一部分就行了    SUCCESS(0,"SUCCESS"),    ERROR(1,"ERROR"),    NEED_LOGIN(10,"NEED_LOGIN"),    ILLEGAL_ARGUMENT(2,"ILLEGAL_ARGUMENT");    private final int code;    private final String desc;    //这里使用default的修饰,只允许类内部及本包调用,我不能很好理解这样的好处    ResponseCode(int code, String desc) {        this.code = code;        this.desc = desc;    }    public int getCode() {        return code;    }    public String getDesc() {        return desc;    }}

用户登录

在controller包下创建一个portal包,这个是门户的意思,是给前端用的,这里新建UserController类。可以看到这里的@RequestMapping注解是和上一节定义的用户接口相对应的。

package top.winxblast.happymall.controller.portal;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.ResponseBody;import top.winxblast.happymall.common.Const;import top.winxblast.happymall.common.ResponseCode;import top.winxblast.happymall.common.ServerResponse;import top.winxblast.happymall.pojo.User;import top.winxblast.happymall.service.IUserService;import javax.servlet.http.HttpSession;/** * 前台用户接口设计的控制层 * * @author winxblast * @create 2017/10/19 **/@Controller//因为都要给到这个地址下,所以放在类前面,方法前面放更细的地址@RequestMapping("/user/")public class UserController {    @Autowired    private IUserService iUserService;    /**     * 用户登录     * @param username     * @param password     * @param session     * @return     */    //这里login.do的设计要与之前的用户接口定义相同,method指定请求的方式    @RequestMapping(value = "login.do",method = RequestMethod.POST)    //responsebody注解表示返回时自动使用SpringMVC Jackson插件将返回值序列化为json    //它的配置在dispatcher-servlet.xml中    @ResponseBody    public ServerResponse<User> login(String username, String password, HttpSession session) {        //service-->mybatis-->dao        ServerResponse<User> response = iUserService.login(username,password);        if(response.isSuccess()) {            session.setAttribute(Const.CURRENT_USER, response.getData());        }        return response;    }    /**     * 用户登出     * @param session     * @return     */    //这里method也就只使用了GET方法,登出比较简单    @RequestMapping(value = "logout.do", method = RequestMethod.POST)    @ResponseBody    public ServerResponse<String> logout(HttpSession session) {        //登出就直接在session中把当前用户删除即可        session.removeAttribute(Const.CURRENT_USER);        return ServerResponse.createBySuccess();    }    /**     * 用户注册     * @param user     * @return     */    @RequestMapping(value = "register.do", method = RequestMethod.POST)    @ResponseBody    public ServerResponse<String> register(User user) {        return iUserService.register(user);    }    /**     * 检查用户名,email是否存在     * 虽然注册时已经含有这个检查了(注册中的检查是为了防止恶意调用注册接口)     * 这里的检查是为了返回前端一个检查结果,这样好实时显示     * @param str     * @param type 通过type是email还是username,去判断str     * @return     */    @RequestMapping(value = "check_valid.do", method = RequestMethod.POST)    @ResponseBody    public ServerResponse<String> checkValid(String str, String type) {        return iUserService.checkValid(str, type);    }    /**     * 获取用户信息     * @param session     * @return     */    @RequestMapping(value = "get_user_info.do", method = RequestMethod.POST)    @ResponseBody    public ServerResponse<User> getUserInfo(HttpSession session) {        User user = (User)session.getAttribute(Const.CURRENT_USER);        if(user != null) {            return ServerResponse.createBySuccess(user);        }        return ServerResponse.createByErrorMessage("用户未登录,无法获取当前用户信息");    }    /**     * 忘记密码,获得密码提示问题     * @param username     * @return     */    @RequestMapping(value = "forget_get_question.do", method = RequestMethod.POST)    @ResponseBody    public ServerResponse<String> forgetGetQuestion(String username) {        return iUserService.selectQuestion(username);    }    /**     * 验证密码提示问题的答案,token要放到ServerResponse的泛型T中     * 在写service时就会用到guava,先用本地的guava缓存来做token,利用     * 缓存的有效期来搞定token的有效期     * @param username     * @param question     * @param answer     * @return     */    @RequestMapping(value = "forget_check_answer.do", method = RequestMethod.POST)    @ResponseBody    public ServerResponse<String> forgetCheckAnswer(String username, String question, String answer) {        return iUserService.checkAnswer(username,question,answer);    }    /**     * 重置密码     * 现在更加能理解为什么要使用token的理由了,原来一直天真的以为验证答案正确     * 直接进行修改不是一连串的动作么,现在发现不是这样,修改密码是通过另外一个     * 接口的,那么破坏者就能利用那个没有验证答案的接口直接修改密码了     * @param username     * @param passwordNew     * @param forgetToken     * @return     */    @RequestMapping(value = "forget_reset_password.do", method = RequestMethod.POST)    @ResponseBody    public ServerResponse<String> forgetResetPassword(String username, String passwordNew, String forgetToken) {        return iUserService.forgetResetPassword(username, passwordNew, forgetToken);    }    /**     * 登录状态下重置密码     * @param session     * @param passwordOld     * @param passwordNew     * @return     */    @RequestMapping(value = "reset_password.do", method = RequestMethod.POST)    @ResponseBody    public ServerResponse<String> resetPassword(HttpSession session, String passwordOld, String passwordNew) {        User user = (User)session.getAttribute(Const.CURRENT_USER);        if(user == null) {            return ServerResponse.createByErrorMessage("用户未登录");        }        return iUserService.resetPassword(user, passwordOld, passwordNew);    }    /**     * 更新用户信息     * @param session     * @param user     * @return 这里消息的泛型选择User,这样把新的用户信息更新到session中,返回前端后,前端也要把新信息直接更新     */    @RequestMapping(value = "update_information.do", method = RequestMethod.POST)    @ResponseBody    public ServerResponse<User> updateInformation(HttpSession session, User user) {        User currentUser = (User) session.getAttribute(Const.CURRENT_USER);        if(currentUser == null) {            return ServerResponse.createByErrorMessage("用户未登录");        }        //因为user放的更新信息,里面没有用户的id,所以要先放进去,这里也是为了防止越权        user.setId(currentUser.getId());        user.setUsername(currentUser.getUsername());        ServerResponse<User> response = iUserService.updateInformation(user);        if(response.isSuccess()) {            //成功则要更新session            session.setAttribute(Const.CURRENT_USER, response.getData());        }        //最后不管成功失败,直接返回就行        return response;    }    /**     * 获取用户信息     * 调用这个方法如果发现没有登录要强制登录     * @param session     * @return     */    @RequestMapping(value = "get_information.do", method = RequestMethod.POST)    @ResponseBody    public ServerResponse<User> getInformation(HttpSession session) {        User currentUser = (User) session.getAttribute(Const.CURRENT_USER);        if(currentUser == null) {            return ServerResponse.createByErrorCodeMessage(ResponseCode.NEED_LOGIN.getCode(), "未登录,需要强制登录status=10");        }        return iUserService.getInformation(currentUser.getId());    }}

同时也在common中新建了一个常量类,这里暂时只有一点内容,以后慢慢扩展。

package top.winxblast.happymall.common;/** * 常量类 * * @author winxblast * @create 2017/10/20 **/public class Const {    public static final String CURRENT_USER = "currentUser";    public static final String EMAIL = "email";    public static final String USERNAME = "username";     /**     * 这个分组如果使用枚举类可能显得比较重,这里用一个接口     * 接口不能包含实例域或静态方法,但可以包含常量     * 接口中的域将被自动设为 public static final     */    public interface Role{        int ROLE_CUSTOMER = 0; //普通用户        int ROLE_ADMIN = 1; //管理员    }}

用户service接口

对于上一小节注入controller的IUserService,这里给出接口的定义和实现类。controller调用service的接口,service接口实现类里面调用dao的接口进行数据库操作。

这里就是一个典型的先定义接口再实现类的实践,当然我自己还没能体会到这种方式的好处···

在service包下创建IUserService.java,这里接口名字还要添加大写“I”的习惯可能有些过时了,先按照老师的习惯写吧···

package top.winxblast.happymall.service;import top.winxblast.happymall.common.ServerResponse;import top.winxblast.happymall.pojo.User;/** * 前台用户接口设计 * 使用接口及接口实现,为以后的AOP做准备;无论在用静态代理还是动态代理包括后续发展 * 成的AOP,我们都用接口代理。类的代理扩展性没有接口强 * * @author winxblast * @create 2017/10/19 **/public interface IUserService {    ServerResponse<User> login(String username, String password);    ServerResponse<String> register(User user);    ServerResponse<String> checkValid(String str, String type);    ServerResponse<String> selectQuestion(String username);    ServerResponse<String> checkAnswer(String username, String question, String answer);    ServerResponse<String> forgetResetPassword(String username, String passwordNew, String forgetToken);    ServerResponse<String> resetPassword(User user, String passwordOld, String passwordNew);    ServerResponse<User> updateInformation(User user);    ServerResponse<User> getInformation(Integer userId);}

在service包下创建impl包,再创建UserServiceImpl.java

package top.winxblast.happymall.service.impl;import org.apache.commons.lang3.StringUtils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import top.winxblast.happymall.common.Const;import top.winxblast.happymall.common.ServerResponse;import top.winxblast.happymall.common.TokenCache;import top.winxblast.happymall.dao.UserMapper;import top.winxblast.happymall.pojo.User;import top.winxblast.happymall.service.IUserService;import top.winxblast.happymall.util.MD5Util;import java.util.UUID;/** * 前台用户接口实现 * * @author winxblast * @create 2017/10/19 **///注意这个service的注解,名字改为接口的首字母小写,后续注入controller也是用这个名字@Service("iUserService")public class UserServiceImpl implements IUserService {    @Autowired    private UserMapper userMapper;    @Override    public ServerResponse<User> login(String username, String password) {        int resultCount = userMapper.checkUsername(username);        if(resultCount == 0) {            return ServerResponse.createByErrorMessage("用户名不存在");        }        //密码登录MD5        String md5Password = MD5Util.MD5EncodeUtf8(password);        User user = userMapper.selectLogin(username, md5Password);        if(user == null) {            //因为上面已经判断过有没有用户名了,所以这里就是密码错误            return ServerResponse.createByErrorMessage("密码错误");        }        //如果到这都没有return,则就是登录成功        user.setPassword(StringUtils.EMPTY);        return ServerResponse.createBySuccess("登录成功", user);    }    @Override    public ServerResponse<String> register(User user) {//        int resultCount = userMapper.checkUsername(user.getUsername());//        if(resultCount > 0) {//            return ServerResponse.createByErrorMessage("用户名已存在");//        }////        resultCount = userMapper.checkEmail(user.getEmail());//        if(resultCount > 0) {//            return ServerResponse.createByErrorMessage("email已存在");//        }        //根据下面的checkValid方法改造这段内容        ServerResponse validResponse = this.checkValid(user.getUsername(), Const.USERNAME);        if(!validResponse.isSuccess()) {            return validResponse;        }        validResponse = this.checkValid(user.getEmail(), Const.EMAIL);        if(!validResponse.isSuccess()) {            return validResponse;        }        user.setRole(Const.Role.ROLE_CUSTOMER);        //MD5加密,数据库中不能存储明文,这里的安全知识还涉及重放攻击等,这里是比较简单的设计        user.setPassword(MD5Util.MD5EncodeUtf8(user.getPassword()));        //mybatis的insert、delete、update默认返回操作的条数        int resultCount = userMapper.insert(user);        if(resultCount == 0) {            return ServerResponse.createByErrorMessage("注册失败");        }        return ServerResponse.createBySuccessMessage("注册成功");    }    /**     * 通过type是email还是username去调用不同的sql判断     * @param str     * @param type 同样也把type的值设置常量     * @return     */    @Override    public ServerResponse<String> checkValid(String str, String type) {        //注意isNotBlank和isNotEmpty的区别,请看源码        if(StringUtils.isNotBlank(type)) {            //type不空才开始校验            if(Const.USERNAME.equals(type)) {                int resultCount = userMapper.checkUsername(str);                if(resultCount > 0) {                    return ServerResponse.createByErrorMessage("用户名已存在");                }            }            if(Const.EMAIL.equals(type)) {                int resultCount = userMapper.checkEmail(str);                if(resultCount > 0) {                    return ServerResponse.createByErrorMessage("email已存在");                }            }        } else {            return ServerResponse.createByErrorMessage("参数错误");        }        return ServerResponse.createBySuccessMessage("校验成功");    }    /**     * 通过用户名获取密码找回问题     * @param username     * @return     */    @Override    public ServerResponse<String> selectQuestion(String username) {        ServerResponse validResponse = this.checkValid(username, Const.USERNAME);        if(validResponse.isSuccess()) {            //用户不存在            return ServerResponse.createByErrorMessage("用户不存在");        }        String question = userMapper.selectQuestionByUsername(username);        if(StringUtils.isNotBlank(question)) {            return ServerResponse.createBySuccess(question);        }        return ServerResponse.createByErrorMessage("找回密码的问题是空的");    }    /**     * 检查密码提示问题的答案是否正确     * @param username     * @param question     * @param answer     * @return     */    @Override    public ServerResponse<String> checkAnswer(String username, String question, String answer) {        int resultCount = userMapper.checkAnswer(username,question,answer);        if(resultCount > 0) {            //说明问题及问题答案是这个用户的,并且是正确的            //这个UUID类是java.util自带的类,可以生成一个重复概率非常非常低的字符串            String forgeToken = UUID.randomUUID().toString();            //然后需要把这个forgetToken放到cache中并设置有效期            TokenCache.setKey(TokenCache.TOKEN_PREFIX + username, forgeToken);            return ServerResponse.createBySuccess(forgeToken);        }        return ServerResponse.createByErrorMessage("问题的答案错误");    }    @Override    public ServerResponse<String> forgetResetPassword(String username, String passwordNew, String forgetToken) {        if(StringUtils.isBlank(forgetToken)) {            return ServerResponse.createByErrorMessage("参数错误,token需要传递");        }        ServerResponse validResponse = this.checkValid(username,Const.USERNAME);        if(validResponse.isSuccess()) {            return ServerResponse.createByErrorMessage("用户不存在");        }        String token = TokenCache.getKey(TokenCache.TOKEN_PREFIX + username);        if(StringUtils.isBlank(token)) {            return ServerResponse.createByErrorMessage("token无效或者过期");        }        if(StringUtils.equals(forgetToken, token)) {            //看了一些安全方面资料,这里的password应该已经是前端把密码加了固定盐值再MD5之后的内容了            //所以这里可能是有漏洞的,不应该在后端加密            //todo 修改加密的模式            String md5Password = MD5Util.MD5EncodeUtf8(passwordNew);            int rowCount = userMapper.updatePasswordByUsername(username,md5Password);            if(rowCount > 0) {                return ServerResponse.createBySuccessMessage("修改密码成功");            }        } else {            return ServerResponse.createByErrorMessage("token错误,请重新获取重置密码的token");        }        return ServerResponse.createByErrorMessage("修改密码失败");    }    @Override    public ServerResponse<String> resetPassword(User user, String passwordOld, String passwordNew) {        //防止横向越权,要校验一下这个用户的旧密码,且一定要指定是不是这个用户的,因为我们会查询一个count(1),所以还是要全部核对一遍再修改        int resultCount = userMapper.checkPassword(user.getId(), MD5Util.MD5EncodeUtf8(passwordOld));        if(resultCount == 0) {            return ServerResponse.createByErrorMessage("旧密码错误");        }        user.setPassword(MD5Util.MD5EncodeUtf8(passwordNew));        int updateCount = userMapper.updateByPrimaryKeySelective(user);        if(updateCount > 0) {            return ServerResponse.createBySuccessMessage("密码更新成功");        }        return ServerResponse.createByErrorMessage("密码更新失败");    }    @Override    public ServerResponse<User> updateInformation(User user) {        //username是不能被更新的        //email也要进行一个校验,校验新的email是不是已经存在,并且存在的emai如果相同的话,不能是我们当前这个用户的        int resultCount = userMapper.checkEmailByUserId(user.getId(), user.getEmail());        if(resultCount > 0) {            return ServerResponse.createByErrorMessage("email已存在,请更换email再尝试更新");        }        //todo 这个项目的实例还是很简单的,包括手机号的验证等都没有写,等待扩展        User updateUser = new User();        updateUser.setId(user.getId());        updateUser.setUsername(user.getUsername());        updateUser.setEmail(user.getEmail());        updateUser.setPhone(user.getPhone());        updateUser.setQuestion(user.getQuestion());        updateUser.setAnswer(user.getAnswer());        int updateCount = userMapper.updateByPrimaryKeySelective(updateUser);        if(updateCount > 0) {            return ServerResponse.createBySuccess("更新个人信息成功", updateUser);        }        return ServerResponse.createByErrorMessage("更新个人信息失败");    }    @Override    public ServerResponse<User> getInformation(Integer userId) {        User user = userMapper.selectByPrimaryKey(userId);        if(user == null) {            return ServerResponse.createByErrorMessage("找不到当前用户");        }        //不能把密码传出去        user.setPassword(StringUtils.EMPTY);        return ServerResponse.createBySuccess(user);    }}

DAO部分

这里给出用户表相关接口,以及xml的配置,UserMapper.xml中没有mybatis generator注解的就是我后来自己加的

UserMapper.java

package top.winxblast.happymall.dao;import org.apache.ibatis.annotations.Param;import top.winxblast.happymall.pojo.User;public interface UserMapper {    /**     * This method was generated by MyBatis Generator.     * This method corresponds to the database table happymall_user     *     * @mbggenerated Sun Oct 08 14:03:47 CST 2017     */    int deleteByPrimaryKey(Integer id);    /**     * This method was generated by MyBatis Generator.     * This method corresponds to the database table happymall_user     *     * @mbggenerated Sun Oct 08 14:03:47 CST 2017     */    int insert(User record);    /**     * This method was generated by MyBatis Generator.     * This method corresponds to the database table happymall_user     *     * @mbggenerated Sun Oct 08 14:03:47 CST 2017     */    int insertSelective(User record);    /**     * This method was generated by MyBatis Generator.     * This method corresponds to the database table happymall_user     *     * @mbggenerated Sun Oct 08 14:03:47 CST 2017     */    User selectByPrimaryKey(Integer id);    /**     * This method was generated by MyBatis Generator.     * This method corresponds to the database table happymall_user     *     * @mbggenerated Sun Oct 08 14:03:47 CST 2017     */    int updateByPrimaryKeySelective(User record);    /**     * This method was generated by MyBatis Generator.     * This method corresponds to the database table happymall_user     *     * @mbggenerated Sun Oct 08 14:03:47 CST 2017     */    int updateByPrimaryKey(User record);    /**     * 通过用户名来查询数据库中是否存在用户     * @param username     * @return 用户数量     */    int checkUsername(String username);    /**     * 查看email是否已经被注册     * @param email     * @return     */    int checkEmail(String email);    /**     * 通过用户名和密码查看是否有这个用户     * mybatis在传递多个参数的时候需要用到param注解,写sql时就对应注解里的string     * @param username     * @param password     * @return     */    User selectLogin(@Param("username") String username, @Param("password") String password);    /**     * 通过用户名来获取密码提示问题     * @param username     * @return     */    String selectQuestionByUsername(String username);    /**     * 检查密码找回问题的答案是否正确     * 多个传入参数还是老规矩要使用@Param     * @param username     * @param question     * @param answer     * @return     */    int checkAnswer(@Param("username")String username, @Param("question")String question, @Param("answer")String answer);    /**     * 通过用户名修改密码     * @param username     * @param passwordNew     * @return     */    int updatePasswordByUsername(@Param("username")String username, @Param("passwordNew")String passwordNew);    /**     * 通过用户id验证密码是否正确,这个感觉跟selectLogin有点重复,只不过一个返回用户,     * 一个返回查询到的数量,可能取用户的多个字段性能会差一点,具体差多少有待试验     * @param userId     * @param password     * @return     */    int checkPassword(@Param("userId")Integer userId, @Param("password")String password);    /**     * 检查email地址是否有其他用户使用     * @param userId     * @param email     * @return 返回1,则email地址已被使用,返回0,则email未被使用     */    int checkEmailByUserId(@Param("userId")Integer userId, @Param("email")String email);}

UserMapper.xml
这里特别要注意一点就是sql函数count后面不要接空格···不然SQL会执行错误

<?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="top.winxblast.happymall.dao.UserMapper" >  <resultMap id="BaseResultMap" type="top.winxblast.happymall.pojo.User" >    <!--      WARNING - @mbggenerated      This element is automatically generated by MyBatis Generator, do not modify.      This element was generated on Sun Oct 08 14:03:47 CST 2017.    -->    <constructor >      <idArg column="id" jdbcType="INTEGER" javaType="java.lang.Integer" />      <arg column="username" jdbcType="VARCHAR" javaType="java.lang.String" />      <arg column="password" jdbcType="VARCHAR" javaType="java.lang.String" />      <arg column="email" jdbcType="VARCHAR" javaType="java.lang.String" />      <arg column="phone" jdbcType="VARCHAR" javaType="java.lang.String" />      <arg column="question" jdbcType="VARCHAR" javaType="java.lang.String" />      <arg column="answer" jdbcType="VARCHAR" javaType="java.lang.String" />      <arg column="role" jdbcType="INTEGER" javaType="java.lang.Integer" />      <arg column="create_time" jdbcType="TIMESTAMP" javaType="java.util.Date" />      <arg column="update_time" jdbcType="TIMESTAMP" javaType="java.util.Date" />    </constructor>  </resultMap>  <sql id="Base_Column_List" >    <!--      WARNING - @mbggenerated      This element is automatically generated by MyBatis Generator, do not modify.      This element was generated on Sun Oct 08 14:03:47 CST 2017.    -->    id, username, password, email, phone, question, answer, role, create_time, update_time  </sql>  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >    <!--      WARNING - @mbggenerated      This element is automatically generated by MyBatis Generator, do not modify.      This element was generated on Sun Oct 08 14:03:47 CST 2017.    -->    select     <include refid="Base_Column_List" />    from happymall_user    where id = #{id,jdbcType=INTEGER}  </select>  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer" >    <!--      WARNING - @mbggenerated      This element is automatically generated by MyBatis Generator, do not modify.      This element was generated on Sun Oct 08 14:03:47 CST 2017.    -->    delete from happymall_user    where id = #{id,jdbcType=INTEGER}  </delete>  <insert id="insert" parameterType="top.winxblast.happymall.pojo.User" >    <!--      WARNING - @mbggenerated      This element is automatically generated by MyBatis Generator, do not modify.      This element was generated on Sun Oct 08 14:03:47 CST 2017.    -->    insert into happymall_user (id, username, password,       email, phone, question,       answer, role, create_time,       update_time)    values (#{id,jdbcType=INTEGER}, #{username,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR},       #{email,jdbcType=VARCHAR}, #{phone,jdbcType=VARCHAR}, #{question,jdbcType=VARCHAR},       #{answer,jdbcType=VARCHAR}, #{role,jdbcType=INTEGER}, now(),      now())  </insert>  <insert id="insertSelective" parameterType="top.winxblast.happymall.pojo.User" >    <!--      WARNING - @mbggenerated      This element is automatically generated by MyBatis Generator, do not modify.      This element was generated on Sun Oct 08 14:03:47 CST 2017.    -->    insert into happymall_user    <trim prefix="(" suffix=")" suffixOverrides="," >      <if test="id != null" >        id,      </if>      <if test="username != null" >        username,      </if>      <if test="password != null" >        password,      </if>      <if test="email != null" >        email,      </if>      <if test="phone != null" >        phone,      </if>      <if test="question != null" >        question,      </if>      <if test="answer != null" >        answer,      </if>      <if test="role != null" >        role,      </if>      <if test="createTime != null" >        create_time,      </if>      <if test="updateTime != null" >        update_time,      </if>    </trim>    <trim prefix="values (" suffix=")" suffixOverrides="," >      <if test="id != null" >        #{id,jdbcType=INTEGER},      </if>      <if test="username != null" >        #{username,jdbcType=VARCHAR},      </if>      <if test="password != null" >        #{password,jdbcType=VARCHAR},      </if>      <if test="email != null" >        #{email,jdbcType=VARCHAR},      </if>      <if test="phone != null" >        #{phone,jdbcType=VARCHAR},      </if>      <if test="question != null" >        #{question,jdbcType=VARCHAR},      </if>      <if test="answer != null" >        #{answer,jdbcType=VARCHAR},      </if>      <if test="role != null" >        #{role,jdbcType=INTEGER},      </if>      <if test="createTime != null" >        now(),      </if>      <if test="updateTime != null" >        now(),      </if>    </trim>  </insert>  <update id="updateByPrimaryKeySelective" parameterType="top.winxblast.happymall.pojo.User" >    <!--      WARNING - @mbggenerated      This element is automatically generated by MyBatis Generator, do not modify.      This element was generated on Sun Oct 08 14:03:47 CST 2017.    -->    update happymall_user    <set >      <if test="username != null" >        username = #{username,jdbcType=VARCHAR},      </if>      <if test="password != null" >        password = #{password,jdbcType=VARCHAR},      </if>      <if test="email != null" >        email = #{email,jdbcType=VARCHAR},      </if>      <if test="phone != null" >        phone = #{phone,jdbcType=VARCHAR},      </if>      <if test="question != null" >        question = #{question,jdbcType=VARCHAR},      </if>      <if test="answer != null" >        answer = #{answer,jdbcType=VARCHAR},      </if>      <if test="role != null" >        role = #{role,jdbcType=INTEGER},      </if>      <if test="createTime != null" >        create_time = #{createTime,jdbcType=TIMESTAMP},      </if>      <if test="updateTime != null" >        update_time = now(),      </if>    </set>    where id = #{id,jdbcType=INTEGER}  </update>  <update id="updateByPrimaryKey" parameterType="top.winxblast.happymall.pojo.User" >    <!--      WARNING - @mbggenerated      This element is automatically generated by MyBatis Generator, do not modify.      This element was generated on Sun Oct 08 14:03:47 CST 2017.    -->    update happymall_user    set username = #{username,jdbcType=VARCHAR},      password = #{password,jdbcType=VARCHAR},      email = #{email,jdbcType=VARCHAR},      phone = #{phone,jdbcType=VARCHAR},      question = #{question,jdbcType=VARCHAR},      answer = #{answer,jdbcType=VARCHAR},      role = #{role,jdbcType=INTEGER},      create_time = #{createTime,jdbcType=TIMESTAMP},      update_time = now()    where id = #{id,jdbcType=INTEGER}  </update>  <select id="checkUsername" resultType="java.lang.Integer" parameterType="java.lang.String">    <!--#号是预编译,防止sql注入-->    SELECT COUNT(1) FROM happymall_user    WHERE username = #{username}  </select>  <select id="checkEmail" resultType="int" parameterType="string">    SELECT COUNT(1) FROM happymall_user    WHERE email = #{email}  </select>  <select id="selectLogin" resultMap="BaseResultMap" parameterType="map">    SELECT    <include refid="Base_Column_List"/>    FROM happymall_user    WHERE username = #{username}    AND password = #{password}  </select>  <select id="selectQuestionByUsername" resultType="string" parameterType="string">    SELECT    question    FROM happymall_user    WHERE username = #{username}  </select>  <select id="checkAnswer" resultType="int" parameterType="map">    SELECT    COUNT(1)    FROM happymall_user    WHERE username = #{username}    AND question = #{question}    AND answer = #{answer}  </select>  <update id="updatePasswordByUsername" parameterType="map">    UPDATE happymall_user    SET password = #{passwordNew}, update_time = now()    WHERE username = #{username}  </update>  <select id="checkPassword" resultType="int" parameterType="map">    SELECT COUNT(1)    FROM happymall_user    WHERE id = #{userId}    AND password = #{password}  </select>  <select id="checkEmailByUserId" resultType="int" parameterType="map">    <!--其实就是看看这个新的email是不是属于其他用户的-->    SELECT COUNT(1)    FROM happymall_user    WHERE email = #{email}    AND id != #{userId}  </select></mapper>

后台管理员用户

后台管理员在这里本质是和普通用户一样的,都在同一张user表中,只不过role字段不同而已,所以这里针对后台管理员用户只需要写controller层就行了,service层和dao层在功能够用的情况下完全可以不用重写。
血泪教训:RequestMapping不要用manager,会跟tomcat冲突,改用manage

package top.winxblast.happymall.controller.backend;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.ResponseBody;import top.winxblast.happymall.common.Const;import top.winxblast.happymall.common.ServerResponse;import top.winxblast.happymall.pojo.User;import top.winxblast.happymall.service.IUserService;import javax.servlet.http.HttpSession;/** * 后台管理员相关功能类,控制层 * * @author winxblast * @create 2017/10/22 **/@Controller//这里有个问题RequestMapping这里最好不要用manager,因为可能汇合tomcat自己的manager界面冲突,所以改为manage@RequestMapping("/manage/user")public class UserManageController {    @Autowired    private IUserService iUserService;    @RequestMapping(value = "/login.do", method = RequestMethod.POST)    @ResponseBody    public ServerResponse<User> login(String username, String password, HttpSession session) {        ServerResponse<User> response = iUserService.login(username, password);        if(response.isSuccess()) {            User user = response.getData();            if(user.getRole() == Const.Role.ROLE_ADMIN) {                //说明登录的是管理员                session.setAttribute(Const.CURRENT_USER, user);                return response;            } else {                return ServerResponse.createByErrorMessage("不是管理员,无法登录");            }        }        return response;    }}

小结(一些感想,想到啥说啥)

其实自己以前也没有接触过正规的开发内容,现在看来自己的习惯还是有很多要修改的,从上一篇的5-1先设计用户接口,再到这一篇的实现,就是一个比较规范的过程了。

这些规范就是代码质量,工程化的例子吧···(请原谅我简单的认识 ̄□ ̄||)

对于哪些代码要放在controller层中,哪些放在service层中有了些模模糊糊的认识,以后还要多看看别人的代码~