前言
在我们进行用户注册的时候,有的时候会碰到需要先登录到邮箱,点击激活后,才可以进行登录。
站在用户角度来说,这我还要登录邮箱,麻烦的一批,站在公司的角度来说,我需要确认你填写的邮箱是否是准确的,这样后续我才好打广告呀,手动滑稽。
那么现在站在一个开发者的角度来说,我们应该如何去实现这个邮箱激活的功能呢,个人思路,还有待完善,欢迎各位大佬提出建议,欢迎评论~~
正文
主要的思路是,当用户进行注册时,在用户未点击注册邮件进行激活时,用户的数据会被保存在redis中,并给定一个过期时间,用户需要在过期时间内进行邮件激活,从而才可以登录,这样的好处是可以拦截一些恶意的注册避免数据库中无用数据过多,在这里,后端使用SSM框架,前端主要使用JQuery的ajax方法实现,通过html+ajax向后台发起请求,当用户点击注册按钮时,则发起一个ajax请求,并将表单信息作为params传递给controller层。代码示例如下:
$(function(){
$("#regist").click(function () {
if(doCheckUsername()&&doCheckPassword()&&doCheckPhone()&&doCheckEmail()){
var username=$("#usernameId").val();
var password = $("#password").val();
var mobile = $("#mobile").val();
var email = $("#email").val();
var params = {};
params.username=username;
params.password = password;
params.mobile = mobile;
params.email = email;
$.getJSON("register/doRegister",params, function (result) {
if(result.data){
alert("注册成功,请登录邮箱进行激活账号");
location.href="login.html";
}else{
alert("注册失败");
}
})
}
});
Controller层
当前端发起ajax请求后,controller层用user对象接收用户信息的参数,随后调用Service层方法进行逻辑处理
@Controller
@RequestMapping("/register/")
public class registerController {
@Autowired
private RegisterService registerService;
@RequestMapping("doRegister")
@ResponseBody
public JsonResult doRegister(SysUser user){
registerService.doRegister(user);
return new JsonResult(new RegisterWarn("注册成功,请登录邮箱进行激活"));
}
}
Service层
为了用户的信息安全,我对用户密码进行了md5加密,邮箱的激活码通过UUID.randomUUID().toString() 生成一个随机数,保证每个用户的激活码不一致。并将生成的激活码作为redis中的key,将用户的信息转换为json作为value,并设置过期时间,随后调用发送邮件的工具类发送邮件。(关于如何编写邮件工具类,这边不再展开,大家可以自行百度)
@Service
public class RegisterServiceImpl implements RegisterService {
@Override
public void doRegister(SysUser user) {
if(user==null)
throw new ServiceException("保存的对象不能为空");
//对密码进行加密
String password = user.getPassword();
String salt = UUID.randomUUID().toString();
SimpleHash md5 = new SimpleHash("MD5", password, salt);
user.setSalt(salt);
user.setPassword(md5.toHex());
String createdUser = user.getUsername();
String modifiedUser = user.getUsername();
user.setCreatedUser(createdUser);
user.setModifiedUser(modifiedUser);
String activecode = UUID.randomUUID().toString();//生成一个随机数,作为邮箱的激活码
//将User对象转化成json格式的数据
ObjectMapper mapper = new ObjectMapper();
String sUser = null;
try {
sUser = mapper.writeValueAsString(user);
} catch (Exception e) {
e.printStackTrace();
}
//将数据保存进redis缓存中
Jedis jedis = JedisPoolUtil.getJedis();//使用自定义jedis池
jedis.setex(activecode,60*60,sUser);//设置过期时间
jedis.close();
//发送邮件
MailUtils.sendMail(user.getEmail(),"这是一封激活邮件,请在1小时内点击下方链接进行账号激活,<br/><a href=http://localhost/register/"+activecode+">请点击进行这里进行用户激活</a>","激活账户");
}
}
http://localhost/register/activecode,实际上走的是另一个controller的请求路径,当用户点击激活时,则跳转到相应的controller中。
激活邮件Controller
使用 @PathVariable注解来接收Rest风格的参数,通过activeCode从redis中取出相对应的value值,也就是用户的信息的json数据,随后解析json转换为user对象,到了这一步就表示用户已激活邮件,我们可以将用户信息保存到数据库中了,调用service层处理。
@Controller
@RequestMapping("/register/")
public class registerController {
@Autowired
private RegisterService registerService;
@RequestMapping("{activecode}")
public String doCheckEmail(@PathVariable String activecode){
Jedis jedis = JedisPoolUtil.getJedis();
String sUser = jedis.get(activecode);
//将json格式的数据转换为user对象
ObjectMapper objectMapper = new ObjectMapper();
SysUser user = null;
try {
user = objectMapper.readValue(sUser, SysUser.class);
} catch (IOException e) {
e.printStackTrace();
}
Boolean flag = registerService.doInsertUser(user);
if(!flag){//如果注册失败,跳回注册页面
return "register";
}
return "register_ok";
}
}
激活邮件Service
调用DAO层去连接数据库并保存用户信息
@Service
public class RegisterServiceImpl implements RegisterService {
@Autowired
private SysUserDao userDao;
@Override
public Boolean doInsertUser(SysUser user) {
if(user==null)
return false;
int rows = userDao.insertObject(user);
if(rows==0){
throw new ServiceException("数据写入失败");
}
return true;
}
}
激活邮件DAO层
@Mapper
public interface SysUserDao {
int insertObject(SysUser user);
}
Mapper文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cy.pj.sys.dao.SysUserDao">
<insert id="insertObject" parameterType="com.cy.pj.sys.entity.SysUser">
insert into sys_users values(null,#{username},#{password},#{salt},#{email},#{mobile},now(),now(),#{createdUser},#{modifiedUser})
</insert>
</mapper>
总结
第一次记录博客,相信也不会是最后一次哈哈,以后有好的想法就会上来记录一下,欢迎各位大佬和我讨论
现在来说说我所知道的不足吧。
1.没有使用nginx反向代理,某些情况下在邮件里访问http://localhost/register/activecode这个链接时,会无法跳转,导致没有办法激活成功。
解决方案:
- 配置nginx反向代理和修改相应的hosts文件
- 打开万能的开发者模式F12,找到对应的a标签下的链接点击访问
个人推荐第一种方法,我在这里没有配置只是因为我懒
2.没有做redis集群,如果redis服务器宕机了会导致用户无法注册也无法激活
解决方案:
- 配置redis哨兵,当主服务器宕机后会通过哨兵机制会自动将slave切换成master
- 配置redis集群
推荐使用redis集群,原因是虽然哨兵模式实现了高可用,但是当保存的数据量越来越大时,无法实现扩容。而redis集群既实现了高可用也实现扩容。