1. 程式人生 > >Java電商專案面試--使用者模組

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<T> 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 <T> ServerResponse<T> createBySuccess(){ return new ServerResponse<T>(ResponseCode.SUCCESS.getCode()); } //呼叫4號構造器 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); } 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); } }

響應物件主要包括狀態、資訊和資料
狀態是利用列舉來實現的:

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<User> login(String username, String password, HttpSession session) {
        ServerResponse<User> 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<User> 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
      <include refid="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<String> register(User user){
    return iUserService.register(user);
}
//校驗功能
@RequestMapping(value = "check_valid.do",method = RequestMethod.POST)
@ResponseBody
public ServerResponse<String> checkValid(String str,String type){
     return iUserService.checkValid(str,type);
}

Service層:

//註冊
@Service("iUserService")
public class UserServiceImpl implements IUserService {
    public ServerResponse<String> register(User user) {
        @Autowired    //注入userMapper
        private UserMapper userMapper;
        ServerResponse<String> 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 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>

四、忘記密碼重置密碼功能
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<String> forgetGetQuestion(String username){
    return iUserService.selectQuestion(username);     //返回提示問題
}
//檢查使用者回答的答案是否正確
@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);
}
//忘記密碼中的重置密碼
@RequestMapping(value = "forget_reset_password.do",method = RequestMethod.POST)
@ResponseBody
public ServerResponse<String> 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<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(passwordOld,passwordNew,user);   //重置密碼
}

Service層:

//登陸狀態下的重置密碼
public ServerResponse<String> 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<User> 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<User> 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<User> 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("更新個人資訊失敗");
}

Java面試的完整部落格目錄如下:Java筆試面試目錄