Java电商项目面试--用户模块

面试:用户模块技术要点:
1、横向越权和纵向越权
2、MD5明文加密、guava缓存
3、高复用服务响应对象的设计思想和封装

一、用户模块功能
用户模块包含功能如下:
1、登录功能
2、用户名校验
3、注册功能
4、忘记密码
5、提交问题答案
6、重置密码
7、获取用户信息
8、更新用户信息
9、退出登录
二、高复用服务响应对象的设计思想和封装
在web开发中,现在比较流行的是从控制层往前台返回json格式的数据,而若每次的返回都设计一个类的话,不方便使用的同时也会显得很臃肿。因此可以利用泛型的设计思想设计一个高可用复用的对象,来统一返回的json格式的数据。
代码如下:

//保证序列化json的时候,如果是null的对象,key也会消失
/*
    这里加这个的原因是:
        当登陆失败的话返回的是{
            status : 1
            msg : 密码错误
            而data是一个有key的空节点,即value = null
        }
        这样的话data将不会出现在json之中了。
补充:
将该标记放在属性上,如果该属性为NULL则不参与序列化 
如果放在类上边,那对这个类的全部属性起作用 
Include.Include.ALWAYS 默认 
Include.NON_DEFAULT 属性为默认值不序列化 
Include.NON_EMPTY 属性为 空(“”) 或者为 NULL 都不序列化 
Include.NON_NULL 属性为NULL 不序列化 。
 */
@JsonSerialize(include =  JsonSerialize.Inclusion.NON_NULL)
public class ServerResponse implements Serializable {

    private int status;   //状态
    private String msg;   //信息
    private T data;       //数据

    //定义4个构造方法
    //1
    private ServerResponse(int status) {
        this.status = status;
    }
    //2
    private ServerResponse(int status,T data){
        this.status = status;
        this.data = data;
    }
    //3
    private ServerResponse(int status,String msg,T data){
        this.status = status;
        this.msg = msg;
        this.data = data;
    }
    //4
    private ServerResponse(int status,String msg){
        this.status = status;
        this.msg = msg;
    }
    @JsonIgnore
    //使之不在json序列化结果当中
    //即json里面不会出现这个
    public boolean isSuccess(){
        //如果status == 0 则返回true
        //SUCCESS的枚举值为0
        return this.status == ResponseCode.SUCCESS.getCode();
    }
    //返回状态
    public int getStatus(){
        return status;
    }
    public T getData(){
        return data;
    }
    public String getMsg(){
        return msg;
    }
    //调用status构造器
    public static  ServerResponse createBySuccess(){
        return new ServerResponse(ResponseCode.SUCCESS.getCode());
    }
    //调用4号构造器
    public static  ServerResponse createBySuccessMessage(String msg){
        return new ServerResponse(ResponseCode.SUCCESS.getCode(),msg);
    }

    public static  ServerResponse createBySuccess(T data){
        return new ServerResponse(ResponseCode.SUCCESS.getCode(),data);
    }

    public static  ServerResponse createBySuccess(String msg,T data){
        return new ServerResponse(ResponseCode.SUCCESS.getCode(),msg,data);
    }

    public static  ServerResponse createByError(){
        return new ServerResponse(ResponseCode.ERROR.getCode(),ResponseCode.ERROR.getDesc());
    }

    public static  ServerResponse createByErrorMessage(String errorMessage){
        return new ServerResponse(ResponseCode.ERROR.getCode(),errorMessage);
    }

    public static  ServerResponse createByErrorCodeMessage(int errorCode,String errorMessage){
        return new ServerResponse(errorCode,errorMessage);
    }
}

响应对象主要包括状态、信息和数据
状态是利用枚举来实现的:

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;
    }
    public static void main(String args[]){
        System.out.println(ResponseCode.SUCCESS.getDesc());
        System.out.println(ResponseCode.SUCCESS.getCode());
    }
}

二、登录功能
1、用户名是否存在
2、如果存在则将密码转换为MD5加密形式
3、校验用户名和密码是否正确
4、正确则将用户放入到Session中
具体实现:
Controller层:

@Controller                   //WEB层(用于标注类本身的用途)
@RequestMapping("/user/")     //将请求地址的前面加上/user
public class UserController {
    //按类型进行注入
    //将iUserService注入进来
    @Autowired
    private IUserService iUserService;

    //登陆功能
    //访问地址为login.do 访问方式为POST
    @RequestMapping(value = "login.do",method = RequestMethod.POST)
    @ResponseBody         //自动通过SpingMvc的json插件将返回值转换成json
    public ServerResponse login(String username, String password, HttpSession session) {
        ServerResponse response = iUserService.login(username, password);
        if (response.isSuccess())    //如果登陆成功,将用户放入到Session中
            session.setAttribute(Const.CURRENT_USER, response.getData());
        return response;   //将响应信息返回到前端
    }
}

Service层:

//Service表示业务层
//创建iUserService对象,放入到容器中
@Service("iUserService")
public class UserServiceImpl implements IUserService {
    @Autowired      //注入userMapper
    private UserMapper userMapper;

    @Override
    public ServerResponse login(String username, String password) {   
        int resultCount = userMapper.checkUsername(username); //先查询用户名,看用户名是否存在
        if (resultCount == 0)     //如果查不到的话,用户不存在
            return ServerResponse.createByErrorMessage("用户名不存在");  
        String md5Password = MD5Util.MD5EncodeUtf8(password); //将密码转化为MD5
        User user = userMapper.selectLogin(username, md5Password);  //通过用户名和密码进行查询
        if (user == null) 
            return ServerResponse.createByErrorMessage("密码错误");
        //将密码设置为空
        user.setPassword(org.apache.commons.lang3.StringUtils.EMPTY);
        return ServerResponse.createBySuccess("登录成功", user);
    }
 }

UserMapper:

public interface UserMapper {
    int checkUsername(String username);   //查询用户名,看此用户名是否存在
    //@Param为传入的参数起名字,这样在Dao层可以获得数据
    User selectLogin(@Param("username") String username, @Param("password")String password);
}

<select id="checkUsername" resultType="int" parameterType="string" >
     select count(1) from mmall_user where username = #{username}
select>

<select id="selectLogin" resultMap="BaseResultMap" parameterType="map">
      SELECT
      "Base_Column_List" />   
      from mmall_user
      where username = #{username}
      and password = #{password}
select>

三、注册功能
1、用户名是否存在
2、校验邮箱是否存在
3、将密码转化为MD5形式
4、将用户放入数据库中
具体实现:
Controller层:

//注册功能
@RequestMapping(value = "register.do",method = RequestMethod.POST)
@ResponseBody
public ServerResponse register(User user){
    return iUserService.register(user);
}
//校验功能
@RequestMapping(value = "check_valid.do",method = RequestMethod.POST)
@ResponseBody
public ServerResponse checkValid(String str,String type){
     return iUserService.checkValid(str,type);
}

Service层:

//注册
@Service("iUserService")
public class UserServiceImpl implements IUserService {
    public ServerResponse register(User user) {
        @Autowired    //注入userMapper
        private UserMapper userMapper;
        ServerResponse vaildResponse = 
                        this.checkValid(user.getUsername(),Const.USERNAME);
        if(!vaildResponse.isSuccess())   //校验用户名
            return vaildResponse; 
        vaildResponse = this.checkValid(user.getEmail(),Const.EMAIL);
        if(!vaildResponse.isSuccess())   //校验邮箱
            return vaildResponse;
        user.setRole(Const.Role.ROLE_CUSTOMER); //设置用户角色   
        user.setPassword(MD5Util.MD5EncodeUtf8(user.getPassword()));    //MD5加密
        int resultCount = userMapper.insert(user); //插入用户
        if (resultCount == 0) 
            return ServerResponse.createByErrorMessage("注册失败");
        return ServerResponse.createBySuccessMessage("注册成功");
    }
}

校验:

//校验
public ServerResponse<String> checkValid(String str,String type){
        if(org.apache.commons.lang3.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)){  //判断email
                 int resultCount = userMapper.checkEmail(str);
                 if(resultCount > 0 )
                     return ServerResponse.createByErrorMessage("email已存在");
            }
        }else{
            return ServerResponse.createByErrorMessage("参数错误");
        }
        return ServerResponse.createBySuccessMessage("校验成功");
}

UserMapper:

public interface UserMapper {
    int checkEmail(String email);   //查询邮箱,看此邮箱是否存在
    int insert(User record);
}


   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())

四、忘记密码重置密码功能
1、用户名是否存在
2、根据用户名查询问题
3、答案正确则生成token
4、将token存入到guava cache本地缓存中,有效期为12小时(防止横向越权)
5、校验用户名是否存在
6、校验token是否正确
7、正确则重新设置密码
具体实现:
Controller层:

//忘记密码的时候根据用户名查询问题
@RequestMapping(value = "forget_get_question.do",method = RequestMethod.POST)
@ResponseBody
public ServerResponse forgetGetQuestion(String username){
    return iUserService.selectQuestion(username);     //返回提示问题
}
//检查用户回答的答案是否正确
@RequestMapping(value = "forget_check_answer.do",method = RequestMethod.POST)
@ResponseBody
public ServerResponse forgetCheckAnswer(String username,String question,String answer){
    return iUserService.checkAnswer(username,question,answer);
}
//忘记密码中的重置密码
@RequestMapping(value = "forget_reset_password.do",method = RequestMethod.POST)
@ResponseBody
public ServerResponse forgetRestPassword(String username,String passwordNew,String forgetToken){
    return iUserService.forgetResetPassword(username,passwordNew,forgetToken);
}

Service层:

//忘记密码,查询问题
public ServerResponse selectQuestion(String username){
    ServerResponse validResponse = this.checkValid(username,Const.USERNAME);   //先看下用户是否存在
    if(validResponse.isSuccess())
        return ServerResponse.createByErrorMessage("用户不存在");
    String question = userMapper.selectQuestionByUsername(username);  //存在的话根据用户名查询问题
    if(org.apache.commons.lang3.StringUtils.isNotBlank(question))     //当问题不为空的时候返回
        return ServerResponse.createBySuccess(question);
    return ServerResponse.createByErrorMessage("找回密码的问题是空的");
}

//检查用户回答的答案是否正确
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();       //声明一个token
        TokenCache.setKey(TokenCache.TOKEN_PREFIX + username,forgetToken);
        return ServerResponse.createBySuccess(forgetToken);
    }
    return ServerResponse.createByErrorMessage("问题的答案错误");
}

//忘记密码中的重置密码
public ServerResponse<String> forgetResetPassword(String username,String passwordNew,String forgetToken) {
    if (org.apache.commons.lang3.StringUtils.isBlank(forgetToken))      //先判断是否携带了token
        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);   //从缓存中获取用户的token
    if (org.apache.commons.lang3.StringUtils.isBlank(token))                //获取到看token是否为空
        return ServerResponse.createByErrorMessage("token无效或者过期");
    if (org.apache.commons.lang3.StringUtils.equals(forgetToken, token)) {         //比较token是否相等
            String md5Password = MD5Util.MD5EncodeUtf8(passwordNew);
            int rowCount = userMapper.updatePasswordByUsername(username, md5Password);  //更新密码
            if (rowCount > 0)  //如果个数大于1,则更新密码成功
                return ServerResponse.createBySuccessMessage("修改密码成功");
    }else {
            return ServerResponse.createByErrorMessage("token错误,请重新获取重置密码的token");
    }
    return ServerResponse.createByErrorMessage("修改密码失败");
}

五、登录状态下重置密码功能
1、从session中取出用户
2、校验旧密码是否正确(防止横向越权)
3、正确则修改密码
具体实现:
Controller层:

//登陆状态下的重置密码
@RequestMapping(value = "reset_password.do",method = RequestMethod.POST)
@ResponseBody
public ServerResponse resetPassword(HttpSession session,String passwordOld,String passwordNew){
    User user = (User)session.getAttribute(Const.CURRENT_USER);
    if(user == null)
        return ServerResponse.createByErrorMessage("用户未登录");
    return iUserService.resetPassword(passwordOld,passwordNew,user);   //重置密码
}

Service层:

//登陆状态下的重置密码
public ServerResponse resetPassword(String passwordOld, String passwordNew,User user) {
    //防止横向越权,要校验一下这个用户的旧密码,一定要指定是这个用户.因为我们会查询一个count(1),如果不指定id,那么结果就是true啦count>0;
    int resultCount = userMapper.checkPassword(MD5Util.MD5EncodeUtf8(passwordOld),user.getId());
    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("密码更新失败");
}

六、更新用户信息
1、判断用户是否登录
2、取出用户的id和username
3、判断邮箱是否重复
3、不重复则更新用户信息并将其放入到session中
具体实现:
Controller层:

//更新用户信息
@RequestMapping(value = "update_information.do",method = RequestMethod.POST)
@ResponseBody
public ServerResponse update_information(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());  //取出id和username

    ServerResponse response = iUserService.updateInformation(user);
    if(response.isSuccess()){     //如果更新成功
        response.getData().setUsername(currentUser.getUsername());
        session.setAttribute(Const.CURRENT_USER,response.getData());   //将其放入到session中
    }
    return response;
}

Service层:

//更新用户信息,更新完用户信息之后,将其放入到session中
public ServerResponse updateInformation(User user){
    //username是不能被更新的
    //email也要进行一个校验,校验新的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("更新个人信息失败");
}

七、获取用户信息和退出登录
获取用户信息:
1、判断用户是否登录
2、登录则将用户信息取出来
退出登录:
1、将用户从session中移除
八、项目要点分析
Java电商项目面试–横向越权和纵向越权
Java电商项目面试–Token
Java电商项目面试–缓存(Guava Cache)
Java面试–String和byte[]之间的转化(MD5加密)


Java面试的完整博客目录如下:Java笔试面试目录

转载请标明出处,原文地址:https://blog.csdn.net/weixin_41835916 如果觉得本文对您有帮助,请点击支持一下,您的支持是我写作最大的动力,谢谢。

你可能感兴趣的:(Java笔试面试,Java笔试面试)