当前位置:网站首页>仿牛客论坛项目
仿牛客论坛项目
2022-08-01 20:31:00 【我乃cv大师】
个人记录一下觉得有意思的功能模块
目录
1.发送邮件(包括html页面)
1.1 导入maven依赖
发送邮件的功能封装在spring中,可以直接调用,首先导入相应的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
<version>2.1.5.RELEASE</version>
</dependency>
1.2 修改配置文件
再修改一下application.properties配置文件,加入以下内容
# MailProperties
spring.mail.host=smtp.sina.com
spring.mail.port=465
spring.mail.username=你自己的邮箱地址
spring.mail.password=邮箱的授权码
spring.mail.protocol=smtps
spring.mail.properties.mail.smtp.ssl.enable=true
1.3 编写邮件发送工具类
好了,接下来我们开始写一下发送邮件的工具类MailClient
@Component
public class MailClient {
private static final Logger logger = LoggerFactory.getLogger(MailClient.class);
@Autowired
private JavaMailSender mailSender;
@Value("${spring.mail.username}")
private String from;
public void sendMail(String to, String subject, String content) {
try {
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message);
helper.setFrom(from);
helper.setTo(to);
helper.setSubject(subject);
helper.setText(content, true);
mailSender.send(helper.getMimeMessage());
} catch (MessagingException e) {
logger.error("发送邮件失败:" + e.getMessage());
}
}
}
1.4 工具类测试
接下来我们测试一下这个工具类
package com.nowcoder.community;
import com.nowcoder.community.util.MailClient;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = CommunityApplication.class)
public class MailTests {
@Autowired
private MailClient mailClient;
@Autowired
private TemplateEngine templateEngine;
//发送文字消息
@Test
public void testTextMail() {
mailClient.sendMail("[email protected]", "TEST", "Welcome.");
}
//发送网页(需要事先准备好网页资源)
@Test
public void testHtmlMail() {
//Context为网页正文内容
Context context = new Context();
//为正文内容的变量进行赋值
context.setVariable("username", "sunday");
//静态资源路径 template/context
String content = templateEngine.process("/mail/demo", context);
System.out.println(content);
//发送邮件
mailClient.sendMail("[email protected]", "HTML", content);
}
}
网页版测试结果
特别给力,这个方法可以用到后面的账号注册激活功能模块里面
2.登陆注册功能
通过注册页面,输入账号,密码,邮箱,点击注册后,工程会往注册邮箱发送一份html,点击html中的链接,以实现用户的激活。
2.1 对用户的信息进行加密处理
用户的账号,密码的注册,为安全性考虑,不能直接存储进数据库,下场就是学习通,需要进行加密存储。需要编写一个加密工具类,对用户信息进行加密。
采用MD5加密方法,为防止密码过于简单,导致即便是加密后也容易破解的问题,设置了加密盐,也就是在原密码后,生成n位随机字符串,再进行整体的一个加密。
工具类如下所示
package com.nowcoder.community.util;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.DigestUtils;
import java.util.UUID;
public class CommunityUtil {
// 生成随机字符串
public static String generateUUID() {
return UUID.randomUUID().toString().replaceAll("-", "");
}
// MD5加密
// hello -> abc123def456,这样的弊端就是简单的密码加密之后依旧是容易破解的
//在原账号的基础上加上随机的字符串,再进行加密,这样不容易被破解
// hello + 3e4a8 -> abc123def456abc
public static String md5(String key) {
//空值不处理
if (StringUtils.isBlank(key)) {
return null;
}
//spring自带的mp5加密
return DigestUtils.md5DigestAsHex(key.getBytes());
}
}
2.2 用户重复注册问题和用户未激活问题
注册用户的账号,邮箱不能重复注册,账号需要激活以后才能使用,账号不能重复激活。
不能重复注册实现方式,即去数据库找是否已存在,账号需要激活,不能重复激活,通过定义一组常量,通过它属性的变化,给user赋予相应的值
package com.nowcoder.community.util;
public interface CommunityConstant {
/**
* 激活成功
*/
int ACTIVATION_SUCCESS = 0;
/**
* 重复激活
*/
int ACTIVATION_REPEAT = 1;
/**
* 激活失败
*/
int ACTIVATION_FAILURE = 2;
}
user类为如下所示
package com.nowcoder.community.entity;
import java.util.Date;
public class User {
private int id;
//用户名
private String username;
//密码
private String password;
//加密盐
private String salt;
//邮箱
private String email;
//用户种类
private int type;
//用户状态 0为未激活,1为激活
private int status;
//用户的激活码
private String activationCode;
//用户的头像地址,获取头像
private String headerUrl;
//用户的注册时间
private Date createTime;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = salt;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getActivationCode() {
return activationCode;
}
public void setActivationCode(String activationCode) {
this.activationCode = activationCode;
}
public String getHeaderUrl() {
return headerUrl;
}
public void setHeaderUrl(String headerUrl) {
this.headerUrl = headerUrl;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", salt='" + salt + '\'' +
", email='" + email + '\'' +
", type=" + type +
", status=" + status +
", activationCode='" + activationCode + '\'' +
", headerUrl='" + headerUrl + '\'' +
", createTime=" + createTime +
'}';
}
}
2.3 确定邮件发送的具体网址信息
注册成功以后,工程会向用户发送激活邮件,点击里面的链接才能成功对用户进行激活。
加入配置项
# community域名
community.path.domain=http://localhost:8088
编写service层,实现注册的具体功能,以及邮件收发,UserService
package com.nowcoder.community.service;
import com.nowcoder.community.dao.UserMapper;
import com.nowcoder.community.entity.User;
import com.nowcoder.community.util.CommunityConstant;
import com.nowcoder.community.util.CommunityUtil;
import com.nowcoder.community.util.MailClient;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
@Service
public class UserService implements CommunityConstant {
@Autowired
private UserMapper userMapper;
@Autowired
private MailClient mailClient;
@Autowired
private TemplateEngine templateEngine;
//@Value注解,从配置文件中提取值
@Value("${community.path.domain}")
private String domain;
//@Value注解,从配置文件中提取值
@Value("${server.servlet.context-path}")
private String contextPath;
public User findUserById(int id) {
return userMapper.selectById(id);
}
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;
}
// 验证账号
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));
//原注册密码加上加密盐,再用md5进行加密,这样的密码难以破解
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);
// 激活邮件
Context context = new Context();
context.setVariable("email", user.getEmail());
// http://localhost:8080/community/activation/101/code 项目的访问路径,展示在网页上
String url = domain + contextPath + "/activation/" + user.getId() + "/" + user.getActivationCode();
context.setVariable("url", url);
//将正文内容放到模板引擎中,即可跳转访问template文件夹下mail文件夹下的activation.html资源
String content = templateEngine.process("/mail/activation", context);
mailClient.sendMail(user.getEmail(), "激活账号", content);
return map;
}
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
package com.nowcoder.community.controller;
import com.nowcoder.community.entity.User;
import com.nowcoder.community.service.UserService;
import com.nowcoder.community.util.CommunityConstant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import java.util.Map;
@Controller
public class LoginController implements CommunityConstant {
@Autowired
private UserService userService;
@RequestMapping(path = "/register", method = RequestMethod.GET)
public String getRegisterPage() {
return "/site/register";
}
@RequestMapping(path = "/login", method = RequestMethod.GET)
public String getLoginPage() {
return "/site/login";
}
//注册触发的函数,成功则发送一封邮件
@RequestMapping(path = "/register", method = RequestMethod.POST)
public String register(Model model, User user) {
Map<String, Object> map = userService.register(user);
//成功
if (map == null || map.isEmpty()) {
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";
}
}
//发送激活邮件,通过点击链接触发的函数,通过读取请求地址里面的参数,判定激活码是否有效,判定用户是否成功激活
// http://localhost:8080/community/activation/101/code
@RequestMapping(path = "/activation/{userId}/{code}", method = RequestMethod.GET)
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";
}
}
注册界面为register.html
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="icon" href="https://static.nowcoder.com/images/logo_87_87.png"/>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" crossorigin="anonymous">
<link rel="stylesheet" th:href="@{/css/global.css}" />
<link rel="stylesheet" th:href="@{/css/login.css}" />
<title>牛客网-注册</title>
</head>
<body>
<div class="nk-container">
<!-- 头部 -->
<header class="bg-dark sticky-top" th:replace="index::header">
<div class="container">
<!-- 导航 -->
<nav class="navbar navbar-expand-lg navbar-dark">
<!-- logo -->
<a class="navbar-brand" href="#"></a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<!-- 功能 -->
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item ml-3 btn-group-vertical">
<a class="nav-link" href="../index.html">首页</a>
</li>
<li class="nav-item ml-3 btn-group-vertical">
<a class="nav-link position-relative" href="letter.html">消息<span class="badge badge-danger">12</span></a>
</li>
<li class="nav-item ml-3 btn-group-vertical">
<a class="nav-link" href="register.html">注册</a>
</li>
<li class="nav-item ml-3 btn-group-vertical">
<a class="nav-link" href="login.html">登录</a>
</li>
<li class="nav-item ml-3 btn-group-vertical dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<img src="http://images.nowcoder.com/head/1t.png" class="rounded-circle" style="width:30px;"/>
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item text-center" href="profile.html">个人主页</a>
<a class="dropdown-item text-center" href="setting.html">账号设置</a>
<a class="dropdown-item text-center" href="login.html">退出登录</a>
<div class="dropdown-divider"></div>
<span class="dropdown-item text-center text-secondary">nowcoder</span>
</div>
</li>
</ul>
<!-- 搜索 -->
<form class="form-inline my-2 my-lg-0" action="search.html">
<input class="form-control mr-sm-2" type="search" aria-label="Search" />
<button class="btn btn-outline-light my-2 my-sm-0" type="submit">搜索</button>
</form>
</div>
</nav>
</div>
</header>
<!-- 内容 -->
<div class="main">
<div class="container pl-5 pr-5 pt-3 pb-3 mt-3 mb-3">
<h3 class="text-center text-info border-bottom pb-3">注 册</h3>
<form class="mt-5" method="post" th:action="@{/register}">
<div class="form-group row">
<label for="username" class="col-sm-2 col-form-label text-right">账号:</label>
<div class="col-sm-10">
<input type="text"
th:class="|form-control ${usernameMsg!=null?'is-invalid':''}|"
th:value="${user!=null?user.username:''}"
id="username" name="username" placeholder="请输入您的账号!" required>
<div class="invalid-feedback" th:text="${usernameMsg}">
该账号已存在!
</div>
</div>
</div>
<div class="form-group row mt-4">
<label for="password" class="col-sm-2 col-form-label text-right">密码:</label>
<div class="col-sm-10">
<input type="password"
th:class="|form-control ${passwordMsg!=null?'is-invalid':''}|"
th:value="${user!=null?user.password:''}"
id="password" name="password" placeholder="请输入您的密码!" required>
<div class="invalid-feedback" th:text="${passwordMsg}">
密码长度不能小于8位!
</div>
</div>
</div>
<div class="form-group row mt-4">
<label for="confirm-password" class="col-sm-2 col-form-label text-right">确认密码:</label>
<div class="col-sm-10">
<input type="password" class="form-control"
th:value="${user!=null?user.password:''}"
id="confirm-password" placeholder="请再次输入密码!" required>
<div class="invalid-feedback">
两次输入的密码不一致!
</div>
</div>
</div>
<div class="form-group row">
<label for="email" class="col-sm-2 col-form-label text-right">邮箱:</label>
<div class="col-sm-10">
<input type="email"
th:class="|form-control ${emailMsg!=null?'is-invalid':''}|"
th:value="${user!=null?user.email:''}"
id="email" name="email" placeholder="请输入您的邮箱!" required>
<div class="invalid-feedback" th:text="${emailMsg}">
该邮箱已注册!
</div>
</div>
</div>
<div class="form-group row mt-4">
<div class="col-sm-2"></div>
<div class="col-sm-10 text-center">
<button type="submit" class="btn btn-info text-white form-control">立即注册</button>
</div>
</div>
</form>
</div>
</div>
<!-- 尾部 -->
<footer class="bg-dark">
<div class="container">
<div class="row">
<!-- 二维码 -->
<div class="col-4 qrcode">
<img src="https://uploadfiles.nowcoder.com/app/app_download.png" class="img-thumbnail" style="width:136px;" />
</div>
<!-- 公司信息 -->
<div class="col-8 detail-info">
<div class="row">
<div class="col">
<ul class="nav">
<li class="nav-item">
<a class="nav-link text-light" href="#">关于我们</a>
</li>
<li class="nav-item">
<a class="nav-link text-light" href="#">加入我们</a>
</li>
<li class="nav-item">
<a class="nav-link text-light" href="#">意见反馈</a>
</li>
<li class="nav-item">
<a class="nav-link text-light" href="#">企业服务</a>
</li>
<li class="nav-item">
<a class="nav-link text-light" href="#">联系我们</a>
</li>
<li class="nav-item">
<a class="nav-link text-light" href="#">免责声明</a>
</li>
<li class="nav-item">
<a class="nav-link text-light" href="#">友情链接</a>
</li>
</ul>
</div>
</div>
<div class="row">
<div class="col">
<ul class="nav btn-group-vertical company-info">
<li class="nav-item text-white-50">
公司地址:北京市朝阳区大屯路东金泉时代3-2708北京牛客科技有限公司
</li>
<li class="nav-item text-white-50">
联系方式:010-60728802(电话) [email protected]
</li>
<li class="nav-item text-white-50">
牛客科技2018 All rights reserved
</li>
<li class="nav-item text-white-50">
京ICP备14055008号-4
<img src="http://static.nowcoder.com/company/images/res/ghs.png" style="width:18px;" />
京公网安备 11010502036488号
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</footer>
</div>
<script src="https://code.jquery.com/jquery-3.3.1.min.js" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" crossorigin="anonymous"></script>
<script th:src="@{/js/global.js}"></script>
<script th:src="@{/js/register.js}"></script>
</body>
</html>
激活界面activation.html
边栏推荐
- 数据库内核面试中我不会的问题(1)
- "Torch" tensor multiplication: matmul, einsum
- Does LabVIEW really close the COM port using VISA Close?
- Win10, the middle mouse button cannot zoom in and out in proe/creo
- 模板特例化和常用用法
- 【无标题】
- [Multi-task learning] Modeling Task Relationships in Multi-task Learning with Multi-gate Mixture-of-Experts KDD18
- KDD2022 | 自监督超图Transformer推荐系统
- 专利检索常用的网站有哪些?
- 【节能学院】推进农业水价综合改革的意见解读
猜你喜欢
面试突击70:什么是粘包和半包?怎么解决?
The graphic details Eureka's caching mechanism/level 3 cache
LTE time domain and frequency domain resources
第59章 ApplicationPart内置依赖注入中间件
智能硬件开发怎么做?机智云全套自助式开发工具助力高效开发
【kali-信息收集】(1.2)SNMP枚举:Snmpwalk、Snmpcheck;SMTP枚举:smtp-user-enum
【Social Media Marketing】How to know if your WhatsApp is blocked?
【nn.Parameter()】生成和为什么要初始化
解除360对默认浏览器的检测与修改
通配符 SSL/TLS 证书
随机推荐
【七夕特别篇】七夕已至,让爱闪耀
面试突击70:什么是粘包和半包?怎么解决?
大整数相加,相减,相乘,大整数与普通整数的相乘,相除
30-day question brushing plan (5)
数据可视化
MongoDB快速上手
为什么限制了Oracle的SGA和PGA,OS仍然会用到SWAP?
启明云端分享|盘点ESP8684开发板有哪些功能
[Multi-task optimization] DWA, DTP, Gradnorm (CVPR 2019, ECCV 2018, ICML 2018)
useful website
作为程序员你应该会的软件
瀚高数据导入
虚拟机的IP地址自动变为127.0.0.1
How PROE/Croe edits a completed sketch and brings it back to sketching state
数据库单字段存储多个标签(位移操作)
Compose实战-实现一个带下拉加载更多功能的LazyColumn
myid file is missing
"No title"
AQS原理和介绍
大神经验:软件测试的自我发展规划