写在前面
本篇我们介绍用户登录获取信息,忘记密码,提示问题与答案,重置密码功能开发这四者的实现,里面的内容很多,希望大家对此有一个提前的认识。
还记得我们上一篇的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就说明没有修改,否则就进行了修改,这个应该很好理解的吧。
这样,我们本篇关于用户登录获取信息,忘记密码,提示问题与答案,重置密码功能开发这四者的介绍就到此为止了,感谢你的赏阅!