diff --git a/pom.xml b/pom.xml index e275ee6..a694854 100644 --- a/pom.xml +++ b/pom.xml @@ -66,6 +66,14 @@ java-jwt 4.3.0 + + org.springframework.boot + spring-boot-starter-mail + + + org.springframework.boot + spring-boot-starter-amqp + org.springframework.security spring-security-test diff --git a/src/main/java/com/example/config/RabbitConfiguration.java b/src/main/java/com/example/config/RabbitConfiguration.java new file mode 100644 index 0000000..7d5f70a --- /dev/null +++ b/src/main/java/com/example/config/RabbitConfiguration.java @@ -0,0 +1,18 @@ +package com.example.config; + +import org.springframework.amqp.core.Queue; +import org.springframework.amqp.core.QueueBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + + + +@Configuration +public class RabbitConfiguration { + @Bean("emailQueue") + public Queue emailQueue(){ + return QueueBuilder + .durable("mail") + .build(); + } +} diff --git a/src/main/java/com/example/controller/AuthorizeController.java b/src/main/java/com/example/controller/AuthorizeController.java new file mode 100644 index 0000000..81c29f8 --- /dev/null +++ b/src/main/java/com/example/controller/AuthorizeController.java @@ -0,0 +1,26 @@ +package com.example.controller; + +import com.example.entity.RestBean; +import com.example.service.AccountService; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/auth") +public class AuthorizeController { + + @Resource + AccountService service; + @GetMapping("/ask-code") + public RestBean askVerifyCode(@RequestParam String email, + @RequestParam String type , + HttpServletRequest request){ + String message = service.registerEmailVerityCode(type , email , request.getRemoteAddr()); + return message == null ? RestBean.success() : RestBean.failure(400 , message); + } + +} diff --git a/src/main/java/com/example/docker-compose.yaml b/src/main/java/com/example/docker-compose.yaml deleted file mode 100644 index 24943a9..0000000 --- a/src/main/java/com/example/docker-compose.yaml +++ /dev/null @@ -1,29 +0,0 @@ -version: '3' -services: - nginx: - image: nginx - ports: - - 8888:80 - deploy: - mode: replicated - replicas: 3 - visualizer: - image: dockersamples/visualizer - ports: - - 8080:8080 - volumes: - - /var/run/docker.sock:/var/run/docker.sock - deploy: - replicas: 1 - placement: - constraints: [node.repo == manager] - portainer: - image: portainer/portainer - ports: - - 9000:9000 - volumes: - - /var/run/docker.sock:/var/run/docker.sock - deploy: - replicas: 1 - placement: - constraints: [node.role == manager] \ No newline at end of file diff --git a/src/main/java/com/example/listener/MailQueueListener.java b/src/main/java/com/example/listener/MailQueueListener.java new file mode 100644 index 0000000..166662a --- /dev/null +++ b/src/main/java/com/example/listener/MailQueueListener.java @@ -0,0 +1,46 @@ +package com.example.listener; + +import jakarta.annotation.Resource; +import org.springframework.amqp.rabbit.annotation.RabbitHandler; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.mail.SimpleMailMessage; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.stereotype.Component; + +import java.util.Map; + +@Component +@RabbitListener(queues = "mail") +public class MailQueueListener { + @Resource + JavaMailSender sender; + + @Value("${spring.mail.username}") + String username; + + @RabbitHandler + public void sendMailMessage(Map data){ + String email = (String) data.get("email"); + Integer code = (Integer) data.get("code"); + String type = (String) data.get("type"); + SimpleMailMessage message = switch (type) { + case "register" -> createMessage("欢迎注册我们的网站" , + "您的邮件注册验证码为:" + code + ",有效时间三分钟,为了保障您的安全 , 请勿向他人泄漏验证码信息.", email); + case "reset" -> createMessage("你的密码重置邮件" , + "你好,您正在进行重置密码操作,验证码:" + code + ", 有效时间三分钟,如非本人操作,请无视." ,email); + default -> null; + }; + if (message == null) return; + sender.send(message); + } + + private SimpleMailMessage createMessage(String title , String content , String email){ + SimpleMailMessage message = new SimpleMailMessage(); + message.setSubject(title); + message.setText(content); + message.setTo(email); + message.setFrom(username); + return message; + } +} diff --git a/src/main/java/com/example/service/AccountService.java b/src/main/java/com/example/service/AccountService.java index 5cae6c1..5530672 100644 --- a/src/main/java/com/example/service/AccountService.java +++ b/src/main/java/com/example/service/AccountService.java @@ -7,4 +7,5 @@ import org.springframework.security.core.userdetails.UserDetailsService; public interface AccountService extends IService , UserDetailsService { public Account findAccountByNameOrEmail(String text); + String registerEmailVerityCode(String type , String email , String ip); } diff --git a/src/main/java/com/example/service/impl/AccountServiceImpl.java b/src/main/java/com/example/service/impl/AccountServiceImpl.java index fb6df79..7f96166 100644 --- a/src/main/java/com/example/service/impl/AccountServiceImpl.java +++ b/src/main/java/com/example/service/impl/AccountServiceImpl.java @@ -4,14 +4,32 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.example.entity.dto.Account; import com.example.mapper.AccountMapper; import com.example.service.AccountService; +import com.example.utils.Const; +import com.example.utils.FlowUtils; +import jakarta.annotation.Resource; +import org.springframework.amqp.core.AmqpTemplate; +import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; +import java.util.Map; +import java.util.Objects; +import java.util.Random; +import java.util.concurrent.TimeUnit; + @Service public class AccountServiceImpl extends ServiceImpl implements AccountService { + @Resource + FlowUtils utils; + @Resource + AmqpTemplate amqpTemplate; + + @Resource + StringRedisTemplate stringRedisTemplate; + @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { Account account = this.findAccountByNameOrEmail(username); @@ -24,10 +42,34 @@ public class AccountServiceImpl extends ServiceImpl imp .build(); } + + @Override + public String registerEmailVerityCode(String type, String email, String ip) { + synchronized (ip.intern()){ + if (this.verifyLimit(ip)){ + return "请求频繁,请稍后再试"; + } + Random random = new Random(); + int code = random.nextInt(899999) + 100000; + Map data = Map.of("type" , type , "email", email , "code" , code); + amqpTemplate.convertAndSend("mail" , data); + stringRedisTemplate.opsForValue() + .set(Const.VERIFY_EMAIL_DATA + email ,String.valueOf(code), 3 , TimeUnit.MINUTES ); + return null; + } + + + } + public Account findAccountByNameOrEmail(String text){ return this.query() .eq("username",text).or() .eq("email",text) .one(); } + + private boolean verifyLimit(String ip){ + String key = Const.VERIFY_EMAIL_LIMIT + ip; + return utils.limitOnceCheck(key , 60); + } } diff --git a/src/main/java/com/example/utils/Const.java b/src/main/java/com/example/utils/Const.java index 1d743e2..e60413f 100644 --- a/src/main/java/com/example/utils/Const.java +++ b/src/main/java/com/example/utils/Const.java @@ -3,6 +3,9 @@ package com.example.utils; public class Const { public static final String JWT_BLACK_LIST = "jwt:blacklist:"; + public static final String VERIFY_EMAIL_LIMIT = "verify:email:limit:"; + public static final String VERIFY_EMAIL_DATA = "verify:email:data:"; + public static final int ORDER_CORS = -102; } diff --git a/src/main/java/com/example/utils/FlowUtils.java b/src/main/java/com/example/utils/FlowUtils.java new file mode 100644 index 0000000..4714127 --- /dev/null +++ b/src/main/java/com/example/utils/FlowUtils.java @@ -0,0 +1,22 @@ +package com.example.utils; + +import jakarta.annotation.Resource; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Component; + +import java.util.concurrent.TimeUnit; + +@Component +public class FlowUtils { + @Resource + StringRedisTemplate template; + + public boolean limitOnceCheck(String key , int blockTime){ + if (Boolean.TRUE.equals(template.hasKey(key))){ + return false; + }else { + template.opsForValue().set(key , "" , blockTime , TimeUnit.SECONDS); + return true; + } + } +} diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index b2617ed..c67296d 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -1,5 +1,15 @@ spring: + mail: + host: smtp.163.com + username: realmeamadeus@163.com + password: SFTIRQAPGTZJFEPY + rabbitmq: + addresses: 8.138.140.94 + username: rabbitmq + password: rabbitmq_Ek=hv-W0 + virtual-host: / + port: 5672 security: filter: order: -100 #默认值为-100