java web 邮箱找回密码

邮箱激活

背景:几乎每个网站或论坛之类的用户注册后都需要通过发送邮件到邮箱激活用户。
设计:
激活步骤:
  1. 发送激活邮件

  2. 用户查收邮件

  3. 用户点击链接,跳转至成功页面(修改激活状态),激活成功。

  4. 加密能防止伪造攻击,一次url只能验证一次,并且绑定了用户。生成url: 可以用UUID生成随机密钥。

数字签名 = MD5(用户名+'′+过期时间+‘’+密钥key)
数据库字段(用户名(主键),密钥key,过期时间)
url参数(用户名,数字签名) ,密钥key的生成:在每一个用户找回密码时候为这个用户生成一个密钥key ,

详细步骤

点击找回密码按钮 > 跳到找回密码页面 > 输入手机号或者邮箱找回 > 后台验证根据邮箱或者密码是否能在数据库中找到对应的user(信息),如果找不到,证明手机号或者邮箱伪造的,如果找得到 > 把生成的uuid 作为token(证明本次用户的唯一标示)当成键user做为值放入缓存中,然后拼接链接,同时放入要发送的邮箱中, http://localhost:8080/foundpassword?token=f3a29a4b795a4f77be98ea931cf9884d 发送邮箱 > 当用户点击邮箱链接的时候,后台获取token, 首先判断token 是否为空,如果存在去缓存中获取对应的user 防止伪造token,
如果都没有问题,请求转发到重置密码页面把token放到表单隐藏域中,当用户提交表单的时候,对比两次密码是否一致,然后再次验证toke是否伪造,如果都是ok的话,根据token值删除缓存,同时更新用户密码,然后重定向到登陆页面.

代码

package com.kaishengit.service.impl;


/**
 * Created by wanggs on 2017/9/26.
 */
@Service
public class UserServiceImpl implements UserService {
    private static Logger logger = (Logger) LoggerFactory.getLogger(UserService.class);
    @Autowired
    private SimpleCache simpleCache;
    // 从配置文件中获取盐值
    @Value("${user.password.salt}")
    private String salt;
    @Value("${email.smpt}")
    protected String smpt;
    @Value("${email.port}")
    public String port;
    @Value("${email.username}")
    private String username;
    @Value("${email.password}")
    private String password;
    @Value("${email.frommail}")
    private String formmail;
    @Autowired
    private UserMapper userMapper;

    // 发送激活用户账号的邮件,写进去时间,如果没人访问就过期,有人访问就延期时间
    private static Cache<String, String> cache = CacheBuilder
            .newBuilder()
            .expireAfterWrite(6, TimeUnit.HOURS)
            .build();
    // 找回密码token
    private static Cache<String, String> passwordCache = CacheBuilder.newBuilder()
            .expireAfterWrite(30, TimeUnit.MINUTES)
            .build();
    // 限制操作pinlv
    private static Cache<String, String> activeCache = CacheBuilder.newBuilder()
            .expireAfterWrite(30, TimeUnit.SECONDS)
            .build();

    @Override
    public User findByName(String username) {
        return userMapper.findByUsername(username);
    }

    @Override
    public User findByEmail(String email) {
        return userMapper.finByEmail(email);
    }

    /**
     * 注册用户
     *
     * @param user
     * @throws UserExistException
     */
    @Override
    public void saveNewUser(User user) throws UserExistException {
        User u = userMapper.findByUsername(user.getUsername());
        if (u != null) {
            throw new UserExistException("用户名已经存在");
        }
        user.setPassword(DigestUtils.md5Hex(user.getPassword() + salt));
        user.setState(User.USERSTATE_UNACTIVE);
        user.setAvatar(User.DEFAULT_AVATAR_NAME);
        logger.info("盐值:{}" + salt);
        logger.info("加密后密码是: {}", user.getPassword());

        userMapper.saveNewUser(user);
        // 创建线程发激活邮件
        getThread(user);
    }

    /**
     * 使用线程发送激活邮件
     *
     * @param user
     */
    private void getThread(User user) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                // 发送激活邮件
                String uuid = UUID.randomUUID().toString().replace("-", "");
                String url = "http://localhost/user/active?_=" + uuid;
                // 放入缓存等待6小时
                cache.put(uuid, user.getUsername());
                String html = "<h3>Dear " + user.getUsername() + ":</h3>请点击<a href='" + url + "'>该链接</a>去激活你的账号. <br> 凯盛软件";
                Email email = getEmail(smpt, port, username, password, formmail);
                logger.info("邮箱:{}" + email);
                EmailUtil.sendHtmlEmail(email, user.getEmail(), "用户激活邮件", html);
            }
        });

        thread.start();
    }

    /**
     * 验证token
     *
     * @param token
     */
    @Override
    public void activeUser(String token) throws ServiceException {
        String userName = cache.getIfPresent(token);
        logger.info("token: {}" + token);
        if (token == null) {
            throw new ServiceException("token无效或者过期");
        } else {
            User user = userMapper.findByUsername(userName);
            if (user == null) {
                throw new ServiceException("账号不存在");
            } else {
                // 设置激活
                user.setState(Statu.Active.getValue());
                // 更新用户
                userMapper.update(user);

                // 删除缓存中的键对应的值
                cache.invalidate(token);
            }
        }
    }

    /**
     * 用户找回密码
     *
     * @param sessionId 客户端的sessionID,限制客户端的操作频率
     * @param type      找回密码方式 email | phone
     * @param value     电子邮件地址 | 手机号码
     */
    @Override
    public void funndpassword(String sessionId, String type, String value) {

        if (activeCache.getIfPresent(sessionId) == null) {
            if ("phone".equals(type)) {
                // TODO 根据手机号找
            }
            if ("email".equals(type)) {
                // 根据邮箱找
                User user = userMapper.finByEmail(value);
                if (user != null) {
                    // 开启线程发邮件
                    getThread(value, user);
                }

            }
            activeCache.put(sessionId, "xxxx");
        } else {
            throw new ServiceException("操作频率过快,稍后再试");
        }
    }

    /**
     * 使用线程发送找回密码
     *
     * @param value
     * @param user
     */
    private void getThread(String value, User user) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                String uuid = UUID.randomUUID().toString().replace("-", "");
                passwordCache.put(uuid, user.getUsername());
                String url = "http://localhost/foundpassword/newpassword?token=" + uuid;
                String html = user.getUsername() + "<<br>请点击该<a href='" + url + "'>链接</a>进行找回密码操作,链接在30分钟内有效";
                Email email = getEmail(smpt, port, username, password, formmail);
                logger.info("邮箱:{}" + email);
                EmailUtil.sendHtmlEmail(email, value, "密码找回邮件", html);
            }
        });
        thread.start();
    }

    @Override
    public void update(User user) {
        userMapper.update(user);
    }

    /**
     * 找回密码
     *
     * @param token
     * @return
     */
    @Override
    public User foundPasswordGetUserByToken(String token) throws ServiceException {
        String username = passwordCache.getIfPresent(token);
        if (StringUtils.isBlank(username)) {
            throw new ServiceException("token过期或错误");
        } else {
            User user = userMapper.findByUsername(username);
            if (user == null) {
                throw new ServiceException("未找到对应账号");
            } else {
                return user;
            }
        }
    }

    /**
     * 重置密码
     *
     * @param user
     * @param toke
     */
    @Override
    public void resetPassword(User user, String toke) {
        String username = passwordCache.getIfPresent(toke);
        if (StringUtils.isBlank(toke) && StringUtils.isBlank(username)) {
            throw new ServiceException("token过期,或者错误");
        } else {
            User u = userMapper.findByUsername(username);
            if (u != null) {
                u.setPassword(DigestUtils.md5Hex(user.getPassword() + salt));
                userMapper.update(u);

                // 删除token
                passwordCache.invalidate(toke);
                logger.info("{} 重置了密码", u.getUsername());
            }
        }
    }

    /**
     * 更改邮箱
     *
     * @param user
     */
    @Override
    public void updateEmail(User user, FormParam param) {
        if (StringUtils.isBlank(user.getUsername()) && StringUtils.isBlank(user.getEmail())) {
            throw new ServiceException("帐号或者密码不能为空");
        }
        user.setEmail(param.getEmail());
        userMapper.update(user);
    }

    /**
     * 修改密码
     *
     * @param param
     */
    @Override
    public void updatePassword(User user, FormParam param) {
        if (param == null) {
            throw new ServiceException("参数不能为空");
        }
        String oldpassword = DigestUtils.md5Hex(param.getOldpassword() + salt);
        System.out.println("原始密码:" + oldpassword);
        System.out.println("密码:" + user.getPassword());
        if (!user.getPassword().equals(oldpassword)) {
            throw new ServiceException("原始密码错误");
        }
        // 密码加密
        user.setPassword(DigestUtils.md5Hex(param.getNewpassword() + salt));
        userMapper.update(user);

    }

    @Override
    public void updateAvatar(User user, FormParam param) {
        if (StringUtils.isBlank(param.getFileKey())) {
            throw new ServiceException("七牛云参数为空");
        }
        user.setAvatar(param.getFileKey());
        userMapper.update(user);
    }

    @Override
    public AjaxResult getAjaxResult(FormParam param, String action, User user, String password) {
        if ("avatar".equals(action)) {
            try {
                this.updateAvatar(user, param);
                return new AjaxResult(AjaxResult.SUCCESS);
            } catch (ServiceException e) {
                return new AjaxResult(AjaxResult.ERROR, e.getMessage());
            }
        }
        if ("profile".equals(action)) {
            try {
                this.updateEmail(user, param);
                return new AjaxResult(AjaxResult.SUCCESS);
            } catch (ServiceException e) {
                return new AjaxResult(AjaxResult.ERROR, e.getMessage());
            }
        }
        if ("password".equals(action)) {
            try {
                this.updatePassword(user, param);
                return new AjaxResult(AjaxResult.SUCCESS);
            } catch (ServiceException e) {
                return new AjaxResult(AjaxResult.ERROR, e.getMessage());
            }

        }
        return new AjaxResult(AjaxResult.ERROR, "参数有误");
    }


    /**
     * 根据id查找对象
     *
     * @param userid
     * @return
     */
   private  static Map<String,Object> map = Maps.newHashMap();
    @Override
    public User findById(Integer userid) {


        User user = (User)map.get("user:" + userid);
        if (user == null) {
            user = userMapper.findById(userid);
            map.put("user:" + userid, user);
        }else {
            logger.debug("load user from cache");
        }

        return user;
    }


    /**
     * 封装Email对象
     *
     * @param smpt
     * @param port
     * @param username
     * @param password
     * @param formmail
     * @return
     */
    private Email getEmail(String smpt, String port, String username, String password, String formmail) {
        return new Email(smpt, port, username, password, formmail);
    }
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 135,292评论 19 139
  • 22年12月更新:个人网站关停,如果仍旧对旧教程有兴趣参考 Github 的markdown内容[https://...
    tangyefei阅读 35,289评论 22 257
  • width: 65%;border: 1px solid #ddd;outline: 1300px solid #...
    邵胜奥阅读 10,316评论 0 1
  • 不是一个最好的人 也不是 一个最坏的人 在每个人的内心都有这样的自己 富有,善良,真诚 每时每刻都希望能坦然的面对...
    柒云草阅读 3,987评论 0 1
  • 你怎么那么美,美得令人陶醉 走路柔柔带风,让我迈不开腿 你怎么那么美,胜过娇艳玫瑰 优雅吐气如兰,飘来淡淡香味 你...
    叫我哆啦美阅读 4,310评论 12 15