Ver Fonte

feat: add email activate registration

ggbocoder há 2 anos atrás
pai
commit
4837174643

+ 15 - 11
db/user.sql

@@ -1,14 +1,18 @@
 
 CREATE TABLE users (
-                       user_id INT PRIMARY KEY AUTO_INCREMENT,
-                       username VARCHAR(50) NOT NULL UNIQUE,
-                       password VARCHAR(100) NOT NULL,
-                       email VARCHAR(100) NOT NULL UNIQUE,
-                       full_name VARCHAR(100),
-                       date_of_birth DATE,
-                       gender CHAR(1),
-                       phone_number VARCHAR(20),
-                       registration_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
-                       is_active BOOLEAN DEFAULT true,
-                       role VARCHAR(20) DEFAULT 'USER'
+                      user_id BIGINT PRIMARY KEY AUTO_INCREMENT,
+                      username VARCHAR(255) NOT NULL,
+                      password VARCHAR(255) NOT NULL,
+                      email VARCHAR(255) NOT NULL,
+                      full_name VARCHAR(255),
+                      date_of_birth DATE,
+                      gender VARCHAR(10),
+                      phone_number VARCHAR(20),
+                      registration_date DATE,
+                      active BOOLEAN,
+                      role VARCHAR(50),
+                      activation_code VARCHAR(255),
+                      create_time TIMESTAMP,
+                      salt VARCHAR(255)
 );
+

+ 17 - 0
pom.xml

@@ -51,6 +51,23 @@
             <version>8.0.23</version>
         </dependency>
 
+        <dependency>
+            <groupId>jakarta.mail</groupId>
+            <artifactId>jakarta.mail-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-thymeleaf</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-mail</artifactId>
+        </dependency>
+
+
+
     </dependencies>
 
     <build>

+ 15 - 5
src/main/java/cn/seecoder/fdroidrepository/Controller/UserController.java

@@ -3,16 +3,15 @@ package cn.seecoder.fdroidrepository.Controller;
 
 
 import cn.seecoder.fdroidrepository.DataObject.User;
+import cn.seecoder.fdroidrepository.Service.ServiceImpl.USER_STATUS;
 import cn.seecoder.fdroidrepository.Service.UserService;
+import cn.seecoder.fdroidrepository.utils.Result;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
+
 
-// UserController.java
 @RestController
 @RequestMapping("/api/users")
 public class UserController {
@@ -29,6 +28,17 @@ public class UserController {
         return new ResponseEntity<>("User registered successfully", HttpStatus.OK);
     }
 
+    @GetMapping("/activation/{userId}/{code}")
+    private Result<?> activate(@PathVariable int userId,@PathVariable String code){
+        USER_STATUS result=userService.activate(userId,code);
+        if(result==USER_STATUS.START_ACTIVATE){
+            return new Result<>("200","activate success");
+        }else if(result==USER_STATUS.REPEAT_ACTIVATE){
+            return new Result<>("409","repeat activation");
+        }
+        return new Result<>("400","activate failed!");
+    }
+
     @PostMapping("/login")
     public ResponseEntity<String> login(@RequestBody User user) {
         User existingUser = userService.findByUsername(user.getUsername());

+ 6 - 89
src/main/java/cn/seecoder/fdroidrepository/DataObject/User.java

@@ -1,7 +1,10 @@
 package cn.seecoder.fdroidrepository.DataObject;
 
+import lombok.Data;
+
 import java.util.Date;
 
+@Data
 public class User {
     private Long userId;
     private String username;
@@ -14,94 +17,8 @@ public class User {
     private Date registrationDate;
     private boolean active;
     private String role;
+    private String activationCode;
+    private Date createTime;
+    private String salt;
 
-    public Long getUserId() {
-        return userId;
-    }
-
-    public void setUserId(Long userId) {
-        this.userId = userId;
-    }
-
-    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 getEmail() {
-        return email;
-    }
-
-    public void setEmail(String email) {
-        this.email = email;
-    }
-
-    public String getFullName() {
-        return fullName;
-    }
-
-    public void setFullName(String fullName) {
-        this.fullName = fullName;
-    }
-
-    public Date getDateOfBirth() {
-        return dateOfBirth;
-    }
-
-    public void setDateOfBirth(Date dateOfBirth) {
-        this.dateOfBirth = dateOfBirth;
-    }
-
-    public String getGender() {
-        return gender;
-    }
-
-    public void setGender(String gender) {
-        this.gender = gender;
-    }
-
-    public String getPhoneNumber() {
-        return phoneNumber;
-    }
-
-    public void setPhoneNumber(String phoneNumber) {
-        this.phoneNumber = phoneNumber;
-    }
-
-    public boolean isActive() {
-        return active;
-    }
-
-    public void setActive(boolean active) {
-        this.active = active;
-    }
-
-    public Date getRegistrationDate() {
-        return registrationDate;
-    }
-
-    public void setRegistrationDate(Date registrationDate) {
-        this.registrationDate = registrationDate;
-    }
-
-
-
-    public String getRole() {
-        return role;
-    }
-
-    public void setRole(String role) {
-        this.role = role;
-    }
 }

+ 17 - 0
src/main/java/cn/seecoder/fdroidrepository/DataObject/vo/GitlabUserCreateVO.java

@@ -0,0 +1,17 @@
+package cn.seecoder.fdroidrepository.DataObject.vo;
+
+
+import lombok.Data;
+
+
+
+@Data
+public class GitlabUserCreateVO {
+
+
+    private String username;
+
+    private String password;
+
+    private String email;
+}

+ 6 - 1
src/main/java/cn/seecoder/fdroidrepository/Mapper/UserMapper.java

@@ -1,11 +1,16 @@
 package cn.seecoder.fdroidrepository.Mapper;
 
 import cn.seecoder.fdroidrepository.DataObject.User;
+import org.apache.ibatis.annotations.Insert;
 import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Options;
 
 @Mapper
 public interface UserMapper {
     User findByUsername(String username);
 
-    void save(User user);
+
+    long save(User user);
+
+    User selectByUserId(int userId);
 }

+ 7 - 0
src/main/java/cn/seecoder/fdroidrepository/Service/ServiceImpl/USER_STATUS.java

@@ -0,0 +1,7 @@
+package cn.seecoder.fdroidrepository.Service.ServiceImpl;
+
+public enum USER_STATUS {
+    REPEAT_ACTIVATE,
+    START_ACTIVATE,
+    FAIL_ACTIVATE
+}

+ 53 - 0
src/main/java/cn/seecoder/fdroidrepository/Service/ServiceImpl/UserServiceImpl.java

@@ -1,13 +1,36 @@
 package cn.seecoder.fdroidrepository.Service.ServiceImpl;
 
+
 import cn.seecoder.fdroidrepository.DataObject.User;
 import cn.seecoder.fdroidrepository.Mapper.UserMapper;
 import cn.seecoder.fdroidrepository.Service.UserService;
+import cn.seecoder.fdroidrepository.utils.MailClient;
+import cn.seecoder.fdroidrepository.utils.Tools;
 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.UUID;
+
 @Service
 public class UserServiceImpl implements UserService {
+
+    @Value("${community.path.domain}")
+    private String domain;
+
+    @Value("${server.servlet.context-path}")
+    private String contextPath;
+
+    @Autowired
+    private MailClient mailClient;
+
+    @Autowired
+    private TemplateEngine templateEngine;
+
     @Autowired
     private UserMapper userMapper;
 
@@ -18,7 +41,35 @@ public class UserServiceImpl implements UserService {
 
     @Override
     public void save(User user) {
+
+        // register user
+        user.setSalt(UUID.randomUUID().toString().substring(0,5));
+        user.setPassword(Tools.md5(user.getPassword()+user.getSalt()));
+        user.setActive(false);
+        user.setCreateTime(new Date());
+        user.setActivationCode(Tools.generateUUID());
         userMapper.save(user);
+
+
+        //send email
+        Context context = new Context();
+        context.setVariable("email",user.getEmail());
+        String url = domain + contextPath + "/api/users/activation/" + user.getUserId() + "/" + user.getActivationCode();
+        context.setVariable("url",url);
+        String content = templateEngine.process("mail/activation",context);
+        mailClient.sendMail(user.getEmail(),"activate account",content);
+    }
+
+    @Override
+    public USER_STATUS activate(int userId, String code) {
+        User user=userMapper.selectByUserId(userId);
+        if(user.isActive()){
+            return USER_STATUS.REPEAT_ACTIVATE;
+        }else if(user.getActivationCode().equals(code)){
+            return USER_STATUS.START_ACTIVATE;
+        }else{
+            return USER_STATUS.FAIL_ACTIVATE;
+        }
     }
 
     @Override
@@ -26,4 +77,6 @@ public class UserServiceImpl implements UserService {
         User existingUser = findByUsername(username);
         return existingUser == null;
     }
+
+
 }

+ 3 - 0
src/main/java/cn/seecoder/fdroidrepository/Service/UserService.java

@@ -1,6 +1,7 @@
 package cn.seecoder.fdroidrepository.Service;
 
 import cn.seecoder.fdroidrepository.DataObject.User;
+import cn.seecoder.fdroidrepository.Service.ServiceImpl.USER_STATUS;
 import org.springframework.stereotype.Service;
 
 
@@ -9,5 +10,7 @@ public interface UserService {
 
     void save(User user);
 
+    USER_STATUS activate(int userId, String code);
+
     boolean isUsernameUnique(String username);
 }

+ 41 - 0
src/main/java/cn/seecoder/fdroidrepository/utils/MailClient.java

@@ -0,0 +1,41 @@
+package cn.seecoder.fdroidrepository.utils;
+
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.mail.javamail.MimeMessageHelper;
+import org.springframework.stereotype.Component;
+
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMessage;
+
+
+
+
+@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());
+        }
+
+    }
+
+}

+ 61 - 0
src/main/java/cn/seecoder/fdroidrepository/utils/Result.java

@@ -0,0 +1,61 @@
+/*
+ *  Copyright 1999-2019 Seata.io Group.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package cn.seecoder.fdroidrepository.utils;
+
+import java.io.Serializable;
+
+/**
+ * The basic result
+ * @author zhongxiang.wang
+ */
+public class Result<T>  implements Serializable {
+    private static final long serialVersionUID = 7761261124298767L;
+
+    public static final String SUCCESS_CODE = "200";
+    public static final String SUCCESS_MSG = "success";
+
+    private String code = SUCCESS_CODE;
+    private String message = SUCCESS_MSG;
+
+    public Result() {
+    }
+
+    public Result(String code, String message) {
+        this.code = code;
+        this.message = message;
+    }
+
+    public boolean isSuccess() {
+        return SUCCESS_CODE.equals(this.code);
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public void setCode(String code) {
+        this.code = code;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+
+}

+ 16 - 0
src/main/java/cn/seecoder/fdroidrepository/utils/Tools.java

@@ -0,0 +1,16 @@
+package cn.seecoder.fdroidrepository.utils;
+
+import org.springframework.util.DigestUtils;
+
+import java.util.UUID;
+
+public class Tools {
+
+    public static String md5(String key){
+        return DigestUtils.md5DigestAsHex(key.getBytes());
+    }
+
+    public static String generateUUID(){
+        return UUID.randomUUID().toString().replaceAll("-","");
+    }
+}

+ 34 - 0
src/main/resources/application.properties

@@ -1,3 +1,4 @@
+server.servlet.context-path=
 
 # 数据库连接配置
 spring.datasource.url=jdbc:mysql://localhost:3306/fdroid
@@ -7,3 +8,36 @@ spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
 
 # MyBatis配置
 mybatis.mapper-locations=classpath:mapper/*.xml
+
+
+community.path.domain=http://127.0.0.1:8080
+
+# 邮箱配置
+# SMTP服务器主机
+spring.mail.host=smtp.163.com
+
+# SMTP服务器端口
+spring.mail.port=25
+
+# 启用TLS
+spring.mail.properties.mail.smtp.starttls.enable=true
+
+# 指定TLS版本
+spring.mail.properties.mail.smtp.starttls.required=true
+
+# 使用SSL连接
+# spring.mail.properties.mail.smtp.ssl.enable=true
+
+# SMTP服务器用户名
+spring.mail.username=jiangjunminggggg@163.com
+
+# SMTP服务器密码(注意:使用授权码而不是邮箱登录密码)
+spring.mail.password=JGRWKGXRJRZXRVVH
+
+# 默认发件人地址
+spring.mail.properties.mail.smtp.from=jiangjunminggggg@163.com
+
+# 默认编码
+spring.mail.default-encoding=UTF-8
+
+

+ 26 - 4
src/main/resources/mapper/UserMapper.xml

@@ -13,8 +13,11 @@
         <result property="gender" column="gender" />
         <result property="phoneNumber" column="phone_number" />
         <result property="registrationDate" column="registration_date" />
-        <result property="active" column="is_active" />
+        <result property="active" column="active" />
         <result property="role" column="role" />
+        <result property="activationCode" column="activation_code" />
+        <result property="createTime" column="create_time" />
+        <result property="salt" column="salt" />
     </resultMap>
 
     <!-- 根据用户名查询用户 -->
@@ -22,10 +25,29 @@
         SELECT * FROM users WHERE username = #{username}
     </select>
 
+    <select id="selectByUserId" resultType="cn.seecoder.fdroidrepository.DataObject.User">
+        SELECT
+            user_id as userId,
+            username,
+            password,
+            email,
+            full_name as fullName,
+            date_of_birth as dateOfBirth,
+            gender,
+            phone_number as phoneNumber,
+            registration_date as registrationDate,
+            active,
+            role,
+            activation_code as activationCode,
+            create_time as createTime,
+            salt
+            FROM users WHERE user_id = #{userId}
+    </select>
+
     <!-- 插入用户信息 -->
-    <insert id="save" parameterType="cn.seecoder.fdroidrepository.DataObject.User">
-        INSERT INTO users (username, password, email, full_name, date_of_birth, gender, phone_number, registration_date, is_active, role)
-        VALUES (#{username}, #{password}, #{email}, #{fullName}, #{dateOfBirth}, #{gender}, #{phoneNumber}, #{registrationDate}, #{active}, #{role})
+    <insert id="save" parameterType="cn.seecoder.fdroidrepository.DataObject.User" useGeneratedKeys="true" keyProperty="userId">
+        INSERT INTO users (username, password, email, full_name, date_of_birth, gender, phone_number, registration_date, active, role, activation_code)
+        VALUES (#{username}, #{password}, #{email}, #{fullName}, #{dateOfBirth}, #{gender}, #{phoneNumber}, #{registrationDate}, #{active}, #{role}, #{activationCode})
     </insert>
 
 </mapper>

+ 20 - 0
src/main/resources/templates/mail/activation.html

@@ -0,0 +1,20 @@
+<!doctype html>
+<html lang="en" xmlns:th="http://www.thymeleaf.org">
+<head>
+    <meta charset="utf-8">
+    <link rel="icon" href="https://static.nowcoder.com/images/logo_87_87.png"/>
+    <title>Fdroid应用市场-激活账号</title>
+</head>
+<body>
+	<div>
+		<p>
+			<b th:text="${email}">xxx@xxx.com</b>, 您好!
+		</p>
+		<p>
+			您正在注册Fdroid应用市场, 这是一封激活邮件, 请点击
+			<a th:href="${url}">此链接</a>,
+			激活您的账号!
+		</p>
+	</div>
+</body>
+</html>