mirror of https://github.com/halo-dev/halo
refactor: remove use of hutool toolset (#1488)
* feat: Add date parse methods and test * feat: Add utility methods of get the specified part of the given date * refactor: Replace the use of dateutil in hutool * refactor: Replace the StrUtil of hutool with StringUtils of commons-lang3 * refactor: Replace the use of Tuple in hutool * refactor: Replace the use of ServltUtil in hutool * refactor: Replace the use of PageUtil in hutool * refactor: Replace the use of CollectionUtil in hutool * refactor: Add QRcode generate method * refactor: replace all hutool utility and add some utils * fix: check style of tests * refactor: add logging * fix: logging * fix: set default timezone * fix: code style * refactor: rename variable tfaKey to mfaKey * refactor: Use commons-lang3's RandomStringUtils to replace some methods * refactor: update javadoc * refactor: update test * refactor: reformat code * feat: Add more test case * feat: Add sourcepull/1492/head
parent
5acabee7a9
commit
de71f40de6
|
@ -74,7 +74,7 @@ bootBuildImage {
|
|||
}
|
||||
|
||||
ext {
|
||||
hutoolVersion = "5.3.8"
|
||||
guavaVersion= "31.0-jre"
|
||||
upyunSdkVersion = "4.2.0"
|
||||
qiniuSdkVersion = "7.2.29"
|
||||
aliyunSdkVersion = "3.11.3"
|
||||
|
@ -109,9 +109,7 @@ dependencies {
|
|||
implementation 'org.springframework.boot:spring-boot-starter-validation'
|
||||
|
||||
implementation "com.sun.mail:jakarta.mail"
|
||||
implementation "cn.hutool:hutool-core:$hutoolVersion"
|
||||
implementation "cn.hutool:hutool-crypto:$hutoolVersion"
|
||||
implementation "cn.hutool:hutool-extra:$hutoolVersion"
|
||||
implementation "com.google.guava:guava:$guavaVersion"
|
||||
implementation "com.upyun:java-sdk:$upyunSdkVersion"
|
||||
implementation "com.qiniu:qiniu-java-sdk:$qiniuSdkVersion"
|
||||
implementation "com.aliyun.oss:aliyun-sdk-oss:$aliyunSdkVersion"
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package run.halo.app.controller.admin.api;
|
||||
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
@ -8,6 +7,7 @@ import java.util.HashSet;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
@ -279,7 +279,7 @@ public class InstallController {
|
|||
return userService.update(user);
|
||||
}).orElseGet(() -> {
|
||||
String gravatar =
|
||||
"//cn.gravatar.com/avatar/" + SecureUtil.md5(installParam.getEmail())
|
||||
"//cn.gravatar.com/avatar/" + DigestUtils.md5Hex(installParam.getEmail())
|
||||
+ "?s=256&d=mm";
|
||||
installParam.setAvatar(gravatar);
|
||||
return userService.createBy(installParam);
|
||||
|
|
|
@ -2,7 +2,6 @@ package run.halo.app.controller.admin.api;
|
|||
|
||||
import static org.springframework.data.domain.Sort.Direction.DESC;
|
||||
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URISyntaxException;
|
||||
|
@ -37,6 +36,7 @@ import run.halo.app.model.params.PostQuery;
|
|||
import run.halo.app.model.vo.PostDetailVO;
|
||||
import run.halo.app.service.OptionService;
|
||||
import run.halo.app.service.PostService;
|
||||
import run.halo.app.utils.HaloUtils;
|
||||
|
||||
/**
|
||||
* Post controller.
|
||||
|
@ -188,7 +188,7 @@ public class PostController {
|
|||
|
||||
BasePostMinimalDTO postMinimalDTO = postService.convertToMinimal(post);
|
||||
|
||||
String token = IdUtil.simpleUUID();
|
||||
String token = HaloUtils.simpleUUID();
|
||||
|
||||
// cache preview token
|
||||
cacheStore.putAny(token, token, 10, TimeUnit.MINUTES);
|
||||
|
|
|
@ -2,7 +2,6 @@ package run.halo.app.controller.admin.api;
|
|||
|
||||
import static org.springframework.data.domain.Sort.Direction.DESC;
|
||||
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
|
@ -34,6 +33,7 @@ import run.halo.app.model.vo.SheetDetailVO;
|
|||
import run.halo.app.model.vo.SheetListVO;
|
||||
import run.halo.app.service.OptionService;
|
||||
import run.halo.app.service.SheetService;
|
||||
import run.halo.app.utils.HaloUtils;
|
||||
|
||||
/**
|
||||
* Sheet controller.
|
||||
|
@ -149,7 +149,7 @@ public class SheetController {
|
|||
|
||||
BasePostMinimalDTO sheetMinimalDTO = sheetService.convertToMinimal(sheet);
|
||||
|
||||
String token = IdUtil.simpleUUID();
|
||||
String token = HaloUtils.simpleUUID();
|
||||
|
||||
// cache preview token
|
||||
cacheStore.putAny(token, token, 10, TimeUnit.MINUTES);
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
package run.halo.app.controller.admin.api;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.qrcode.QrCodeUtil;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import javax.validation.Valid;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.util.Base64Utils;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
|
@ -23,6 +22,7 @@ import run.halo.app.model.support.BaseResponse;
|
|||
import run.halo.app.model.support.UpdateCheck;
|
||||
import run.halo.app.model.vo.MultiFactorAuthVO;
|
||||
import run.halo.app.service.UserService;
|
||||
import run.halo.app.utils.HaloUtils;
|
||||
import run.halo.app.utils.TwoFactorAuthUtils;
|
||||
import run.halo.app.utils.ValidationUtils;
|
||||
|
||||
|
@ -83,7 +83,8 @@ public class UserController {
|
|||
String optAuthUrl =
|
||||
TwoFactorAuthUtils.generateOtpAuthUrl(user.getNickname(), mfaKey);
|
||||
String qrImageBase64 = "data:image/png;base64,"
|
||||
+ Base64.encode(QrCodeUtil.generatePng(optAuthUrl, 128, 128));
|
||||
+ Base64Utils.encodeToString(
|
||||
HaloUtils.generateQrCodeToPng(optAuthUrl, 128, 128));
|
||||
return new MultiFactorAuthVO(qrImageBase64, optAuthUrl, mfaKey, MFAType.TFA_TOTP);
|
||||
} else {
|
||||
throw new BadRequestException("暂不支持的 MFA 认证的方式");
|
||||
|
@ -99,16 +100,16 @@ public class UserController {
|
|||
@DisableOnCondition
|
||||
public MultiFactorAuthVO updateMFAuth(
|
||||
@RequestBody @Valid MultiFactorAuthParam multiFactorAuthParam, User user) {
|
||||
if (StrUtil.isNotBlank(user.getMfaKey())
|
||||
if (StringUtils.isNotBlank(user.getMfaKey())
|
||||
&& MFAType.useMFA(multiFactorAuthParam.getMfaType())) {
|
||||
return new MultiFactorAuthVO(MFAType.TFA_TOTP);
|
||||
} else if (StrUtil.isBlank(user.getMfaKey())
|
||||
} else if (StringUtils.isBlank(user.getMfaKey())
|
||||
&& !MFAType.useMFA(multiFactorAuthParam.getMfaType())) {
|
||||
return new MultiFactorAuthVO(MFAType.NONE);
|
||||
} else {
|
||||
final String tfaKey = StrUtil.isNotBlank(user.getMfaKey()) ? user.getMfaKey() :
|
||||
final String mfaKey = StringUtils.isNotBlank(user.getMfaKey()) ? user.getMfaKey() :
|
||||
multiFactorAuthParam.getMfaKey();
|
||||
TwoFactorAuthUtils.validateTFACode(tfaKey, multiFactorAuthParam.getAuthcode());
|
||||
TwoFactorAuthUtils.validateTFACode(mfaKey, multiFactorAuthParam.getAuthcode());
|
||||
}
|
||||
// update MFA key
|
||||
User updateUser = userService
|
||||
|
|
|
@ -3,7 +3,6 @@ package run.halo.app.controller.content.model;
|
|||
import static run.halo.app.model.support.HaloConst.POST_PASSWORD_TEMPLATE;
|
||||
import static run.halo.app.model.support.HaloConst.SUFFIX_FTL;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -91,7 +90,7 @@ public class PostModel {
|
|||
if (PostStatus.RECYCLE.equals(post.getStatus())) {
|
||||
// Articles in the recycle bin are not allowed to be accessed.
|
||||
throw new NotFoundException("查询不到该文章的信息");
|
||||
} else if (StrUtil.isNotEmpty(token)) {
|
||||
} else if (StringUtils.isNotBlank(token)) {
|
||||
// If the token is not empty, it means it is an admin request,
|
||||
// then verify the token.
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package run.halo.app.core;
|
||||
|
||||
import cn.hutool.extra.servlet.ServletUtil;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Objects;
|
||||
|
@ -22,6 +21,7 @@ import org.springframework.web.context.request.RequestContextHolder;
|
|||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import run.halo.app.utils.JsonUtils;
|
||||
import run.halo.app.utils.ServletUtils;
|
||||
|
||||
/**
|
||||
* @author johnniang
|
||||
|
@ -82,7 +82,7 @@ public class ControllerLogAop {
|
|||
request.getRequestURL(),
|
||||
request.getRequestURI(),
|
||||
request.getMethod(),
|
||||
ServletUtil.getClientIP(request));
|
||||
ServletUtils.getClientIP(request));
|
||||
|
||||
if (args == null || !log.isDebugEnabled()) {
|
||||
return;
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
package run.halo.app.core.freemarker.method;
|
||||
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import freemarker.template.Configuration;
|
||||
import freemarker.template.SimpleNumber;
|
||||
import freemarker.template.TemplateMethodModelEx;
|
||||
import freemarker.template.TemplateModelException;
|
||||
import java.util.List;
|
||||
import org.apache.commons.lang3.RandomUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
import run.halo.app.utils.HaloUtils;
|
||||
|
||||
/**
|
||||
* Freemarker template random method.
|
||||
|
@ -42,6 +43,6 @@ public class RandomMethod implements TemplateMethodModelEx {
|
|||
SimpleNumber argTwo = (SimpleNumber) arguments.get(1);
|
||||
int start = argOne.getAsNumber().intValue();
|
||||
int end = argTwo.getAsNumber().intValue();
|
||||
return RandomUtil.randomInt(start, end);
|
||||
return RandomUtils.nextInt(start, end);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package run.halo.app.core.freemarker.tag;
|
|||
|
||||
import static run.halo.app.model.support.HaloConst.URL_SEPARATOR;
|
||||
|
||||
import cn.hutool.core.util.PageUtil;
|
||||
import freemarker.core.Environment;
|
||||
import freemarker.template.Configuration;
|
||||
import freemarker.template.DefaultObjectWrapperBuilder;
|
||||
|
@ -19,6 +18,7 @@ import run.halo.app.model.support.HaloConst;
|
|||
import run.halo.app.model.support.Pagination;
|
||||
import run.halo.app.model.support.RainbowPage;
|
||||
import run.halo.app.service.OptionService;
|
||||
import run.halo.app.utils.HaloUtils;
|
||||
|
||||
/**
|
||||
* @author ryanwang
|
||||
|
@ -60,7 +60,7 @@ public class PaginationTagDirective implements TemplateDirectiveModel {
|
|||
prevPageFullPath.append(optionService.getBlogBaseUrl());
|
||||
}
|
||||
|
||||
int[] rainbow = PageUtil.rainbow(page + 1, total, display);
|
||||
int[] rainbow = HaloUtils.rainbow(page + 1, total, display);
|
||||
|
||||
List<RainbowPage> rainbowPages = new ArrayList<>();
|
||||
StringBuilder fullPath = new StringBuilder();
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package run.halo.app.core.freemarker.tag;
|
||||
|
||||
import cn.hutool.core.util.PageUtil;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import freemarker.core.Environment;
|
||||
import freemarker.template.Configuration;
|
||||
import freemarker.template.DefaultObjectWrapperBuilder;
|
||||
|
@ -11,8 +9,10 @@ import freemarker.template.TemplateException;
|
|||
import freemarker.template.TemplateModel;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import org.apache.commons.lang3.RandomUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
import run.halo.app.model.support.HaloConst;
|
||||
import run.halo.app.utils.HaloUtils;
|
||||
|
||||
/**
|
||||
* Freemarker custom tag of tools.
|
||||
|
@ -41,12 +41,12 @@ public class ToolTagDirective implements TemplateDirectiveModel {
|
|||
int total = Integer.parseInt(params.get("total").toString());
|
||||
int display = Integer.parseInt(params.get("display").toString());
|
||||
env.setVariable("numbers",
|
||||
builder.build().wrap(PageUtil.rainbow(page, total, display)));
|
||||
builder.build().wrap(HaloUtils.rainbow(page, total, display)));
|
||||
break;
|
||||
case "random":
|
||||
int min = Integer.parseInt(params.get("min").toString());
|
||||
int max = Integer.parseInt(params.get("max").toString());
|
||||
env.setVariable("number", builder.build().wrap(RandomUtil.randomInt(min, max)));
|
||||
env.setVariable("number", builder.build().wrap(RandomUtils.nextInt(min, max)));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package run.halo.app.filter;
|
||||
|
||||
import cn.hutool.extra.servlet.ServletUtil;
|
||||
import java.io.IOException;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
|
@ -11,6 +10,7 @@ import org.springframework.core.Ordered;
|
|||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
import run.halo.app.utils.ServletUtils;
|
||||
|
||||
/**
|
||||
* Filter for logging.
|
||||
|
@ -26,7 +26,7 @@ public class LogFilter extends OncePerRequestFilter {
|
|||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
|
||||
FilterChain filterChain) throws ServletException, IOException {
|
||||
|
||||
final String remoteAddr = ServletUtil.getClientIP(request);
|
||||
final String remoteAddr = ServletUtils.getClientIP(request);
|
||||
|
||||
log.debug("Starting url: [{}], method: [{}], ip: [{}]",
|
||||
request.getRequestURL(),
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package run.halo.app.listener.comment;
|
||||
|
||||
import cn.hutool.core.lang.Validator;
|
||||
import cn.hutool.core.text.StrBuilder;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -29,6 +27,7 @@ import run.halo.app.service.SheetCommentService;
|
|||
import run.halo.app.service.SheetService;
|
||||
import run.halo.app.service.ThemeService;
|
||||
import run.halo.app.service.UserService;
|
||||
import run.halo.app.utils.ValidationUtils;
|
||||
|
||||
/**
|
||||
* PostComment event listener.
|
||||
|
@ -146,10 +145,10 @@ public class CommentEventListener {
|
|||
|
||||
Journal journal = journalService.getById(journalComment.getPostId());
|
||||
|
||||
StrBuilder url = new StrBuilder(optionService.getBlogBaseUrl())
|
||||
StringBuilder url = new StringBuilder(optionService.getBlogBaseUrl())
|
||||
.append("/")
|
||||
.append(optionService.getJournalsPrefix());
|
||||
data.put("pageFullPath", url.toString());
|
||||
data.put("pageFullPath", url);
|
||||
data.put("pageTitle", journal.getCreateTime());
|
||||
data.put("author", journalComment.getAuthor());
|
||||
data.put("content", journalComment.getContent());
|
||||
|
@ -201,7 +200,7 @@ public class CommentEventListener {
|
|||
PostComment baseComment = postCommentService.getById(postComment.getParentId());
|
||||
|
||||
if (StringUtils.isEmpty(baseComment.getEmail())
|
||||
&& !Validator.isEmail(baseComment.getEmail())) {
|
||||
&& !ValidationUtils.isEmail(baseComment.getEmail())) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -234,7 +233,7 @@ public class CommentEventListener {
|
|||
SheetComment baseComment = sheetCommentService.getById(sheetComment.getParentId());
|
||||
|
||||
if (StringUtils.isEmpty(baseComment.getEmail())
|
||||
&& !Validator.isEmail(baseComment.getEmail())) {
|
||||
&& !ValidationUtils.isEmail(baseComment.getEmail())) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -268,7 +267,7 @@ public class CommentEventListener {
|
|||
journalCommentService.getById(journalComment.getParentId());
|
||||
|
||||
if (StringUtils.isEmpty(baseComment.getEmail())
|
||||
&& !Validator.isEmail(baseComment.getEmail())) {
|
||||
&& !ValidationUtils.isEmail(baseComment.getEmail())) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -280,7 +279,7 @@ public class CommentEventListener {
|
|||
|
||||
Journal journal = journalService.getById(journalComment.getPostId());
|
||||
|
||||
StrBuilder url = new StrBuilder(optionService.getBlogBaseUrl())
|
||||
StringBuilder url = new StringBuilder(optionService.getBlogBaseUrl())
|
||||
.append("/")
|
||||
.append(optionService.getJournalsPrefix());
|
||||
data.put("pageFullPath", url);
|
||||
|
|
|
@ -2,7 +2,7 @@ package run.halo.app.model.enums;
|
|||
|
||||
/**
|
||||
* @author zhixiang.yuan
|
||||
* @since 2021/01/24 10:45:33
|
||||
* @date 2021/01/24 10:45:33
|
||||
*/
|
||||
public enum EncryptTypeEnum {
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package run.halo.app.security.handler;
|
||||
|
||||
import cn.hutool.extra.servlet.ServletUtil;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import java.io.IOException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
@ -12,6 +11,7 @@ import run.halo.app.exception.AbstractHaloException;
|
|||
import run.halo.app.model.support.BaseResponse;
|
||||
import run.halo.app.utils.ExceptionUtils;
|
||||
import run.halo.app.utils.JsonUtils;
|
||||
import run.halo.app.utils.ServletUtils;
|
||||
|
||||
/**
|
||||
* Default AuthenticationFailureHandler.
|
||||
|
@ -32,7 +32,7 @@ public class DefaultAuthenticationFailureHandler implements AuthenticationFailur
|
|||
@Override
|
||||
public void onFailure(HttpServletRequest request, HttpServletResponse response,
|
||||
AbstractHaloException exception) throws IOException {
|
||||
log.warn("Handle unsuccessful authentication, ip: [{}]", ServletUtil.getClientIP(request));
|
||||
log.warn("Handle unsuccessful authentication, ip: [{}]", ServletUtils.getClientIP(request));
|
||||
log.error("Authentication failure: [{}], status: [{}], data: [{}]", exception.getMessage(),
|
||||
exception.getStatus(), exception.getErrorData());
|
||||
|
||||
|
|
|
@ -2,9 +2,6 @@ package run.halo.app.service.impl;
|
|||
|
||||
import static run.halo.app.model.support.HaloConst.DATABASE_PRODUCT_NAME;
|
||||
|
||||
import cn.hutool.core.lang.Validator;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
|
@ -15,6 +12,7 @@ import java.util.Collections;
|
|||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.lang.NonNull;
|
||||
|
@ -45,6 +43,7 @@ import run.halo.app.service.OptionService;
|
|||
import run.halo.app.service.UserService;
|
||||
import run.halo.app.utils.HaloUtils;
|
||||
import run.halo.app.utils.TwoFactorAuthUtils;
|
||||
import run.halo.app.utils.ValidationUtils;
|
||||
|
||||
/**
|
||||
* Admin service implementation.
|
||||
|
@ -98,7 +97,7 @@ public class AdminServiceImpl implements AdminService {
|
|||
|
||||
try {
|
||||
// Get user by username or email
|
||||
user = Validator.isEmail(username)
|
||||
user = ValidationUtils.isEmail(username)
|
||||
? userService.getByEmailOfNonNull(username) :
|
||||
userService.getByUsernameOfNonNull(username);
|
||||
} catch (NotFoundException e) {
|
||||
|
@ -132,7 +131,7 @@ public class AdminServiceImpl implements AdminService {
|
|||
|
||||
// check authCode
|
||||
if (MFAType.useMFA(user.getMfaType())) {
|
||||
if (StrUtil.isBlank(loginParam.getAuthcode())) {
|
||||
if (StringUtils.isBlank(loginParam.getAuthcode())) {
|
||||
throw new BadRequestException("请输入两步验证码");
|
||||
}
|
||||
TwoFactorAuthUtils.validateTFACode(user.getMfaKey(), loginParam.getAuthcode());
|
||||
|
@ -195,7 +194,7 @@ public class AdminServiceImpl implements AdminService {
|
|||
}
|
||||
|
||||
// Gets random code.
|
||||
String code = RandomUtil.randomNumbers(6);
|
||||
String code = RandomStringUtils.randomNumeric(6);
|
||||
|
||||
log.info("Got reset password code:{}", code);
|
||||
|
||||
|
@ -389,7 +388,7 @@ public class AdminServiceImpl implements AdminService {
|
|||
|
||||
boolean useMFA = true;
|
||||
try {
|
||||
final User user = Validator.isEmail(username)
|
||||
final User user = ValidationUtils.isEmail(username)
|
||||
? userService.getByEmailOfNonNull(username) :
|
||||
userService.getByUsernameOfNonNull(username);
|
||||
useMFA = MFAType.useMFA(user.getMfaType());
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package run.halo.app.service.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import run.halo.app.model.entity.Category;
|
||||
import run.halo.app.model.entity.Post;
|
||||
|
@ -40,7 +40,7 @@ public class AuthenticationServiceImpl implements AuthenticationService {
|
|||
public boolean postAuthentication(Post post, String password) {
|
||||
Set<String> accessPermissionStore = authorizationService.getAccessPermissionStore();
|
||||
|
||||
if (StrUtil.isNotBlank(post.getPassword())) {
|
||||
if (StringUtils.isNotBlank(post.getPassword())) {
|
||||
if (accessPermissionStore.contains(AuthorizationService.buildPostToken(post.getId()))) {
|
||||
return true;
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ public class AuthenticationServiceImpl implements AuthenticationService {
|
|||
|
||||
Category category = idToCategoryMap.get(categoryId);
|
||||
|
||||
if (StrUtil.isNotBlank(category.getPassword())) {
|
||||
if (StringUtils.isNotBlank(category.getPassword())) {
|
||||
if (accessPermissionStore.contains(
|
||||
AuthorizationService.buildCategoryToken(category.getId()))) {
|
||||
return true;
|
||||
|
|
|
@ -6,16 +6,10 @@ import static run.halo.app.model.support.HaloConst.HALO_DATA_EXPORT_PREFIX;
|
|||
import static run.halo.app.utils.DateTimeUtils.HORIZONTAL_LINE_DATETIME_FORMATTER;
|
||||
import static run.halo.app.utils.FileUtils.checkDirectoryTraversal;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.io.file.FileWriter;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.Path;
|
||||
|
@ -96,6 +90,8 @@ import run.halo.app.service.TagService;
|
|||
import run.halo.app.service.ThemeSettingService;
|
||||
import run.halo.app.service.UserService;
|
||||
import run.halo.app.utils.DateTimeUtils;
|
||||
import run.halo.app.utils.DateUtils;
|
||||
import run.halo.app.utils.FileUtils;
|
||||
import run.halo.app.utils.HaloUtils;
|
||||
import run.halo.app.utils.JsonUtils;
|
||||
|
||||
|
@ -105,7 +101,8 @@ import run.halo.app.utils.JsonUtils;
|
|||
* @author johnniang
|
||||
* @author ryanwang
|
||||
* @author Raremaa
|
||||
* @date 2019-04-26
|
||||
* @author guqing
|
||||
* @date 2019-04-26
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
|
@ -209,7 +206,7 @@ public class BackupServiceImpl implements BackupService {
|
|||
public BasePostDetailDTO importMarkdown(MultipartFile file) throws IOException {
|
||||
|
||||
// Read markdown content.
|
||||
String markdown = IoUtil.read(file.getInputStream(), StandardCharsets.UTF_8);
|
||||
String markdown = FileUtils.readString(file.getInputStream());
|
||||
|
||||
// TODO sheet import
|
||||
return postService.importMarkdown(markdown, file.getOriginalFilename());
|
||||
|
@ -222,7 +219,7 @@ public class BackupServiceImpl implements BackupService {
|
|||
// Create zip path for halo zip
|
||||
String haloZipFileName = HALO_BACKUP_PREFIX
|
||||
+ DateTimeUtils.format(LocalDateTime.now(), HORIZONTAL_LINE_DATETIME_FORMATTER)
|
||||
+ IdUtil.simpleUUID().hashCode() + ".zip";
|
||||
+ HaloUtils.simpleUUID().hashCode() + ".zip";
|
||||
// Create halo zip file
|
||||
Path haloZipFilePath = Paths.get(haloProperties.getBackupDir(), haloZipFileName);
|
||||
if (!Files.exists(haloZipFilePath.getParent())) {
|
||||
|
@ -334,7 +331,7 @@ public class BackupServiceImpl implements BackupService {
|
|||
public BackupDTO exportData() {
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("version", HaloConst.HALO_VERSION);
|
||||
data.put("export_date", DateUtil.now());
|
||||
data.put("export_date", DateUtils.now());
|
||||
data.put("attachments", attachmentService.listAll());
|
||||
data.put("categories", categoryService.listAll(true));
|
||||
data.put("comment_black_list", commentBlackListService.listAll());
|
||||
|
@ -360,7 +357,7 @@ public class BackupServiceImpl implements BackupService {
|
|||
try {
|
||||
String haloDataFileName = HALO_DATA_EXPORT_PREFIX
|
||||
+ DateTimeUtils.format(LocalDateTime.now(), HORIZONTAL_LINE_DATETIME_FORMATTER)
|
||||
+ IdUtil.simpleUUID().hashCode() + ".json";
|
||||
+ HaloUtils.simpleUUID().hashCode() + ".json";
|
||||
|
||||
Path haloDataFilePath = Paths.get(haloProperties.getDataExportDir(), haloDataFileName);
|
||||
if (!Files.exists(haloDataFilePath.getParent())) {
|
||||
|
@ -368,9 +365,7 @@ public class BackupServiceImpl implements BackupService {
|
|||
}
|
||||
Path haloDataPath = Files.createFile(haloDataFilePath);
|
||||
|
||||
FileWriter fileWriter = new FileWriter(haloDataPath.toFile(), CharsetUtil.UTF_8);
|
||||
fileWriter.write(JsonUtils.objectToJson(data));
|
||||
|
||||
FileUtils.writeStringToFile(haloDataPath.toFile(), JsonUtils.objectToJson(data));
|
||||
return buildBackupDto(DATA_EXPORT_BASE_URI, haloDataPath);
|
||||
} catch (IOException e) {
|
||||
throw new ServiceException("导出数据失败", e);
|
||||
|
@ -420,7 +415,7 @@ public class BackupServiceImpl implements BackupService {
|
|||
|
||||
@Override
|
||||
public void importData(MultipartFile file) throws IOException {
|
||||
String jsonContent = IoUtil.read(file.getInputStream(), StandardCharsets.UTF_8);
|
||||
String jsonContent = FileUtils.readString(file.getInputStream());
|
||||
|
||||
ObjectMapper mapper = JsonUtils.createDefaultJsonMapper();
|
||||
TypeReference<HashMap<String, Object>> typeRef =
|
||||
|
@ -534,7 +529,7 @@ public class BackupServiceImpl implements BackupService {
|
|||
|
||||
// Write files to the temporary directory
|
||||
String markdownFileTempPathName =
|
||||
haloProperties.getBackupMarkdownDir() + IdUtil.simpleUUID().hashCode();
|
||||
haloProperties.getBackupMarkdownDir() + HaloUtils.simpleUUID().hashCode();
|
||||
for (PostMarkdownVO postMarkdownVo : postMarkdownList) {
|
||||
StringBuilder content = new StringBuilder();
|
||||
Boolean needFrontMatter =
|
||||
|
@ -552,9 +547,7 @@ public class BackupServiceImpl implements BackupService {
|
|||
Files.createDirectories(markdownFilePath.getParent());
|
||||
}
|
||||
Path markdownDataPath = Files.createFile(markdownFilePath);
|
||||
FileWriter fileWriter =
|
||||
new FileWriter(markdownDataPath.toFile(), CharsetUtil.UTF_8);
|
||||
fileWriter.write(content.toString());
|
||||
FileUtils.writeStringToFile(markdownDataPath.toFile(), content.toString());
|
||||
} catch (IOException e) {
|
||||
throw new ServiceException("导出数据失败", e);
|
||||
}
|
||||
|
@ -563,7 +556,7 @@ public class BackupServiceImpl implements BackupService {
|
|||
// Create zip path
|
||||
String markdownZipFileName = HALO_BACKUP_MARKDOWN_PREFIX
|
||||
+ DateTimeUtils.format(LocalDateTime.now(), HORIZONTAL_LINE_DATETIME_FORMATTER)
|
||||
+ IdUtil.simpleUUID().hashCode() + ".zip";
|
||||
+ HaloUtils.simpleUUID().hashCode() + ".zip";
|
||||
|
||||
// Create zip file
|
||||
Path markdownZipFilePath =
|
||||
|
|
|
@ -2,7 +2,6 @@ package run.halo.app.service.impl;
|
|||
|
||||
import static org.springframework.data.domain.Sort.Direction.DESC;
|
||||
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
@ -54,6 +53,7 @@ import run.halo.app.service.OptionService;
|
|||
import run.halo.app.service.UserService;
|
||||
import run.halo.app.service.base.AbstractCrudService;
|
||||
import run.halo.app.service.base.BaseCommentService;
|
||||
import run.halo.app.utils.HaloUtils;
|
||||
import run.halo.app.utils.ServiceUtils;
|
||||
import run.halo.app.utils.ServletUtils;
|
||||
import run.halo.app.utils.ValidationUtils;
|
||||
|
@ -328,7 +328,7 @@ public abstract class BaseCommentServiceImpl<COMMENT extends BaseComment>
|
|||
}
|
||||
|
||||
if (StringUtils.isNotEmpty(comment.getAuthorUrl())) {
|
||||
comment.setAuthorUrl(URLUtil.normalize(comment.getAuthorUrl()));
|
||||
comment.setAuthorUrl(HaloUtils.normalizeUrl(comment.getAuthorUrl()));
|
||||
}
|
||||
|
||||
if (authentication != null) {
|
||||
|
|
|
@ -2,8 +2,6 @@ package run.halo.app.service.impl;
|
|||
|
||||
import static run.halo.app.model.support.HaloConst.URL_SEPARATOR;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.google.common.base.Objects;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
@ -15,6 +13,7 @@ import java.util.Optional;
|
|||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.data.domain.Page;
|
||||
|
@ -43,6 +42,7 @@ import run.halo.app.service.PostCategoryService;
|
|||
import run.halo.app.service.PostService;
|
||||
import run.halo.app.service.base.AbstractCrudService;
|
||||
import run.halo.app.utils.BeanUtils;
|
||||
import run.halo.app.utils.HaloUtils;
|
||||
import run.halo.app.utils.ServiceUtils;
|
||||
|
||||
/**
|
||||
|
@ -113,7 +113,7 @@ public class CategoryServiceImpl extends AbstractCrudService<Category, Integer>
|
|||
}
|
||||
}
|
||||
|
||||
if (StrUtil.isNotBlank(category.getPassword())) {
|
||||
if (StringUtils.isNotBlank(category.getPassword())) {
|
||||
category.setPassword(category.getPassword().trim());
|
||||
}
|
||||
|
||||
|
@ -298,7 +298,7 @@ public class CategoryServiceImpl extends AbstractCrudService<Category, Integer>
|
|||
|
||||
@Override
|
||||
public void refreshPostStatus(List<Integer> affectedPostIdList) {
|
||||
if (CollectionUtil.isEmpty(affectedPostIdList)) {
|
||||
if (CollectionUtils.isEmpty(affectedPostIdList)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -307,7 +307,7 @@ public class CategoryServiceImpl extends AbstractCrudService<Category, Integer>
|
|||
|
||||
post.setStatus(null);
|
||||
|
||||
if (StrUtil.isNotBlank(post.getPassword())) {
|
||||
if (StringUtils.isNotBlank(post.getPassword())) {
|
||||
post.setStatus(PostStatus.INTIMATE);
|
||||
} else {
|
||||
postCategoryService.listByPostId(postId)
|
||||
|
@ -368,7 +368,7 @@ public class CategoryServiceImpl extends AbstractCrudService<Category, Integer>
|
|||
|
||||
@Override
|
||||
public List<Category> filterEncryptCategory(List<Category> categories) {
|
||||
if (CollectionUtil.isEmpty(categories)) {
|
||||
if (CollectionUtils.isEmpty(categories)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
|
@ -399,7 +399,7 @@ public class CategoryServiceImpl extends AbstractCrudService<Category, Integer>
|
|||
* @param categoryList category list
|
||||
*/
|
||||
private void doFilterEncryptCategory(List<CategoryVO> categoryList) {
|
||||
if (CollectionUtil.isEmpty(categoryList)) {
|
||||
if (CollectionUtils.isEmpty(categoryList)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -423,7 +423,7 @@ public class CategoryServiceImpl extends AbstractCrudService<Category, Integer>
|
|||
private void collectAllChild(List<Category> collectorList,
|
||||
List<CategoryVO> childrenList,
|
||||
Boolean doNotCollectEncryptedCategory) {
|
||||
if (CollectionUtil.isEmpty(childrenList)) {
|
||||
if (CollectionUtils.isEmpty(childrenList)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -439,7 +439,7 @@ public class CategoryServiceImpl extends AbstractCrudService<Category, Integer>
|
|||
continue;
|
||||
}
|
||||
|
||||
if (CollectionUtil.isNotEmpty(categoryVO.getChildren())) {
|
||||
if (HaloUtils.isNotEmpty(categoryVO.getChildren())) {
|
||||
collectAllChild(collectorList,
|
||||
categoryVO.getChildren(), doNotCollectEncryptedCategory);
|
||||
}
|
||||
|
@ -459,7 +459,7 @@ public class CategoryServiceImpl extends AbstractCrudService<Category, Integer>
|
|||
List<CategoryVO> childrenList,
|
||||
Integer categoryId,
|
||||
Boolean doNotCollectEncryptedCategory) {
|
||||
if (CollectionUtil.isEmpty(childrenList)) {
|
||||
if (CollectionUtils.isEmpty(childrenList)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -532,7 +532,7 @@ public class CategoryServiceImpl extends AbstractCrudService<Category, Integer>
|
|||
public Category update(Category category) {
|
||||
Category update = super.update(category);
|
||||
|
||||
if (StrUtil.isNotBlank(category.getPassword())) {
|
||||
if (StringUtils.isNotBlank(category.getPassword())) {
|
||||
doEncryptPost(category);
|
||||
} else {
|
||||
doDecryptPost(category);
|
||||
|
@ -618,7 +618,7 @@ public class CategoryServiceImpl extends AbstractCrudService<Category, Integer>
|
|||
|
||||
.filter(postList -> !postList.isEmpty())
|
||||
.map(postList -> postList.stream()
|
||||
.filter(post -> StrUtil.isBlank(post.getPassword()))
|
||||
.filter(post -> StringUtils.isBlank(post.getPassword()))
|
||||
.filter(post -> PostStatus.INTIMATE.equals(post.getStatus()))
|
||||
.map(Post::getId).collect(Collectors.toList()))
|
||||
|
||||
|
@ -652,7 +652,7 @@ public class CategoryServiceImpl extends AbstractCrudService<Category, Integer>
|
|||
|
||||
Category category = idToCategoryMap.get(categoryId);
|
||||
|
||||
if (StrUtil.isNotBlank(category.getPassword())) {
|
||||
if (StringUtils.isNotBlank(category.getPassword())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package run.halo.app.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -12,6 +11,7 @@ import org.springframework.data.domain.PageImpl;
|
|||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import run.halo.app.exception.NotFoundException;
|
||||
import run.halo.app.model.dto.JournalDTO;
|
||||
import run.halo.app.model.entity.Journal;
|
||||
|
@ -56,7 +56,7 @@ public class JournalCommentServiceImpl extends BaseCommentServiceImpl<JournalCom
|
|||
public List<JournalCommentWithJournalVO> convertToWithJournalVo(
|
||||
List<JournalComment> journalComments) {
|
||||
|
||||
if (CollectionUtil.isEmpty(journalComments)) {
|
||||
if (CollectionUtils.isEmpty(journalComments)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ package run.halo.app.service.impl;
|
|||
|
||||
import static run.halo.app.model.support.HaloConst.URL_SEPARATOR;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -32,6 +31,7 @@ import run.halo.app.service.CommentBlackListService;
|
|||
import run.halo.app.service.OptionService;
|
||||
import run.halo.app.service.PostCommentService;
|
||||
import run.halo.app.service.UserService;
|
||||
import run.halo.app.utils.DateUtils;
|
||||
import run.halo.app.utils.ServiceUtils;
|
||||
import run.halo.app.utils.ServletUtils;
|
||||
|
||||
|
@ -128,11 +128,11 @@ public class PostCommentServiceImpl extends BaseCommentServiceImpl<PostComment>
|
|||
|
||||
String archivesPrefix = optionService.getArchivesPrefix();
|
||||
|
||||
int month = DateUtil.month(post.getCreateTime()) + 1;
|
||||
int month = DateUtils.month(post.getCreateTime()) + 1;
|
||||
|
||||
String monthString = month < 10 ? "0" + month : String.valueOf(month);
|
||||
|
||||
int day = DateUtil.dayOfMonth(post.getCreateTime());
|
||||
int day = DateUtils.dayOfMonth(post.getCreateTime());
|
||||
|
||||
String dayString = day < 10 ? "0" + day : String.valueOf(day);
|
||||
|
||||
|
@ -153,14 +153,14 @@ public class PostCommentServiceImpl extends BaseCommentServiceImpl<PostComment>
|
|||
fullPath.append("?p=")
|
||||
.append(post.getId());
|
||||
} else if (permalinkType.equals(PostPermalinkType.DATE)) {
|
||||
fullPath.append(DateUtil.year(post.getCreateTime()))
|
||||
fullPath.append(DateUtils.year(post.getCreateTime()))
|
||||
.append(URL_SEPARATOR)
|
||||
.append(monthString)
|
||||
.append(URL_SEPARATOR)
|
||||
.append(post.getSlug())
|
||||
.append(pathSuffix);
|
||||
} else if (permalinkType.equals(PostPermalinkType.DAY)) {
|
||||
fullPath.append(DateUtil.year(post.getCreateTime()))
|
||||
fullPath.append(DateUtils.year(post.getCreateTime()))
|
||||
.append(URL_SEPARATOR)
|
||||
.append(monthString)
|
||||
.append(URL_SEPARATOR)
|
||||
|
@ -169,7 +169,7 @@ public class PostCommentServiceImpl extends BaseCommentServiceImpl<PostComment>
|
|||
.append(post.getSlug())
|
||||
.append(pathSuffix);
|
||||
} else if (permalinkType.equals(PostPermalinkType.YEAR)) {
|
||||
fullPath.append(DateUtil.year(post.getCreateTime()))
|
||||
fullPath.append(DateUtils.year(post.getCreateTime()))
|
||||
.append(URL_SEPARATOR)
|
||||
.append(post.getSlug())
|
||||
.append(pathSuffix);
|
||||
|
|
|
@ -3,9 +3,6 @@ package run.halo.app.service.impl;
|
|||
import static org.springframework.data.domain.Sort.Direction.DESC;
|
||||
import static run.halo.app.model.support.HaloConst.URL_SEPARATOR;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
|
@ -72,6 +69,7 @@ import run.halo.app.service.PostService;
|
|||
import run.halo.app.service.PostTagService;
|
||||
import run.halo.app.service.TagService;
|
||||
import run.halo.app.utils.DateUtils;
|
||||
import run.halo.app.utils.HaloUtils;
|
||||
import run.halo.app.utils.MarkdownUtils;
|
||||
import run.halo.app.utils.ServiceUtils;
|
||||
import run.halo.app.utils.SlugUtils;
|
||||
|
@ -382,9 +380,9 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
|
|||
for (String key : frontMatter.keySet()) {
|
||||
elementValue = frontMatter.get(key);
|
||||
for (String ele : elementValue) {
|
||||
ele = StrUtil.strip(ele, "[", "]");
|
||||
ele = StrUtil.strip(ele, "\"");
|
||||
ele = StrUtil.strip(ele, "\'");
|
||||
ele = HaloUtils.strip(ele, "[", "]");
|
||||
ele = StringUtils.strip(ele, "\"");
|
||||
ele = StringUtils.strip(ele, "\'");
|
||||
if ("".equals(ele)) {
|
||||
continue;
|
||||
}
|
||||
|
@ -393,7 +391,7 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
|
|||
post.setTitle(ele);
|
||||
break;
|
||||
case "date":
|
||||
post.setCreateTime(DateUtil.parse(ele));
|
||||
post.setCreateTime(DateUtils.parseDate(ele));
|
||||
break;
|
||||
case "permalink":
|
||||
post.setSlug(ele);
|
||||
|
@ -411,8 +409,8 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
|
|||
Tag tag;
|
||||
for (String tagName : ele.split(",")) {
|
||||
tagName = tagName.trim();
|
||||
tagName = StrUtil.strip(tagName, "\"");
|
||||
tagName = StrUtil.strip(tagName, "\'");
|
||||
tagName = StringUtils.strip(tagName, "\"");
|
||||
tagName = StringUtils.strip(tagName, "\'");
|
||||
tag = tagService.getByName(tagName);
|
||||
if (null == tag) {
|
||||
tag = new Tag();
|
||||
|
@ -427,8 +425,8 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
|
|||
Integer lastCategoryId = null;
|
||||
for (String categoryName : ele.split(",")) {
|
||||
categoryName = categoryName.trim();
|
||||
categoryName = StrUtil.strip(categoryName, "\"");
|
||||
categoryName = StrUtil.strip(categoryName, "\'");
|
||||
categoryName = StringUtils.strip(categoryName, "\"");
|
||||
categoryName = StringUtils.strip(categoryName, "\'");
|
||||
Category category = categoryService.getByName(categoryName);
|
||||
if (null == category) {
|
||||
category = new Category();
|
||||
|
@ -843,7 +841,7 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
|
|||
|
||||
// Create or update post
|
||||
Boolean needEncrypt = Optional.ofNullable(categoryIds)
|
||||
.filter(CollectionUtil::isNotEmpty)
|
||||
.filter(HaloUtils::isNotEmpty)
|
||||
.map(categoryIdSet -> {
|
||||
for (Integer categoryId : categoryIdSet) {
|
||||
if (categoryService.categoryHasEncrypt(categoryId)) {
|
||||
|
@ -996,11 +994,11 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
|
|||
|
||||
String archivesPrefix = optionService.getArchivesPrefix();
|
||||
|
||||
int month = DateUtil.month(post.getCreateTime()) + 1;
|
||||
int month = DateUtils.month(post.getCreateTime()) + 1;
|
||||
|
||||
String monthString = month < 10 ? "0" + month : String.valueOf(month);
|
||||
|
||||
int day = DateUtil.dayOfMonth(post.getCreateTime());
|
||||
int day = DateUtils.dayOfMonth(post.getCreateTime());
|
||||
|
||||
String dayString = day < 10 ? "0" + day : String.valueOf(day);
|
||||
|
||||
|
@ -1021,14 +1019,14 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
|
|||
fullPath.append("?p=")
|
||||
.append(post.getId());
|
||||
} else if (permalinkType.equals(PostPermalinkType.DATE)) {
|
||||
fullPath.append(DateUtil.year(post.getCreateTime()))
|
||||
fullPath.append(DateUtils.year(post.getCreateTime()))
|
||||
.append(URL_SEPARATOR)
|
||||
.append(monthString)
|
||||
.append(URL_SEPARATOR)
|
||||
.append(post.getSlug())
|
||||
.append(pathSuffix);
|
||||
} else if (permalinkType.equals(PostPermalinkType.DAY)) {
|
||||
fullPath.append(DateUtil.year(post.getCreateTime()))
|
||||
fullPath.append(DateUtils.year(post.getCreateTime()))
|
||||
.append(URL_SEPARATOR)
|
||||
.append(monthString)
|
||||
.append(URL_SEPARATOR)
|
||||
|
@ -1037,7 +1035,7 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
|
|||
.append(post.getSlug())
|
||||
.append(pathSuffix);
|
||||
} else if (permalinkType.equals(PostPermalinkType.YEAR)) {
|
||||
fullPath.append(DateUtil.year(post.getCreateTime()))
|
||||
fullPath.append(DateUtils.year(post.getCreateTime()))
|
||||
.append(URL_SEPARATOR)
|
||||
.append(post.getSlug())
|
||||
.append(pathSuffix);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package run.halo.app.service.impl;
|
||||
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.FileAlreadyExistsException;
|
||||
|
@ -28,6 +27,7 @@ import run.halo.app.exception.ServiceException;
|
|||
import run.halo.app.model.support.StaticFile;
|
||||
import run.halo.app.service.StaticStorageService;
|
||||
import run.halo.app.utils.FileUtils;
|
||||
import run.halo.app.utils.HaloUtils;
|
||||
|
||||
/**
|
||||
* StaticStorageService implementation class.
|
||||
|
@ -69,7 +69,7 @@ public class StaticStorageServiceImpl
|
|||
|
||||
pathStream.forEach(path -> {
|
||||
StaticFile staticFile = new StaticFile();
|
||||
staticFile.setId(IdUtil.fastSimpleUUID());
|
||||
staticFile.setId(HaloUtils.simpleUUID());
|
||||
staticFile.setName(path.getFileName().toString());
|
||||
staticFile.setPath(path.toString());
|
||||
staticFile.setRelativePath(
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package run.halo.app.service.impl;
|
||||
|
||||
import cn.hutool.crypto.digest.BCrypt;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
@ -25,6 +24,7 @@ import run.halo.app.model.params.UserParam;
|
|||
import run.halo.app.repository.UserRepository;
|
||||
import run.halo.app.service.UserService;
|
||||
import run.halo.app.service.base.AbstractCrudService;
|
||||
import run.halo.app.utils.BCrypt;
|
||||
import run.halo.app.utils.DateUtils;
|
||||
import run.halo.app.utils.HaloUtils;
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package run.halo.app.task;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
|
@ -17,9 +16,11 @@ import run.halo.app.model.enums.TimeUnit;
|
|||
import run.halo.app.model.properties.PostProperties;
|
||||
import run.halo.app.service.OptionService;
|
||||
import run.halo.app.service.PostService;
|
||||
import run.halo.app.utils.DateTimeUtils;
|
||||
|
||||
/**
|
||||
* @author Wh1te
|
||||
* @author guqing
|
||||
* @date 2020-10-19
|
||||
*/
|
||||
@Slf4j
|
||||
|
@ -73,7 +74,7 @@ public class RecycledPostCleaningTask {
|
|||
List<Post> recyclePost = postService.listAllBy(PostStatus.RECYCLE);
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
List<Integer> ids = recyclePost.stream().filter(post -> {
|
||||
LocalDateTime updateTime = DateUtil.toLocalDateTime(post.getUpdateTime());
|
||||
LocalDateTime updateTime = DateTimeUtils.toLocalDateTime(post.getUpdateTime());
|
||||
long until = updateTime.until(now, ChronoUnit.HOURS);
|
||||
return until >= expiredIn;
|
||||
}).map(BasePost::getId).collect(Collectors.toList());
|
||||
|
|
|
@ -0,0 +1,910 @@
|
|||
package run.halo.app.utils;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* BCrypt implements OpenBSD-style Blowfish password hashing using the scheme described in
|
||||
* "A Future-Adaptable Password Scheme" by Niels Provos and David Mazieres.
|
||||
*
|
||||
* <p>This password hashing system tries to thwart off-line password cracking using a
|
||||
* computationally-intensive hashing algorithm, based on Bruce Schneier's Blowfish cipher.
|
||||
* The work factor of the algorithm is parameterised, so it can be increased as computers
|
||||
* get faster.
|
||||
*
|
||||
* <p>Usage is really simple. To hash a password for the first time, call the hashpw method
|
||||
* with a random salt, like this:
|
||||
*
|
||||
* <p><code>
|
||||
* String pw_hash = BCrypt.hashpw(plain_password, BCrypt.gensalt()); <br />
|
||||
* </code>
|
||||
*
|
||||
* <p>To check whether a plaintext password matches one that has been hashed previously, use
|
||||
* the checkpw method:
|
||||
*
|
||||
* <p><code>
|
||||
* if (BCrypt.checkpw(candidate_password, stored_hash))<br />
|
||||
* System.out.println("It matches");<br />
|
||||
* else<br />
|
||||
* System.out.println("It does not match");<br />
|
||||
* </code>
|
||||
*
|
||||
* <p>The gensalt() method takes an optional parameter (log_rounds) that determines the
|
||||
* computational complexity of the hashing:
|
||||
*
|
||||
* <p><code>
|
||||
* String strong_salt = BCrypt.gensalt(10)<br />
|
||||
* String stronger_salt = BCrypt.gensalt(12)<br />
|
||||
* </code>
|
||||
*
|
||||
* <p>The amount of work increases exponentially (2**log_rounds), so each increment is twice
|
||||
* as much work. The default log_rounds is 10, and the valid range is 4 to 31.
|
||||
*
|
||||
* @author guqing
|
||||
* @see <a href="https://github.com/spring-projects/spring-security/blob/main/crypto/src/main/java/org/springframework/security/crypto/bcrypt/BCrypt.java">Spring Security BCrypt</a>
|
||||
* @date 2021-09-28
|
||||
*/
|
||||
public class BCrypt {
|
||||
|
||||
// BCrypt parameters
|
||||
private static final int GENSALT_DEFAULT_LOG2_ROUNDS = 10;
|
||||
|
||||
private static final int BCRYPT_SALT_LEN = 16;
|
||||
|
||||
// Blowfish parameters
|
||||
private static final int BLOWFISH_NUM_ROUNDS = 16;
|
||||
|
||||
// Initial contents of key schedule
|
||||
private static final int[] P_orig =
|
||||
{0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0,
|
||||
0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, 0xc0ac29b7,
|
||||
0xc97c50dd, 0x3f84d5b5,
|
||||
0xb5470917, 0x9216d5d9, 0x8979fb1b};
|
||||
|
||||
private static final int[] S_orig =
|
||||
{0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96,
|
||||
0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, 0x636920d8,
|
||||
0x71574e69, 0xa458fea3,
|
||||
0xf4933d7e, 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5,
|
||||
0x9c30d539, 0x2af26013,
|
||||
0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b,
|
||||
0xb01e8a3e, 0xd71577c1,
|
||||
0xbd314b27, 0x78af2fda, 0x55605c60, 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
|
||||
0x55ca396a, 0x2aab10b6,
|
||||
0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, 0x2ba9c55d,
|
||||
0x741831f6, 0xce5c3e16,
|
||||
0x9b87931e, 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af,
|
||||
0xc4bfe81b, 0x66282193,
|
||||
0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1, 0xdc262302,
|
||||
0xeb651b88, 0x23893e81,
|
||||
0xd396acc5, 0x0f6d6ff3, 0x83f44239, 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e,
|
||||
0x21c66842, 0xf6e96c9a,
|
||||
0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, 0x6eef0b6c,
|
||||
0x137a3be4, 0xba3bf050,
|
||||
0x7efb2a98, 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4,
|
||||
0x7d84a5c3, 0x3b8b5ebe,
|
||||
0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, 0x1bfedf72,
|
||||
0x429b023d, 0x37d0d724,
|
||||
0xd00a1248, 0xdb0fead3, 0x49f1c09b, 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
|
||||
0xe3fe501a, 0xb6794c3b,
|
||||
0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, 0x68fb6faf,
|
||||
0x3e6c53b5, 0x1339b2eb,
|
||||
0x3b52ec6f, 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd,
|
||||
0x660f2807, 0x192e4bb3,
|
||||
0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a, 0xd6a100c6,
|
||||
0x402c7279, 0x679f25fe,
|
||||
0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab,
|
||||
0x323db5fa, 0xfd238760,
|
||||
0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, 0xd542a8f6,
|
||||
0x287effc3, 0xac6732c6,
|
||||
0x8c4f5573, 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8,
|
||||
0x4afcb56c, 0x2dd1d35b,
|
||||
0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341,
|
||||
0xcee4c6e8, 0xef20cada,
|
||||
0x36774c01, 0xd07e9efe, 0x2bf11fb4, 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
|
||||
0xd08ed1d0, 0xafc725e0,
|
||||
0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, 0x4fad5ea0,
|
||||
0x688fc31c, 0xd1cff191,
|
||||
0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8,
|
||||
0x18acf3d6, 0xce89e299,
|
||||
0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705, 0x93cc7314,
|
||||
0x211a1477, 0xe6ad2065,
|
||||
0x77b5fa86, 0xc75442f5, 0xfb9d35cf, 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49,
|
||||
0x00250e2d, 0x2071b35e,
|
||||
0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, 0x78c14389,
|
||||
0xd95a537f, 0x207d5ba2,
|
||||
0x02e5b9c5, 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a,
|
||||
0x1b510052, 0x9a532915,
|
||||
0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f, 0xf296ec6b,
|
||||
0x2a0dd915, 0xb6636521,
|
||||
0xe7b9f9b6, 0xff34052e, 0xc5855664, 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a,
|
||||
0x4b7a70e9, 0xb5b32944,
|
||||
0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, 0xecaa8c71,
|
||||
0x699a17ff, 0x5664526c,
|
||||
0xc2b19ee1, 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65,
|
||||
0x6b8fe4d6, 0x99f73fd6,
|
||||
0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, 0x4cdd2086, 0x8470eb26, 0x6382e9c6,
|
||||
0x021ecc5e, 0x09686b3f,
|
||||
0x3ebaefc9, 0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
|
||||
0x3e07841c, 0x7fdeae5c,
|
||||
0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, 0xae0cf51a,
|
||||
0x3cb574b2, 0x25837a58,
|
||||
0xdc0921bd, 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc,
|
||||
0xc8b57634, 0x9af3dda7,
|
||||
0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, 0xe238cd99, 0x3bea0e2f, 0x3280bba1,
|
||||
0x183eb331, 0x4e548b38,
|
||||
0x4f6db908, 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
|
||||
0xde9a771f, 0xd9930810,
|
||||
0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, 0x7a584718,
|
||||
0x7408da17, 0xbc9f9abc,
|
||||
0xe94b7d8c, 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908,
|
||||
0xdd433b37, 0x24c2ba16,
|
||||
0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, 0x71dff89e, 0x10314e55, 0x81ac77d6,
|
||||
0x5f11199b, 0x043556f1,
|
||||
0xd7a3c76b, 0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
|
||||
0x86e34570, 0xeae96fb1,
|
||||
0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, 0x803e89d6,
|
||||
0x5266c825, 0x2e4cc978,
|
||||
0x9c10b36a, 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d,
|
||||
0x1939260f, 0x19c27960,
|
||||
0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, 0xe3bc4595, 0xa67bc883, 0xb17f37d1,
|
||||
0x018cff28, 0xc332ddef,
|
||||
0xbe6c5aa5, 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
|
||||
0x1521b628, 0x29076170,
|
||||
0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, 0xb5735c90,
|
||||
0x4c70a239, 0xd59e9e0b,
|
||||
0xcbaade14, 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca,
|
||||
0xa02369b9, 0x655abb50,
|
||||
0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, 0x9b540b19, 0x875fa099, 0x95f7997e,
|
||||
0x623d7da8, 0xf837889a,
|
||||
0x97e32d77, 0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
|
||||
0x57f584a5, 0x1b227263,
|
||||
0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef,
|
||||
0x34c6ffea, 0xfe28ed61,
|
||||
0xee7c3c73, 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea,
|
||||
0xdb6c4f15, 0xfacb4fd0,
|
||||
0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, 0xd81e799e, 0x86854dc7, 0xe44b476a,
|
||||
0x3d816250, 0xcf62a1f2,
|
||||
0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
|
||||
0x095bbf00, 0xad19489d,
|
||||
0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, 0x3372f092,
|
||||
0x8d937e41, 0xd65fecf1,
|
||||
0x6c223bdb, 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e,
|
||||
0xe8efd855, 0x61d99735,
|
||||
0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, 0x9e447a2e, 0xc3453484, 0xfdd56705,
|
||||
0x0e1e9ec9, 0xdb73dbd3,
|
||||
0x105588cd, 0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
|
||||
0x153e21e7, 0x8fb03d4a,
|
||||
0xe6e39f2b, 0xdb83adf7, 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7,
|
||||
0x7602d4f7, 0xbcf46b2e,
|
||||
0xd4a20068, 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 0x1e39f62e, 0x97244546,
|
||||
0x14214f74, 0xbf8b8840,
|
||||
0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0,
|
||||
0x31cb8504, 0x96eb27b3,
|
||||
0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
|
||||
0x68dc1462, 0xd7486900,
|
||||
0x680ec0a4, 0x27a18dee, 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, 0xaace1e7c,
|
||||
0xd3375fec, 0xce78a399,
|
||||
0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, 0x1dc9faf7, 0x4b6d1856,
|
||||
0x26a36631, 0xeae397b2,
|
||||
0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397, 0x454056ac,
|
||||
0xba489527, 0x55533a3a,
|
||||
0x20838d87, 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33,
|
||||
0xa62a4a56, 0x3f3125f9,
|
||||
0x5ef47e1c, 0x9029317c, 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548,
|
||||
0xe4c66d22, 0x48c1133f,
|
||||
0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, 0x325f51eb, 0xd59bc0d1,
|
||||
0xf2bcc18f, 0x41113564,
|
||||
0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1,
|
||||
0xcad18115, 0x6b2395e0,
|
||||
0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
|
||||
0xd0127845, 0x95b794fd,
|
||||
0x647d0862, 0xe7ccf5f0, 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341,
|
||||
0x992eff74, 0x3a6f6eab,
|
||||
0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, 0xc67b5510, 0x6d672c37,
|
||||
0x2765d43b, 0xdcd0e804,
|
||||
0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b,
|
||||
0xd9155ea3, 0xbb132f88,
|
||||
0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d,
|
||||
0x6842ada7, 0xc66a2b3b,
|
||||
0x12754ccc, 0x782ef11c, 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018,
|
||||
0x11caedfa, 0x3d25bdd8,
|
||||
0xe2e1c3c9, 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, 0x64af674e, 0xda86a85f,
|
||||
0xbebfe988, 0x64e4c3fe,
|
||||
0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04,
|
||||
0xd736fccc, 0x83426b33,
|
||||
0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
|
||||
0x4e58f48f, 0xf2ddfda2,
|
||||
0xf474ef38, 0x8789bdc2, 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, 0x7aeb2661,
|
||||
0x8b1ddf84, 0x846a0e79,
|
||||
0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, 0xb90bace1, 0xbb8205d0,
|
||||
0x11a86248, 0x7574a99e,
|
||||
0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025,
|
||||
0x1d6efe10, 0x1ab93d1d,
|
||||
0x0ba5a4df, 0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52,
|
||||
0x50115e01, 0xa70683fa,
|
||||
0xa002b5c4, 0x0de6d027, 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28,
|
||||
0xc0f586e0, 0x006058aa,
|
||||
0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, 0xbbcbee56, 0x90bcb6de,
|
||||
0xebfc7da1, 0xce591d76,
|
||||
0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4,
|
||||
0xd39eb8fc, 0xed545578,
|
||||
0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
|
||||
0x6fd5c7e7, 0x56e14ec4,
|
||||
0x362abfce, 0xddc6c837, 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, 0x3a39ce37,
|
||||
0xd3faf5cf, 0xabc27737,
|
||||
0x5ac52d1b, 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315,
|
||||
0xd62d1c7e, 0xc700c47b,
|
||||
0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79, 0xc6a376d2,
|
||||
0x6549c2c8, 0x530ff8ee,
|
||||
0x468dde7d, 0xd5730a1d, 0x4cd04dc6, 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304,
|
||||
0xa1fad5f0, 0x6a2d519a,
|
||||
0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, 0x83c061ba,
|
||||
0x9be96a4d, 0x8fe51550,
|
||||
0xba645bd6, 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da,
|
||||
0x3f046f69, 0x77fa0a59,
|
||||
0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9,
|
||||
0x022b8b51, 0x96d5ac3a,
|
||||
0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
|
||||
0xe029ac71, 0xe019a5e6,
|
||||
0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, 0x785f0191,
|
||||
0xed756055, 0xf7960e44,
|
||||
0xe3d35e8c, 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2,
|
||||
0x97271aec, 0xa93a072a,
|
||||
0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5, 0x03563482,
|
||||
0x8aba3cbb, 0x28517711,
|
||||
0xc20ad9f8, 0xabcc5167, 0xccad925f, 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991,
|
||||
0xea7a90c2, 0xfb3e7bce,
|
||||
0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, 0xa2ae0810,
|
||||
0xdd6db224, 0x69852dfd,
|
||||
0x09072166, 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40,
|
||||
0xccd2017f, 0x6bb4e3bb,
|
||||
0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, 0x8d6612ae,
|
||||
0xbf3c6f47, 0xd29be463,
|
||||
0x542f5d9e, 0xaec2771b, 0xf64e6370, 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
|
||||
0x4040cb08, 0x4eb4e2cc,
|
||||
0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, 0x6f3f3b82,
|
||||
0x3520ab82, 0x011a1d4b,
|
||||
0x277227f8, 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b,
|
||||
0x2f32c9b7, 0xa01fbac9,
|
||||
0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a, 0xd0dadecb,
|
||||
0xd50ada38, 0x0339c32a,
|
||||
0xc6913667, 0x8df9317c, 0xe0b12b4f, 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c,
|
||||
0xbf97222c, 0x15e6fc2a,
|
||||
0x0f91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, 0xb6c1075e,
|
||||
0xe3056a0c, 0x10d25065,
|
||||
0xcb03a442, 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df,
|
||||
0xd3a0342b, 0x8971f21e,
|
||||
0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e, 0xe60b6f47,
|
||||
0x0fe3f11d, 0xe54cda54,
|
||||
0x1edad891, 0xce6279cf, 0xcd3e7e6f, 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
|
||||
0xf523f357, 0xa6327623,
|
||||
0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, 0xde966292,
|
||||
0x81b949d0, 0x4c50901b,
|
||||
0x71c65614, 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00,
|
||||
0xbb25bfe2, 0x35bdd2f6,
|
||||
0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 0x53113ec0, 0x1640e3d3, 0x38abbd60,
|
||||
0x2547adf0, 0xba38209c,
|
||||
0xf746ce76, 0x77afa1c5, 0x20756060, 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e,
|
||||
0x1948c25c, 0x02fb8a8c,
|
||||
0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, 0xb74e6132,
|
||||
0xce77e25b, 0x578fdfe3,
|
||||
0x3ac372e6};
|
||||
|
||||
// bcrypt IV: "OrpheanBeholderScryDoubt"
|
||||
private static final int[] bf_crypt_ciphertext =
|
||||
{0x4f727068, 0x65616e42, 0x65686f6c, 0x64657253, 0x63727944, 0x6f756274};
|
||||
|
||||
// Table for Base64 encoding
|
||||
private static final char[] base64_code =
|
||||
{'.', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
|
||||
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c',
|
||||
'd', 'e', 'f', 'g',
|
||||
'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
|
||||
'y', 'z', '0', '1',
|
||||
'2', '3', '4', '5', '6', '7', '8', '9'};
|
||||
|
||||
// Table for Base64 decoding
|
||||
private static final byte[] index_64 =
|
||||
{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1,
|
||||
0, 1, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, -1, -1, -1, -1, -1, -1, -1, 2, 3, 4, 5, 6,
|
||||
7, 8, 9, 10, 11,
|
||||
12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, -1, -1, -1, -1, -1, -1,
|
||||
28, 29, 30, 31, 32,
|
||||
33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, -1,
|
||||
-1, -1, -1, -1};
|
||||
static final int MIN_LOG_ROUNDS = 4;
|
||||
static final int MAX_LOG_ROUNDS = 31;
|
||||
|
||||
// Expanded Blowfish key
|
||||
private int[] pbox;
|
||||
|
||||
private int[] sbox;
|
||||
|
||||
/**
|
||||
* Encode a byte array using bcrypt's slightly-modified base64 encoding scheme. Note
|
||||
* that this is <strong>not</strong> compatible with the standard MIME-base64
|
||||
* encoding.
|
||||
*
|
||||
* @param d the byte array to encode
|
||||
* @param len the number of bytes to encode
|
||||
* @param rs the destination buffer for the base64-encoded string
|
||||
* @exception IllegalArgumentException if the length is invalid
|
||||
*/
|
||||
static void encode_base64(byte[] d, int len, StringBuilder rs) throws IllegalArgumentException {
|
||||
int off = 0;
|
||||
int c1;
|
||||
int c2;
|
||||
|
||||
if (len <= 0 || len > d.length) {
|
||||
throw new IllegalArgumentException("Invalid len");
|
||||
}
|
||||
|
||||
while (off < len) {
|
||||
c1 = d[off++] & 0xff;
|
||||
rs.append(base64_code[(c1 >> 2) & 0x3f]);
|
||||
c1 = (c1 & 0x03) << 4;
|
||||
if (off >= len) {
|
||||
rs.append(base64_code[c1 & 0x3f]);
|
||||
break;
|
||||
}
|
||||
c2 = d[off++] & 0xff;
|
||||
c1 |= (c2 >> 4) & 0x0f;
|
||||
rs.append(base64_code[c1 & 0x3f]);
|
||||
c1 = (c2 & 0x0f) << 2;
|
||||
if (off >= len) {
|
||||
rs.append(base64_code[c1 & 0x3f]);
|
||||
break;
|
||||
}
|
||||
c2 = d[off++] & 0xff;
|
||||
c1 |= (c2 >> 6) & 0x03;
|
||||
rs.append(base64_code[c1 & 0x3f]);
|
||||
rs.append(base64_code[c2 & 0x3f]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up the 3 bits base64-encoded by the specified character, range-checking againt
|
||||
* conversion table.
|
||||
*
|
||||
* @param x the base64-encoded value
|
||||
* @return the decoded value of x
|
||||
*/
|
||||
private static byte char64(char x) {
|
||||
if (x < 0 || x >= index_64.length) {
|
||||
return -1;
|
||||
}
|
||||
return index_64[x];
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a string encoded using bcrypt's base64 scheme to a byte array. Note that
|
||||
* this is *not* compatible with the standard MIME-base64 encoding.
|
||||
*
|
||||
* @param s the string to decode
|
||||
* @param maxolen the maximum number of bytes to decode
|
||||
* @return an array containing the decoded bytes
|
||||
* @throws IllegalArgumentException if maxolen is invalid
|
||||
*/
|
||||
static byte[] decode_base64(String s, int maxolen) throws IllegalArgumentException {
|
||||
StringBuilder rs = new StringBuilder();
|
||||
int off = 0;
|
||||
int slen = s.length();
|
||||
int olen = 0;
|
||||
byte[] ret;
|
||||
byte c1;
|
||||
byte c2;
|
||||
byte c3;
|
||||
byte c4;
|
||||
byte o;
|
||||
|
||||
if (maxolen <= 0) {
|
||||
throw new IllegalArgumentException("Invalid maxolen");
|
||||
}
|
||||
|
||||
while (off < slen - 1 && olen < maxolen) {
|
||||
c1 = char64(s.charAt(off++));
|
||||
c2 = char64(s.charAt(off++));
|
||||
if (c1 == -1 || c2 == -1) {
|
||||
break;
|
||||
}
|
||||
o = (byte) (c1 << 2);
|
||||
o |= (c2 & 0x30) >> 4;
|
||||
rs.append((char) o);
|
||||
if (++olen >= maxolen || off >= slen) {
|
||||
break;
|
||||
}
|
||||
c3 = char64(s.charAt(off++));
|
||||
if (c3 == -1) {
|
||||
break;
|
||||
}
|
||||
o = (byte) ((c2 & 0x0f) << 4);
|
||||
o |= (c3 & 0x3c) >> 2;
|
||||
rs.append((char) o);
|
||||
if (++olen >= maxolen || off >= slen) {
|
||||
break;
|
||||
}
|
||||
c4 = char64(s.charAt(off++));
|
||||
o = (byte) ((c3 & 0x03) << 6);
|
||||
o |= c4;
|
||||
rs.append((char) o);
|
||||
++olen;
|
||||
}
|
||||
|
||||
ret = new byte[olen];
|
||||
for (off = 0; off < olen; off++) {
|
||||
ret[off] = (byte) rs.charAt(off);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Blowfish encipher a single 64-bit block encoded as two 32-bit halves.
|
||||
*
|
||||
* @param lr an array containing the two 32-bit half blocks
|
||||
* @param off the position in the array of the blocks
|
||||
*/
|
||||
private void encipher(int[] lr, int off) {
|
||||
int i;
|
||||
int n;
|
||||
int l = lr[off];
|
||||
int r = lr[off + 1];
|
||||
|
||||
l ^= this.pbox[0];
|
||||
for (i = 0; i <= BLOWFISH_NUM_ROUNDS - 2;) {
|
||||
// Feistel substitution on left word
|
||||
n = this.sbox[(l >> 24) & 0xff];
|
||||
n += this.sbox[0x100 | ((l >> 16) & 0xff)];
|
||||
n ^= this.sbox[0x200 | ((l >> 8) & 0xff)];
|
||||
n += this.sbox[0x300 | (l & 0xff)];
|
||||
r ^= n ^ this.pbox[++i];
|
||||
|
||||
// Feistel substitution on right word
|
||||
n = this.sbox[(r >> 24) & 0xff];
|
||||
n += this.sbox[0x100 | ((r >> 16) & 0xff)];
|
||||
n ^= this.sbox[0x200 | ((r >> 8) & 0xff)];
|
||||
n += this.sbox[0x300 | (r & 0xff)];
|
||||
l ^= n ^ this.pbox[++i];
|
||||
}
|
||||
lr[off] = r ^ this.pbox[BLOWFISH_NUM_ROUNDS + 1];
|
||||
lr[off + 1] = l;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cycically extract a word of key material.
|
||||
*
|
||||
* @param data the string to extract the data from
|
||||
* @param offp a "pointer" (as a one-entry array) to the current offset into data
|
||||
* @param signp a "pointer" (as a one-entry array) to the cumulative flag for
|
||||
* non-benign sign extension
|
||||
* @return correct and buggy next word of material from data as int[2]
|
||||
*/
|
||||
private static int[] streamtowords(byte[] data, int[] offp, int[] signp) {
|
||||
int i;
|
||||
int[] words = { 0, 0 };
|
||||
int off = offp[0];
|
||||
int sign = signp[0];
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
words[0] = (words[0] << 8) | (data[off] & 0xff);
|
||||
words[1] = (words[1] << 8) | data[off]; // sign extension bug
|
||||
if (i > 0) {
|
||||
sign |= words[1] & 0x80;
|
||||
}
|
||||
off = (off + 1) % data.length;
|
||||
}
|
||||
|
||||
offp[0] = off;
|
||||
signp[0] = sign;
|
||||
return words;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cycically extract a word of key material.
|
||||
*
|
||||
* @param data the string to extract the data from
|
||||
* @param offp a "pointer" (as a one-entry array) to the current offset into data
|
||||
* @return the next word of material from data
|
||||
*/
|
||||
private static int streamtoword(byte[] data, int[] offp) {
|
||||
int[] signp = { 0 };
|
||||
return streamtowords(data, offp, signp)[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Cycically extract a word of key material, with sign-extension bug.
|
||||
*
|
||||
* @param data the string to extract the data from
|
||||
* @param offp a "pointer" (as a one-entry array) to the current offset into data
|
||||
* @return the next word of material from data
|
||||
*/
|
||||
private static int streamtoword_bug(byte[] data, int[] offp) {
|
||||
int[] signp = { 0 };
|
||||
return streamtowords(data, offp, signp)[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the Blowfish key schedule.
|
||||
*/
|
||||
private void init_key() {
|
||||
this.pbox = P_orig.clone();
|
||||
this.sbox = S_orig.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Key the Blowfish cipher.
|
||||
*
|
||||
* @param key an array containing the key
|
||||
* @param signExtBug true to implement the 2x bug
|
||||
* @param safety bit 16 is set when the safety measure is requested
|
||||
*/
|
||||
private void key(byte[] key, boolean signExtBug, int safety) {
|
||||
int i;
|
||||
int[] koffp = { 0 };
|
||||
int[] lr = { 0, 0 };
|
||||
int plen = this.pbox.length;
|
||||
int slen = this.sbox.length;
|
||||
|
||||
for (i = 0; i < plen; i++) {
|
||||
if (!signExtBug) {
|
||||
this.pbox[i] = this.pbox[i] ^ streamtoword(key, koffp);
|
||||
} else {
|
||||
this.pbox[i] = this.pbox[i] ^ streamtoword_bug(key, koffp);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < plen; i += 2) {
|
||||
encipher(lr, 0);
|
||||
this.pbox[i] = lr[0];
|
||||
this.pbox[i + 1] = lr[1];
|
||||
}
|
||||
|
||||
for (i = 0; i < slen; i += 2) {
|
||||
encipher(lr, 0);
|
||||
this.sbox[i] = lr[0];
|
||||
this.sbox[i + 1] = lr[1];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the "enhanced key schedule" step described by Provos and Mazieres in "A
|
||||
* Future-Adaptable Password Scheme" https://www.openbsd.org/papers/bcrypt-paper.ps.
|
||||
*
|
||||
* @param data salt information
|
||||
* @param key password information
|
||||
* @param signExtBug true to implement the 2x bug
|
||||
* @param safety bit 16 is set when the safety measure is requested
|
||||
*/
|
||||
private void ekskey(byte[] data, byte[] key, boolean signExtBug, int safety) {
|
||||
int i;
|
||||
int[] koffp = { 0 };
|
||||
int[] doffp = { 0 };
|
||||
int[] lr = { 0, 0 };
|
||||
int plen = this.pbox.length;
|
||||
int slen = this.sbox.length;
|
||||
int[] signp = { 0 }; // non-benign sign-extension flag
|
||||
int diff = 0; // zero iff correct and buggy are same
|
||||
|
||||
for (i = 0; i < plen; i++) {
|
||||
int[] words = streamtowords(key, koffp, signp);
|
||||
diff |= words[0] ^ words[1];
|
||||
this.pbox[i] = this.pbox[i] ^ words[signExtBug ? 1 : 0];
|
||||
}
|
||||
|
||||
int sign = signp[0];
|
||||
|
||||
/*
|
||||
* At this point, "diff" is zero iff the correct and buggy algorithms produced
|
||||
* exactly the same result. If so and if "sign" is non-zero, which indicates that
|
||||
* there was a non-benign sign extension, this means that we have a collision
|
||||
* between the correctly computed hash for this password and a set of passwords
|
||||
* that could be supplied to the buggy algorithm. Our safety measure is meant to
|
||||
* protect from such many-buggy to one-correct collisions, by deviating from the
|
||||
* correct algorithm in such cases. Let's check for this.
|
||||
*/
|
||||
diff |= diff >> 16; /* still zero iff exact match */
|
||||
diff &= 0xffff; /* ditto */
|
||||
diff += 0xffff; /* bit 16 set iff "diff" was non-zero (on non-match) */
|
||||
sign <<= 9; /* move the non-benign sign extension flag to bit 16 */
|
||||
sign &= ~diff & safety; /* action needed? */
|
||||
|
||||
/*
|
||||
* If we have determined that we need to deviate from the correct algorithm, flip
|
||||
* bit 16 in initial expanded key. (The choice of 16 is arbitrary, but let's stick
|
||||
* to it now. It came out of the approach we used above, and it's not any worse
|
||||
* than any other choice we could make.)
|
||||
*
|
||||
* It is crucial that we don't do the same to the expanded key used in the main
|
||||
* Eksblowfish loop. By doing it to only one of these two, we deviate from a state
|
||||
* that could be directly specified by a password to the buggy algorithm (and to
|
||||
* the fully correct one as well, but that's a side-effect).
|
||||
*/
|
||||
this.pbox[0] ^= sign;
|
||||
|
||||
for (i = 0; i < plen; i += 2) {
|
||||
lr[0] ^= streamtoword(data, doffp);
|
||||
lr[1] ^= streamtoword(data, doffp);
|
||||
encipher(lr, 0);
|
||||
this.pbox[i] = lr[0];
|
||||
this.pbox[i + 1] = lr[1];
|
||||
}
|
||||
|
||||
for (i = 0; i < slen; i += 2) {
|
||||
lr[0] ^= streamtoword(data, doffp);
|
||||
lr[1] ^= streamtoword(data, doffp);
|
||||
encipher(lr, 0);
|
||||
this.sbox[i] = lr[0];
|
||||
this.sbox[i + 1] = lr[1];
|
||||
}
|
||||
}
|
||||
|
||||
static long roundsForLogRounds(int logRounds) {
|
||||
if (logRounds < 4 || logRounds > 31) {
|
||||
throw new IllegalArgumentException("Bad number of rounds");
|
||||
}
|
||||
return 1L << logRounds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the central password hashing step in the bcrypt scheme.
|
||||
*
|
||||
* @param password the password to hash
|
||||
* @param salt the binary salt to hash with the password
|
||||
* @param logRounds the binary logarithm of the number of rounds of hashing to apply
|
||||
* @param signExtBug true to implement the 2x bug
|
||||
* @param safety bit 16 is set when the safety measure is requested
|
||||
* @return an array containing the binary hashed password
|
||||
*/
|
||||
private byte[] crypt_raw(byte[] password, byte[] salt, int logRounds, boolean signExtBug,
|
||||
int safety) {
|
||||
int rounds;
|
||||
int i;
|
||||
int j;
|
||||
int[] cdata = bf_crypt_ciphertext.clone();
|
||||
int clen = cdata.length;
|
||||
byte[] ret;
|
||||
|
||||
if (logRounds < 4 || logRounds > 31) {
|
||||
throw new IllegalArgumentException("Bad number of rounds");
|
||||
}
|
||||
rounds = 1 << logRounds;
|
||||
if (salt.length != BCRYPT_SALT_LEN) {
|
||||
throw new IllegalArgumentException("Bad salt length");
|
||||
}
|
||||
|
||||
init_key();
|
||||
ekskey(salt, password, signExtBug, safety);
|
||||
for (i = 0; i < rounds; i++) {
|
||||
key(password, signExtBug, safety);
|
||||
key(salt, false, safety);
|
||||
}
|
||||
|
||||
for (i = 0; i < 64; i++) {
|
||||
for (j = 0; j < (clen >> 1); j++) {
|
||||
encipher(cdata, j << 1);
|
||||
}
|
||||
}
|
||||
|
||||
ret = new byte[clen * 4];
|
||||
for (i = 0, j = 0; i < clen; i++) {
|
||||
ret[j++] = (byte) ((cdata[i] >> 24) & 0xff);
|
||||
ret[j++] = (byte) ((cdata[i] >> 16) & 0xff);
|
||||
ret[j++] = (byte) ((cdata[i] >> 8) & 0xff);
|
||||
ret[j++] = (byte) (cdata[i] & 0xff);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash a password using the OpenBSD bcrypt scheme.
|
||||
*
|
||||
* @param password the password to hash
|
||||
* @param salt the salt to hash with (perhaps generated using BCrypt.gensalt)
|
||||
* @return the hashed password
|
||||
*/
|
||||
public static String hashpw(String password, String salt) {
|
||||
byte[] passwordb;
|
||||
|
||||
passwordb = password.getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
return hashpw(passwordb, salt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash a password using the OpenBSD bcrypt scheme.
|
||||
*
|
||||
* @param passwordb the password to hash, as a byte array
|
||||
* @param salt the salt to hash with (perhaps generated using BCrypt.gensalt)
|
||||
* @return the hashed password
|
||||
*/
|
||||
public static String hashpw(byte[] passwordb, String salt) {
|
||||
BCrypt B;
|
||||
String real_salt;
|
||||
byte[] saltb;
|
||||
byte[] hashed;
|
||||
char minor = (char) 0;
|
||||
int rounds;
|
||||
int off;
|
||||
StringBuilder rs = new StringBuilder();
|
||||
|
||||
if (salt == null) {
|
||||
throw new IllegalArgumentException("salt cannot be null");
|
||||
}
|
||||
|
||||
int saltLength = salt.length();
|
||||
|
||||
if (saltLength < 28) {
|
||||
throw new IllegalArgumentException("Invalid salt");
|
||||
}
|
||||
|
||||
if (salt.charAt(0) != '$' || salt.charAt(1) != '2') {
|
||||
throw new IllegalArgumentException("Invalid salt version");
|
||||
}
|
||||
if (salt.charAt(2) == '$') {
|
||||
off = 3;
|
||||
} else {
|
||||
minor = salt.charAt(2);
|
||||
if ((minor != 'a' && minor != 'x' && minor != 'y' && minor != 'b')
|
||||
|| salt.charAt(3) != '$') {
|
||||
throw new IllegalArgumentException("Invalid salt revision");
|
||||
}
|
||||
off = 4;
|
||||
}
|
||||
|
||||
// Extract number of rounds
|
||||
if (salt.charAt(off + 2) > '$') {
|
||||
throw new IllegalArgumentException("Missing salt rounds");
|
||||
}
|
||||
|
||||
if (off == 4 && saltLength < 29) {
|
||||
throw new IllegalArgumentException("Invalid salt");
|
||||
}
|
||||
rounds = Integer.parseInt(salt.substring(off, off + 2));
|
||||
|
||||
real_salt = salt.substring(off + 3, off + 25);
|
||||
saltb = decode_base64(real_salt, BCRYPT_SALT_LEN);
|
||||
|
||||
if (minor >= 'a') {
|
||||
passwordb = Arrays.copyOf(passwordb, passwordb.length + 1);
|
||||
}
|
||||
|
||||
B = new BCrypt();
|
||||
hashed = B.crypt_raw(passwordb, saltb, rounds, minor == 'x', minor == 'a' ? 0x10000 : 0);
|
||||
|
||||
rs.append("$2");
|
||||
if (minor >= 'a') {
|
||||
rs.append(minor);
|
||||
}
|
||||
rs.append("$");
|
||||
if (rounds < 10) {
|
||||
rs.append("0");
|
||||
}
|
||||
rs.append(rounds);
|
||||
rs.append("$");
|
||||
encode_base64(saltb, saltb.length, rs);
|
||||
encode_base64(hashed, bf_crypt_ciphertext.length * 4 - 1, rs);
|
||||
return rs.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a salt for use with the BCrypt.hashpw() method.
|
||||
*
|
||||
* @param prefix the prefix value (default $2a)
|
||||
* @param logRounds the log2 of the number of rounds of hashing to apply - the work
|
||||
* factor therefore increases as 2**logRounds
|
||||
* @param random an instance of SecureRandom to use
|
||||
* @return an encoded salt value
|
||||
* @exception IllegalArgumentException if prefix or logRounds is invalid
|
||||
*/
|
||||
public static String gensalt(String prefix, int logRounds, SecureRandom random)
|
||||
throws IllegalArgumentException {
|
||||
StringBuilder rs = new StringBuilder();
|
||||
byte[] rnd = new byte[BCRYPT_SALT_LEN];
|
||||
|
||||
if (!prefix.startsWith("$2")
|
||||
|| (prefix.charAt(2) != 'a' && prefix.charAt(2) != 'y' && prefix.charAt(2) != 'b')) {
|
||||
throw new IllegalArgumentException("Invalid prefix");
|
||||
}
|
||||
if (logRounds < 4 || logRounds > 31) {
|
||||
throw new IllegalArgumentException("Invalid logRounds");
|
||||
}
|
||||
|
||||
random.nextBytes(rnd);
|
||||
|
||||
rs.append("$2");
|
||||
rs.append(prefix.charAt(2));
|
||||
rs.append("$");
|
||||
if (logRounds < 10) {
|
||||
rs.append("0");
|
||||
}
|
||||
rs.append(logRounds);
|
||||
rs.append("$");
|
||||
encode_base64(rnd, rnd.length, rs);
|
||||
return rs.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a salt for use with the BCrypt.hashpw() method.
|
||||
*
|
||||
* @param prefix the prefix value (default $2a)
|
||||
* @param logRounds the log2 of the number of rounds of hashing to apply - the work
|
||||
* factor therefore increases as 2**logRounds.
|
||||
* @return an encoded salt value
|
||||
* @exception IllegalArgumentException if prefix or logRounds is invalid
|
||||
*/
|
||||
public static String gensalt(String prefix, int logRounds) throws IllegalArgumentException {
|
||||
return gensalt(prefix, logRounds, new SecureRandom());
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a salt for use with the BCrypt.hashpw() method。
|
||||
*
|
||||
* @param logRounds the log2 of the number of rounds of hashing to apply - the work
|
||||
* factor therefore increases as 2**logRounds.
|
||||
* @param random an instance of SecureRandom to use
|
||||
* @return an encoded salt value
|
||||
* @exception IllegalArgumentException if logRounds is invalid
|
||||
*/
|
||||
public static String gensalt(int logRounds, SecureRandom random)
|
||||
throws IllegalArgumentException {
|
||||
return gensalt("$2a", logRounds, random);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a salt for use with the BCrypt.hashpw() method.
|
||||
*
|
||||
* @param logRounds the log2 of the number of rounds of hashing to apply - the work
|
||||
* factor therefore increases as 2**logRounds.
|
||||
* @return an encoded salt value
|
||||
* @exception IllegalArgumentException if logRounds is invalid
|
||||
*/
|
||||
public static String gensalt(int logRounds) throws IllegalArgumentException {
|
||||
return gensalt(logRounds, new SecureRandom());
|
||||
}
|
||||
|
||||
public static String gensalt(String prefix) {
|
||||
return gensalt(prefix, GENSALT_DEFAULT_LOG2_ROUNDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a salt for use with the BCrypt.hashpw() method, selecting a reasonable
|
||||
* default for the number of hashing rounds to apply.
|
||||
*
|
||||
* @return an encoded salt value
|
||||
*/
|
||||
public static String gensalt() {
|
||||
return gensalt(GENSALT_DEFAULT_LOG2_ROUNDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that a plaintext password matches a previously hashed one.
|
||||
*
|
||||
* @param plaintext the plaintext password to verify
|
||||
* @param hashed the previously-hashed password
|
||||
* @return true if the passwords match, false otherwise
|
||||
*/
|
||||
public static boolean checkpw(String plaintext, String hashed) {
|
||||
return equalsNoEarlyReturn(hashed, hashpw(plaintext, hashed));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that a password (as a byte array) matches a previously hashed one.
|
||||
*
|
||||
* @param passwordb the password to verify, as a byte array
|
||||
* @param hashed the previously-hashed password
|
||||
* @return true if the passwords match, false otherwise
|
||||
* @since 5.3
|
||||
*/
|
||||
public static boolean checkpw(byte[] passwordb, String hashed) {
|
||||
return equalsNoEarlyReturn(hashed, hashpw(passwordb, hashed));
|
||||
}
|
||||
|
||||
static boolean equalsNoEarlyReturn(String a, String b) {
|
||||
return MessageDigest.isEqual(a.getBytes(StandardCharsets.UTF_8),
|
||||
b.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
|
@ -1,10 +1,5 @@
|
|||
package run.halo.app.utils;
|
||||
|
||||
import static cn.hutool.core.date.DatePattern.NORM_DATETIME_PATTERN;
|
||||
import static cn.hutool.core.date.DatePattern.NORM_DATE_PATTERN;
|
||||
import static cn.hutool.core.date.DatePattern.PURE_DATETIME_PATTERN;
|
||||
import static cn.hutool.core.date.DatePattern.PURE_DATE_PATTERN;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
|
@ -15,14 +10,34 @@ import java.time.format.DateTimeFormatter;
|
|||
import java.time.format.DateTimeFormatterBuilder;
|
||||
import java.time.temporal.ChronoField;
|
||||
import java.time.temporal.Temporal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 日期工具
|
||||
*
|
||||
* @author LeiXinXin
|
||||
* @author guqing
|
||||
* @date 2019/12/10
|
||||
*/
|
||||
public class DateTimeUtils {
|
||||
|
||||
/**
|
||||
* 标准日期时间格式,精确到秒:yyyy-MM-dd HH:mm:ss
|
||||
*/
|
||||
public static final String NORM_DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
|
||||
/**
|
||||
* 标准日期格式:yyyy-MM-dd
|
||||
*/
|
||||
public static final String NORM_DATE_PATTERN = "yyyy-MM-dd";
|
||||
/**
|
||||
* 标准日期格式:yyyyMMddHHmmss
|
||||
*/
|
||||
public static final String PURE_DATETIME_PATTERN = "yyyyMMddHHmmss";
|
||||
/**
|
||||
* 标准日期格式:yyyyMMdd
|
||||
*/
|
||||
public static final String PURE_DATE_PATTERN = "yyyyMMdd";
|
||||
|
||||
/**
|
||||
* 标准日期格式 {@link DateTimeFormatter}:yyyyMMddHHmmssSSS
|
||||
*/
|
||||
|
@ -570,4 +585,14 @@ public class DateTimeUtils {
|
|||
public static boolean isLessThanOrEqual(Temporal startInclusive, Temporal endInclusive) {
|
||||
return Duration.between(startInclusive, endInclusive).toNanos() <= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* convert date to LocalDateTime
|
||||
*
|
||||
* @param date date
|
||||
* @return LocalDateTime by default zone
|
||||
*/
|
||||
public static LocalDateTime toLocalDateTime(Date date) {
|
||||
return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
package run.halo.app.utils;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.util.Assert;
|
||||
|
@ -10,9 +13,11 @@ import org.springframework.util.Assert;
|
|||
* Date utilities.
|
||||
*
|
||||
* @author johnniang
|
||||
* @author guqing
|
||||
* @date 3/18/19
|
||||
*/
|
||||
public class DateUtils {
|
||||
private static final int FIRST_DAY_OF_WEEK = Calendar.MONDAY;
|
||||
|
||||
private DateUtils() {
|
||||
}
|
||||
|
@ -88,4 +93,119 @@ public class DateUtils {
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a string representing a date by trying a variety of different patterns,
|
||||
* using the default date format symbols for the default locale.
|
||||
*
|
||||
* @param str date string
|
||||
* @return the parsed date
|
||||
*/
|
||||
public static Date parseDate(String str) {
|
||||
return parseDate(str, Locale.getDefault());
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a string representing a date by trying a variety of different patterns,
|
||||
* using the default date format symbols for the given locale.
|
||||
*
|
||||
* @param str date string
|
||||
* @param locale locale the locale whose date format symbols should be used.
|
||||
* If null, the system locale is used (as per parseDate(String, String...)).
|
||||
* @return the parsed date object
|
||||
*/
|
||||
public static Date parseDate(String str, Locale locale) {
|
||||
String[] patterns = new String[] {"yyyy-MM-dd HH:mm:ss",
|
||||
"yyyy/MM/dd HH:mm:ss",
|
||||
"yyyy.MM.dd HH:mm:ss",
|
||||
"yyyy年MM月dd日 HH时mm分ss秒",
|
||||
"yyyy-MM-dd",
|
||||
"yyyy/MM/dd",
|
||||
"yyyy.MM.dd",
|
||||
"HH:mm:ss",
|
||||
"HH时mm分ss秒",
|
||||
"yyyy-MM-dd HH:mm",
|
||||
"yyyy-MM-dd HH:mm:ss.SSS",
|
||||
"yyyyMMddHHmmss",
|
||||
"yyyyMMddHHmmssSSS",
|
||||
"yyyyMMdd",
|
||||
"yyyy-MM-dd'T'HH:mm:ss",
|
||||
|
||||
"yyyy-MM-dd'T'HH:mm:ss'Z'",
|
||||
"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",
|
||||
|
||||
"yyyy-MM-dd'T'HH:mm:ssZ",
|
||||
"yyyy-MM-dd'T'HH:mm:ss.SSSZ",
|
||||
};
|
||||
try {
|
||||
return org.apache.commons.lang3.time.DateUtils.parseDate(str, locale, patterns);
|
||||
} catch (ParseException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the year in the date.
|
||||
*
|
||||
* @param date date
|
||||
* @return year in the date
|
||||
*/
|
||||
public static int year(Date date) {
|
||||
return getField(date, Calendar.YEAR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the month, counting from 0.
|
||||
*
|
||||
* @return month for the given date.
|
||||
*/
|
||||
public static int month(Date date) {
|
||||
return getField(date, Calendar.MONTH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the specified date is the day of the month in which the date is located.
|
||||
*
|
||||
* @param date date
|
||||
* @return day of the month in given date
|
||||
*/
|
||||
public static int dayOfMonth(Date date) {
|
||||
return getField(date, Calendar.DAY_OF_MONTH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the specified part of the given date.
|
||||
*
|
||||
* @return the specified part
|
||||
*/
|
||||
public static int getField(Date date, int field) {
|
||||
return toCalendar(date).get(field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert date to calendar.
|
||||
* default locale is sets by the Java Virtual Machine during startup
|
||||
* based on the host environment.
|
||||
*
|
||||
* @param date date
|
||||
* @return calendar object using the default locale for the given date
|
||||
*/
|
||||
public static Calendar toCalendar(Date date) {
|
||||
return toCalendar(date, Locale.getDefault(Locale.Category.FORMAT));
|
||||
}
|
||||
|
||||
public static Calendar toCalendar(Date date, Locale locale) {
|
||||
return toCalendar(date, TimeZone.getDefault(), locale);
|
||||
}
|
||||
|
||||
public static Calendar toCalendar(Date date, TimeZone zone, Locale locale) {
|
||||
if (null == locale) {
|
||||
locale = Locale.getDefault(Locale.Category.FORMAT);
|
||||
}
|
||||
final Calendar cal =
|
||||
(null != zone) ? Calendar.getInstance(zone, locale) : Calendar.getInstance(locale);
|
||||
cal.setFirstDayOfWeek(FIRST_DAY_OF_WEEK);
|
||||
cal.setTime(date);
|
||||
return cal;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
package run.halo.app.utils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.DirectoryNotEmptyException;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
|
@ -512,4 +515,28 @@ public class FileUtils {
|
|||
return tempDirectory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an InputStream to a String.
|
||||
*
|
||||
* @param inputStream input stream
|
||||
* @return the string content read through the input stream without closing the InputStream
|
||||
*/
|
||||
public static String readString(InputStream inputStream) {
|
||||
return new BufferedReader(
|
||||
new InputStreamReader(inputStream, StandardCharsets.UTF_8))
|
||||
.lines()
|
||||
.collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a String to a file creating the file if it does not exist using the UTF_8 encoding.
|
||||
* NOTE: the parent directories of the file will be created if they do not exist.
|
||||
*
|
||||
* @param file the file to write
|
||||
* @param content the content to write to the file
|
||||
* @throws IOException in case of an I/O error
|
||||
*/
|
||||
public static void writeStringToFile(File file, String content) throws IOException {
|
||||
org.apache.commons.io.FileUtils.writeStringToFile(file, content, StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,15 +2,30 @@ package run.halo.app.utils;
|
|||
|
||||
import static run.halo.app.model.support.HaloConst.FILE_SEPARATOR;
|
||||
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.EncodeHintType;
|
||||
import com.google.zxing.WriterException;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import com.google.zxing.qrcode.QRCodeWriter;
|
||||
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import javax.imageio.ImageIO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import run.halo.app.model.support.HaloConst;
|
||||
|
||||
/**
|
||||
|
@ -18,6 +33,7 @@ import run.halo.app.model.support.HaloConst;
|
|||
*
|
||||
* @author ryanwang
|
||||
* @author johnniang
|
||||
* @author guqing
|
||||
* @date 2017-12-22
|
||||
*/
|
||||
@Slf4j
|
||||
|
@ -235,7 +251,7 @@ public class HaloUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* Normalize url
|
||||
* Normalize url.
|
||||
*
|
||||
* @param originalUrl original url
|
||||
* @return normalized url.
|
||||
|
@ -250,7 +266,40 @@ public class HaloUtils {
|
|||
return originalUrl;
|
||||
}
|
||||
|
||||
return URLUtil.normalize(originalUrl);
|
||||
final int sepIndex = originalUrl.indexOf("://");
|
||||
String protocol;
|
||||
String body;
|
||||
if (sepIndex > 0) {
|
||||
protocol = StringUtils.substring(originalUrl, 0, sepIndex + 3);
|
||||
body = StringUtils.substring(originalUrl, sepIndex + 3, originalUrl.length());
|
||||
} else {
|
||||
protocol = "http://";
|
||||
body = originalUrl;
|
||||
}
|
||||
|
||||
final int paramsSepIndex = StringUtils.indexOf(body, '?');
|
||||
String params = null;
|
||||
if (paramsSepIndex > 0) {
|
||||
params = StringUtils.substring(body, paramsSepIndex, body.length());
|
||||
body = StringUtils.substring(body, 0, paramsSepIndex);
|
||||
}
|
||||
|
||||
if (StringUtils.isNotEmpty(body)) {
|
||||
// 去除开头的\或者/
|
||||
body = body.replaceAll("^[\\\\/]+", StringUtils.EMPTY);
|
||||
// 替换多个\或/为单个/
|
||||
body = body.replace("\\", "/").replaceAll("//+", "/");
|
||||
}
|
||||
|
||||
final int pathSepIndex = StringUtils.indexOf(body, '/');
|
||||
String domain = body;
|
||||
String path = null;
|
||||
if (pathSepIndex > 0) {
|
||||
domain = StringUtils.substring(body, 0, pathSepIndex);
|
||||
path = StringUtils.substring(body, pathSepIndex, body.length());
|
||||
}
|
||||
return protocol + domain + StringUtils.defaultIfEmpty(path, StringUtils.EMPTY)
|
||||
+ StringUtils.defaultIfEmpty(params, StringUtils.EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -269,7 +318,7 @@ public class HaloUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* Clean all html tag
|
||||
* Clean all html tag.
|
||||
*
|
||||
* @param content html document
|
||||
* @return text before cleaned
|
||||
|
@ -280,4 +329,134 @@ public class HaloUtils {
|
|||
}
|
||||
return content.replaceAll(RE_HTML_MARK, StringUtils.EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the collection is empty.
|
||||
*
|
||||
* @param collection collection
|
||||
* @return true if this collection not null and contains elements.
|
||||
*/
|
||||
public static boolean isNotEmpty(Collection<?> collection) {
|
||||
return !CollectionUtils.isEmpty(collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Strips any of a set of characters from the start and end of a String.
|
||||
* This is similar to {@link String#trim()} but allows the characters
|
||||
* to be stripped to be controlled.</p>
|
||||
*
|
||||
* @param str the String to remove characters from, may be null
|
||||
* @param prefixStripChars the characters to remove from start of str, null treated as
|
||||
* whitespace
|
||||
* @param suffixStripChars the characters to remove from end of str, null treated as whitespace
|
||||
* @return the stripped String, {@code null} if null String input
|
||||
*/
|
||||
public static String strip(String str, final String prefixStripChars,
|
||||
final String suffixStripChars) {
|
||||
if (StringUtils.isEmpty(str)) {
|
||||
return str;
|
||||
}
|
||||
str = StringUtils.stripStart(str, prefixStripChars);
|
||||
return StringUtils.stripEnd(str, suffixStripChars);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 分页彩虹算法<br>
|
||||
* 来自:https://github.com/iceroot/iceroot/blob/master/src/main/java/com/icexxx/util/IceUtil.java<br>
|
||||
* 通过传入的信息,生成一个分页列表显示
|
||||
*
|
||||
* @param pageNo 当前页
|
||||
* @param totalPage 总页数
|
||||
* @param displayCount 每屏展示的页数
|
||||
* @return 分页条
|
||||
*/
|
||||
public static int[] rainbow(int pageNo, int totalPage, int displayCount) {
|
||||
boolean isEven = displayCount % 2 == 0;
|
||||
int left = displayCount / 2;
|
||||
int right = displayCount / 2;
|
||||
|
||||
int length = displayCount;
|
||||
if (isEven) {
|
||||
right++;
|
||||
}
|
||||
if (totalPage < displayCount) {
|
||||
length = totalPage;
|
||||
}
|
||||
int[] result = new int[length];
|
||||
if (totalPage >= displayCount) {
|
||||
if (pageNo <= left) {
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[i] = i + 1;
|
||||
}
|
||||
} else if (pageNo > totalPage - right) {
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[i] = i + totalPage - displayCount + 1;
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[i] = i + pageNo - left + (isEven ? 1 : 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[i] = i + 1;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static String simpleUUID() {
|
||||
return UUID.randomUUID().toString().replace("-", "");
|
||||
}
|
||||
|
||||
/**
|
||||
* generate png qrcode to byte array.
|
||||
*
|
||||
* @param content qrcode content
|
||||
* @param width qrcode width
|
||||
* @param height qrcode height
|
||||
* @return QR code byte array in png format
|
||||
* @throws UnsupportedOperationException If the QR code fails to be generated
|
||||
*/
|
||||
public static byte[] generateQrCodeToPng(String content, int width, int height) {
|
||||
Map<EncodeHintType, Object> hints = new HashMap<>(3, 1);
|
||||
hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
|
||||
// 纠错级别
|
||||
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
|
||||
// 空白
|
||||
hints.put(EncodeHintType.MARGIN, 2);
|
||||
|
||||
QRCodeWriter qrCodeWriter = new QRCodeWriter();
|
||||
|
||||
try {
|
||||
BitMatrix byteMatrix =
|
||||
qrCodeWriter.encode(content, BarcodeFormat.QR_CODE, width, height, hints);
|
||||
// Make the BufferedImage that are to hold the QRCode
|
||||
int matrixWidth = byteMatrix.getWidth();
|
||||
int matrixHeight = byteMatrix.getHeight();
|
||||
BufferedImage image =
|
||||
new BufferedImage(matrixWidth, matrixHeight, BufferedImage.TYPE_INT_RGB);
|
||||
image.createGraphics();
|
||||
|
||||
Graphics2D graphics = (Graphics2D) image.getGraphics();
|
||||
graphics.setColor(Color.WHITE);
|
||||
graphics.fillRect(0, 0, matrixWidth, matrixHeight);
|
||||
// Paint and save the image using the ByteMatrix
|
||||
graphics.setColor(Color.BLACK);
|
||||
|
||||
for (int i = 0; i < matrixWidth; i++) {
|
||||
for (int j = 0; j < matrixWidth; j++) {
|
||||
if (byteMatrix.get(i, j)) {
|
||||
graphics.fillRect(i, j, 1, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
ImageIO.write(image, "png", os);
|
||||
return os.toByteArray();
|
||||
} catch (IOException | WriterException e) {
|
||||
throw new UnsupportedOperationException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package run.halo.app.utils;
|
||||
|
||||
import cn.hutool.core.lang.Tuple;
|
||||
import java.net.URI;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStoreException;
|
||||
|
@ -71,15 +70,15 @@ public class HttpClientUtils {
|
|||
final HttpClientBuilder httpClientBuilder) {
|
||||
final String httpProxyEnv = System.getenv("http_proxy");
|
||||
if (StringUtils.isNotBlank(httpProxyEnv)) {
|
||||
final Tuple httpProxy = resolveHttpProxy(httpProxyEnv);
|
||||
final HttpHost httpHost = HttpHost.create(httpProxy.get(0));
|
||||
final String[] httpProxy = resolveHttpProxy(httpProxyEnv);
|
||||
final HttpHost httpHost = HttpHost.create(httpProxy[0]);
|
||||
httpClientBuilder.setProxy(httpHost);
|
||||
if (httpProxy.getMembers().length == 3) {
|
||||
if (httpProxy.length == 3) {
|
||||
//set proxy credentials
|
||||
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
|
||||
credentialsProvider
|
||||
.setCredentials(new AuthScope(httpHost.getHostName(), httpHost.getPort()),
|
||||
new UsernamePasswordCredentials(httpProxy.get(1), httpProxy.get(2)));
|
||||
new UsernamePasswordCredentials(httpProxy[1], httpProxy[2]));
|
||||
httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
|
||||
}
|
||||
}
|
||||
|
@ -91,7 +90,7 @@ public class HttpClientUtils {
|
|||
* @return resolved http proxy values; first is host(@nonNull), second is username(@nullable)
|
||||
* , third is password(@nullable)
|
||||
*/
|
||||
private static Tuple resolveHttpProxy(final String httpProxy) {
|
||||
private static String[] resolveHttpProxy(final String httpProxy) {
|
||||
final URI proxyUri = URI.create(httpProxy);
|
||||
int port = proxyUri.getPort();
|
||||
if (port == -1) {
|
||||
|
@ -115,9 +114,9 @@ public class HttpClientUtils {
|
|||
username = usernamePassword;
|
||||
password = null;
|
||||
}
|
||||
return new Tuple(hostUrl, username, password);
|
||||
return new String[] {hostUrl, username, password};
|
||||
} else {
|
||||
return new Tuple(hostUrl);
|
||||
return new String[] {hostUrl};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package run.halo.app.utils;
|
||||
|
||||
import cn.hutool.extra.servlet.ServletUtil;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Optional;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
|
@ -12,6 +14,7 @@ import org.springframework.web.context.request.ServletRequestAttributes;
|
|||
* Servlet utilities.
|
||||
*
|
||||
* @author johnniang
|
||||
* @author guqing
|
||||
* @date 19-4-21
|
||||
*/
|
||||
public class ServletUtils {
|
||||
|
@ -39,7 +42,7 @@ public class ServletUtils {
|
|||
*/
|
||||
@Nullable
|
||||
public static String getRequestIp() {
|
||||
return getCurrentRequest().map(ServletUtil::getClientIP).orElse(null);
|
||||
return getCurrentRequest().map(ServletUtils::getClientIP).orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -50,8 +53,122 @@ public class ServletUtils {
|
|||
*/
|
||||
@Nullable
|
||||
public static String getHeaderIgnoreCase(String header) {
|
||||
return getCurrentRequest().map(request -> ServletUtil.getHeaderIgnoreCase(request, header))
|
||||
return getCurrentRequest().map(request -> ServletUtils.getHeaderIgnoreCase(request, header))
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 忽略大小写获得请求header中的信息.
|
||||
*
|
||||
* @param request 请求对象{@link HttpServletRequest}
|
||||
* @param nameIgnoreCase 忽略大小写头信息的KEY
|
||||
* @return header值
|
||||
*/
|
||||
public static String getHeaderIgnoreCase(HttpServletRequest request, String nameIgnoreCase) {
|
||||
final Enumeration<String> names = request.getHeaderNames();
|
||||
String name;
|
||||
while (names.hasMoreElements()) {
|
||||
name = names.nextElement();
|
||||
if (name != null && name.equalsIgnoreCase(nameIgnoreCase)) {
|
||||
return request.getHeader(name);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户端IP.
|
||||
*
|
||||
* <p>
|
||||
* 默认检测的Header:
|
||||
*
|
||||
* <pre>
|
||||
* 1、X-Forwarded-For
|
||||
* 2、X-Real-IP
|
||||
* 3、Proxy-Client-IP
|
||||
* 4、WL-Proxy-Client-IP
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* otherHeaderNames参数用于自定义检测的Header<br>
|
||||
* 需要注意的是,使用此方法获取的客户IP地址必须在Http服务器(例如Nginx)中配置头信息,否则容易造成IP伪造。
|
||||
* </p>
|
||||
*
|
||||
* @param request 请求对象{@link HttpServletRequest}
|
||||
* @param otherHeaderNames 其他自定义头文件,通常在Http服务器(例如Nginx)中配置
|
||||
* @see <a href="https://github.com/dromara/hutool/blob/v5-master/hutool-extra/src/main/java/cn/hutool/extra/servlet/ServletUtil.java">查看来源</a>
|
||||
* @return IP地址
|
||||
*/
|
||||
public static String getClientIP(HttpServletRequest request, String... otherHeaderNames) {
|
||||
String[] headers = {"X-Forwarded-For", "X-Real-IP", "Proxy-Client-IP", "WL-Proxy-Client-IP",
|
||||
"HTTP_CLIENT_IP", "HTTP_X_FORWARDED_FOR"};
|
||||
if (ArrayUtils.isNotEmpty(otherHeaderNames)) {
|
||||
headers = ArrayUtils.addAll(headers, otherHeaderNames);
|
||||
}
|
||||
|
||||
return getClientIPByHeader(request, headers);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 检测给定字符串是否为未知,多用于检测HTTP请求相关.
|
||||
*
|
||||
* @param checkString 被检测的字符串
|
||||
* @return 是否未知
|
||||
* @since 1.4.13
|
||||
*/
|
||||
public static boolean isUnknown(String checkString) {
|
||||
return StringUtils.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户端IP.
|
||||
*
|
||||
* <p>
|
||||
* headerNames参数用于自定义检测的Header<br>
|
||||
* 需要注意的是,使用此方法获取的客户IP地址必须在Http服务器(例如Nginx)中配置头信息,否则容易造成IP伪造。
|
||||
* </p>
|
||||
*
|
||||
* @param request 请求对象{@link HttpServletRequest}
|
||||
* @param headerNames 自定义头,通常在Http服务器(例如Nginx)中配置
|
||||
* @see <a href="https://github.com/dromara/hutool/blob/v5-master/hutool-extra/src/main/java/cn/hutool/extra/servlet/ServletUtil.java">查看来源</a>
|
||||
* @return IP地址
|
||||
* @since 1.4.13
|
||||
*/
|
||||
public static String getClientIPByHeader(HttpServletRequest request, String... headerNames) {
|
||||
String ip;
|
||||
for (String header : headerNames) {
|
||||
ip = request.getHeader(header);
|
||||
if (!isUnknown(ip)) {
|
||||
return getMultistageReverseProxyIp(ip);
|
||||
}
|
||||
}
|
||||
|
||||
ip = request.getRemoteAddr();
|
||||
return getMultistageReverseProxyIp(ip);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 从多级反向代理中获得第一个非unknown IP地址.
|
||||
*
|
||||
* @param ip 获得的IP地址
|
||||
* @return 第一个非unknown IP地址
|
||||
* @since 1.4.13
|
||||
*/
|
||||
public static String getMultistageReverseProxyIp(String ip) {
|
||||
// 多级反向代理检测
|
||||
if (ip != null && ip.indexOf(",") > 0) {
|
||||
final String[] ips = ip.trim().split(",");
|
||||
for (String subIp : ips) {
|
||||
if (!isUnknown(subIp)) {
|
||||
ip = subIp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ip;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.validation.ConstraintViolation;
|
||||
import javax.validation.ConstraintViolationException;
|
||||
import javax.validation.Path;
|
||||
|
@ -26,6 +27,15 @@ import org.springframework.validation.FieldError;
|
|||
*/
|
||||
public class ValidationUtils {
|
||||
|
||||
public static final Pattern EMAIL = Pattern.compile(
|
||||
"(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:"
|
||||
+ "[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09"
|
||||
+ "\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9]"
|
||||
+ "(?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}"
|
||||
+ "(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b"
|
||||
+ "\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)])",
|
||||
Pattern.CASE_INSENSITIVE);
|
||||
|
||||
private static volatile Validator VALIDATOR;
|
||||
|
||||
private ValidationUtils() {
|
||||
|
@ -146,4 +156,14 @@ public class ValidationUtils {
|
|||
filedError -> errMap.put(filedError.getField(), filedError.getDefaultMessage()));
|
||||
return errMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证给定的字符串是否为邮箱地址
|
||||
*
|
||||
* @param email 邮箱地址字符串
|
||||
* @return 如果给定字符串是邮箱地址返回 {@code true},否则返回 {@code false}
|
||||
*/
|
||||
public static boolean isEmail(String email) {
|
||||
return EMAIL.matcher(email).matches();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,482 @@
|
|||
package run.halo.app.utils;
|
||||
|
||||
import cn.hutool.crypto.digest.BCrypt;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* BCrypt test.
|
||||
*
|
||||
* @see <a href="https://github.com/spring-projects/spring-security/blob/main/crypto/src/test/java/org/springframework/security/crypto/bcrypt/BCryptTests.java">测试用例参考</a>
|
||||
* @author johnniang
|
||||
* @author guqing
|
||||
* @date 3/28/19
|
||||
*/
|
||||
@Slf4j
|
||||
class BcryptTest {
|
||||
|
||||
private static class TestObject<T> {
|
||||
|
||||
private final T password;
|
||||
|
||||
private final String salt;
|
||||
|
||||
private final String expected;
|
||||
|
||||
private TestObject(T password, String salt, String expected) {
|
||||
this.password = password;
|
||||
this.salt = salt;
|
||||
this.expected = expected;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void print(String s) {
|
||||
// System.out.print(s);
|
||||
}
|
||||
|
||||
private static void println(String s) {
|
||||
// System.out.println(s);
|
||||
}
|
||||
|
||||
private static List<TestObject<String>> testObjectsString;
|
||||
|
||||
private static List<TestObject<byte[]>> testObjectsByteArray;
|
||||
|
||||
@BeforeAll
|
||||
public static void setupTestObjects() {
|
||||
testObjectsString = new ArrayList<>();
|
||||
testObjectsString.add(new TestObject<>("", "$2a$06$DCq7YPn5Rq63x1Lad4cll.",
|
||||
"$2a$06$DCq7YPn5Rq63x1Lad4cll.TV4S6ytwfsfvkgY8jIucDrjc8deX1s."));
|
||||
testObjectsString.add(new TestObject<>("", "$2a$08$HqWuK6/Ng6sg9gQzbLrgb.",
|
||||
"$2a$08$HqWuK6/Ng6sg9gQzbLrgb.Tl.ZHfXLhvt/SgVyWhQqgqcZ7ZuUtye"));
|
||||
testObjectsString.add(new TestObject<>("", "$2a$10$k1wbIrmNyFAPwPVPSVa/ze",
|
||||
"$2a$10$k1wbIrmNyFAPwPVPSVa/zecw2BCEnBwVS2GbrmgzxFUOqW9dk4TCW"));
|
||||
testObjectsString.add(new TestObject<>("", "$2a$12$k42ZFHFWqBp3vWli.nIn8u",
|
||||
"$2a$12$k42ZFHFWqBp3vWli.nIn8uYyIkbvYRvodzbfbK18SSsY.CsIQPlxO"));
|
||||
testObjectsString.add(new TestObject<>("", "$2b$06$8eVN9RiU8Yki430X.wBvN.",
|
||||
"$2b$06$8eVN9RiU8Yki430X.wBvN.LWaqh2962emLVSVXVZIXJvDYLsV0oFu"));
|
||||
testObjectsString.add(new TestObject<>("", "$2b$06$NlgfNgpIc6GlHciCkMEW8u",
|
||||
"$2b$06$NlgfNgpIc6GlHciCkMEW8uKOBsyvAp7QwlHpysOlKdtyEw50WQua2"));
|
||||
testObjectsString.add(new TestObject<>("", "$2y$06$mFDtkz6UN7B3GZ2qi2hhaO",
|
||||
"$2y$06$mFDtkz6UN7B3GZ2qi2hhaO3OFWzNEdcY84ELw6iHCPruuQfSAXBLK"));
|
||||
testObjectsString.add(new TestObject<>("", "$2y$06$88kSqVttBx.e9iXTPCLa5u",
|
||||
"$2y$06$88kSqVttBx.e9iXTPCLa5uFPrVFjfLH4D.KcO6pBiAmvUkvdg0EYy"));
|
||||
testObjectsString.add(new TestObject<>("a", "$2a$06$m0CrhHm10qJ3lXRY.5zDGO",
|
||||
"$2a$06$m0CrhHm10qJ3lXRY.5zDGO3rS2KdeeWLuGmsfGlMfOxih58VYVfxe"));
|
||||
testObjectsString.add(new TestObject<>("a", "$2a$08$cfcvVd2aQ8CMvoMpP2EBfe",
|
||||
"$2a$08$cfcvVd2aQ8CMvoMpP2EBfeodLEkkFJ9umNEfPD18.hUF62qqlC/V."));
|
||||
testObjectsString.add(new TestObject<>("a", "$2a$10$k87L/MF28Q673VKh8/cPi.",
|
||||
"$2a$10$k87L/MF28Q673VKh8/cPi.SUl7MU/rWuSiIDDFayrKk/1tBsSQu4u"));
|
||||
testObjectsString.add(new TestObject<>("a", "$2a$12$8NJH3LsPrANStV6XtBakCe",
|
||||
"$2a$12$8NJH3LsPrANStV6XtBakCez0cKHXVxmvxIlcz785vxAIZrihHZpeS"));
|
||||
testObjectsString.add(new TestObject<>("a", "$2b$06$ehKGYiS4wt2HAr7KQXS5z.",
|
||||
"$2b$06$ehKGYiS4wt2HAr7KQXS5z.OaRjB4jHO7rBHJKlGXbqEH3QVJfO7iO"));
|
||||
testObjectsString.add(new TestObject<>("a", "$2b$06$PWxFFHA3HiCD46TNOZh30e",
|
||||
"$2b$06$PWxFFHA3HiCD46TNOZh30eNto1hg5uM9tHBlI4q/b03SW/gGKUYk6"));
|
||||
testObjectsString.add(new TestObject<>("a", "$2y$06$LUdD6/aD0e/UbnxVAVbvGu",
|
||||
"$2y$06$LUdD6/aD0e/UbnxVAVbvGuUmIoJ3l/OK94ThhadpMWwKC34LrGEey"));
|
||||
testObjectsString.add(new TestObject<>("a", "$2y$06$eqgY.T2yloESMZxgp76deO",
|
||||
"$2y$06$eqgY.T2yloESMZxgp76deOROa7nzXDxbO0k.PJvuClTa.Vu1AuemG"));
|
||||
testObjectsString.add(new TestObject<>("abc", "$2a$06$If6bvum7DFjUnE9p2uDeDu",
|
||||
"$2a$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i"));
|
||||
testObjectsString.add(new TestObject<>("abc", "$2a$08$Ro0CUfOqk6cXEKf3dyaM7O",
|
||||
"$2a$08$Ro0CUfOqk6cXEKf3dyaM7OhSCvnwM9s4wIX9JeLapehKK5YdLxKcm"));
|
||||
testObjectsString.add(new TestObject<>("abc", "$2a$10$WvvTPHKwdBJ3uk0Z37EMR.",
|
||||
"$2a$10$WvvTPHKwdBJ3uk0Z37EMR.hLA2W6N9AEBhEgrAOljy2Ae5MtaSIUi"));
|
||||
testObjectsString.add(new TestObject<>("abc", "$2a$12$EXRkfkdmXn2gzds2SSitu.",
|
||||
"$2a$12$EXRkfkdmXn2gzds2SSitu.MW9.gAVqa9eLS1//RYtYCmB1eLHg.9q"));
|
||||
testObjectsString.add(new TestObject<>("abc", "$2b$06$5FyQoicpbox1xSHFfhhdXu",
|
||||
"$2b$06$5FyQoicpbox1xSHFfhhdXuR2oxLpO1rYsQh5RTkI/9.RIjtoF0/ta"));
|
||||
testObjectsString.add(new TestObject<>("abc", "$2b$06$1kJyuho8MCVP3HHsjnRMkO",
|
||||
"$2b$06$1kJyuho8MCVP3HHsjnRMkO1nvCOaKTqLnjG2TX1lyMFbXH/aOkgc."));
|
||||
testObjectsString.add(new TestObject<>("abc", "$2y$06$ACfku9dT6.H8VjdKb8nhlu",
|
||||
"$2y$06$ACfku9dT6.H8VjdKb8nhluaoBmhJyK7GfoNScEfOfrJffUxoUeCjK"));
|
||||
testObjectsString.add(new TestObject<>("abc", "$2y$06$9JujYcoWPmifvFA3RUP90e",
|
||||
"$2y$06$9JujYcoWPmifvFA3RUP90e5rSEHAb5Ye6iv3.G9ikiHNv5cxjNEse"));
|
||||
testObjectsString.add(
|
||||
new TestObject<>("abcdefghijklmnopqrstuvwxyz", "$2a$06$.rCVZVOThsIa97pEDOxvGu",
|
||||
"$2a$06$.rCVZVOThsIa97pEDOxvGuRRgzG64bvtJ0938xuqzv18d3ZpQhstC"));
|
||||
testObjectsString.add(
|
||||
new TestObject<>("abcdefghijklmnopqrstuvwxyz", "$2a$08$aTsUwsyowQuzRrDqFflhge",
|
||||
"$2a$08$aTsUwsyowQuzRrDqFflhgekJ8d9/7Z3GV3UcgvzQW3J5zMyrTvlz."));
|
||||
testObjectsString.add(
|
||||
new TestObject<>("abcdefghijklmnopqrstuvwxyz", "$2a$10$fVH8e28OQRj9tqiDXs1e1u",
|
||||
"$2a$10$fVH8e28OQRj9tqiDXs1e1uxpsjN0c7II7YPKXua2NAKYvM6iQk7dq"));
|
||||
testObjectsString.add(
|
||||
new TestObject<>("abcdefghijklmnopqrstuvwxyz", "$2a$12$D4G5f18o7aMMfwasBL7Gpu",
|
||||
"$2a$12$D4G5f18o7aMMfwasBL7GpuQWuP3pkrZrOAnqP.bmezbMng.QwJ/pG"));
|
||||
testObjectsString.add(
|
||||
new TestObject<>("abcdefghijklmnopqrstuvwxyz", "$2b$06$O8E89AQPj1zJQA05YvIAU.",
|
||||
"$2b$06$O8E89AQPj1zJQA05YvIAU.hMpj25BXri1bupl/Q7CJMlpLwZDNBoO"));
|
||||
testObjectsString.add(
|
||||
new TestObject<>("abcdefghijklmnopqrstuvwxyz", "$2b$06$PDqIWr./o/P3EE/P.Q0A/u",
|
||||
"$2b$06$PDqIWr./o/P3EE/P.Q0A/uFg86WL/PXTbaW267TDALEwDylqk00Z."));
|
||||
testObjectsString.add(
|
||||
new TestObject<>("abcdefghijklmnopqrstuvwxyz", "$2y$06$34MG90ZLah8/ZNr3ltlHCu",
|
||||
"$2y$06$34MG90ZLah8/ZNr3ltlHCuz6bachF8/3S5jTuzF1h2qg2cUk11sFW"));
|
||||
testObjectsString.add(
|
||||
new TestObject<>("abcdefghijklmnopqrstuvwxyz", "$2y$06$AK.hSLfMyw706iEW24i68u",
|
||||
"$2y$06$AK.hSLfMyw706iEW24i68uKAc2yorPTrB0cimvjJHEBUrPkOq7VvG"));
|
||||
testObjectsString.add(
|
||||
new TestObject<>("~!@#$%^&*() ~!@#$%^&*()PNBFRD", "$2a$06$fPIsBO8qRqkjj273rfaOI.",
|
||||
"$2a$06$fPIsBO8qRqkjj273rfaOI.HtSV9jLDpTbZn782DC6/t7qT67P6FfO"));
|
||||
testObjectsString.add(
|
||||
new TestObject<>("~!@#$%^&*() ~!@#$%^&*()PNBFRD", "$2a$08$Eq2r4G/76Wv39MzSX262hu",
|
||||
"$2a$08$Eq2r4G/76Wv39MzSX262huzPz612MZiYHVUJe/OcOql2jo4.9UxTW"));
|
||||
testObjectsString.add(
|
||||
new TestObject<>("~!@#$%^&*() ~!@#$%^&*()PNBFRD", "$2a$10$LgfYWkbzEvQ4JakH7rOvHe",
|
||||
"$2a$10$LgfYWkbzEvQ4JakH7rOvHe0y8pHKF9OaFgwUZ2q7W2FFZmZzJYlfS"));
|
||||
testObjectsString.add(
|
||||
new TestObject<>("~!@#$%^&*() ~!@#$%^&*()PNBFRD", "$2a$12$WApznUOJfkEGSmYRfnkrPO",
|
||||
"$2a$12$WApznUOJfkEGSmYRfnkrPOr466oFDCaj4b6HY3EXGvfxm43seyhgC"));
|
||||
testObjectsString.add(
|
||||
new TestObject<>("~!@#$%^&*() ~!@#$%^&*()PNBFRD", "$2b$06$FGWA8OlY6RtQhXBXuCJ8Wu",
|
||||
"$2b$06$FGWA8OlY6RtQhXBXuCJ8WusVipRI15cWOgJK8MYpBHEkktMfbHRIG"));
|
||||
testObjectsString.add(
|
||||
new TestObject<>("~!@#$%^&*() ~!@#$%^&*()PNBFRD", "$2b$06$G6aYU7UhUEUDJBdTgq3CRe",
|
||||
"$2b$06$G6aYU7UhUEUDJBdTgq3CRekiopCN4O4sNitFXrf5NUscsVZj3a2r6"));
|
||||
testObjectsString.add(
|
||||
new TestObject<>("~!@#$%^&*() ~!@#$%^&*()PNBFRD", "$2y$06$sYDFHqOcXTjBgOsqC0WCKe",
|
||||
"$2y$06$sYDFHqOcXTjBgOsqC0WCKeMd3T1UhHuWQSxncLGtXDLMrcE6vFDti"));
|
||||
testObjectsString.add(
|
||||
new TestObject<>("~!@#$%^&*() ~!@#$%^&*()PNBFRD", "$2y$06$6Xm0gCw4g7ZNDCEp4yTise",
|
||||
"$2y$06$6Xm0gCw4g7ZNDCEp4yTisez0kSdpXEl66MvdxGidnmChIe8dFmMnq"));
|
||||
testObjectsByteArray = new ArrayList<>();
|
||||
testObjectsByteArray.add(new TestObject<>(new byte[] {}, "$2a$06$fPIsBO8qRqkjj273rfaOI.",
|
||||
"$2a$06$fPIsBO8qRqkjj273rfaOI.uiVGfgi6Z1Iz.vZr11mi/38o09TUVCy"));
|
||||
testObjectsByteArray.add(new TestObject<>(new byte[] {}, "$2a$08$Eq2r4G/76Wv39MzSX262hu",
|
||||
"$2a$08$Eq2r4G/76Wv39MzSX262hu2lrqIItOWKIkPsMMvm5LAFD.iVB7Nmm"));
|
||||
testObjectsByteArray.add(new TestObject<>(new byte[] {}, "$2a$10$LgfYWkbzEvQ4JakH7rOvHe",
|
||||
"$2a$10$LgfYWkbzEvQ4JakH7rOvHeU6pINYiHnazYxe4GikGWx9MaUr27Vpa"));
|
||||
testObjectsByteArray.add(new TestObject<>(new byte[] {}, "$2a$12$WApznUOJfkEGSmYRfnkrPO",
|
||||
"$2a$12$WApznUOJfkEGSmYRfnkrPONS3wcUvmKuh3LpjxSs6g78T77gZta3W"));
|
||||
testObjectsByteArray.add(new TestObject<>(new byte[] {}, "$2b$06$FGWA8OlY6RtQhXBXuCJ8Wu",
|
||||
"$2b$06$FGWA8OlY6RtQhXBXuCJ8Wu5oPJaT8BeCRmS273I6cpp5RwwjAWn7S"));
|
||||
testObjectsByteArray.add(new TestObject<>(new byte[] {}, "$2b$06$G6aYU7UhUEUDJBdTgq3CRe",
|
||||
"$2b$06$G6aYU7UhUEUDJBdTgq3CRebzUYAyG8MCS3WdBk0CcPb9bfj1.3cSG"));
|
||||
testObjectsByteArray.add(new TestObject<>(new byte[] {}, "$2y$06$sYDFHqOcXTjBgOsqC0WCKe",
|
||||
"$2y$06$sYDFHqOcXTjBgOsqC0WCKeOv88fqPKkuV1yGVh./TROmn1mL8gYh2"));
|
||||
testObjectsByteArray.add(new TestObject<>(new byte[] {}, "$2y$06$6Xm0gCw4g7ZNDCEp4yTise",
|
||||
"$2y$06$6Xm0gCw4g7ZNDCEp4yTisecBqTHmLJBHxTNZa8w2hupJKsIhPWOgG"));
|
||||
testObjectsByteArray.add(new TestObject<>(new byte[] {-11}, "$2a$06$fPIsBO8qRqkjj273rfaOI.",
|
||||
"$2a$06$fPIsBO8qRqkjj273rfaOI.AyMTPwvUEmZ2EdJM/p0S0eP3UQpBas."));
|
||||
testObjectsByteArray.add(new TestObject<>(new byte[] {-11}, "$2a$08$Eq2r4G/76Wv39MzSX262hu",
|
||||
"$2a$08$Eq2r4G/76Wv39MzSX262huG.pmfTOWNaSXeVmr8y6qut1BpUiou6m"));
|
||||
testObjectsByteArray.add(new TestObject<>(new byte[] {-11}, "$2a$10$LgfYWkbzEvQ4JakH7rOvHe",
|
||||
"$2a$10$LgfYWkbzEvQ4JakH7rOvHeNm5INR.iq7bbwMewV0Tydrmqq3mZ5IK"));
|
||||
testObjectsByteArray.add(new TestObject<>(new byte[] {-11}, "$2a$12$WApznUOJfkEGSmYRfnkrPO",
|
||||
"$2a$12$WApznUOJfkEGSmYRfnkrPOi2qWwoWBJvfFzMrkqJwDedE3poicqwO"));
|
||||
testObjectsByteArray.add(new TestObject<>(new byte[] {-11}, "$2b$06$FGWA8OlY6RtQhXBXuCJ8Wu",
|
||||
"$2b$06$FGWA8OlY6RtQhXBXuCJ8Wuwip8vUd9WHq9onEGUjOS6CBHFkM./IG"));
|
||||
testObjectsByteArray.add(new TestObject<>(new byte[] {-11}, "$2b$06$G6aYU7UhUEUDJBdTgq3CRe",
|
||||
"$2b$06$G6aYU7UhUEUDJBdTgq3CRe6RQpRSN.PQ28XtDFT7zUVvpXNbg.K4i"));
|
||||
testObjectsByteArray.add(new TestObject<>(new byte[] {-11}, "$2y$06$sYDFHqOcXTjBgOsqC0WCKe",
|
||||
"$2y$06$sYDFHqOcXTjBgOsqC0WCKeduM9n5k0YfzTlgg69FIgGpw4ChTQNu2"));
|
||||
testObjectsByteArray.add(new TestObject<>(new byte[] {-11}, "$2y$06$6Xm0gCw4g7ZNDCEp4yTise",
|
||||
"$2y$06$6Xm0gCw4g7ZNDCEp4yTisetcxOr0uSWmFiVtNpDxjd5iaFWs/tyjG"));
|
||||
testObjectsByteArray.add(
|
||||
new TestObject<>(new byte[] {76, -56, -12, 9, -116}, "$2a$06$fPIsBO8qRqkjj273rfaOI.",
|
||||
"$2a$06$fPIsBO8qRqkjj273rfaOI.5m8yX4eGfjqx/tyHtmte7/HbWtUS9u."));
|
||||
testObjectsByteArray.add(
|
||||
new TestObject<>(new byte[] {76, -56, -12, 9, -116}, "$2a$08$Eq2r4G/76Wv39MzSX262hu",
|
||||
"$2a$08$Eq2r4G/76Wv39MzSX262hu0Vc3YdKF53qtdTtZJKD7uQfsPeGfkP6"));
|
||||
testObjectsByteArray.add(
|
||||
new TestObject<>(new byte[] {76, -56, -12, 9, -116}, "$2a$10$LgfYWkbzEvQ4JakH7rOvHe",
|
||||
"$2a$10$LgfYWkbzEvQ4JakH7rOvHeQBR1Mm2USNr//tnItwdVSZFNZfR/L9."));
|
||||
testObjectsByteArray.add(
|
||||
new TestObject<>(new byte[] {76, -56, -12, 9, -116}, "$2a$12$WApznUOJfkEGSmYRfnkrPO",
|
||||
"$2a$12$WApznUOJfkEGSmYRfnkrPO2WxEe4rN3gMECOFt21H8ozd661HB8Za"));
|
||||
testObjectsByteArray.add(
|
||||
new TestObject<>(new byte[] {76, -56, -12, 9, -116}, "$2b$06$FGWA8OlY6RtQhXBXuCJ8Wu",
|
||||
"$2b$06$FGWA8OlY6RtQhXBXuCJ8Wu5SNpYypZvM0j3zTq7vSCtzqOllUArQW"));
|
||||
testObjectsByteArray.add(
|
||||
new TestObject<>(new byte[] {76, -56, -12, 9, -116}, "$2b$06$G6aYU7UhUEUDJBdTgq3CRe",
|
||||
"$2b$06$G6aYU7UhUEUDJBdTgq3CRejcZ96XDmofwo2r3O/Lw0hoDHQy/Utxq"));
|
||||
testObjectsByteArray.add(
|
||||
new TestObject<>(new byte[] {76, -56, -12, 9, -116}, "$2y$06$sYDFHqOcXTjBgOsqC0WCKe",
|
||||
"$2y$06$sYDFHqOcXTjBgOsqC0WCKej6.o3knVxc7obV8y47GTTFc9uUWC4OO"));
|
||||
testObjectsByteArray.add(
|
||||
new TestObject<>(new byte[] {76, -56, -12, 9, -116}, "$2y$06$6Xm0gCw4g7ZNDCEp4yTise",
|
||||
"$2y$06$6Xm0gCw4g7ZNDCEp4yTiseKCvXMhtv0IrQPu9d36a893DjJ880Vb6"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for 'BCrypt.hashpw(String, String)'
|
||||
*/
|
||||
@Test
|
||||
public void testHashpw() {
|
||||
print("BCrypt.hashpw(): ");
|
||||
for (TestObject<String> test : testObjectsString) {
|
||||
String hashed = BCrypt.hashpw(test.password, test.salt);
|
||||
assertEquals(test.expected, hashed);
|
||||
print(".");
|
||||
}
|
||||
println("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for 'BCrypt.hashpw(byte[], String)'
|
||||
*/
|
||||
@Test
|
||||
public void testHashpwByteArray() {
|
||||
for (TestObject<byte[]> test : testObjectsByteArray) {
|
||||
String hashed = BCrypt.hashpw(test.password, test.salt);
|
||||
assertEquals(test.expected, hashed);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for 'BCrypt.gensalt(int)'
|
||||
*/
|
||||
@Test
|
||||
public void testGensaltInt() {
|
||||
print("BCrypt.gensalt(log_rounds):");
|
||||
for (int i = 4; i <= 12; i++) {
|
||||
print(" " + Integer.toString(i) + ":");
|
||||
for (int j = 0; j < testObjectsString.size(); j += 4) {
|
||||
String plain = testObjectsString.get(j).password;
|
||||
String salt = BCrypt.gensalt(i);
|
||||
String hashed1 = BCrypt.hashpw(plain, salt);
|
||||
String hashed2 = BCrypt.hashpw(plain, hashed1);
|
||||
assertEquals(hashed1, hashed2);
|
||||
print(".");
|
||||
}
|
||||
}
|
||||
println("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for 'BCrypt.gensalt()'
|
||||
*/
|
||||
@Test
|
||||
public void testGensalt() {
|
||||
print("BCrypt.gensalt(): ");
|
||||
for (int i = 0; i < testObjectsString.size(); i += 4) {
|
||||
String plain = testObjectsString.get(i).password;
|
||||
String salt = BCrypt.gensalt();
|
||||
String hashed1 = BCrypt.hashpw(plain, salt);
|
||||
String hashed2 = BCrypt.hashpw(plain, hashed1);
|
||||
assertEquals(hashed1, hashed2);
|
||||
print(".");
|
||||
}
|
||||
println("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for 'BCrypt.checkpw(String, String)' expecting success
|
||||
*/
|
||||
@Test
|
||||
public void testCheckpw_success() {
|
||||
print("BCrypt.checkpw w/ good passwords: ");
|
||||
for (TestObject<String> test : testObjectsString) {
|
||||
assertTrue(BCrypt.checkpw(test.password, test.expected));
|
||||
print(".");
|
||||
}
|
||||
println("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for 'BCrypt.checkpw(byte[], String)' expecting success
|
||||
*/
|
||||
@Test
|
||||
public void testCheckpwByteArray_success() {
|
||||
for (TestObject<byte[]> test : testObjectsByteArray) {
|
||||
assertTrue(BCrypt.checkpw(test.password, test.expected));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for 'BCrypt.checkpw(String, String)' expecting failure
|
||||
*/
|
||||
@Test
|
||||
public void testCheckpw_failure() {
|
||||
print("BCrypt.checkpw w/ bad passwords: ");
|
||||
for (int i = 0; i < testObjectsString.size(); i++) {
|
||||
int broken_index = (i + 8) % testObjectsString.size();
|
||||
String plain = testObjectsString.get(i).password;
|
||||
String expected = testObjectsString.get(broken_index).expected;
|
||||
assertFalse(BCrypt.checkpw(plain, expected));
|
||||
print(".");
|
||||
}
|
||||
println("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for 'BCrypt.checkpw(byte[], String)' expecting failure
|
||||
*/
|
||||
@Test
|
||||
public void testCheckpwByteArray_failure() {
|
||||
for (int i = 0; i < testObjectsByteArray.size(); i++) {
|
||||
int broken_index = (i + 8) % testObjectsByteArray.size();
|
||||
byte[] plain = testObjectsByteArray.get(i).password;
|
||||
String expected = testObjectsByteArray.get(broken_index).expected;
|
||||
assertFalse(BCrypt.checkpw(plain, expected));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for correct hashing of non-US-ASCII passwords
|
||||
*/
|
||||
@Test
|
||||
public void testInternationalChars() {
|
||||
print("BCrypt.hashpw w/ international chars: ");
|
||||
String pw1 = "ππππππππ";
|
||||
String pw2 = "????????";
|
||||
String h1 = BCrypt.hashpw(pw1, BCrypt.gensalt());
|
||||
assertFalse(BCrypt.checkpw(pw2, h1));
|
||||
print(".");
|
||||
String h2 = BCrypt.hashpw(pw2, BCrypt.gensalt());
|
||||
assertFalse(BCrypt.checkpw(pw1, h2));
|
||||
print(".");
|
||||
println("");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void roundsForDoesNotOverflow() {
|
||||
assertEquals(1024, BCrypt.roundsForLogRounds(10));
|
||||
assertEquals(0x80000000L, BCrypt.roundsForLogRounds(31));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emptyByteArrayCannotBeEncoded() {
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> BCrypt.encode_base64(new byte[0], 0, new StringBuilder()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void moreBytesThanInTheArrayCannotBeEncoded() {
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> BCrypt.encode_base64(new byte[1], 2, new StringBuilder()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decodingMustRequestMoreThanZeroBytes() {
|
||||
assertThrows(IllegalArgumentException.class, () -> BCrypt.decode_base64("", 0));
|
||||
}
|
||||
|
||||
private static String encode_base64(byte[] d, int len) throws IllegalArgumentException {
|
||||
StringBuilder rs = new StringBuilder();
|
||||
BCrypt.encode_base64(d, len, rs);
|
||||
return rs.toString();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBase64EncodeSimpleByteArrays() {
|
||||
assertEquals("..", encode_base64(new byte[] {0}, 1));
|
||||
assertEquals("...", encode_base64(new byte[] {0, 0}, 2));
|
||||
assertEquals("....", encode_base64(new byte[] {0, 0, 0}, 3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decodingCharsOutsideAsciiGivesNoResults() {
|
||||
byte[] ba = BCrypt.decode_base64("ππππππππ", 1);
|
||||
assertTrue(ArrayUtils.isEmpty(ba));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decodingStopsWithFirstInvalidCharacter() {
|
||||
assertEquals(1, BCrypt.decode_base64("....", 1).length);
|
||||
assertTrue(ArrayUtils.isEmpty(BCrypt.decode_base64(" ....", 1)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decodingOnlyProvidesAvailableBytes() {
|
||||
assertTrue(ArrayUtils.isEmpty(BCrypt.decode_base64("", 1)));
|
||||
assertEquals(3, BCrypt.decode_base64("......", 3).length);
|
||||
assertEquals(4, BCrypt.decode_base64("......", 4).length);
|
||||
assertEquals(4, BCrypt.decode_base64("......", 5).length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode and decode each byte value in each position.
|
||||
*/
|
||||
@Test
|
||||
public void testBase64EncodeDecode() {
|
||||
byte[] ba = new byte[3];
|
||||
for (int b = 0; b <= 0xFF; b++) {
|
||||
for (int i = 0; i < ba.length; i++) {
|
||||
Arrays.fill(ba, (byte) 0);
|
||||
ba[i] = (byte) b;
|
||||
String s = encode_base64(ba, 3);
|
||||
assertEquals(4, s.length());
|
||||
byte[] decoded = BCrypt.decode_base64(s, 3);
|
||||
assertTrue(Objects.deepEquals(ba, decoded));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genSaltFailsWithTooFewRounds() {
|
||||
assertThrows(IllegalArgumentException.class, () -> BCrypt.gensalt(3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genSaltFailsWithTooManyRounds() {
|
||||
assertThrows(IllegalArgumentException.class, () -> BCrypt.gensalt(32));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genSaltGeneratesCorrectSaltPrefix() {
|
||||
assertTrue(StringUtils.startsWith(BCrypt.gensalt(4), "$2a$04$"));
|
||||
assertTrue(StringUtils.startsWith(BCrypt.gensalt(31), "$2a$31$"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hashpwFailsWhenSaltIsNull() {
|
||||
assertThrows(IllegalArgumentException.class, () -> BCrypt.hashpw("password", null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hashpwFailsWhenSaltSpecifiesTooFewRounds() {
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> BCrypt.hashpw("password", "$2a$03$......................"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hashpwFailsWhenSaltSpecifiesTooManyRounds() {
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> BCrypt.hashpw("password", "$2a$32$......................"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saltLengthIsChecked() {
|
||||
assertThrows(IllegalArgumentException.class, () -> BCrypt.hashpw("", ""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hashpwWorksWithOldRevision() {
|
||||
assertEquals("$2$05$......................bvpG2UfzdyW/S0ny/4YyEZrmczoJfVm",
|
||||
BCrypt.hashpw("password", "$2$05$......................"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hashpwFailsWhenSaltIsTooShort() {
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> BCrypt.hashpw("password", "$2a$10$123456789012345678901"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equalsOnStringsIsCorrect() {
|
||||
assertTrue(BCrypt.equalsNoEarlyReturn("", ""));
|
||||
assertTrue(BCrypt.equalsNoEarlyReturn("test", "test"));
|
||||
assertFalse(BCrypt.equalsNoEarlyReturn("test", ""));
|
||||
assertFalse(BCrypt.equalsNoEarlyReturn("", "test"));
|
||||
assertFalse(BCrypt.equalsNoEarlyReturn("test", "pass"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void cryptTest() {
|
||||
String cryptPassword = BCrypt.hashpw("opentest", BCrypt.gensalt());
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package run.halo.app.utils;
|
||||
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
@ -13,17 +12,21 @@ import java.time.LocalDateTime;
|
|||
import java.time.LocalTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* DateTimeUtils 测试用例
|
||||
* DateTimeUtils 测试用例.
|
||||
*
|
||||
* @author LeiXinXin
|
||||
* @author guqing
|
||||
* @date 2020/1/9
|
||||
*/
|
||||
class DateTimeUtilsTest {
|
||||
|
||||
/**
|
||||
* 获取上海时区的当前时间
|
||||
* 获取上海时区的当前时间.
|
||||
*/
|
||||
@Test
|
||||
void nowTest() {
|
||||
|
@ -37,7 +40,7 @@ class DateTimeUtilsTest {
|
|||
}
|
||||
|
||||
/**
|
||||
* 格式化日期时间
|
||||
* 格式化日期时间.
|
||||
*/
|
||||
@Test
|
||||
void formatTest() {
|
||||
|
@ -63,7 +66,7 @@ class DateTimeUtilsTest {
|
|||
}
|
||||
|
||||
/**
|
||||
* 增加时间
|
||||
* 增加时间.
|
||||
*/
|
||||
@Test
|
||||
void plusTest() {
|
||||
|
@ -75,7 +78,6 @@ class DateTimeUtilsTest {
|
|||
final LocalDateTime localDateTime1 = DateTimeUtils.plusOneMinute(now, localTime);
|
||||
assertEquals("2020-01-09T07:31", localDateTime1.toString());
|
||||
|
||||
|
||||
final LocalDate date = LocalDate.of(2020, 1, 3);
|
||||
final LocalDateTime localDateTime2 = DateTimeUtils.plusOneMinute(date, localTime);
|
||||
assertEquals("2020-01-03T07:31", localDateTime2.toString());
|
||||
|
@ -88,7 +90,7 @@ class DateTimeUtilsTest {
|
|||
}
|
||||
|
||||
/**
|
||||
* 解析时间格式为LocalDateTime
|
||||
* 解析时间格式为LocalDateTime.
|
||||
*/
|
||||
@Test
|
||||
void parseTest() {
|
||||
|
@ -103,7 +105,7 @@ class DateTimeUtilsTest {
|
|||
}
|
||||
|
||||
/**
|
||||
* 减少日期时间
|
||||
* 减少日期时间.
|
||||
*/
|
||||
@Test
|
||||
void minusTest() {
|
||||
|
@ -113,7 +115,7 @@ class DateTimeUtilsTest {
|
|||
}
|
||||
|
||||
/**
|
||||
* 转为Instant
|
||||
* 转为Instant.
|
||||
*/
|
||||
@Test
|
||||
void toInstantTest() {
|
||||
|
@ -127,7 +129,7 @@ class DateTimeUtilsTest {
|
|||
}
|
||||
|
||||
/**
|
||||
* 一些其他的使用方法
|
||||
* 一些其他的使用方法.
|
||||
*/
|
||||
@Test
|
||||
void other() {
|
||||
|
@ -163,4 +165,12 @@ class DateTimeUtilsTest {
|
|||
final LocalDateTime time = DateTimeUtils.secondAndNanoSetZero(localDateTime);
|
||||
assertEquals("2020-01-05T06:40", time.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void toLocalDateTime() {
|
||||
GregorianCalendar january31th =
|
||||
new GregorianCalendar(2021, Calendar.JANUARY, 31, 12, 45, 20);
|
||||
LocalDateTime localDateTime = DateTimeUtils.toLocalDateTime(january31th.getTime());
|
||||
assertEquals("2021-01-31T12:45:20", localDateTime.toString());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,150 @@
|
|||
package run.halo.app.utils;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* DateUtils test.
|
||||
*
|
||||
* @author guqing
|
||||
* @date 2021-09-27
|
||||
*/
|
||||
@Slf4j
|
||||
public class DateUtilsTest {
|
||||
|
||||
private TimeZone defaultTimeZone;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
defaultTimeZone = TimeZone.getDefault();
|
||||
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void cleanUp() {
|
||||
TimeZone.setDefault(defaultTimeZone);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDateParse() {
|
||||
// yyyy-MM-dd HH:mm:ss
|
||||
assertDateParseEquals("Sun Sep 26 06:39:12 CST 2021", "2021-09-26 06:39:12");
|
||||
|
||||
// yyyy/MM/dd HH:mm:ss
|
||||
assertDateParseEquals("Sun Sep 26 06:39:12 CST 2021", "2021/09/26 06:39:12");
|
||||
|
||||
// yyyy.MM.dd HH:mm:ss
|
||||
assertDateParseEquals("Sun Sep 26 06:39:12 CST 2021", "2021.09.26 06:39:12");
|
||||
|
||||
// yyyy年MM月dd日 HH时mm分ss秒
|
||||
assertDateParseEquals("Sun Sep 26 06:39:12 CST 2021", "2021年09月26日 06时39分12秒");
|
||||
|
||||
// yyyy-MM-dd
|
||||
assertDateParseEquals("Sun Sep 26 00:00:00 CST 2021", "2021-09-26");
|
||||
|
||||
// yyyy/MM/dd
|
||||
assertDateParseEquals("Sun Sep 26 00:00:00 CST 2021", "2021/09/26");
|
||||
|
||||
// yyyy.MM.dd
|
||||
assertDateParseEquals("Sun Sep 26 00:00:00 CST 2021", "2021.09.26");
|
||||
|
||||
// HH:mm:ss
|
||||
assertDateParseEquals("Thu Jan 01 06:39:12 CST 1970", "06:39:12");
|
||||
|
||||
// HH时mm分ss秒
|
||||
assertDateParseEquals("Thu Jan 01 06:39:12 CST 1970", "06时39分12秒");
|
||||
|
||||
// yyyy-MM-dd HH:mm
|
||||
assertDateParseEquals("Sun Sep 26 06:39:00 CST 2021", "2021-09-26 06:39");
|
||||
|
||||
// yyyy-MM-dd HH:mm:ss.SSS
|
||||
assertDateParseEquals("Sun Sep 26 06:39:12 CST 2021", "2021-09-26 06:39:12.015");
|
||||
|
||||
// yyyyMMddHHmmss
|
||||
assertDateParseEquals("Sun Sep 26 06:39:12 CST 2021", "20210926063912");
|
||||
|
||||
// yyyyMMddHHmmssSSS
|
||||
assertDateParseEquals("Sun Sep 26 09:59:15 CST 2021", "20210926063912015");
|
||||
|
||||
// yyyyMMdd
|
||||
assertDateParseEquals("Sun Sep 26 00:00:00 CST 2021", "20210926");
|
||||
|
||||
// yyyy-MM-dd'T'HH:mm:ss
|
||||
assertDateParseEquals("Sun Sep 26 10:09:10 CST 2021", "2021-09-26T10:09:10");
|
||||
|
||||
// yyyy-MM-dd'T'HH:mm:ss'Z'
|
||||
assertDateParseEquals("Sun Sep 26 10:09:10 CST 2021", "2021-09-26T10:09:10Z");
|
||||
|
||||
// yyyy-MM-dd'T'HH:mm:ss.SSS'Z'
|
||||
assertDateParseEquals("Sun Sep 26 10:09:10 CST 2021", "2021-09-26T10:09:10.999Z");
|
||||
|
||||
// yyyy-MM-dd'T'HH:mm:ssZ
|
||||
assertDateParseEquals("Sun Sep 26 05:34:31 CST 2021", "2021-09-26T05:34:31+0800");
|
||||
|
||||
// yyyy-MM-dd'T'HH:mm:ss.SSSZ
|
||||
assertDateParseEquals("Sun Sep 26 05:34:31 CST 2021", "2021-09-26T05:34:31.999+0800");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMonth() {
|
||||
GregorianCalendar january =
|
||||
new GregorianCalendar(2021, Calendar.JANUARY, 1);
|
||||
assertEquals(1, DateUtils.month(january.getTime()) + 1);
|
||||
|
||||
GregorianCalendar february =
|
||||
new GregorianCalendar(2021, Calendar.FEBRUARY, 2);
|
||||
assertEquals(2, DateUtils.month(february.getTime()) + 1);
|
||||
|
||||
GregorianCalendar december =
|
||||
new GregorianCalendar(2021, Calendar.DECEMBER, 31);
|
||||
assertEquals(12, DateUtils.month(december.getTime()) + 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testDayOfMonth() {
|
||||
GregorianCalendar january1st =
|
||||
new GregorianCalendar(2021, Calendar.JANUARY, 1);
|
||||
assertEquals(1, DateUtils.dayOfMonth(january1st.getTime()));
|
||||
|
||||
GregorianCalendar january2nd =
|
||||
new GregorianCalendar(2021, Calendar.JANUARY, 2);
|
||||
assertEquals(2, DateUtils.dayOfMonth(january2nd.getTime()));
|
||||
|
||||
GregorianCalendar january15th =
|
||||
new GregorianCalendar(2021, Calendar.JANUARY, 15);
|
||||
assertEquals(15, DateUtils.dayOfMonth(january15th.getTime()));
|
||||
|
||||
GregorianCalendar january31th =
|
||||
new GregorianCalendar(2021, Calendar.JANUARY, 31);
|
||||
assertEquals(31, DateUtils.dayOfMonth(january31th.getTime()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testYear() {
|
||||
GregorianCalendar year2015 =
|
||||
new GregorianCalendar(2015, Calendar.JANUARY, 1);
|
||||
assertEquals(2015, DateUtils.year(year2015.getTime()));
|
||||
|
||||
GregorianCalendar year2019 =
|
||||
new GregorianCalendar(2019, Calendar.JANUARY, 1);
|
||||
assertEquals(2019, DateUtils.year(year2019.getTime()));
|
||||
|
||||
GregorianCalendar year2021 =
|
||||
new GregorianCalendar(2021, Calendar.JANUARY, 1);
|
||||
assertEquals(2021, DateUtils.year(year2021.getTime()));
|
||||
}
|
||||
|
||||
private void assertDateParseEquals(String expected, String dateStr) {
|
||||
log.debug("expected: {}, actual: {}, Locale: {}", expected,
|
||||
DateUtils.parseDate(dateStr, Locale.CHINESE), Locale.getDefault());
|
||||
assertEquals(expected, DateUtils.parseDate(dateStr, Locale.CHINESE).toString());
|
||||
}
|
||||
}
|
|
@ -1,10 +1,13 @@
|
|||
package run.halo.app.utils;
|
||||
|
||||
import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.file.FileAlreadyExistsException;
|
||||
import java.nio.file.Files;
|
||||
|
@ -306,4 +309,12 @@ class FileUtilsTest {
|
|||
assertTrue(Files.exists(target.resolve(".git")));
|
||||
assertEquals("test", Files.readString(target.resolve(".git").resolve("test.txt")));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void convertInputStreamToString() {
|
||||
String originalString = randomAlphabetic(8);
|
||||
InputStream inputStream = new ByteArrayInputStream(originalString.getBytes());
|
||||
|
||||
assertEquals(originalString, FileUtils.readString(inputStream));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package run.halo.app.utils;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import com.vladsch.flexmark.ext.emoji.EmojiExtension;
|
||||
import com.vladsch.flexmark.ext.emoji.EmojiImageType;
|
||||
import com.vladsch.flexmark.ext.emoji.EmojiShortcutType;
|
||||
|
@ -54,7 +55,7 @@ public class FootnoteTest {
|
|||
+ "with continuation";
|
||||
String s = renderHtml(markdown);
|
||||
System.out.println(s);
|
||||
Assert.isTrue(StringUtils.equals(s, "<p>text <sup class=\"footnote-ref\"><a id=\"fnref1\""
|
||||
assertTrue(StringUtils.equals(s, "<p>text <sup class=\"footnote-ref\"><a id=\"fnref1\""
|
||||
+ " href=\"#fn1\">[1]</a></sup> embedded.</p>\n"
|
||||
+ "<hr class=\"footnotes-sep\" />\n"
|
||||
+ "<section class=\"footnotes\">\n"
|
||||
|
@ -78,7 +79,7 @@ public class FootnoteTest {
|
|||
+ "[^another]: footnote text\n"
|
||||
+ "with continuation";
|
||||
String s = renderHtml(markdown);
|
||||
Assert.isTrue(StringUtils.equals(s, "<p>text <sup class=\"footnote-ref\"><a id=\"fnref1\""
|
||||
assertTrue(StringUtils.equals(s, "<p>text <sup class=\"footnote-ref\"><a id=\"fnref1\""
|
||||
+ " href=\"#fn1\">[1]</a></sup> embedded.</p>\n"
|
||||
+ "<hr class=\"footnotes-sep\" />\n"
|
||||
+ "<section class=\"footnotes\">\n"
|
||||
|
@ -107,7 +108,7 @@ public class FootnoteTest {
|
|||
+ "[^another]: footnote text with [^another] embedded footnote\n"
|
||||
+ "with continuation";
|
||||
String s = renderHtml(markdown);
|
||||
Assert.isTrue(StringUtils.equals(s, "<p>text <sup class=\"footnote-ref\"><a id=\"fnref1\""
|
||||
assertTrue(StringUtils.equals(s, "<p>text <sup class=\"footnote-ref\"><a id=\"fnref1\""
|
||||
+ " href=\"#fn1\">[1]</a></sup> embedded.</p>\n"
|
||||
+ "<hr class=\"footnotes-sep\" />\n"
|
||||
+ "<section class=\"footnotes\">\n"
|
||||
|
@ -142,7 +143,7 @@ public class FootnoteTest {
|
|||
+ " - item 2\n"
|
||||
+ ".";
|
||||
String s = renderHtml(markdown);
|
||||
Assert.isTrue(StringUtils.equals(s, "<p>This paragraph has a footnote<sup "
|
||||
assertTrue(StringUtils.equals(s, "<p>This paragraph has a footnote<sup "
|
||||
+ "class=\"footnote-ref\"><a id=\"fnref1\" href=\"#fn1\">[1]</a></sup>.</p>\n"
|
||||
+ "<hr class=\"footnotes-sep\" />\n"
|
||||
+ "<section class=\"footnotes\">\n"
|
||||
|
@ -180,7 +181,7 @@ public class FootnoteTest {
|
|||
+ " - item 2\n"
|
||||
+ ".";
|
||||
String s = renderHtml(markdown);
|
||||
Assert.isTrue(StringUtils.equals(s, "<p>This paragraph has no footnote[ ^footnote].</p>\n"
|
||||
assertTrue(StringUtils.equals(s, "<p>This paragraph has no footnote[ ^footnote].</p>\n"
|
||||
+ "<p>[ ^footnote]: This is the body of the footnote.<br />\n"
|
||||
+ "with continuation text. Inline <em>italic</em> and<br />\n"
|
||||
+ "<strong>bold</strong>.</p>\n"
|
||||
|
@ -213,7 +214,7 @@ public class FootnoteTest {
|
|||
+ " - item 2\n"
|
||||
+ ".";
|
||||
String s = renderHtml(markdown);
|
||||
Assert.isTrue(StringUtils.equals(s, "<p>This paragraph has a footnote<sup "
|
||||
assertTrue(StringUtils.equals(s, "<p>This paragraph has a footnote<sup "
|
||||
+ "class=\"footnote-ref\"><a id=\"fnref1\" href=\"#fn1\">[1]</a></sup>.</p>\n"
|
||||
+ "<hr class=\"footnotes-sep\" />\n"
|
||||
+ "<section class=\"footnotes\">\n"
|
||||
|
@ -251,7 +252,7 @@ public class FootnoteTest {
|
|||
+ " - item 2\n"
|
||||
+ ".";
|
||||
String s = renderHtml(markdown);
|
||||
Assert.isTrue(StringUtils
|
||||
assertTrue(StringUtils
|
||||
.equals(s, "<p>This paragraph has a footnote[^<strong>footnote</strong>].</p>\n"));
|
||||
}
|
||||
|
||||
|
@ -275,7 +276,7 @@ public class FootnoteTest {
|
|||
+ " - item 2\n"
|
||||
+ ".";
|
||||
String s = renderHtml(markdown);
|
||||
Assert.isTrue(StringUtils.equals(s, "<p>This paragraph has a footnote<sup "
|
||||
assertTrue(StringUtils.equals(s, "<p>This paragraph has a footnote<sup "
|
||||
+ "class=\"footnote-ref\"><a id=\"fnref1\" href=\"#fn1\">[1]</a></sup>. Followed by "
|
||||
+ "another<sup class=\"footnote-ref\"><a id=\"fnref2\" href=\"#fn2\">[2]</a></sup>"
|
||||
+ ".</p>\n"
|
||||
|
@ -325,7 +326,7 @@ public class FootnoteTest {
|
|||
+ " **bold**.\n"
|
||||
+ ".";
|
||||
String s = renderHtml(markdown);
|
||||
Assert.isTrue(StringUtils.equals(s, "<p>This paragraph has a footnote<sup "
|
||||
assertTrue(StringUtils.equals(s, "<p>This paragraph has a footnote<sup "
|
||||
+ "class=\"footnote-ref\"><a id=\"fnref1\" href=\"#fn1\">[1]</a></sup>.</p>\n"
|
||||
+ "<hr class=\"footnotes-sep\" />\n"
|
||||
+ "<section class=\"footnotes\">\n"
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
package run.halo.app.utils;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.RandomUtils;
|
||||
|
@ -13,6 +16,7 @@ import org.junit.jupiter.api.Test;
|
|||
*
|
||||
* @author johnniang
|
||||
* @author ryanwang
|
||||
* @author guqing
|
||||
* @date 2019-03-29
|
||||
*/
|
||||
@Slf4j
|
||||
|
@ -153,4 +157,11 @@ class HaloUtilsTest {
|
|||
assertEquals("https://cn.gravatar.com/avatar?d=mm",
|
||||
HaloUtils.normalizeUrl("https://cn.gravatar.com/avatar?d=mm"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void collectionIsNotEmpty() {
|
||||
assertFalse(HaloUtils.isNotEmpty(null));
|
||||
assertFalse(HaloUtils.isNotEmpty(List.of()));
|
||||
assertTrue(HaloUtils.isNotEmpty(List.of("A")));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package run.halo.app.utils;
|
|||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import cn.hutool.core.lang.Tuple;
|
||||
import java.lang.reflect.Method;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
@ -15,20 +14,20 @@ public class HttpClientUtilsTest {
|
|||
HttpClientUtils.class.getDeclaredMethod("resolveHttpProxy", String.class);
|
||||
resolveHttpProxy.setAccessible(true);
|
||||
|
||||
Tuple result = (Tuple) resolveHttpProxy.invoke(null, "http://127.0.0.1");
|
||||
assertEquals(result.get(0), "http://127.0.0.1:80");
|
||||
Object[] result = (Object[]) resolveHttpProxy.invoke(null, "http://127.0.0.1");
|
||||
assertEquals("http://127.0.0.1:80", result[0]);
|
||||
|
||||
result = (Tuple) resolveHttpProxy.invoke(null, "https://127.0.0.1");
|
||||
assertEquals(result.get(0), "https://127.0.0.1:443");
|
||||
result = (Object[]) resolveHttpProxy.invoke(null, "https://127.0.0.1");
|
||||
assertEquals("https://127.0.0.1:443", result[0]);
|
||||
|
||||
result = (Tuple) resolveHttpProxy.invoke(null, "https://127.0.0.1:123");
|
||||
assertEquals(result.get(0), "https://127.0.0.1:123");
|
||||
result = (Object[]) resolveHttpProxy.invoke(null, "https://127.0.0.1:123");
|
||||
assertEquals("https://127.0.0.1:123", result[0]);
|
||||
|
||||
result = (Tuple) resolveHttpProxy.invoke(null, "https://u:p@127.0.0.1:123");
|
||||
assertArrayEquals(result.getMembers(), new Object[] {"https://127.0.0.1:123", "u", "p"});
|
||||
result = (Object[]) resolveHttpProxy.invoke(null, "https://u:p@127.0.0.1:123");
|
||||
assertArrayEquals(new Object[] {"https://127.0.0.1:123", "u", "p"}, result);
|
||||
|
||||
result = (Tuple) resolveHttpProxy.invoke(null, "https://u@127.0.0.1");
|
||||
assertArrayEquals(result.getMembers(), new Object[] {"https://127.0.0.1:443", "u", null});
|
||||
result = (Object[]) resolveHttpProxy.invoke(null, "https://u@127.0.0.1");
|
||||
assertArrayEquals(new Object[] {"https://127.0.0.1:443", "u", null}, result);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
package run.halo.app.utils;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* @author zhixiang.yuan
|
||||
* @since 2020/07/19 20:22:58
|
||||
* @author guqing
|
||||
* @date 2020/07/19 20:22:58
|
||||
*/
|
||||
@Slf4j
|
||||
class MarkdownUtilsTest {
|
||||
|
@ -17,19 +20,19 @@ class MarkdownUtilsTest {
|
|||
String markdown = "---\n"
|
||||
+ "title: \"test remove\"\n"
|
||||
+ "---";
|
||||
Assert.isTrue("".equals(MarkdownUtils.removeFrontMatter(markdown)));
|
||||
assertEquals("", MarkdownUtils.removeFrontMatter(markdown));
|
||||
|
||||
markdown = "---\n"
|
||||
+ "title: \"test remove\"\n"
|
||||
+ "---"
|
||||
+ "test";
|
||||
Assert.isTrue("test".equals(MarkdownUtils.removeFrontMatter(markdown)));
|
||||
assertEquals("test", MarkdownUtils.removeFrontMatter(markdown));
|
||||
|
||||
markdown = "---\n"
|
||||
+ "title: \"test remove\"\n"
|
||||
+ "---"
|
||||
+ "test---";
|
||||
Assert.isTrue("test---".equals(MarkdownUtils.removeFrontMatter(markdown)));
|
||||
assertEquals("test---", MarkdownUtils.removeFrontMatter(markdown));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -37,7 +40,7 @@ class MarkdownUtilsTest {
|
|||
String markdown1 = "驿外[^1]断桥边,寂寞开无主。已是黄昏独自愁,更着风和雨\n"
|
||||
+ "[^1]: 驿(yì)外:指荒僻、冷清之地。驿,驿站。";
|
||||
String s1 = MarkdownUtils.renderHtml(markdown1);
|
||||
Assert.isTrue(StringUtils.isNotBlank(s1));
|
||||
assertTrue(StringUtils.isNotBlank(s1));
|
||||
String s1Expected = "<p>驿外<sup class=\"footnote-ref\"><a id=\"fnref1\" "
|
||||
+ "href=\"#fn1\">[1]</a></sup>断桥边,寂寞开无主。已是黄昏独自愁,更着风和雨</p>\n"
|
||||
+ "<hr class=\"footnotes-sep\" />\n"
|
||||
|
@ -49,7 +52,7 @@ class MarkdownUtilsTest {
|
|||
+ "</li>\n"
|
||||
+ "</ol>\n"
|
||||
+ "</section>\n";
|
||||
Assert.isTrue(StringUtils.equals(s1Expected, s1));
|
||||
assertTrue(StringUtils.equals(s1Expected, s1));
|
||||
|
||||
String markdown2 = "Paragraph with a footnote reference[^1]\n"
|
||||
+ "[^1]: Footnote text added at the bottom of the document";
|
||||
|
@ -65,6 +68,6 @@ class MarkdownUtilsTest {
|
|||
+ "</li>\n"
|
||||
+ "</ol>\n"
|
||||
+ "</section>\n";
|
||||
Assert.isTrue(StringUtils.equals(s2Expected, s2));
|
||||
assertTrue(StringUtils.equals(s2Expected, s2));
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Time unit test
|
||||
* Time unit test.
|
||||
*
|
||||
* @author johnniang
|
||||
* @date 19-4-29
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package run.halo.app.utils;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
@ -55,6 +57,33 @@ class ValidationUtilsTest {
|
|||
validateIteratorTest(exception.getConstraintViolations());
|
||||
}
|
||||
|
||||
@Test
|
||||
void emailValidateTest() {
|
||||
// Valid email addresses
|
||||
assertTrue(ValidationUtils.isEmail("example@example.com"));
|
||||
assertTrue(ValidationUtils.isEmail("12313213@example.com"));
|
||||
assertTrue(ValidationUtils.isEmail("user.name@example.com.cn"));
|
||||
assertTrue(ValidationUtils.isEmail("very.common@example.com"));
|
||||
assertTrue(ValidationUtils.isEmail("disposable.style.email.with+symbol@example.com"));
|
||||
assertTrue(ValidationUtils.isEmail("other.email-with-hyphen@example.com"));
|
||||
assertTrue(ValidationUtils.isEmail("fully-qualified-domain@example.com"));
|
||||
assertTrue(ValidationUtils.isEmail("user.name+tag+sorting@example.com"));
|
||||
assertTrue(ValidationUtils.isEmail("x@example.com"));
|
||||
assertTrue(ValidationUtils.isEmail("example-indeed@strange-example.com"));
|
||||
assertTrue(ValidationUtils.isEmail("example@s.example"));
|
||||
assertTrue(ValidationUtils.isEmail("\"john..doe\"@example.org"));
|
||||
|
||||
// Invalid email addresses
|
||||
assertFalse(ValidationUtils.isEmail("Abc.example.com"));
|
||||
assertFalse(ValidationUtils.isEmail("A@b@c@example.com"));
|
||||
assertFalse(ValidationUtils.isEmail("a\"b(c)d,e:f;g<h>i[j\\k]l@example.com"));
|
||||
assertFalse(ValidationUtils.isEmail("just\"not\"right@example.com"));
|
||||
assertFalse(ValidationUtils.isEmail("this is\"not\\allowed@example.com"));
|
||||
assertFalse(ValidationUtils.isEmail("this\\ still\\\"not\\\\allowed@example.com"));
|
||||
assertFalse(ValidationUtils.isEmail("john..doe@example.com"));
|
||||
assertFalse(ValidationUtils.isEmail("john.doe@example..com"));
|
||||
}
|
||||
|
||||
void validateIteratorTest(Set<? extends ConstraintViolation<?>> violations) {
|
||||
assertEquals(2, violations.size());
|
||||
|
||||
|
|
Loading…
Reference in New Issue