springBoot邮件发送支持多邮箱账号改造

springboot带有spring-boot-starter-mail包,可用于发送邮件。但是SpringBoot mail默认只能配置一个账号,配置如下:

spring:
  mail:
    protocol: smtp
    host: smtp.126.com
    port: 25
    username: xxxx@126.com
    password: xxxxxxx
    default-encoding: UTF-8
    properties:
      mail:
        smtp:
          socketFactoryClass: javax.net.ssl.SSLSocketFactory
        debug: true

为了支持多个账号,需要自定义IOC的机制。

查看Springboot mail相关的源代码,发现邮件的配置主要是在类org.springframework.boot.autoconfigure.mail.MailSenderPropertiesConfiguration里完成,这里会配置JavaMailSenderMailProperties

因此解决问题的关键是重写MailSenderPropertiesConfiguration类,能够按照自定义的prefix配置单独的JavaMailSender和MailProperties。

代码如下:

  • 引入依赖
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-mail</artifactId>
		</dependency>

		<!-- 使用 thymeleaf 编写邮箱内容模板 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
  • 配置 yml:
spring:
  mail:
  	# springBoot原配置  这里做默认配置
  	protocol: smtp
    host: oa.zzsymls.com
    port: 9225
    username: report@zzsymls.com
    password: Sy123456
    default-encoding: UTF-8
    properties:
      fromName: 申友检验
      mail:
#        smtp:
#          ssl:
#            enable: true
#          socketFactoryClass: javax.net.ssl.SSLSocketFactory
        debug: false
    # 自定义扩展的配置 (支持多邮箱)
    mails:
      - protocol: smtp
        host: smtp.126.com
        port: 25
        username: xxxxx@126.com
        password: xxxxx
        default-encoding: UTF-8
        properties:
          mail:
            smtp:
              socketFactoryClass: javax.net.ssl.SSLSocketFactory
            debug: true
      - protocol: smtp
        host: smtp.163.com
        port: 25
        username: xxxxx@163.com
        password: xxxxx
      - protocol: smtp
        host: xxxxx
        port: 0000
        username: xxxxx
        password: xxxxx
        
  thymeleaf:
    mode: HTML
    encoding: utf-8
    # 禁用缓存
    cache: false
import org.springframework.boot.autoconfigure.mail.MailProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;

import java.util.List;

/** 加载邮箱账号配置列表
 * description: BooMailProperties <br>
 * date: 2024/3/26 13:44 <br>
 * author: Boo <br>
 * version: 1.0 <br>
 */
@ConfigurationProperties(
        prefix = "spring.mail"
)
public class BooMailsProperties {
    private List<MailProperties> mails;

    public List<MailProperties> getMails() {
        return mails;
    }

    public void setMails(List<MailProperties> mails) {
        this.mails = mails;
    }
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.mail.MailProperties;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/** 获取邮件发送者
 * description: MailSenderFactory <br>
 * date: 2024/3/26 14:24 <br>
 * author: Boo <br>
 * version: 1.0 <br>
 */
@Component
public class MailSenderFactory {

    @Autowired
    BooMailsProperties booMailsProperties;

    private Map<String, JavaMailSender> mailSenderMap;

    /** 功能描述:
     * @param booMailsProperties 多个邮箱配置
     * @param mailPropertiesCommon springBoot原有的邮箱配置  这里做默认配置用
    * @Author: Boo
    * @Date: 2024/4/7 14:00
    */
    public MailSenderFactory(BooMailsProperties booMailsProperties,
                             MailProperties mailPropertiesCommon){
        List<MailProperties> emailArr = booMailsProperties.getMails();
        if (CollectionUtils.isEmpty(emailArr)){
            emailArr.add(mailPropertiesCommon);
            log.error("You must configuration the mails for spring.mail!");
            return;
        }

        mailSenderMap = new HashMap<>();
        for (MailProperties mailProperties : emailArr) {
            copyNonNullProperties(mailPropertiesCommon, mailProperties);
            JavaMailSenderImpl sender = new JavaMailSenderImpl();
            this.applyProperties(mailProperties, sender);
            mailSenderMap.put(mailProperties.getUsername(), sender);
        }
    }

    /**
     * 获取邮件发送者
     * @param username
     * @return
     */
    public JavaMailSender getMailSender(String username){
        return mailSenderMap.get(username);
    }

    private void applyProperties(MailProperties properties, JavaMailSenderImpl sender) {
        sender.setHost(properties.getHost());
        if (properties.getPort() != null) {
            sender.setPort(properties.getPort());
        }

        sender.setUsername(properties.getUsername());
        sender.setPassword(properties.getPassword());
        sender.setProtocol(properties.getProtocol());
        if (properties.getDefaultEncoding() != null) {
            sender.setDefaultEncoding(properties.getDefaultEncoding().name());
        }

        if (!properties.getProperties().isEmpty()) {
            sender.setJavaMailProperties(this.asProperties(properties.getProperties()));
        }

    }

    private Properties asProperties(Map<String, String> source) {
        Properties properties = new Properties();
        properties.putAll(source);
        return properties;
    }

	/** 功能描述: 拷贝target中没有的属性
     * @param source
     * @param target
    * @return: void
    * @Author: Boo
    * @Date: 2024/4/7 14:01
    */
    public void copyNonNullProperties(Object source, Object target) {
        BeanWrapper sourceWrapper = PropertyAccessorFactory.forBeanPropertyAccess(source);
        BeanWrapper targetWrapper = PropertyAccessorFactory.forBeanPropertyAccess(target);

        PropertyDescriptor[] propertyDescriptors = BeanUtils.getPropertyDescriptors(source.getClass());
        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
            String propertyName = propertyDescriptor.getName();
            Object srcValue = sourceWrapper.getPropertyValue(propertyName);
            Object targetValue = targetWrapper.getPropertyValue(propertyName);
            if (srcValue != null && targetValue == null) {
                targetWrapper.setPropertyValue(propertyName, srcValue);
            }

            if (srcValue != null && srcValue instanceof Map && targetValue instanceof Map) {
                copyNonNullProperties4Map(srcValue, targetValue);
            }
        }
    }

	/** 拷贝target中没有的属性 */
    private void copyNonNullProperties4Map(Object srcValue, Object targetValue) {
        Map srcMap = (Map) srcValue;
        Map targetMap = (Map) targetValue;

        srcMap.forEach((k, v) -> {
            if (targetMap.get(k) == null) {
                targetMap.put(k, v);
            }
        });
    }

}

import lombok.Synchronized;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.mail.MailProperties;
import org.springframework.mail.MailAuthenticationException;
import org.springframework.mail.MailException;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.io.File;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * description: EmailSender <br>
 * date: 2023/12/4 11:07 <br>
 * author: Boo <br>
 * version: 1.0 <br>
 */
@Component
@Slf4j
public class EmailSender {

	private List<MailProperties> mailPropertiesList;

	private int activitedMailIndex = 0;
	private String activitedMailDate = "";

	@Autowired
	BooMailsProperties booMailsProperties;

	@Autowired
	MailSenderFactory mailSenderFactory;

	@Autowired
	TemplateEngine templateEngine;

	public EmailSender(BooMailsProperties booMailsProperties) {
		this.mailPropertiesList = booMailsProperties.getMails();
	}


	public boolean send(String[] mailAddressArr,
						String subject,
						String title,
						String text,
						File file) {

		String errMsg = "";

		initActivitedMailIndex();

		MailProperties mailProperties = mailPropertiesList.get(activitedMailIndex);
		JavaMailSender javaMailSender = mailSenderFactory.getMailSender(mailProperties.getUsername());
		MimeMessage msg = javaMailSender.createMimeMessage();

		if (mailAddressArr == null || mailAddressArr.length < 1){
			throw new RuntimeException("Sending email failed: The recipient's email must not be empty!");
		}

		// 收件邮箱去重
		if (mailAddressArr.length > 0){
			mailAddressArr = Arrays.stream(mailAddressArr)
					.map(a -> a.trim())
					.distinct().toArray(String[]::new);
		}

		// 校验邮箱格式
		for (int i = 0; i < mailAddressArr.length; i++) {
			if (!Validator.isEmail(mailAddressArr[i])){
				throw new RuntimeException(String.format("The mail [%s] incorrect format!", mailAddressArr[i]));
			}
		}

		try {
			MimeMessageHelper helper = new MimeMessageHelper(msg,true);
			//设置邮件元信息
			helper.setTo(mailAddressArr);
			// 自定义发送方名称
			String fromName = mailProperties.getProperties().get("fromName");
			if (StrUtil.isNotBlank(fromName)){
				helper.setFrom(String.format("%s<%s>", fromName, mailProperties.getUsername()));
			}else {
				helper.setFrom(mailProperties.getUsername());
			}
			helper.setSubject(subject);
			helper.setSentDate(new Date());
			if (file!=null && file.exists()){
				// helper.addAttachment(file.getName(), file);
				// 防止附件名乱码
				helper.addAttachment(MimeUtility.encodeWord(file.getName(),"utf-8","B"), file);

			}

			//模板渲染
			Context context = new Context();
			context.setVariable("title", title);
			context.setVariable("text", text);
			String mail = templateEngine.process("mail/def", context);
			helper.setText(mail, true);
			javaMailSender.send(msg);
			return true;
		} catch (MessagingException e) {
			changeMail();
			errMsg = e.getMessage();
		}catch(MailAuthenticationException e){
			changeMail();
			errMsg = e.getMessage();
		}catch(MailException e){
			changeMail();
			errMsg = e.getMessage();
		}
		throw new RuntimeException(String.format("Send email fail: %s, please adjust or try again!",  errMsg));
	}


	@Synchronized
	private void changeMail() {
		if (activitedMailIndex < mailPropertiesList.size() - 1){
			activitedMailIndex ++;
		}else {
			activitedMailIndex = 0;
		}
	}

	@Synchronized
	private void initActivitedMailIndex() {
		String today = DateUtil.today();
		if (!activitedMailDate.equals(today)){
			activitedMailIndex = 0;
			activitedMailDate = today;
		}
	}

}
  • 10
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
很高兴为您解答SpringBoot注册登录及邮箱发送账号激活代码。以下是一个简单的示例: 首先,您需要编写用户实体类。可以包含id、用户名、电子邮件和密码等字段。 ```java @Entity @Table(name = "user") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @NotBlank @Size(max = 20) private String username; @NotBlank @Size(max = 50) @Email private String email; @NotBlank @Size(max = 120) private String password; private boolean enabled; // constructors, getters and setters } ``` 然后,您需要创建一个包含注册、登录和电子邮件验证的控制器。 ```java @RestController @RequestMapping("/api/auth") public class AuthController { @Autowired private AuthenticationManager authenticationManager; @Autowired private UserRepository userRepository; @Autowired private PasswordEncoder passwordEncoder; @Autowired private JwtTokenProvider tokenProvider; @Autowired private EmailSender emailSender; @PostMapping("/signin") public ResponseEntity<?> authenticateUser(@Valid @RequestBody LoginRequest loginRequest) { Authentication authentication = authenticationManager.authenticate( new UsernamePasswordAuthenticationToken( loginRequest.getUsernameOrEmail(), loginRequest.getPassword() ) ); SecurityContextHolder.getContext().setAuthentication(authentication); String jwt = tokenProvider.generateToken(authentication); return ResponseEntity.ok(new JwtAuthenticationResponse(jwt)); } @PostMapping("/signup") public ResponseEntity<?> registerUser(@Valid @RequestBody SignUpRequest signUpRequest) { if(userRepository.existsByUsername(signUpRequest.getUsername())) { return new ResponseEntity(new ApiResponse(false, "Username is already taken!"), HttpStatus.BAD_REQUEST); } if(userRepository.existsByEmail(signUpRequest.getEmail())) { return new ResponseEntity(new ApiResponse(false, "Email Address already in use!"), HttpStatus.BAD_REQUEST); } // Creating user's account User user = new User(signUpRequest.getName(), signUpRequest.getUsername(), signUpRequest.getEmail(), signUpRequest.getPassword()); user.setPassword(passwordEncoder.encode(user.getPassword())); user.setEnabled(false); userRepository.save(user); String emailVerificationToken = tokenProvider.generateEmailVerificationToken(user); String emailVerificationLink = ServletUriComponentsBuilder.fromCurrentContextPath().path("/api/auth/verifyEmail") .queryParam("token", emailVerificationToken).build().toUriString(); emailSender.sendEmailVerification(user.getEmail(), emailVerificationLink); URI location = ServletUriComponentsBuilder .fromCurrentContextPath().path("/users/{username}") .buildAndExpand(user.getUsername()).toUri(); return ResponseEntity.created(location).body(new ApiResponse(true, "User registered successfully")); } @GetMapping("/verifyEmail") public ResponseEntity<?> verifyEmail(@RequestParam("token") String token) { tokenProvider.validateEmailVerificationToken(token); return new ResponseEntity(new ApiResponse(true, "Email verified successfully"), HttpStatus.OK); } } ``` 这里使用了JWT作为身份验证机制,并将邮件验证令牌发送给用户。 最后,您需要编写一个电子邮件发送器。可以使用JavaMailSender或其他电子邮件服务,例如SendGrid或Mailgun。 ```java @Component public class EmailSender { @Autowired private JavaMailSender mailSender; public void sendEmailVerification(String to, String emailVerificationLink) { SimpleMailMessage message = new SimpleMailMessage(); message.setFrom("[email protected]"); message.setTo(to); message.setSubject("Activate your account"); message.setText(emailVerificationLink); mailSender.send(message); } } ``` 这就是一个简单的SpringBoot注册登录及电子邮件验证代码示例了。希望对您有所帮助!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值