本资料来源于牛客网,仅供学习和交流使用,禁止商用。
文章目录
1 用户注册功能开发
1 数据库建表
DROP TABLE IF EXISTS `user`;
SET character_set_client = utf8mb4 ;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) DEFAULT NULL,
`password` varchar(50) DEFAULT NULL,
`salt` varchar(50) DEFAULT NULL,
`email` varchar(100) DEFAULT NULL,
`type` int(11) DEFAULT NULL COMMENT '0-普通用户; 1-超级管理员; 2-版主;',
`status` int(11) DEFAULT NULL COMMENT '0-未激活; 1-已激活;',
`activation_code` varchar(100) DEFAULT NULL,
`header_url` varchar(200) DEFAULT NULL,
`create_time` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `index_username` (`username`(20)),
KEY `index_email` (`email`(20))
) ENGINE=InnoDB AUTO_INCREMENT=101 DEFAULT CHARSET=utf8;
相关参数说明:
password 为加密后的密码
salt 为相关加密参数(目前还没研究懂)
activation_code 存放激活码
header_url 存放用户头像
2 编写用户实体类
public class User {
private int id;
private String username;
private String password;
private String salt;
private String email;
private int type;
private int status;
private String activationCode;
private String headerUrl;
private Date createTime;
// 省略getter和setter方法
// 省略toString方法
}
3 util工具包编写
生成随机字符串和编写加密算法。
public class CommunityUtil {
// 生成随机字符串
public static String generateUUID() {
return UUID.randomUUID().toString().replaceAll("-", "");
}
// MD5 加密 (每次加密的结果不会变的)
// 密码+随机的字符串(防止我们的密码被盗)
public static String md5(String key) {
if(StringUtils.isBlank(key)) {
return null;
}
return DigestUtils.md5DigestAsHex(key.getBytes());
}
}
4 DAO层开发
@Mapper
public interface LoginTicketMapper {
// 使用sql语句注解会比较直观,但是复杂的sql语句推荐使用xml
@Insert({
"insert into login_ticket(user_id, ticket, status, expired) ",
"values(#{userId}, #{ticket}, #{status}, #{expired}) "
})
@Options(useGeneratedKeys = true, keyProperty = "id") // 是否启用主键,将主键注入到哪个属性中
int insertLoginTicket(LoginTicket loginTicket);
}
5 service业务层开发
@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; // 路径
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));
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为thymeleaf下的对象
Context context = new Context();
context.setVariable("email", user.getEmail());
// http://localhost:8080/community/activation/101/code
// /activation/101/code : /激活的路径/用户id/激活码
// user.getId()是通过application配置里的mybatis.configuration.useGeneratedKeys=true获取的
String url = domain + contextPath + "/activation/" + user.getId() + "/" + user.getActivationCode();
context.setVariable("url", url);
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);
// 初始激活状态为0, 激活后变为1
if(user.getStatus() == 1) {
return ACTIVATION_REPEAT; // 重复激活
} else if(user.getActivationCode().equals(code)) {
// 改变用户的激活状态
userMapper.updateStatus(userId, 1);
return ACTIVATION_SUCCESS; // 激活成功
} else {
return ACTIVATION_FAILURE; // 激活失败, 比如激活码不相等
}
}
}
6 Controller层开发
@Controller
public class LoginController implements CommunityConstant {
private static final Logger logger = LoggerFactory.getLogger(LoginController.class);
@Autowired
private UserService userService;
// 跳转到注册页面,get请求
@RequestMapping(path = "/register", method = RequestMethod.GET)
public String getRegisterPage() {
return "/site/register";
}
/**
* 注册: 处理前端的注册请求, 完成注册
* @param model
* @param user
* @return
*/
@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";
}
}
}
7 前端代码处理
login.html
代码解释:在保证用户没有登录的情况下提供注册功能,通过thymeleaf模板进行路径的改写(登录功能会在后续开发,所以刚开始可以不写)
<li th:if="${loginUser==null}">
<a th:href="@{/register}">注册</a>
</li>
register.html
<!-- 内容 -->
<div>
<div>
<h3>注 册</h3>
<form method="post" th:action="@{/register}">
<div>
<label>账号:</label>
<div>
<!-- 此处传入name参数,记得要和user对象的name属性相对应 -->
<input type="text" th:class="|form-control ${usernameMsg!=null?'is-invalid':''}|"
th:value="${user!=null?user.username:''}"
id="username" name="username" placeholder="请输入您的账号!" required>
<div th:text="${usernameMsg}">
该账号已存在!
</div>
</div>
</div>
<div>
<label>密码:</label>
<div>
<input type="password" th:class="|form-control ${passwordMsg!=null?'is-invalid':''}|"
th:value="${user!=null?user.password:''}"
id="password" name="password" placeholder="请输入您的密码!" required>
<div th:text="${passwordMsg}">
密码长度不能小于8位!
</div>
</div>
</div>
<div>
<label>确认密码:</label>
<div>
<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>
<label>邮箱:</label>
<div>
<input type="email" th:class="|form-control ${emailMsg!=null?'is-invalid':''}|"
th:value="${user!=null?user.email:''}"
id="email" name="email" placeholder="请输入您的邮箱!" required>
<div th:text="${emailMsg}">
该邮箱已注册!
</div>
</div>
</div>
<div>
<div></div>
<div>
<button type="submit">立即注册</button>
</div>
</div>
</form>
</div>
</div>
2 用户通过邮箱激活
1 编写用户实体类
public interface CommunityConstant {
// 激活成功
int ACTIVATION_SUCCESS = 0;
// ctrl shift u 大小写转换
// 重复激活
int ACTIVATION_REPEAT = 0;
// 激活失败
int ACTIVATION_FAILURE = 0;
}
2 编写service业务层
@Service
public class UserService implements CommunityConstant {
public int activation(int userId, String code) {
User user = userMapper.selectById(userId);
// 初始激活状态为0, 激活后变为1
if(user.getStatus() == 1) {
return ACTIVATION_REPEAT; // 重复激活
} else if(user.getActivationCode().equals(code)) {
// 改变用户的激活状态
userMapper.updateStatus(userId, 1);
return ACTIVATION_SUCCESS; // 激活成功
} else {
return ACTIVATION_FAILURE; // 激活失败, 比如激活码不相等
}
}
}
3 编写controller层
/**
* 验证激活码状态
* @param model
* @param userId
* @param code
* @return
*/
// 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";
}
4 前端代码处理
<div>
<p>
<b th:text="${email}">xxx@xxx.com</b>, 您好!
</p>
<p>
您正在注册牛客网, 这是一封激活邮件, 请点击
<a th:href="${url}">此链接</a>,
激活您的牛客账号。
</p>
</div>