Java-springboot生鲜电商项目(二)用户模块

Java-springboot生鲜电商项目(二)用户模块

用户模块开发

涉及到的接口

  1. 用户注册登录
  2. 更新个性签名
  3. 退出登录
  4. 管理员登录

开发涉及到内容:

  1. 登录、注册
  2. 重名校验
  3. 密码加密存储
  4. session的使用
  5. 越权校验
  6. 统一响应
  7. 异常枚举
  8. Java异常体系
  9. postman实操
  10. 统一异常处理
  11. 更新个人信息

(一)用户注册

用户注册的开发思路:
  1. 在dao层创建用户注册需要对数据库操作的相关接口
  2. 在对应的mapper.xml下书写SQL语句
  3. 在service层创建业务相关的接口,并在Impl中对接口进行实现,书写主要的业务逻辑
  4. 在controller中定义好用户注册请求响应,相关参数,对前端的参数进行过滤,之后调用service接口实现前端的数据传输到数据库
  5. 在1到4步骤中,对于异常,工具等,进行重构统一处理,提高程序的健壮性
根据文档,创建common包下ApiResponse类来处理响应对象
#响应文档1
{
    "status": 10000,
    "msg": "SUCCESS",
    "data": null
}
#响应文档2
{
    "status": 10000,
    "msg": "SUCCESS",
    "data": {
        "id": 9,
        "username": "xiaomu2",
        "password": null,
        "personalizedSignature": "祝你今天好心情",
        "role": 2,
        "createTime": "2020-02-09T12:39:47.000+0000",
        "updateTime": "2020-02-10T16:56:02.000+0000"
    }
}
package com.hyb.mall.common;

import com.hyb.mall.exception.MallExceptionEnum;

public class ApiRestResponse<T> {
    private Integer status;
    private String msg;
    private T data;

    private static final int OK_CODE = 10000;
    private static final String OK_MSG = "SUCCESS";

    public ApiRestResponse(Integer status, String msg, T data) {
        this.status = status;
        this.msg = msg;
        this.data = data;
    }

    public ApiRestResponse(Integer status, String msg) {
        this.status = status;
        this.msg = msg;
    }

    public ApiRestResponse() {
        this(OK_CODE, OK_MSG);
    }

    //成功的时候调用的方法
    public static <T> ApiRestResponse<T> success() {
        return new ApiRestResponse<>();
    }

    //返回具有data的方法
    public static <T> ApiRestResponse<T> success(T result) {
        ApiRestResponse<T> response = new ApiRestResponse<>();
        response.setData(result);
        return response;
    }

    //失败时的方法
    public static <T> ApiRestResponse<T> error(Integer code, String msg) {
        return new ApiRestResponse<>(code, msg);
    }

    //失败时的方法
    public static <T> ApiRestResponse<T> error(MallExceptionEnum ex) {
        return new ApiRestResponse<>(ex.getCode(),ex.getMsg());
    }

    @Override
    public String toString() {
        return "ApiRestResponse{" +
                "status=" + status +
                ", msg='" + msg + '\'' +
                ", data=" + data +
                '}';
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public static int getOkCode() {
        return OK_CODE;
    }

    public static String getOkMsg() {
        return OK_MSG;
    }
}


定义controller返回的异常枚举
package com.hyb.mall.exception;

/**
 * 定义异常枚举
 */
public enum MallExceptionEnum {
    //业务异常 10000
    NEED_USER_NAME(10001,"用户名不能为空"),
    NEED_PASSWORD(10002,"密码不能为空"),
    PASSWORD_TO_SHORT(10003,"密码长度不小于8位"),
    NAME_EXISTED(10004,"不予许重名"),
    INSERT_FAILED(10005,"插入失败,请重试"),
    WRONG_PASSWORD(10006,"密码错误"),
    NEED_LOGIN(10007,"用户未登录"),
    UPDATE_FAILED(10008,"更新失败"),
    NEED_ADMIN(10009,"无管理员权限"),

    //系统异常 20000
    SYSTEM_ERROR(20000,"系统异常");
    /**
     * 异常码 code
     * 异常信息 msg
     */
    Integer code;
    String msg;

    MallExceptionEnum(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public Integer getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}

在exception包中定义统一异常处理
package com.hyb.mall.exception;

/**
 * 描述:统一异常
 */
public class MallException extends RuntimeException{
    /**
     * 异常码 code
     * 异常信息 msg
     */
    private final Integer code;
    private final String message;

    public MallException(Integer code, String message) {
        this.code = code;
        this.message = message;
    }
    public MallException(MallExceptionEnum exceptionEnum){
        this(exceptionEnum.getCode(),exceptionEnum.getMsg());
    }

    public Integer getCode() {
        return code;
    }

    @Override
    public String getMessage() {
        return message;
    }
}
1.在dao层先定义查询用户的接口,并且返回User查询对象
User selectByName(String userName);
2.在UserMapper映射中添加SQL查询语句
  <select id="selectByName" parameterType="java.lang.String" resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List"/>
    from imooc_mall_user
    where username = #{userName,jdbcType=VARCHAR}
  </select>
3.在UserService定义用户注册接口,并在Service实现接口
//用户注册
    void register(String userName,String password);
    public void register(String userName, String password) throws MallException {
        //1.查询用户名是否存在,不允许重名
        User result = userMapper.selectByName(userName);
        if (result != null) {
            throw new MallException(MallExceptionEnum.NAME_EXISTED);
        }
        //2.将用户名和密码写入数据库
        User user = new User();
        user.setUsername(userName);
        user.setPassword(password);
        int count = userMapper.insertSelective(user);
        if (count == 0) {
            throw new MallException(MallExceptionEnum.INSERT_FAILED);
        }
    }
4.在controller实现调用
      @PostMapping("/register")
    @ResponseBody
    public ApiRestResponse register(@RequestParam("userName") String userName, @RequestParam("password") String password) throws MallException {
        //1.校验:判断用户名是否为空
        if (StringUtils.isEmpty(userName)) {
            return ApiRestResponse.error(MallExceptionEnum.NEED_USER_NAME);
        }
        //2.校验:判断密码是否为空
        if (StringUtils.isEmpty(password)) {
            return ApiRestResponse.error(MallExceptionEnum.NEED_PASSWORD);
        }
        //3.密码长度不少于8位
        if (password.length() < 8) {
            return ApiRestResponse.error(MallExceptionEnum.PASSWORD_TO_SHORT);
        }
        //4.调用useService的注册方法,将用户名和密码写入数据库
        userService.register(userName, password);

        //5.写入成功后返回成功
        return ApiRestResponse.success();
    }
5.使用postman进行测试,效果如下:

在这里插入图片描述

在插入成功后,我将用户名,密码为空,密码小于8位在进行测试,结果成功返回预期效果。但是在测试完后,有一些错误的信息暴露出来,所以错误的处理还需要进行重构!!!

package com.hyb.mall.exception;

import com.hyb.mall.common.ApiRestResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 1. 描述:处理统一异常的handler
 */
@ControllerAdvice
public class GlobalExceptionHandler {

    private final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Object handleException(Exception e) {
        log.error("Default Exception: ", e);
        return ApiRestResponse.error(MallExceptionEnum.SYSTEM_ERROR);
    }

    @ExceptionHandler(MallException.class)
    @ResponseBody
    public Object handleImoocMallException(MallException e) {
        log.error("MallException: ", e);
        return ApiRestResponse.error(e.getCode(), e.getMessage());
    }
}

实现密码加密

  1. 在common下创建一个加盐常量的工具包,避免MD5加密被反破解
  2. 在util下创建MD5加密的工具类,对password进行加密处理
  3. 在注册接口的实现类中,用MD5加密的方法代替原来直接输入密码在数据库的操作
在common下Constant,用于加盐
package com.hyb.mall.common;
/**
 * 描述:常量
 */
public class Constant {
    //自定义常量,越复杂越好,和MD5混合使用,避免MD5反破解
    public static final String SALT = "82ps[d]][sffs.a";
    public static final String HYB_MALL_USER = "hyb_mall_user";
}

创建工具包Util,创建MD5Utils
package com.hyb.mall.util;

import com.hyb.mall.common.Constant;
import org.apache.tomcat.util.codec.binary.Base64;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * 描述:MD5加密工具
 */
public class MD5Utils {
    public static String getMD5Str(String strValue) throws NoSuchAlgorithmException {
        //传入算法MD5
        MessageDigest md5 = MessageDigest.getInstance("MD5");
        //传入的参数还需要进行base64的转码,方便存储
        return Base64.encodeBase64String(md5.digest((strValue + Constant.SALT).getBytes()));
    }

    public static void main(String[] args) throws NoSuchAlgorithmException {
    	//加密后的密码:q+z/brWEdxFpbgLJ2MTXng==
        String md5 = getMD5Str("1234");
        System.out.println(md5);
    }
}

在UserServiceImpl中替换原来password的存储密码方法,使用MD5进行密码存储
    @Override
    public void register(String userName, String password) throws MallException {
        //1.查询用户名是否存在,不允许重名
        User result = userMapper.selectByName(userName);
        if (result != null) {
            throw new MallException(MallExceptionEnum.NAME_EXISTED);
        }
        //2.将用户名和密码写入数据库
        User user = new User();
        user.setUsername(userName);
        //使用MD5加密对密码进行加密处理
        try {
            user.setPassword(MD5Utils.getMD5Str(password));
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        //直接输入密码不进行处理,存在安全隐患
//        user.setPassword(password);
        int count = userMapper.insertSelective(user);
        if (count == 0) {
            throw new MallException(MallExceptionEnum.INSERT_FAILED);
        }
    }

(二)用户登录

1. 在dao中UserMapper中添加查询数据库用户接口
//传入多个参数,需要用@Param
User selectLogin(@Param("userName") String userName,@Param("password") String password);
2. 在resources/mappers中UserMapper.xml中查询语句
  <select id="selectLogin" parameterType="map" resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List"/>
    from imooc_mall_user
    where username = #{userName,jdbcType=VARCHAR}
    and password = #{password}
  </select>
3. 在service中添加用户登录的业务接口
    //用户登录
    User login(String userName, String password) throws MallException;
4. 在service/Impl中实现用户登录的业务接口类,获取用户名,加密后的密码进行登录校验
    @Override
    public User login(String userName,String password) throws MallException {
        String md5Password = null;
        try {
            md5Password = MD5Utils.getMD5Str(password);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        User user = userMapper.selectLogin(userName,md5Password);
        if(user == null){
            throw new MallException(MallExceptionEnum.WRONG_PASSWORD);
        }
        return user;
    }
5. 在controller中主要是实现用户的登录,并将用户信息保存在session中。
    @PostMapping("/login")
    @ResponseBody
    public ApiRestResponse login(@RequestParam("userName") String userName,
                                 @RequestParam("password") String password,
                                 HttpSession session) throws MallException {
        //1.校验:判断用户名是否为空
        if (StringUtils.isEmpty(userName)) {
            return ApiRestResponse.error(MallExceptionEnum.NEED_USER_NAME);
        }
        //2.校验:判断密码是否为空
        if (StringUtils.isEmpty(password)) {
            return ApiRestResponse.error(MallExceptionEnum.NEED_PASSWORD);
        }
        User user = userService.login(userName, password);
        //为了安全,保存用户信息时,不将密码进行保存,返回null
        user.setPassword(null);
        //将用户信息存在session中
        session.setAttribute(Constant.HYB_MALL_USER,user);
        return ApiRestResponse.success(user);
    }

(三)更新用户个性签名

1.先在userService中创建相关接口
    //更新签名
    void updateInformation(User user) throws MallException;
2.实现个性签名中的接口
@Override
    public void updateInformation(User user) throws MallException {
        //更新个性签名
        int updateCount = userMapper.updateByPrimaryKeySelective(user);
        if (updateCount>1){
            throw new MallException(MallExceptionEnum.UPDATE_FAILED);
        }
    }
3.在controller中获取到了登录后的session信息,利用session去更新用户的个性签名。
//验证session是否进行存储
    @PostMapping("/user/update")
    @ResponseBody
    public ApiRestResponse updateUserInfo(HttpSession session,@RequestParam String signature) throws MallException {
        User currentUser = (User) session.getAttribute(Constant.HYB_MALL_USER);
        if(currentUser == null){
            return ApiRestResponse.error(MallExceptionEnum.NEED_LOGIN);
        }
        User user = new User();
        user.setId(currentUser.getId());
        user.setPersonalizedSignature(signature);
        userService.updateInformation(user);
        return ApiRestResponse.success();
    }

(四)退出用户登录

直接将用户信息从session中清除掉,以此来完成登出功能
    @PostMapping("/user/logout")
    @ResponseBody
    public ApiRestResponse logout(HttpSession session){
        session.removeAttribute(Constant.HYB_MALL_USER);
        return ApiRestResponse.success();
    }

(五)管理员登录

1.在service中定义管理员校验接口
    //校验是否管理员
    boolean checkAdminRole(User user);
2.在service/Impl实现类中判断用户是普通用户(1)还是管理员(2)
    @Override
    public boolean checkAdminRole(User user){
        //1是普通用户 2是管理员
        return user.getRole().equals(2);
    }
3.在controller中进行管理员的登录校验,与之前普通用户的登录差不多,只是需要判断登录的用户的身份是否是管理员
    @PostMapping("/adminlogin")
    @ResponseBody
    public ApiRestResponse adminlogin(@RequestParam("userName") String userName,
                                 @RequestParam("password") String password,
                                 HttpSession session) throws MallException {
        //1.校验:判断用户名是否为空
        if (StringUtils.isEmpty(userName)) {
            return ApiRestResponse.error(MallExceptionEnum.NEED_USER_NAME);
        }
        //2.校验:判断密码是否为空
        if (StringUtils.isEmpty(password)) {
            return ApiRestResponse.error(MallExceptionEnum.NEED_PASSWORD);
        }
        User user = userService.login(userName, password);
        //判断登录的用户的身份是否是管理员
        if (userService.checkAdminRole(user)) {
            //为了安全,保存用户信息时,不将密码进行保存,返回null
            user.setPassword(null);
            //将用户信息存在session中
            session.setAttribute(Constant.HYB_MALL_USER, user);
            return ApiRestResponse.success(user);
        }else {
            return ApiRestResponse.error(MallExceptionEnum.NEED_ADMIN);
        }

(六)使用postman进行接口校验

校验步骤:
  1. 先进行更新个性签名的更新,判断是否session能正常工作(更新结果失败,无登录结果)
  2. 进行用户注册,再次进行个性签名更新(更新结果还是失败,同样是无登录)
  3. 用户登录后,检查数据库是否存在和密码是否加密(校验成功)
  4. 再次进行个性签名更新(更新成功)
  5. 登出用户,清除session(登出成功)
  6. 修改数据库用户身份,将普通用户修改成管理员用户,再次进行管理员登录校验(管理员登录成功)
流程如下图所示:

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值