电商项目的用户模块
程序员文章站
2022-04-14 20:39:11
...
mmall用户模块
user数据表设计
CREATE TABLE `mmall_user` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户表id',
`username` varchar(50) NOT NULL COMMENT '用户名',
`password` varchar(50) NOT NULL COMMENT '用户密码,MD5加密',
`email` varchar(50) DEFAULT NULL,
`phone` varchar(20) DEFAULT NULL,
`question` varchar(100) DEFAULT NULL COMMENT '找回密码问题',
`answer` varchar(100) DEFAULT NULL COMMENT '找回密码答案',
`role` int(4) NOT NULL COMMENT '角色0-管理员,1-普通用户',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_time` datetime NOT NULL COMMENT '最后一次更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `user_name_unique` (`username`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8
用户模块接口文档
服务端响应对象(ServerResponse< T>)
响应对象封装以下3个属性
private int status;
private String msg;
private T date;
判断响应是否成功
@JsonIgnore//添加此注解,isSuccess()序列化之后不会显示在Json中
public boolean isSuccess(){
return this.status == ResponseCode.SUCCESS.getCode();
}
私有化构造函数,对外暴露静态方法返回所需要的响应对象,例如:
private ServerResponse(int status, T date) {
this.status = status;
this.date = date;
}
private ServerResponse(int status, String msg) {
this.status = status;
this.msg = msg;
}
响应成功
public static <T> ServerResponse<T> createBySuccess(T data){
return new ServerResponse<T>(ResponseCode.SUCCESS.getCode(), data);
}
public static <T> ServerResponse<T> createBySuccessMessage(String msg){
return new ServerResponse<T>(ResponseCode.SUCCESS.getCode(), msg);
}
响应失败
public static <T> ServerResponse<T> createByErrorMessage(String errorMessage){
return new ServerResponse<T>(ResponseCode.ERROR.getCode(), errorMessage);
}
ResponseCode
public enum ResponseCode {
SUCCESS(0, "SUCCESSS"),
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;
}
}
本地缓存TokenCache
public class TokenCache {
private static Logger logger = LoggerFactory.getLogger(TokenCache.class);
public static final String TOKEN_PREFIX = "token_";
//Guava缓存
private static LoadingCache<String, String> localCache = CacheBuilder.newBuilder().initialCapacity(1000).maximumSize(10000).expireAfterAccess(12, TimeUnit.HOURS)
.build(new CacheLoader<String, String>() {
//默认的数据加载实现,当调用get取值的时候,如果key没有对应的值,就调用这个方法进行加载
@Override
public String load(String s) throws Exception {
return "null";
}
});
public static void setKey(String key, String value) {
localCache.put(key, value);
}
public static String getKey(String key) {
String value = null;
try {
value = localCache.get(key);
if ("null".equals(value)) {
return null;
}
return value;
} catch (ExecutionException e) {
logger.error("localCache get error", e);
}
return null;
}
}
Token 验证的优势
无状态,可扩展和解耦
使用 token 而不是 cookie 的最大优点应该就是无状态,后端不需要保持对 token 的记录,每个 token 都是独立的
例如:用户忘记密码显示提示问题,在输入提示答案的时候校验正确,UUID随机生成的forgetToken(为了token复杂度可以+userID+时间戳),存入到TokenCache中
@Override
public ServerResponse<String> checkAnswer(String username, String question, String answer) {
int resultCount = userMapper.checkAnswer(username, question, answer);
if (resultCount > 0) {
//说明问题及问题答案是这个用户的,并且是正确的
String forgetToken = UUID.randomUUID().toString();
TokenCache.setKey(TokenCache.TOKEN_PREFIX + username, forgetToken);
return ServerResponse.createBySuccess(forgetToken);
}
return ServerResponse.createByErrorMessage("问题的答案错误");
}
紧接着,忘记密码状态下重置密码,需要传递token
@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)) {
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("修改密码失败");
}
总结:为了用户信息的安全性,忘记密码重置密码的情况下,需要客户端传递token进行验证
登录、注册、修改信息、查看、检验、退出登录
以更新用户信息为例:
UserController
@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.setId(currentUser.getId());
user.setUsername(currentUser.getUsername());
ServerResponse<User> response = iUserService.updateInformation(user);
if (response.isSuccess()) {
session.setAttribute(Const.CURRENT_USER, response.getDate());
}
return response;
}
IUserService实现类UserServiceImpl
@Override
public ServerResponse<User> updateInformation(User user) {
//username是不能被更新的
//email校验:校验新的email是不是已经存在,如果存在的话,必须得是当前用户的
int resultCount = userMapper.checkEmailByUserId(user.getEmail(), user.getId());
if (resultCount > 0) {
return ServerResponse.createByErrorMessage("email已经存在,请更换email再尝试更新");
}
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.createByErrorMessage("更新个人信息失败");
}
UserMapper
int updateByPrimaryKeySelective(User record);
UserMapper.xml
<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>
测试门户_用户接口
上一篇: 电商平台搭建--用户功能模块开发(一)
下一篇: 洛谷P3953 逛公园(dp 拓扑排序)