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