【实战】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层中有了些模模糊糊的认识,以后还要多看看别人的代码~
- 【实战】5-2 用户登录相关功能开发
- Spring MVC开发--开发用户登录功能
- weex开发实战(4)-实现登录功能
- 讲项目实战:网站会员功能开发+登录/注册表单
- linux 登录用户相关
- 用户登录相关问题
- Flex4.5实现用户登录功能
- ajax 用户 登录 功能
- 模拟用户登录功能
- 用户免登录功能
- django实战--天天生鲜项目开发(用户注册-登录-用户中心)
- struts实战--登录功能实现
- yii2项目实战-用户管理之登录与注册功能实现
- 实战演练:用gulp+webpack构建用户登录(2):简化
- 【实战】5-1 用户模块开发
- Flask实战2问答平台-完成登录注册功能
- Qt 用户登录相关设置
- 用户限制登录的功能
- 点击显示/隐藏密码
- POJ 2413 How many Fibs?(高精度暴力)
- Spring事务配置解惑
- java-mybatis generator-生成 mysql 数据库访问文件
- 错误记录--更改tomcat端口号方法,Several ports (8005, 8080, 8009)
- 【实战】5-2 用户登录相关功能开发
- 【基本算术定理 && 质因数分解】LightOJ
- 架构师之路-在Dubbo中开发REST风格的远程调用
- 运动健身行业小程序开发详解
- 1.3 事务
- 微信小程序的一些小坑
- 《Linux设备驱动程序》第二章 笔记
- Android 版本控制 git 转 coding相关流程和问题
- 实例测试浮点数在内存中的存储状态