仿牛客网项目(三)

1、邮件功能

功能分析

邮件功能,在用户注册时,用于验证用户邮箱并激活用户账号,用户输入正确邮箱地址并注册成功之后,需要向用户注册邮箱发送激活邮件。本功能旨在实现向指定邮箱发送邮件的功能。

设置邮箱

开通发送方邮箱的SMTP协议,如下图为QQ邮箱,设置完成后,保存更改
在这里插入图片描述

Spring Mail

导入jar包

在项目pom.xml文件中导入对应的jar包

  <dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-mail</artifactId>
		<version>2.2.6.RELEASE</version>
  </dependency>

添加配置文件

在 application.properties 中配置邮箱的相关设置,这里演示的是qq邮箱的相关配置。

# MailProperties
spring.mail.host=smtp.qq.com
spring.mail.port=465
#自己的邮箱账号
spring.mail.username=111111111@qq.com
#邮箱授权码,邮箱设置页面生成,并不是邮箱登录密码
spring.mail.password=********
spring.mail.protocol=smtp
spring.mail.test-connection: true
spring.mail.default-encoding=utf-8
spring.mail.properties.mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory
spring.mail.properties.mail.smtp.port:${spring.mail.port}
spring.mail.properties.mail.smtp.auth:true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true

使用 JavaMailSender 发送邮件

在community包下新建util工具包,在工具类 util 包下创建 MailClient类,用于发送邮件:

@Component
public class MailClient {

    /**定义日志记录信息*/
    private static final Logger logger = LoggerFactory.getLogger(MailClient.class);
    /**注入发送邮件的Java类*/
    @Autowired
    private JavaMailSender mailSender;
    /**注意这个括号一定要是花括号*/
    @Value("${spring.mail.username}")
    private String from;
    /**
     * 发送邮件
     * @param to 发送对象
     * @param subject 发送主题
     * @param content 发送内容
     */
    public void sendMail(String to, String subject, String content){
        try {
            /**创建发送的邮件类模版*/
            MimeMessage mimeMessage = mailSender.createMimeMessage();
            /**根据邮件类模版创建邮件生成类*/
            MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage);
            /**设置发送邮件的邮件信息*/
            mimeMessageHelper.setFrom(from);
            mimeMessageHelper.setTo(to);
            mimeMessageHelper.setSubject(subject);
            mimeMessageHelper.setText(content,true);
            /**发送邮件*/
            mailSender.send(mimeMessageHelper.getMimeMessage());
        } catch (MessagingException e) {
            logger.error("发送邮件报错 :" + e.getMessage());
        }
    }
}

在test包下创建MailTests类,对发送邮件功能进行测试

@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = CommunityApplication.class)
public class MailTest {

    @Autowired
    private MailClient mailClient;
    @Test
    public void testTextMail(){
      mailClient.sendMail("目标邮箱","test","welcome");
    }
}

检查目标邮箱是否收到对应主题和内容

使用thymeleaf模版发送邮件

在MailTests测试类中添加testHtmlMail测试方法,thymeleaf模版文件demo.html在项目源码中。

   @Test
    public void testHtmlMail() {
        Context context = new Context();
        context.setVariable("username", "Tom");

        String content = templateEngine.process("/mail/demo", context);
        System.out.println(context);
        mailClient.sendMail("目标邮箱","HTML",content);
    }

检查目标邮箱是否收到对应主题和内容

2、注册功能

功能分析

用户注册功能,用户点击注册按钮,跳转至注册页面;
输入用户名,密码,邮箱信息;
用户名判空、验证是否已经注册过,密码判空、进行加密处理,邮箱判空、验证是否正确邮箱格式、验证是否已经注册过;
用户注册,插入新用户;
用户激活,向用户注册邮箱发送激活链接;
激活成功后,跳转到登录界面;

注册页面访问

修改index.html中注册链接,利用thymeleaf实现界面跳转至注册网页register.html,前端修改内容详见源代码。

注册和激活

加密工具类

在 util 包下添加 CommunityUtil工具类,这里主要是生成随机字符串、 md5 加密和验证邮箱合法性

public class CommunityUtil {

    /**
     * 生成随机字符串,用于随机密码和随机命名
     * @return
     */
    public static String generateUUID(){
        return UUID.randomUUID().toString().replaceAll("-","");
    }
    /**
     * MD5加密,密码+salt
     * @param key
     * @return
     */
    public static String MD5(String key){
        if(StringUtils.isBlank(key)){
            return null;
        }
        return DigestUtils.md5DigestAsHex(key.getBytes());
    }
    /**
     * 验证邮箱是否合法
     * @param string
     * @return
     */
    public static boolean isEmail(String string) {
        if (string == null){
            return false;
        }
        String regEx1 = "^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$";
        Pattern p;
        Matcher m;
        p = Pattern.compile(regEx1);
        m = p.matcher(string);
        if (m.matches()){
            return true;
        }else{
            return false;
        }
    }
}

util包下添加 CommubityConstant 接口,返回激活的三种状态,代码如下:

public interface CommunityConstant {
    /**
     * 激活成功
     */
    int ACTIVATION_SUCCESS = 0;
    /**
     * 重复激活
     */
    int ACTIVATION_REPEAT = 1;
    /**
     * 激活失败
     */
    int ACTIVATION_FAILURE = 2;
}

在application.properties添加配置文件

# community
community.path.domain=http://localhost:8080

在 service 包下的 UserService 类中添加 register 和 activation 方法分别用于提供注册和激活服务,sendMail用户给指定用户发送邮件,相关代码如下:

@Service
public class UserService implements CommunityConstant {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private MailClient mailClient;
    @Autowired
    private TemplateEngine templateEngine;
    @Value("${community.path.domain}")
    private String domain;
    @Value("${server.servlet.context-path}")
    private String contextPath;
    /**
     * 根据用户id查询用户
     * @param id
     * @return
     */
    public User findUserById(int id) {
        return userMapper.selectById(id);
    }
    /**
     * 用户注册
     * @param user
     * @return
     */
    public Map<String, Object> register(User user) {
        Map<String, Object> map = new HashMap<>();
        //用户、账号、密码、邮箱判空
        if (user == null) {
            throw new IllegalArgumentException("参数不能为空!");
        }
        if (StringUtils.isBlank(user.getUsername())) {
            map.put("usernameMsg", "账号不能为空!");
            return map;
        }
        if (StringUtils.isBlank(user.getPassword())) {
            map.put("passwordMsg", "密码不能为空!");
            return map;
        }
        if (StringUtils.isBlank(user.getEmail())) {
            map.put("emailMsg", "邮箱不能为空!");
            return map;
        }
        // 验证邮箱合法性
        boolean email = CommunityUtil.isEmail(user.getEmail());
        if(!email){
            map.put("emailMsg","邮箱不正确!");
            return map;
        }
        // 验证账号是否已注册
        User u = userMapper.selectByName(user.getUsername());
        if (u != null) {
            map.put("usernameMsg", "该账号已存在!");
            return map;
        }
        // 验证邮箱是否已注册
        u = userMapper.selectByEmail(user.getEmail());
        if (u != null) {
            map.put("emailMsg", "该邮箱已被注册!");
            return map;
        }
        // 注册用户
        user.setSalt(CommunityUtil.generateUUID().substring(0, 5));
        user.setPassword(CommunityUtil.MD5(user.getPassword() + user.getSalt()));
        user.setType(0);
        user.setStatus(0);
        user.setActivationCode(CommunityUtil.generateUUID());
        user.setHeaderUrl(String.format("http://images.nowcoder.com/head/%dt.png", new Random().nextInt(1000)));
        user.setCreateTime(new Date());
        userMapper.insertUser(user);

        return map;
    }
    /**
     * 向指定用户发送邮件
     * @param username 用户名
     */
    public void sendMail(String username){
        User user = userMapper.selectByName(username);
        /**设置邮件模版*/
        Context context = new Context();
        context.setVariable("email",user.getEmail());
        /**设置处理请求的路径*/
        //http://localhost:8086/community/activation/userId/code
        String url = domain + contextPath + "/activation/" + user.getId() + "/" + user.getActivationCode();
        context.setVariable("url",url);
        String process = templateEngine.process("/mail/activation", context);
        mailClient.sendMail(user.getEmail(),"激活账号",process);
    }
    /**
     * 用户激活邮件
     * @param userId
     * @param code
     * @return
     */
    public int activation(int userId, String code) {
        User user = userMapper.selectById(userId);
        if (user.getStatus() == 1) {
            return ACTIVATION_REPEAT;
        } else if (user.getActivationCode().equals(code)) {
            userMapper.updateStatus(userId, 1);
            return ACTIVATION_SUCCESS;
        } else {
            return ACTIVATION_FAILURE;
        }
    }
}

在 controller 包下创建 LoginController 类,用户接受前端发送过来的注册请求,代码如下:

@Controller
public class LoginController implements CommunityConstant {

    private static final Logger logger = LoggerFactory.getLogger(LoginController.class);
    @Autowired
    private UserService userService;
    /**
     * 获取注册页面
     * @return
     */
    @GetMapping("/register")
    public String getRegisterPage() {
        return "/site/register";
    }
    /**
     * 获取登录页面
     * @return
     */
    @GetMapping("/login")
    public String getLoginPage() {
        return "/site/login";
    }
    /**
     * 用户注册
     * @param model
     * @param user
     * @return
     * @throws IllegalAccessException
     */
    @PostMapping("/register")
    public String register(Model model, User user) throws IllegalAccessException {
        Map<String, Object> map = userService.register(user);
        /**map为空或者为null,无报错信息*/
        if (map == null || map.isEmpty()) {
        userService.sendMail(user.getUsername());
            model.addAttribute("msg", "注册成功,我们已经向您的邮箱发送了一份激活邮件,请尽快激活!");
            model.addAttribute("target", "/index");
            return "/site/operate-result";
        } else {
            model.addAttribute("usernameMsg", map.get("usernameMsg"));
            model.addAttribute("passwordMsg", map.get("passwordMsg"));
            model.addAttribute("emailMsg", map.get("emailMsg"));
            return "/site/register";
        }
    }
    /**
     * 激活账号
     * @param model
     * @param userId
     * @param code
     * @return
     */
    // http://localhost:8080/community/activation/101/code
    @GetMapping("/activation/{userId}/{code}")
    public String activation(Model model, @PathVariable("userId") int userId, @PathVariable("code") String code) {
        int result = userService.activation(userId, code);
        if (result == ACTIVATION_SUCCESS) {
            model.addAttribute("msg", "激活成功,您的账号已经可以正常使用了!");
            model.addAttribute("target", "/login");
        } else if (result == ACTIVATION_REPEAT) {
            model.addAttribute("msg", "无效操作,该账号已经注册过了!");
            model.addAttribute("target", "/index");
        } else {
            model.addAttribute("msg", "激活失败,您提供的激活码不正确!");
            model.addAttribute("target", "/index");
        }
        return "/site/operate-result";
    }
}

功能测试

启动该项目,进入注册页面,填写相关的信息;
点击激活链接,激活用户账号;
激活成功,跳转至登录界面;
在这里插入图片描述

3、验证码功能

功能分析

为了方式批量注册,需要开发验证码功能,防止机器人自动注册、批量注册,本项目采用Kaptcha,Kaptcha 是一个可高度配置的实用验证码生成工具。

导入jar包

 <dependency>
            <groupId>com.github.penggle</groupId>
            <artifactId>kaptcha</artifactId>
            <version>2.3.2</version>
</dependency>

编写配置类

在 config 包下创建 Kaptcha 类,编写 Kaptcha 所需的配置信息,代码如下:

@Configuration
public class KaptchaConfig {

   @Bean
   public Producer kaptchaProducer() {
       Properties properties = new Properties();
       /**配置验证码*/
       properties.setProperty("kaptcha.image.width", "100");
       properties.setProperty("kaptcha.image.height", "40");
       properties.setProperty("kaptcha.textproducer.font.size", "32");
       properties.setProperty("kaptcha.textproducer.font.color", "0,0,0");
       // 在哪个字符串中生成验证码
       properties.setProperty("kaptcha.textproducer.char.string", "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYAZ");
       properties.setProperty("kaptcha.textproducer.char.length", "4");
       properties.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.NoNoise");

       DefaultKaptcha kaptcha = new DefaultKaptcha();
       Config config = new Config(properties);
       kaptcha.setConfig(config);
       return kaptcha;
   }
}

在 LoginController 类中添加 getKaptcha 方法,代码如下:

	@Autowired
    private Producer kaptchaProducer;
 /**
     * 获取验证码
     * @param response
     * @param session
     */
    @GetMapping("/kaptcha")
    public void getKaptcha(HttpServletResponse response, HttpSession session) {
        // 生成验证码
        String text = kaptchaProducer.createText();
        BufferedImage image = kaptchaProducer.createImage(text);
        // 将验证码存入session
        session.setAttribute("kaptcha", text);
        // 将图片输出给浏览器
        response.setContentType("image/png");
        try {
            OutputStream os = response.getOutputStream();
            ImageIO.write(image, "png", os);
        } catch (IOException e) {
            logger.error("响应验证码失败:" + e.getMessage());
        }
    }

前端对生成验证码以及刷新验证码进行修改,详情见源代码;

功能测试

启动项目,查看验证码是否正确显示,点击刷新刷新验证码;
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值