pull/296/head
ruibaby 2019-09-05 17:10:17 +08:00
parent ec4932ef4a
commit 042e5af43d
8 changed files with 136 additions and 18 deletions

View File

@ -8,6 +8,7 @@ import run.halo.app.cache.lock.CacheLock;
import run.halo.app.model.dto.EnvironmentDTO; import run.halo.app.model.dto.EnvironmentDTO;
import run.halo.app.model.dto.StatisticDTO; import run.halo.app.model.dto.StatisticDTO;
import run.halo.app.model.params.LoginParam; 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.properties.PrimaryProperties;
import run.halo.app.model.support.BaseResponse; import run.halo.app.model.support.BaseResponse;
import run.halo.app.security.token.AuthToken; import run.halo.app.security.token.AuthToken;
@ -57,6 +58,18 @@ public class AdminController {
adminService.clearToken(); 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}") @PostMapping("refresh/{refreshToken}")
@ApiOperation("Refreshes token") @ApiOperation("Refreshes token")
@CacheLock(autoDelete = false) @CacheLock(autoDelete = false)

View File

@ -99,7 +99,7 @@ public class FreemarkerConfigAwareListener {
private void loadThemeConfig() throws TemplateModelException { private void loadThemeConfig() throws TemplateModelException {
ThemeProperty activatedTheme = themeService.getActivatedTheme(); ThemeProperty activatedTheme = themeService.getActivatedTheme();
configuration.setSharedVariable("theme", activatedTheme); 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("static", baseUrl + "/" + activatedTheme.getFolderName());
configuration.setSharedVariable("settings", themeSettingService.listAsMapBy(themeService.getActivatedThemeId())); configuration.setSharedVariable("settings", themeSettingService.listAsMapBy(themeService.getActivatedThemeId()));
log.debug("Loaded theme and settings"); log.debug("Loaded theme and settings");

View File

@ -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;
}

View File

@ -4,6 +4,7 @@ import org.springframework.lang.NonNull;
import run.halo.app.model.dto.EnvironmentDTO; import run.halo.app.model.dto.EnvironmentDTO;
import run.halo.app.model.dto.StatisticDTO; import run.halo.app.model.dto.StatisticDTO;
import run.halo.app.model.params.LoginParam; import run.halo.app.model.params.LoginParam;
import run.halo.app.model.params.ResetPasswordParam;
import run.halo.app.security.token.AuthToken; import run.halo.app.security.token.AuthToken;
/** /**
@ -42,6 +43,20 @@ public interface AdminService {
*/ */
void clearToken(); 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. * Get system counts.
* *
@ -74,6 +89,7 @@ public interface AdminService {
/** /**
* Get spring logs. * Get spring logs.
*
* @return recently logs. * @return recently logs.
*/ */
String getSpringLogs(); String getSpringLogs();

View File

@ -14,6 +14,7 @@ import java.util.Optional;
* User service interface. * User service interface.
* *
* @author johnniang * @author johnniang
* @author ryanwang
* @date 2019-03-14 * @date 2019-03-14
*/ */
public interface UserService extends CrudService<User, Integer> { public interface UserService extends CrudService<User, Integer> {
@ -125,9 +126,11 @@ public interface UserService extends CrudService<User, Integer> {
void setPassword(@NonNull User user, @NonNull String plainPassword); void setPassword(@NonNull User user, @NonNull String plainPassword);
/** /**
* Set user default avataruse 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);
} }

View File

@ -2,7 +2,9 @@ package run.halo.app.service.impl;
import cn.hutool.core.io.file.FileReader; import cn.hutool.core.io.file.FileReader;
import cn.hutool.core.lang.Validator; import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.RandomUtil;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisher;
import org.springframework.http.ResponseEntity; 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.Mode;
import run.halo.app.model.enums.PostStatus; import run.halo.app.model.enums.PostStatus;
import run.halo.app.model.params.LoginParam; 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.model.support.HaloConst;
import run.halo.app.security.authentication.Authentication; import run.halo.app.security.authentication.Authentication;
import run.halo.app.security.context.SecurityContextHolder; import run.halo.app.security.context.SecurityContextHolder;
@ -36,7 +40,6 @@ import run.halo.app.utils.HaloUtils;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.lang.management.ManagementFactory; import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.List; import java.util.List;
@ -75,6 +78,8 @@ public class AdminServiceImpl implements AdminService {
private final LinkService linkService; private final LinkService linkService;
private final MailService mailService;
private final StringCacheStore cacheStore; private final StringCacheStore cacheStore;
private final RestTemplate restTemplate; private final RestTemplate restTemplate;
@ -96,6 +101,7 @@ public class AdminServiceImpl implements AdminService {
OptionService optionService, OptionService optionService,
UserService userService, UserService userService,
LinkService linkService, LinkService linkService,
MailService mailService,
StringCacheStore cacheStore, StringCacheStore cacheStore,
RestTemplate restTemplate, RestTemplate restTemplate,
HaloProperties haloProperties, HaloProperties haloProperties,
@ -111,6 +117,7 @@ public class AdminServiceImpl implements AdminService {
this.optionService = optionService; this.optionService = optionService;
this.userService = userService; this.userService = userService;
this.linkService = linkService; this.linkService = linkService;
this.mailService = mailService;
this.cacheStore = cacheStore; this.cacheStore = cacheStore;
this.restTemplate = restTemplate; this.restTemplate = restTemplate;
this.haloProperties = haloProperties; 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!"); 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 @Override
public StatisticDTO getCount() { public StatisticDTO getCount() {
StatisticDTO statisticDTO = new StatisticDTO(); StatisticDTO statisticDTO = new StatisticDTO();
@ -221,8 +287,7 @@ public class AdminServiceImpl implements AdminService {
EnvironmentDTO environmentDTO = new EnvironmentDTO(); EnvironmentDTO environmentDTO = new EnvironmentDTO();
// Get application start time. // Get application start time.
RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean(); environmentDTO.setStartTime(ManagementFactory.getRuntimeMXBean().getStartTime());
environmentDTO.setStartTime(runtimeMXBean.getStartTime());
environmentDTO.setDatabase("org.h2.Driver".equals(driverClassName) ? "H2" : "MySQL"); environmentDTO.setDatabase("org.h2.Driver".equals(driverClassName) ? "H2" : "MySQL");

View File

@ -52,7 +52,7 @@ public class MailServiceImpl implements MailService {
} catch (Exception e) { } catch (Exception e) {
log.debug("Email properties: to username: [{}], from username: [{}], subject: [{}], content: [{}]", log.debug("Email properties: to username: [{}], from username: [{}], subject: [{}], content: [{}]",
to, fromUsername, 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) { } catch (Exception e) {
log.debug("Email properties: to username: [{}], from username: [{}], subject: [{}], template name: [{}], content: [{}]", log.debug("Email properties: to username: [{}], from username: [{}], subject: [{}], template name: [{}], content: [{}]",
to, fromUsername, subject, templateName, 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) { } catch (Exception e) {
log.debug("Email properties: to username: [{}], from username: [{}], subject: [{}], template name: [{}], attachment: [{}], content: [{}]", log.debug("Email properties: to username: [{}], from username: [{}], subject: [{}], template name: [{}], attachment: [{}], content: [{}]",
to, fromUsername, subject, templateName, attachFilename, content); to, fromUsername, subject, templateName, attachFilename, content);
throw new EmailException("Failed to send attachment email to " + to, e); throw new EmailException("发送附件邮件到 " + to + " 失败,请检查 SMTP 服务配置是否正确", e);
} }
} }

View File

@ -1,7 +1,5 @@
package run.halo.app.service.impl; package run.halo.app.service.impl;
import cn.hutool.core.text.StrBuilder;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.digest.BCrypt; import cn.hutool.crypto.digest.BCrypt;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationEventPublisher; 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.BadRequestException;
import run.halo.app.exception.ForbiddenException; import run.halo.app.exception.ForbiddenException;
import run.halo.app.exception.NotFoundException; import run.halo.app.exception.NotFoundException;
import run.halo.app.exception.ServiceException;
import run.halo.app.model.entity.User; import run.halo.app.model.entity.User;
import run.halo.app.model.enums.LogType; import run.halo.app.model.enums.LogType;
import run.halo.app.model.params.UserParam; import run.halo.app.model.params.UserParam;
@ -184,11 +183,8 @@ public class UserServiceImpl extends AbstractCrudService<User, Integer> implemen
} }
@Override @Override
public void setDefaultAvatar(User user) { public boolean verifyUser(String username, String password) {
Assert.notNull(user, "User must not be null"); User user = getCurrentUser().orElseThrow(() -> new ServiceException("找不到博主信息"));
StrBuilder gravatar = new StrBuilder("//cn.gravatar.com/avatar/"); return user.getUsername().equals(username) && user.getEmail().equals(password);
gravatar.append(SecureUtil.md5(user.getEmail()));
gravatar.append("?s=256&d=mm");
user.setAvatar(gravatar.toString());
} }
} }