一、用户模块功能概述
二、主要接口WIKI
1 用户接口:https://gitee.com/imooccode/happymmallwiki/wikis/%E9%97%A8%E6%88%B7_%E7%94%A8%E6%88%B7%E6%8E%A5%E5%8F%A3
(1)登录 /user/login.do
(1)注册 /user/register.do
(1)检查用户名是否有效:/user/check_valid.do
(1)获取登录用户信息 /user/get_user_info.do
(1)忘记密码 /user/forget_get_question.do
(1)提交问题答案 /user/forget_check_answer.do
(1)忘记密码的重设密码 /user/forget_reset_password.do
(8)登录中状态重置密码 /user/reset_password.do
(9)登录状态更新个人信息 /user/update_information.do
(10)获取当前登录用户的详细信息,并强制登录 /user/get_information.do
(11)退出登录 /user/logout.do
2 后台用户接口
https://gitee.com/imooccode/happymmallwiki/wikis/%E5%90%8E%E5%8F%B0_%E7%94%A8%E6%88%B7%E6%8E%A5%E5%8F%A3
(1)后台管理员登录 /manage/user/login.do
(2)用户列表 /manage/user/list.do
二、通用类
1 定义常量类Const.java
package com.mmall.common; /** * @author GenshenWang.nomico * @date 2018/4/5. */ public class Const { public static final String CURRENT_USER = "currentUser"; public static final String EMAIL = "email"; public static final String USERNAME = "username"; public interface Role{ int ROLE_CUSTOMER = 0; //普通用户 int ROLE_ADMIN = 1;//管理员 } }
2 返回码类ResponseCode.java
package com.mmall.common; /** * @author GenshenWang.nomico * @date 2018/4/5. */ 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; ResponseCode(int code, String desc){ this.code = code; this.desc = desc; } public int getCode(){ return code; } public String getDesc() { return desc; } }
3 序列化返回ServerResponse.java
package com.mmall.common; import jdk.nashorn.internal.ir.annotations.Ignore; import org.codehaus.jackson.map.annotate.JsonSerialize; import java.io.Serializable; /** * @author GenshenWang.nomico * @date 2018/4/5. */ @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) //保证序列化json的时候,如果是null的对象,key也会消失 public class ServerResponse<T> implements Serializable { private int status; private String msg; private T data; private ServerResponse(int status){ this.status = status; } private ServerResponse(int status, String msg){ this.status = status; this.msg = msg; } private ServerResponse(int status, T data){ this.status = status; this.data = data; } private ServerResponse(int status, String msg, T data){ this.status = status; this.msg = msg; this.data = data; } @Ignore public boolean isSuccess(){ return this.status == ResponseCode.SUCCESS.getCode(); } public int getStatus() { return status; } public String getMsg() { return msg; } public T getData() { return data; } public static<T> ServerResponse<T> createBySuccess(){ return new ServerResponse<T>(ResponseCode.SUCCESS.getCode()); } public static<T> ServerResponse<T> createBySuccess(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); } public static<T> ServerResponse<T> createByError(){ return new ServerResponse<T>(ResponseCode.ERROR.getCode(), ResponseCode.ERROR.getDesc()); } public static<T> ServerResponse<T> createByErrorMsg(String errorMsg){ return new ServerResponse<T>(ResponseCode.ERROR.getCode(), errorMsg); } public static<T> ServerResponse<T> createByErrorCodeMsg(int errorCode, String errorMsg){ return new ServerResponse<T>(errorCode, errorMsg); } }
4 使用内存保存session :TokenCache.java
package com.mmall.common; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; /** * @author GenshenWang.nomico * @date 2018/4/5. */ public class TokenCache { private static Logger logger = LoggerFactory.getLogger(TokenCache.class); public static final String TOKEN_PREFIX = "token_"; /** * 超过maximumSize,就会使用LRU算法 */ private static LoadingCache<String, String> loadingCache = CacheBuilder.newBuilder().initialCapacity(1000).maximumSize(10000).expireAfterAccess(12, TimeUnit.HOURS) .build(new CacheLoader<String, String>() { @Override public String load(String s) throws Exception { return "null"; } }); public static void setKey(String key, String value){ loadingCache.put(key, value); } public static String getKey(String key){ String value = null; try { //若没有命中,则会调用LoadingCache中load(String s)方法 value = loadingCache.get(key); if ("null".equals(value)){ return null; } return value; } catch (ExecutionException e) { logger.error("localCache get error", e); } return null; } }
三、工具类
1 MD5加密类:MD5Util.java
package com.mmall.util; import org.springframework.util.StringUtils; import java.security.MessageDigest; /** * Created by geely */ public class MD5Util { private static String byteArrayToHexString(byte b[]) { StringBuffer resultSb = new StringBuffer(); for (int i = 0; i < b.length; i++){ resultSb.append(byteToHexString(b[i])); } return resultSb.toString(); } private static String byteToHexString(byte b) { int n = b; if (n < 0){ n += 256; } int d1 = n / 16; int d2 = n % 16; return hexDigits[d1] + hexDigits[d2]; } /** * 返回大写MD5 * * @param origin * @param charsetname * @return */ private static String MD5Encode(String origin, String charsetname) { String resultString = null; try { resultString = new String(origin); MessageDigest md = MessageDigest.getInstance("MD5"); if (charsetname == null || "".equals(charsetname)){ resultString = byteArrayToHexString(md.digest(resultString.getBytes())); } else{ resultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname))); } } catch (Exception exception) { } return resultString.toUpperCase(); } public static String MD5EncodeUtf8(String origin) { origin = origin + PropertiesUtil.getProperty("password.salt", ""); return MD5Encode(origin, "utf-8"); } private static final String hexDigits[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"}; public static void main(String[] args) { System.out.println(MD5EncodeUtf8("1234")); } }
2 读取文件类:PropertiesUtil.java
package com.mmall.util; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.InputStreamReader; import java.util.Properties; /** * Created by geely */ public class PropertiesUtil { private static Logger logger = LoggerFactory.getLogger(PropertiesUtil.class); private static Properties props; static { String fileName = "mmall.properties"; props = new Properties(); try { props.load(new InputStreamReader(PropertiesUtil.class.getClassLoader().getResourceAsStream(fileName),"UTF-8")); } catch (IOException e) { logger.error("配置文件读取异常",e); } } public static String getProperty(String key){ String value = props.getProperty(key.trim()); if(StringUtils.isBlank(value)){ return null; } return value.trim(); } public static String getProperty(String key,String defaultValue){ String value = props.getProperty(key.trim()); if(StringUtils.isBlank(value)){ value = defaultValue; } return value.trim(); } }
四、用户DAO层
package com.mmall.dao; import com.mmall.pojo.User; import org.apache.ibatis.annotations.Param; public interface UserMapper { int deleteByPrimaryKey(Integer id); int insert(User record); int insertSelective(User record); User selectByPrimaryKey(Integer id); int updateByPrimaryKeySelective(User record); int updateByPrimaryKey(User record); int checkUsername(String username); int chcekEmail(String email); String selectQuestionByUsername(String username); User selectLogin(@Param("username") String username, @Param("password") String password); int checkAnswer(@Param("username") String username, @Param("question") String question, @Param("answer") String answer); int updatePasswordByUsername(@Param("username") String username, @Param("passwordNew") String passwordNew); int checkPassword(@Param("password") String password, @Param("userId") int userId); int checkEmailByUserId(@Param("email") String email, @Param("userId") int userId); }
xml实现:
<?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.mmall.dao.UserMapper" > <resultMap id="BaseResultMap" type="com.mmall.pojo.User" > <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" > id, username, password, email, phone, question, answer, role, create_time, update_time </sql> <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" > select <include refid="Base_Column_List" /> from mmall_user where id = #{id,jdbcType=INTEGER} </select> <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer" > delete from mmall_user where id = #{id,jdbcType=INTEGER} </delete> <insert id="insert" parameterType="com.mmall.pojo.User" > insert into mmall_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="com.mmall.pojo.User" > insert into mmall_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="com.mmall.pojo.User" > update mmall_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="com.mmall.pojo.User" > update mmall_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="int" parameterType="string"> SELECT count(1) from mmall_user where username=#{username} </select> <select id="chcekEmail" resultType="int" parameterType="string"> SELECT count(1) FROM mmall_user WHERE email = #{email} </select> <select id="selectQuestionByUsername" resultType="string" parameterType="string"> SELECT question FROM mmall_user WHERE username = #{username} </select> <select id="selectLogin" resultMap="BaseResultMap" parameterType="map"> SELECT <include refid="Base_Column_List" /> FROM mmall_user WHERE username=#{username} AND password=#{password} </select> <select id="checkAnswer" resultType="int" parameterType="map"> SELECT COUNT(1) FROM mmall_user WHERE username= #{username} AND question = #{question} AND answer = #{answer} </select> <update id="updatePasswordByUsername" parameterType="map"> UPDATE mmall_user SET password = #{passwordNew}, update_time = now() WHERE username = #{username} </update> <select id="checkPassword" parameterType="map" resultType="int"> SELECT count(1) FROM mmall_user WHERE password = #{password} AND id = #{userId} </select> <select id="checkEmailByUserId" resultType="int" parameterType="map"> SELECT count(1) FROM mmall_user WHERE email = #{email} AND id != #{userId} </select> </mapper>
五、Service层
package com.mmall.service; import com.mmall.common.ServerResponse; import com.mmall.pojo.User; /** * @author GenshenWang.nomico * @date 2018/4/5. */ 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> forgetCheckAnswer(String username, String question, String answer); ServerResponse<String> forgetResetPassword(String username, String passwordNew, String forgetToken); ServerResponse<String> resetPassword(String passwordOld, String passwordNew, User user); ServerResponse<User> updateInformation(User user); ServerResponse<User> getInformation(Integer userId); ServerResponse<String> checkAdminRole(User user); }
Service Impl:
package com.mmall.service.impl; import com.mmall.common.Const; import com.mmall.common.ServerResponse; import com.mmall.common.TokenCache; import com.mmall.dao.UserMapper; import com.mmall.pojo.User; import com.mmall.service.IUserService; import com.mmall.util.MD5Util; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.UUID; /** * @author GenshenWang.nomico * @date 2018/4/5. */ @Service("iUserService") public class UserServiceImpl implements IUserService { @Autowired UserMapper userMapper; @Override public ServerResponse<User> login(String username, String password) { int resultCount = userMapper.checkUsername(username); if (resultCount == 0){ return ServerResponse.createByErrorMsg("用户名不存在!"); } String md5Password = MD5Util.MD5EncodeUtf8(password); User user = userMapper.selectLogin(username, md5Password); if (user == null){ return ServerResponse.createByErrorMsg("密码错误!"); } 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.createByErrorMsg("用户名已经存在"); }*/ ServerResponse<String> validResponse = checkValid(user.getUsername(), Const.USERNAME); if (!validResponse.isSuccess()){ return validResponse; } /* resultCount = userMapper.chcekEmail(user.getEmail()); if (resultCount > 0){ return ServerResponse.createByErrorMsg("邮箱已被注册!"); }*/ validResponse = checkValid(user.getEmail(), Const.EMAIL); if (!validResponse.isSuccess()){ return validResponse; } user.setRole(Const.Role.ROLE_CUSTOMER); user.setPassword(MD5Util.MD5EncodeUtf8(user.getPassword())); int resultCount = userMapper.insert(user); if (resultCount == 0){ return ServerResponse.createByErrorMsg("注册失败"); } return ServerResponse.createBySuccess("注册成功"); } @Override public ServerResponse<String> checkValid(String str, String type){ int resultCount = 0; if (!org.apache.commons.lang.StringUtils.isBlank(str)){ if (Const.USERNAME.equals(type)){ resultCount = userMapper.checkUsername(str); if (resultCount > 0){ return ServerResponse.createByErrorMsg("用户名已经存在"); } } if (Const.EMAIL.equals(type)){ resultCount = userMapper.chcekEmail(str); if (resultCount > 0){ return ServerResponse.createByErrorMsg("邮箱已经被注册"); } } }else { return ServerResponse.createByErrorMsg("参数错误"); } //success 表示用户不存在或者邮箱不存在 return ServerResponse.createBySuccess("校验成功"); } @Override public ServerResponse<String> selectQuestion(String username){ ServerResponse<String> validResponse = checkValid(username, Const.USERNAME); //检查用户名是否不存在 :isSuccess表示之前没有此用户 if (validResponse.isSuccess()){ return ServerResponse.createByErrorMsg("用户名不存在"); } String question = userMapper.selectQuestionByUsername(username); if (StringUtils.isNoneBlank(question)){ return ServerResponse.createBySuccess(question); } return ServerResponse.createByErrorMsg("该用户未设置找回密码问题"); } @Override public ServerResponse<String> forgetCheckAnswer(String username, String question, String answer){ int resultCount = userMapper.checkAnswer(username, question, answer); //用户提交的答案正确,存在该用户 if (resultCount > 0){ String forgetToken = UUID.randomUUID().toString(); //正确的返回值里面有一个token,修改密码的时候需要用这个。 TokenCache.setKey(TokenCache.TOKEN_PREFIX+username, forgetToken); return ServerResponse.createBySuccess(forgetToken); } return ServerResponse.createByErrorMsg("问题答案错误"); } @Override public ServerResponse<String> forgetResetPassword(String username, String passwordNew, String forgetToken){ if (StringUtils.isBlank(forgetToken)){ return ServerResponse.createByErrorMsg("参数错误,token需要传递"); } ServerResponse<String> validResponse = checkValid(username, Const.USERNAME); if (validResponse.isSuccess()){ return ServerResponse.createBySuccess("用户名不存在"); } String token = TokenCache.getKey(TokenCache.TOKEN_PREFIX+username); if (StringUtils.isBlank(token)){ return ServerResponse.createByErrorMsg("token无效或者过期"); } if (StringUtils.equals(token, forgetToken)){ String md5PasswordNew = MD5Util.MD5EncodeUtf8(passwordNew); int resultCount = userMapper.updatePasswordByUsername(username, md5PasswordNew); if (resultCount > 0){ return ServerResponse.createBySuccess("修改密码成功"); } }else { return ServerResponse.createByErrorMsg("token错误,请重新获取重置密码的token"); } return ServerResponse.createByErrorMsg("修改密码失败"); } @Override public ServerResponse<String> resetPassword(String passwordOld, String passwordNew, User user){ //防止横向越权,要校验一下这个用户的旧密码,一定要指定是这个用户.因为我们会查询一个count(1) //即当前password是a用户的话,b用户可能通过字典的方式一一去试,当试到password与a相同的时候,b可能就会登录a的账户 int rowCount = userMapper.checkPassword(MD5Util.MD5EncodeUtf8(passwordOld), user.getId()); if (rowCount == 0){ return ServerResponse.createByErrorMsg("旧密码输入错误"); } user.setPassword(MD5Util.MD5EncodeUtf8(passwordNew)); int updateCount = userMapper.updateByPrimaryKeySelective(user); if (updateCount > 0){ return ServerResponse.createBySuccess("密码更新成功"); } return ServerResponse.createByErrorMsg("密码更新失败"); } @Override public ServerResponse<User> updateInformation(User user){ //首先需要检查更新的email不能有重复 int resultCount = userMapper.checkEmailByUserId(user.getEmail(), user.getId()); if (resultCount > 0){ return ServerResponse.createByErrorMsg("邮箱已经被注册,请重新输入"); } User updateUser = new User(); updateUser.setId(user.getId()); 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.createByErrorMsg("更新个人信息失败"); } @Override public ServerResponse<User> getInformation(Integer userId){ User user = userMapper.selectByPrimaryKey(userId); if (user == null){ return ServerResponse.createByErrorMsg("当前用户不存在"); } user.setPassword(StringUtils.EMPTY); return ServerResponse.createBySuccess(user); } @Override public ServerResponse<String> checkAdminRole(User user){ if (user != null && user.getRole().intValue() == Const.Role.ROLE_CUSTOMER){ return ServerResponse.createBySuccess(); } return ServerResponse.createByErrorMsg("用户无权限操作"); } }
六、Controller层
用户端:
package com.mmall.controller.portal; import com.mmall.common.Const; import com.mmall.common.ResponseCode; import com.mmall.common.ServerResponse; import com.mmall.pojo.User; import com.mmall.service.IUserService; 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 javax.servlet.http.HttpSession; /** * @author GenshenWang.nomico * @date 2018/4/5. */ @Controller @RequestMapping(value = "/user") public class UserController { @Autowired IUserService iUserService; /** * 用户登录 * @param username * @param password * @param session * @return */ @RequestMapping(value = "/login.do", method = RequestMethod.POST) @ResponseBody public ServerResponse<User> login(String username, String password, HttpSession session){ ServerResponse<User> serverResponse = iUserService.login(username, password); //登录成功后,将用户信息存放到Session中 if (serverResponse.isSuccess()){ session.setAttribute(Const.CURRENT_USER, serverResponse.getData()); } return serverResponse; } /** * 退出登录 * @param session * @return */ @RequestMapping(value = "/logout.do", method = RequestMethod.POST) @ResponseBody public ServerResponse<User> logout(HttpSession session){ session.removeAttribute(Const.CURRENT_USER); return ServerResponse.createBySuccess("退出登录!"); } /** * 注册 * @param user * @return */ @RequestMapping(value = "/register", method = RequestMethod.POST) @ResponseBody public ServerResponse<String> register(User user){ return iUserService.register(user); } /** * 实时检测username或邮箱是否规范 * @param str * @param type * @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.createByErrorMsg("用户未登录,无法获取信息"); } return ServerResponse.createBySuccess(user); } /** * 忘记密码时获取提示问题 * @param username * @return */ @RequestMapping(value = "/forget_get_question.do", method = RequestMethod.POST) @ResponseBody public ServerResponse<String> forgetGetQuestion(String username){ return iUserService.selectQuestion(username); } /** * 忘记密码时,获取到提示问题。根据问题填写答案,判断答案正确与否 * @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.forgetCheckAnswer(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 passwordOld * @param passwordNew * @param session * @return */ @RequestMapping(value = "/reset_password.do", method = RequestMethod.POST) @ResponseBody public ServerResponse<String> resetPassword(String passwordOld, String passwordNew, HttpSession session){ User user = (User) session.getAttribute(Const.CURRENT_USER); if (user == null){ return ServerResponse.createByErrorMsg("用户未登录"); } return iUserService.resetPassword(passwordOld, passwordNew, user); } /** * 登录状态更新个人信息 * @param updateUser * @param session * @return */ @RequestMapping(value = "/update_information.do", method = RequestMethod.POST) @ResponseBody public ServerResponse<User> updateInformation(User updateUser, HttpSession session){ User currentUser = (User) session.getAttribute(Const.CURRENT_USER); if (currentUser == null){ return ServerResponse.createByErrorMsg("用户未登录"); } //id 和 username是不能被更改的 updateUser.setId(currentUser.getId()); updateUser.setUsername(currentUser.getUsername()); ServerResponse<User> response = iUserService.updateInformation(updateUser); //更新session if (response.isSuccess()){ response.getData().setUsername(currentUser.getUsername()); 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.createByErrorCodeMsg(ResponseCode.NEED_LOGIN.getCode(), "用户未登录,无法获取当前用户信息,status=10,强制登录"); } return iUserService.getInformation(currentUser.getId()); } }
后台端:
package com.mmall.controller.backend; import com.mmall.common.Const; import com.mmall.common.ServerResponse; import com.mmall.pojo.User; import com.mmall.service.IUserService; 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 javax.servlet.http.HttpSession; /** * @author GenshenWang.nomico * @date 2018/4/6. */ @RequestMapping("/manage/user") @Controller public class UserManageController { @Autowired IUserService iUserService; /** * 后台管理员登录 * @param username * @param password * @param session * @return */ @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(); int role = user.getRole(); if (role == Const.Role.ROLE_ADMIN){ session.setAttribute(Const.CURRENT_USER, user); return response; }else { return ServerResponse.createByErrorMsg("不是管理员,无法登录"); } } return response; } }
七、实体类
package com.mmall.pojo; import java.util.Date; public class User { private Integer id; private String username; private String password; private String email; private String phone; private String question; private String answer; private Integer role; private Date createTime; private Date updateTime; public User(Integer id, String username, String password, String email, String phone, String question, String answer, Integer role, Date createTime, Date updateTime) { this.id = id; this.username = username; this.password = password; this.email = email; this.phone = phone; this.question = question; this.answer = answer; this.role = role; this.createTime = createTime; this.updateTime = updateTime; } public User() { super(); } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username == null ? null : username.trim(); } public String getPassword() { return password; } public void setPassword(String password) { this.password = password == null ? null : password.trim(); } public String getEmail() { return email; } public void setEmail(String email) { this.email = email == null ? null : email.trim(); } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone == null ? null : phone.trim(); } public String getQuestion() { return question; } public void setQuestion(String question) { this.question = question == null ? null : question.trim(); } public String getAnswer() { return answer; } public void setAnswer(String answer) { this.answer = answer == null ? null : answer.trim(); } public Integer getRole() { return role; } public void setRole(Integer role) { this.role = role; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public Date getUpdateTime() { return updateTime; } public void setUpdateTime(Date updateTime) { this.updateTime = updateTime; } }
八、好的思想
1 读取用户的时候,需要对用户密码清除,保护隐私
public ServerResponse<User> getInformation(Integer userId){ User user = userMapper.selectByPrimaryKey(userId); if (user == null){ return ServerResponse.createByErrorMsg("当前用户不存在"); } user.setPassword(StringUtils.EMPTY); return ServerResponse.createBySuccess(user); }
2 LoadingCache的使用
package com.mmall.common; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; /** * @author GenshenWang.nomico * @date 2018/4/5. */ public class TokenCache { private static Logger logger = LoggerFactory.getLogger(TokenCache.class); public static final String TOKEN_PREFIX = "token_"; /** * 超过maximumSize,就会使用LRU算法 */ private static LoadingCache<String, String> loadingCache = CacheBuilder.newBuilder().initialCapacity(1000).maximumSize(10000).expireAfterAccess(12, TimeUnit.HOURS) .build(new CacheLoader<String, String>() { @Override public String load(String s) throws Exception { return "null"; } }); public static void setKey(String key, String value){ loadingCache.put(key, value); } public static String getKey(String key){ String value = null; try { //若没有命中,则会调用LoadingCache中load(String s)方法 value = loadingCache.get(key); if ("null".equals(value)){ return null; } return value; } catch (ExecutionException e) { logger.error("localCache get error", e); } return null; } }
使用:用户忘记密码的时候,根据提示问题回答答案正确后,将此用户的token放到缓存中。
修改密码的时候,需要获取到token进行比对,以验证是同一人在操作,防止横向越权。
public ServerResponse<String> forgetCheckAnswer(String username, String question, String answer){ int resultCount = userMapper.checkAnswer(username, question, answer); //用户提交的答案正确,存在该用户 if (resultCount > 0){ String forgetToken = UUID.randomUUID().toString(); //正确的返回值里面有一个token,修改密码的时候需要用这个。 TokenCache.setKey(TokenCache.TOKEN_PREFIX+username, forgetToken); return ServerResponse.createBySuccess(forgetToken); } return ServerResponse.createByErrorMsg("问题答案错误"); }
public ServerResponse<String> forgetResetPassword(String username, String passwordNew, String forgetToken){ if (StringUtils.isBlank(forgetToken)){ return ServerResponse.createByErrorMsg("参数错误,token需要传递"); } ServerResponse<String> validResponse = checkValid(username, Const.USERNAME); if (validResponse.isSuccess()){ return ServerResponse.createBySuccess("用户名不存在"); } String token = TokenCache.getKey(TokenCache.TOKEN_PREFIX+username); if (StringUtils.isBlank(token)){ return ServerResponse.createByErrorMsg("token无效或者过期"); } if (StringUtils.equals(token, forgetToken)){ String md5PasswordNew = MD5Util.MD5EncodeUtf8(passwordNew); int resultCount = userMapper.updatePasswordByUsername(username, md5PasswordNew); if (resultCount > 0){ return ServerResponse.createBySuccess("修改密码成功"); } }else { return ServerResponse.createByErrorMsg("token错误,请重新获取重置密码的token"); } return ServerResponse.createByErrorMsg("修改密码失败"); }