美文网首页SSM搭建二手市场交易平台网站
SSM搭建二手市场交易平台(八):用户登录信息功能开发

SSM搭建二手市场交易平台(八):用户登录信息功能开发

作者: 啃饼小白 | 来源:发表于2019-01-22 08:35 被阅读37次

    写在前面

    本篇我们介绍用户登录获取信息,忘记密码,提示问题与答案,重置密码功能开发这四者的实现,里面的内容很多,希望大家对此有一个提前的认识。

    还记得我们上一篇的UserController--->IUserService-->UserServiceImpl模式么,我们继续进行开发,把这个放在这里是让大家更清楚的知道我们的开发流程,不至于晕。

    用户登录获取信息

    打开UserController.java文件,写入以下代码:

        /***
         * 用户登录信息的获取
         * */
        @RequestMapping(value = "get_user_info.do",method = RequestMethod.GET)  //这里就是具体的每个方法的url链接
        @ResponseBody   //自动序列化json功能
        public ServerResponse<User> getUserInfo(HttpSession session){
            User user =(User) session.getAttribute(Const.CURRENT_USER);
            if(user != null){
                return ServerResponse.createBySuccess(user);
            }
            return ServerResponse.createByErrorMessage("用户未登录,无法获取当前用户的信息");
        }
    

    这样我们那就完成了用户登录信息的获取操作,你可能会问后面那两个过程哪去了,这里需要说明的是,不涉及到数据库的访问操作就不需要后面两个过程,这一点你需要注意一下。

    忘记密码

    打开UserController.java文件,写入以下代码:(注意我这里就是直接把这个功能的完整实现代码贴这里了,没有一步一步的写,毕竟太浪费时间了,也没有必要,你们懂我的意思吧)

        /***
         * 忘记密码
         * */
        @RequestMapping(value = "forget_get_question.do",method = RequestMethod.GET)  //这里就是具体的每个方法的url链接
        @ResponseBody   //自动序列化json功能
        public ServerResponse<String> forgetGetQuestion(String username){
            return iUserService.forgetGetQuestion(username);
        }
    

    接着打开IUserService.java文件:

     ServerResponse<String> forgetGetQuestion(String username);   //忘记密码
    

    继续打开UserServiceImpl.java文件:

        /***
         *
         *忘记密码时的接口类
         * */
        public ServerResponse<String> forgetGetQuestion(String username){
            //判断用户名是否存在
            ServerResponse validResponse =this.checkValid(username,Const.USERNAME);
            if(validResponse.isSuccess()){  //用户名不存在
                return ServerResponse.createByErrorMessage("该用户不存在");
            }
    
            String question =userMapper.forgetgetQuestionByUsername(username);
            if(StringUtils.isNoneBlank(question)){
                //开始进行校验,如果找回密码的问题不为空,那么
                return ServerResponse.createBySuccess(question);
            }
            return ServerResponse.createByErrorMessage("找回密码的问题是空的");
        }
    }
    

    String question =userMapper.forgetgetQuestionByUsername(username);这行代码我需要说明一下。大家都知道我们这个UserMapper(里面的userMapper是它的实例化对象)就是DAO层,它需要提供一个接口(IUserService)和该接口的实现类(UserServiceImpl)用于访问数据库,但是我们获取数据库里面的信息是通过sql语句来实现的,于是UserMapper.xml负责sql语句的执行,UserMapper.java就负责接收查询的结果。因此就知道我们接下来要干嘛了:
    打开UserMapper.java文件,里面新增代码:

    String forgetgetQuestionByUsername(String username);  //这里找不到对应的实现类,所以应该去UserMapper.xml里面进行配置
    

    然后去UserMapper.xml里面新增代码:

     <select id="forgetgetQuestionByUsername" resultType="String" parameterType="String">
         select question from store_user
         where username = #{username}
    </select>
    

    也就是说实际上我们的开发顺序与上面所说的顺序是相反的,只是为了更好的理解。接下来完成提示问题与答案这个功能。

    提示问题与答案

    打开UserController.java文件,写入以下代码:

       /***
         * 提示问题与答案
         * */
        @RequestMapping(value = "forget_check_answer.do",method = RequestMethod.GET)  //这里就是具体的每个方法的url链接
        @ResponseBody   //自动序列化json功能
        public ServerResponse<String> checkAnswer(String username,String question,String answer){
            return iUserService.checkAnswer(username,question,answer);
        }
    

    接着打开IUserService.java文件:

    ServerResponse<String> checkAnswer(String username,String question,String answer);   //提示问题与答案
    

    继续打开UserServiceImpl.java文件:

        /***
         *
         *提示问题与答案时的接口类
         * */
        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();
                TockenCache.setKey("token_"+username,forgetToken);
                return ServerResponse.createBySuccess(forgetToken);
            }
            return ServerResponse.createByErrorMessage("问题的答案错误!");
        }
    

    同样checkAnswer这个方法需要定义,打开UserMapper.java文件,里面新增代码:

      //这里找不到对应的实现类,所以应该去UserMapper.xml里面进行配置
        int checkAnswer(@Param("username")String username,@Param("question")String question,@Param("answer")String answer);   //注意mybatis传递多个参数时,需要使用param注解
    

    然后去UserMapper.xml里面新增代码:

    <select id="checkAnswer" resultType="int" parameterType="map">  <!--注意使用多个参数是是需要使用map的-->
         select count(1) from store_user
         where username = #{username}
          and question = #{question}
          and answer = #{answer}
    </select>
    

    还有因为我们这个提示问题与答案是需要写入cache的,因此我们需要在common包下面新建一个类,里面写入如下代码:

    package top.store.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.TimeUnit;
    
    public class TockenCache {
        private static Logger logger = LoggerFactory.getLogger(TockenCache.class);
    
        //本地缓存
        //这里采用了LRU算法,初始值是1000,最大值是10000,如果超过最大值就会使用LRU算法进行消除,其实就是删除使用频率低的值,它的有效时间为12个小时
        private static LoadingCache<String,String> localCache = 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){
            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 (Exception e){
                logger.error("localCache get error",e);
            }
            return null;
        }
    }
    

    接下来便是忘记密码中的重置密码的开发了,我们知道这个必须在忘记密码实现以后才能开始。

    重置密码

    打开UserController.java文件,写入以下代码:

        /***
         *
         * 忘记密码中的重置密码
         */
        @RequestMapping(value = "forget_reset_password.do",method = RequestMethod.GET)  //这里就是具体的每个方法的url链接
        @ResponseBody   //自动序列化json功能
        public ServerResponse<String>  forgetResetPassword(String username,String passwordNew, String forgetToken){
            return iUserService.forgetResetPassword(username,passwordNew,forgetToken);
        }
    

    接着打开IUserService.java文件:

    ServerResponse<String>  forgetResetPassword(String username,String passwordNew, String forgetToken);  //忘记密码中的重置密码
    

    继续打开UserServiceImpl.java文件:

    /***
         *
         * 忘记密码中的重置密码的接口类
         */
        public ServerResponse<String>  forgetResetPassword(String username,String passwordNew, String forgetToken){
            //首先进行token的校验
            if(StringUtils.isBlank(forgetToken)){  //如果token是空的话
                return ServerResponse.createByErrorMessage("参数错误,token还没有被传递呢");
            }
    
            //这里我们需要校验username,因为forgetToken是通过token_与username进行拼接的,上面是验证了forgetToken不为空,但这并不代表username就不为空
            ServerResponse validResponse =this.checkValid(username,Const.USERNAME);
            if(validResponse.isSuccess()){  //用户名不存在
                return ServerResponse.createByErrorMessage("该用户不存在");
            }
    
            String token = TockenCache.getKey(TockenCache.TOKEN_PREFIX+username); //可以参看第146行代码
            //对caChe里的token进行校验
            if(StringUtils.isBlank(token)){
                return ServerResponse.createByErrorMessage("token无效或者过期");
            }
    
            /***
            String a = null;
            if("abc".equals(a)){}   //总是错误
            if(a.equals("abc")){}   //引发空指针异常
             */
            //这里使用.equals方法,可以避免出现Null值在前的空指针,在后总是错误的问题
            if(StringUtils.equals(forgetToken,token)){
                //修改密码成功,我们需要更新旧的密码了
                String md5Password = MD5Util.MD5EncodeUtf8(passwordNew);
                int rowCount = userMapper.updatePasswordByUsername(username,md5Password);
                if(rowCount>0){
                    return ServerResponse.createBySuccessMassage("密码修改成功!");
                }
    
            }else{
                return ServerResponse.createByErrorMessage("token错误,请重新获取重置密码的token");
            }
            return ServerResponse.createByErrorMessage("密码修改失败!");
        }
    

    这里面有几个注意的事项:
    1、"token_"这个之前在写提示问题与答案时的接口类时,没有将其设定为一个常量,实际上它是一个常量,为了以后便于调用它,我们将其设定为一个常量:
    打开TockenCache.java文件,我们新增一行代码:

    public static final String TOKEN_PREFIX = "token_"; //这里把token_作为一个常量。因为需要多次使用
    

    然后你将checkAnswer方法里面的这行代码:

    ockenCache.setKey("token_"+username,forgetToken);  //这里没有把token_当做一个全局的常量进行引用
    

    替换为:

    TockenCache.setKey(TockenCache.TOKEN_PREFIX+username,forgetToken);   // 这里把token_当做一个全局的常量进行引用(在TockenCache.TOKEN_PREFIX里面)
    

    2、StringUtils.equals(forgetToken,token)注意这里我们使用了StringUtils.equals方法,它最大的好处就是可以避免null值在前在后的问题:

     public static boolean equals(CharSequence cs1, CharSequence cs2) {
            if (cs1 == cs2) {
                return true;
            } else if (cs1 != null && cs2 != null) {
                if (cs1.length() != cs2.length()) {
                    return false;
                } else {
                    return cs1 instanceof String && cs2 instanceof String ? cs1.equals(cs2) : CharSequenceUtils.regionMatches(cs1, false, 0, cs2, 0, cs1.length());
                }
            } else {
                return false;
            }
        }
    

    那么什么是null值在前在后的问题呢?很简单我举个例子你就知道了:

    String a = null;
    if("abc".equals(a)){}   //总是错误
    if(a.equals("abc")){}   //引发空指针异常
    

    所以这个问题我们就可以不用考虑了。
    3、int rowCount = userMapper.updatePasswordByUsername(username,md5Password);这行代码中的updatePasswordByUsername方法,老规矩还是需要定义这个方法,打开UserMapper.java文件,里面新增代码:

     //这里找不到对应的实现类,所以应该去UserMapper.xml里面进行配置
     int updatePasswordByUsername(@Param("username")String username,@Param("passwordNew")String passwordNew);   //注意mybatis传递多个参数时,需要使用param注解
    

    然后去UserMapper.xml里面新增代码:

    <update id="updatePasswordByUsername"  parameterType="map">  <!--注意使用多个参数是是需要使用map的,而且这里因为是更新操作,因此需要使用update-->
            update store_user
            set password =#{passwordNew},update_time =now()
            where username = #{username}
    </update>
    

    这段代码因为是执行更新操作的,因此需要使用update,而且别忘记使用update_time =now()这个时间戳,否则你不知道具体什么时候修改了密码。

    4、至于为什么我们每次判断的时候总是看它大于0或者等于0,那是因为我们使用的方法最后我们都是让它返回修改的行数(其实就是受影响的行数),如果为0就说明没有修改,否则就进行了修改,这个应该很好理解的吧。

    这样,我们本篇关于用户登录获取信息,忘记密码,提示问题与答案,重置密码功能开发这四者的介绍就到此为止了,感谢你的赏阅!

    相关文章

      网友评论

        本文标题:SSM搭建二手市场交易平台(八):用户登录信息功能开发

        本文链接:https://www.haomeiwen.com/subject/rfewcqtx.html