From 042e5af43d8499e699c96d0058cfce92e08122f1 Mon Sep 17 00:00:00 2001 From: ruibaby Date: Thu, 5 Sep 2019 17:10:17 +0800 Subject: [PATCH] Completed #208 --- .../controller/admin/api/AdminController.java | 13 ++++ .../FreemarkerConfigAwareListener.java | 2 +- .../app/model/params/ResetPasswordParam.java | 25 +++++++ .../run/halo/app/service/AdminService.java | 16 +++++ .../run/halo/app/service/UserService.java | 9 ++- .../app/service/impl/AdminServiceImpl.java | 71 ++++++++++++++++++- .../app/service/impl/MailServiceImpl.java | 6 +- .../app/service/impl/UserServiceImpl.java | 12 ++-- 8 files changed, 136 insertions(+), 18 deletions(-) create mode 100644 src/main/java/run/halo/app/model/params/ResetPasswordParam.java diff --git a/src/main/java/run/halo/app/controller/admin/api/AdminController.java b/src/main/java/run/halo/app/controller/admin/api/AdminController.java index 57b9091d3..0c5f0b332 100644 --- a/src/main/java/run/halo/app/controller/admin/api/AdminController.java +++ b/src/main/java/run/halo/app/controller/admin/api/AdminController.java @@ -8,6 +8,7 @@ import run.halo.app.cache.lock.CacheLock; import run.halo.app.model.dto.EnvironmentDTO; import run.halo.app.model.dto.StatisticDTO; import run.halo.app.model.params.LoginParam; +import run.halo.app.model.params.ResetPasswordParam; import run.halo.app.model.properties.PrimaryProperties; import run.halo.app.model.support.BaseResponse; import run.halo.app.security.token.AuthToken; @@ -57,6 +58,18 @@ public class AdminController { adminService.clearToken(); } + @PostMapping("password/code") + @ApiOperation("Send reset password verify code.") + public void sendResetCode(@RequestBody @Valid ResetPasswordParam param) { + adminService.sendResetPasswordCode(param); + } + + @PutMapping("password/reset") + @ApiOperation("Reset password by verify code.") + public void resetPassword(@RequestBody @Valid ResetPasswordParam param) { + adminService.resetPasswordByCode(param); + } + @PostMapping("refresh/{refreshToken}") @ApiOperation("Refreshes token") @CacheLock(autoDelete = false) diff --git a/src/main/java/run/halo/app/event/freemarker/FreemarkerConfigAwareListener.java b/src/main/java/run/halo/app/event/freemarker/FreemarkerConfigAwareListener.java index 69b855a06..a1bd97a9e 100644 --- a/src/main/java/run/halo/app/event/freemarker/FreemarkerConfigAwareListener.java +++ b/src/main/java/run/halo/app/event/freemarker/FreemarkerConfigAwareListener.java @@ -99,7 +99,7 @@ public class FreemarkerConfigAwareListener { private void loadThemeConfig() throws TemplateModelException { ThemeProperty activatedTheme = themeService.getActivatedTheme(); configuration.setSharedVariable("theme", activatedTheme); - String baseUrl = optionService.getByProperty(OtherProperties.CDN_DOMAIN).orElse(optionService.getBlogBaseUrl()).toString(); + String baseUrl = optionService.getByPropertyOrDefault(OtherProperties.CDN_DOMAIN, String.class, optionService.getBlogBaseUrl()); configuration.setSharedVariable("static", baseUrl + "/" + activatedTheme.getFolderName()); configuration.setSharedVariable("settings", themeSettingService.listAsMapBy(themeService.getActivatedThemeId())); log.debug("Loaded theme and settings"); diff --git a/src/main/java/run/halo/app/model/params/ResetPasswordParam.java b/src/main/java/run/halo/app/model/params/ResetPasswordParam.java new file mode 100644 index 000000000..998e5db0e --- /dev/null +++ b/src/main/java/run/halo/app/model/params/ResetPasswordParam.java @@ -0,0 +1,25 @@ +package run.halo.app.model.params; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +/** + * Reset password params. + * + * @author ryanwang + * @date 2019-09-05 + */ +@Data +public class ResetPasswordParam { + + @NotBlank(message = "用户名不能为空") + private String username; + + @NotBlank(message = "邮箱不能为空") + private String email; + + private String code; + + private String password; +} diff --git a/src/main/java/run/halo/app/service/AdminService.java b/src/main/java/run/halo/app/service/AdminService.java index d8f1b054f..0287c8bda 100644 --- a/src/main/java/run/halo/app/service/AdminService.java +++ b/src/main/java/run/halo/app/service/AdminService.java @@ -4,6 +4,7 @@ import org.springframework.lang.NonNull; import run.halo.app.model.dto.EnvironmentDTO; import run.halo.app.model.dto.StatisticDTO; import run.halo.app.model.params.LoginParam; +import run.halo.app.model.params.ResetPasswordParam; import run.halo.app.security.token.AuthToken; /** @@ -42,6 +43,20 @@ public interface AdminService { */ void clearToken(); + /** + * Send reset password code to administrator's email. + * + * @param param param must not be null + */ + void sendResetPasswordCode(@NonNull ResetPasswordParam param); + + /** + * Reset password by code. + * + * @param param param must not be null + */ + void resetPasswordByCode(@NonNull ResetPasswordParam param); + /** * Get system counts. * @@ -74,6 +89,7 @@ public interface AdminService { /** * Get spring logs. + * * @return recently logs. */ String getSpringLogs(); diff --git a/src/main/java/run/halo/app/service/UserService.java b/src/main/java/run/halo/app/service/UserService.java index 3f2563fd4..c306c5c28 100755 --- a/src/main/java/run/halo/app/service/UserService.java +++ b/src/main/java/run/halo/app/service/UserService.java @@ -14,6 +14,7 @@ import java.util.Optional; * User service interface. * * @author johnniang + * @author ryanwang * @date 2019-03-14 */ public interface UserService extends CrudService { @@ -125,9 +126,11 @@ public interface UserService extends CrudService { void setPassword(@NonNull User user, @NonNull String plainPassword); /** - * Set user default avatar,use Gravatar(http://cn.gravatar.com) + * verify user's email and username * - * @param user user must not be null + * @param username username must not be null + * @param password password must not be null + * @return boolean */ - void setDefaultAvatar(@NonNull User user); + boolean verifyUser(@NonNull String username, @NonNull String password); } diff --git a/src/main/java/run/halo/app/service/impl/AdminServiceImpl.java b/src/main/java/run/halo/app/service/impl/AdminServiceImpl.java index 4744b9c0f..d9a7021b7 100644 --- a/src/main/java/run/halo/app/service/impl/AdminServiceImpl.java +++ b/src/main/java/run/halo/app/service/impl/AdminServiceImpl.java @@ -2,7 +2,9 @@ package run.halo.app.service.impl; import cn.hutool.core.io.file.FileReader; import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.RandomUtil; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.ApplicationEventPublisher; import org.springframework.http.ResponseEntity; @@ -24,6 +26,8 @@ import run.halo.app.model.enums.LogType; import run.halo.app.model.enums.Mode; import run.halo.app.model.enums.PostStatus; import run.halo.app.model.params.LoginParam; +import run.halo.app.model.params.ResetPasswordParam; +import run.halo.app.model.properties.EmailProperties; import run.halo.app.model.support.HaloConst; import run.halo.app.security.authentication.Authentication; import run.halo.app.security.context.SecurityContextHolder; @@ -36,7 +40,6 @@ import run.halo.app.utils.HaloUtils; import java.io.File; import java.io.IOException; import java.lang.management.ManagementFactory; -import java.lang.management.RuntimeMXBean; import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; @@ -75,6 +78,8 @@ public class AdminServiceImpl implements AdminService { private final LinkService linkService; + private final MailService mailService; + private final StringCacheStore cacheStore; private final RestTemplate restTemplate; @@ -96,6 +101,7 @@ public class AdminServiceImpl implements AdminService { OptionService optionService, UserService userService, LinkService linkService, + MailService mailService, StringCacheStore cacheStore, RestTemplate restTemplate, HaloProperties haloProperties, @@ -111,6 +117,7 @@ public class AdminServiceImpl implements AdminService { this.optionService = optionService; this.userService = userService; this.linkService = linkService; + this.mailService = mailService; this.cacheStore = cacheStore; this.restTemplate = restTemplate; this.haloProperties = haloProperties; @@ -191,6 +198,65 @@ public class AdminServiceImpl implements AdminService { log.info("You have been logged out, looking forward to your next visit!"); } + @Override + public void sendResetPasswordCode(ResetPasswordParam param) { + cacheStore.getAny("code", String.class).ifPresent(code -> { + throw new ServiceException("已经获取过验证码,不能重复获取"); + }); + + Boolean emailEnabled = optionService.getByPropertyOrDefault(EmailProperties.ENABLED, Boolean.class, false); + + if (!emailEnabled) { + throw new ServiceException("未启用 SMTP 服务"); + } + + if (!userService.verifyUser(param.getUsername(), param.getEmail())) { + throw new ServiceException("用户名或者邮箱验证错误"); + } + + // Gets random code. + String code = RandomUtil.randomNumbers(6); + + // Send email to administrator. + String content = "您正在进行密码重置操作,如不是本人操作,请尽快做好相应措施。密码重置验证码如下(五分钟有效):\n" + code; + mailService.sendMail(param.getEmail(), "找回密码验证码", content); + + // Cache code. + cacheStore.putAny("code", code, 5, TimeUnit.MINUTES); + } + + @Override + public void resetPasswordByCode(ResetPasswordParam param) { + if (StringUtils.isEmpty(param.getCode())) { + throw new ServiceException("验证码不能为空"); + } + + if (StringUtils.isEmpty(param.getPassword())) { + throw new ServiceException("密码不能为空"); + } + + if (!userService.verifyUser(param.getUsername(), param.getEmail())) { + throw new ServiceException("用户名或者邮箱验证错误"); + } + + // verify code + String code = cacheStore.getAny("code", String.class).orElseThrow(() -> new ServiceException("未获取过验证码")); + if (!code.equals(param.getCode())) { + throw new ServiceException("验证码不正确"); + } + + User user = userService.getCurrentUser().orElseThrow(() -> new ServiceException("找不到博主信息")); + + // reset password + userService.setPassword(user, param.getPassword()); + + // Update this user + userService.update(user); + + // clear code cache + cacheStore.delete("code"); + } + @Override public StatisticDTO getCount() { StatisticDTO statisticDTO = new StatisticDTO(); @@ -221,8 +287,7 @@ public class AdminServiceImpl implements AdminService { EnvironmentDTO environmentDTO = new EnvironmentDTO(); // Get application start time. - RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean(); - environmentDTO.setStartTime(runtimeMXBean.getStartTime()); + environmentDTO.setStartTime(ManagementFactory.getRuntimeMXBean().getStartTime()); environmentDTO.setDatabase("org.h2.Driver".equals(driverClassName) ? "H2" : "MySQL"); diff --git a/src/main/java/run/halo/app/service/impl/MailServiceImpl.java b/src/main/java/run/halo/app/service/impl/MailServiceImpl.java index a2d58ca79..9556619c4 100644 --- a/src/main/java/run/halo/app/service/impl/MailServiceImpl.java +++ b/src/main/java/run/halo/app/service/impl/MailServiceImpl.java @@ -52,7 +52,7 @@ public class MailServiceImpl implements MailService { } catch (Exception e) { log.debug("Email properties: to username: [{}], from username: [{}], subject: [{}], content: [{}]", to, fromUsername, subject, content); - throw new EmailException("Failed to send email to " + to, e); + throw new EmailException("发送邮件到 " + to + " 失败,请检查 SMTP 服务配置是否正确", e); } } @@ -74,7 +74,7 @@ public class MailServiceImpl implements MailService { } catch (Exception e) { log.debug("Email properties: to username: [{}], from username: [{}], subject: [{}], template name: [{}], content: [{}]", to, fromUsername, subject, templateName, content); - throw new EmailException("Failed to send template email to " + to, e).setErrorData(templateName); + throw new EmailException("发送模板邮件到 " + to + " 失败,请检查 SMTP 服务配置是否正确", e); } } @@ -97,7 +97,7 @@ public class MailServiceImpl implements MailService { } catch (Exception e) { log.debug("Email properties: to username: [{}], from username: [{}], subject: [{}], template name: [{}], attachment: [{}], content: [{}]", to, fromUsername, subject, templateName, attachFilename, content); - throw new EmailException("Failed to send attachment email to " + to, e); + throw new EmailException("发送附件邮件到 " + to + " 失败,请检查 SMTP 服务配置是否正确", e); } } diff --git a/src/main/java/run/halo/app/service/impl/UserServiceImpl.java b/src/main/java/run/halo/app/service/impl/UserServiceImpl.java index 7905e1b14..44d15e5e3 100644 --- a/src/main/java/run/halo/app/service/impl/UserServiceImpl.java +++ b/src/main/java/run/halo/app/service/impl/UserServiceImpl.java @@ -1,7 +1,5 @@ package run.halo.app.service.impl; -import cn.hutool.core.text.StrBuilder; -import cn.hutool.crypto.SecureUtil; import cn.hutool.crypto.digest.BCrypt; import org.apache.commons.lang3.StringUtils; import org.springframework.context.ApplicationEventPublisher; @@ -16,6 +14,7 @@ import run.halo.app.event.user.UserUpdatedEvent; import run.halo.app.exception.BadRequestException; import run.halo.app.exception.ForbiddenException; import run.halo.app.exception.NotFoundException; +import run.halo.app.exception.ServiceException; import run.halo.app.model.entity.User; import run.halo.app.model.enums.LogType; import run.halo.app.model.params.UserParam; @@ -184,11 +183,8 @@ public class UserServiceImpl extends AbstractCrudService implemen } @Override - public void setDefaultAvatar(User user) { - Assert.notNull(user, "User must not be null"); - StrBuilder gravatar = new StrBuilder("//cn.gravatar.com/avatar/"); - gravatar.append(SecureUtil.md5(user.getEmail())); - gravatar.append("?s=256&d=mm"); - user.setAvatar(gravatar.toString()); + public boolean verifyUser(String username, String password) { + User user = getCurrentUser().orElseThrow(() -> new ServiceException("找不到博主信息")); + return user.getUsername().equals(username) && user.getEmail().equals(password); } }