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 source
pull/1492/head
guqing 2021-10-01 17:55:58 +08:00 committed by GitHub
parent 5acabee7a9
commit de71f40de6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 2244 additions and 180 deletions

View File

@ -74,7 +74,7 @@ bootBuildImage {
} }
ext { ext {
hutoolVersion = "5.3.8" guavaVersion= "31.0-jre"
upyunSdkVersion = "4.2.0" upyunSdkVersion = "4.2.0"
qiniuSdkVersion = "7.2.29" qiniuSdkVersion = "7.2.29"
aliyunSdkVersion = "3.11.3" aliyunSdkVersion = "3.11.3"
@ -109,9 +109,7 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation "com.sun.mail:jakarta.mail" implementation "com.sun.mail:jakarta.mail"
implementation "cn.hutool:hutool-core:$hutoolVersion" implementation "com.google.guava:guava:$guavaVersion"
implementation "cn.hutool:hutool-crypto:$hutoolVersion"
implementation "cn.hutool:hutool-extra:$hutoolVersion"
implementation "com.upyun:java-sdk:$upyunSdkVersion" implementation "com.upyun:java-sdk:$upyunSdkVersion"
implementation "com.qiniu:qiniu-java-sdk:$qiniuSdkVersion" implementation "com.qiniu:qiniu-java-sdk:$qiniuSdkVersion"
implementation "com.aliyun.oss:aliyun-sdk-oss:$aliyunSdkVersion" implementation "com.aliyun.oss:aliyun-sdk-oss:$aliyunSdkVersion"

View File

@ -1,6 +1,5 @@
package run.halo.app.controller.admin.api; package run.halo.app.controller.admin.api;
import cn.hutool.crypto.SecureUtil;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
@ -8,6 +7,7 @@ import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisher;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
@ -279,7 +279,7 @@ public class InstallController {
return userService.update(user); return userService.update(user);
}).orElseGet(() -> { }).orElseGet(() -> {
String gravatar = String gravatar =
"//cn.gravatar.com/avatar/" + SecureUtil.md5(installParam.getEmail()) "//cn.gravatar.com/avatar/" + DigestUtils.md5Hex(installParam.getEmail())
+ "?s=256&d=mm"; + "?s=256&d=mm";
installParam.setAvatar(gravatar); installParam.setAvatar(gravatar);
return userService.createBy(installParam); return userService.createBy(installParam);

View File

@ -2,7 +2,6 @@ package run.halo.app.controller.admin.api;
import static org.springframework.data.domain.Sort.Direction.DESC; import static org.springframework.data.domain.Sort.Direction.DESC;
import cn.hutool.core.util.IdUtil;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException; 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.model.vo.PostDetailVO;
import run.halo.app.service.OptionService; import run.halo.app.service.OptionService;
import run.halo.app.service.PostService; import run.halo.app.service.PostService;
import run.halo.app.utils.HaloUtils;
/** /**
* Post controller. * Post controller.
@ -188,7 +188,7 @@ public class PostController {
BasePostMinimalDTO postMinimalDTO = postService.convertToMinimal(post); BasePostMinimalDTO postMinimalDTO = postService.convertToMinimal(post);
String token = IdUtil.simpleUUID(); String token = HaloUtils.simpleUUID();
// cache preview token // cache preview token
cacheStore.putAny(token, token, 10, TimeUnit.MINUTES); cacheStore.putAny(token, token, 10, TimeUnit.MINUTES);

View File

@ -2,7 +2,6 @@ package run.halo.app.controller.admin.api;
import static org.springframework.data.domain.Sort.Direction.DESC; import static org.springframework.data.domain.Sort.Direction.DESC;
import cn.hutool.core.util.IdUtil;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URLEncoder; 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.model.vo.SheetListVO;
import run.halo.app.service.OptionService; import run.halo.app.service.OptionService;
import run.halo.app.service.SheetService; import run.halo.app.service.SheetService;
import run.halo.app.utils.HaloUtils;
/** /**
* Sheet controller. * Sheet controller.
@ -149,7 +149,7 @@ public class SheetController {
BasePostMinimalDTO sheetMinimalDTO = sheetService.convertToMinimal(sheet); BasePostMinimalDTO sheetMinimalDTO = sheetService.convertToMinimal(sheet);
String token = IdUtil.simpleUUID(); String token = HaloUtils.simpleUUID();
// cache preview token // cache preview token
cacheStore.putAny(token, token, 10, TimeUnit.MINUTES); cacheStore.putAny(token, token, 10, TimeUnit.MINUTES);

View File

@ -1,10 +1,9 @@
package run.halo.app.controller.admin.api; 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 io.swagger.annotations.ApiOperation;
import javax.validation.Valid; 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.GetMapping;
import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody; 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.support.UpdateCheck;
import run.halo.app.model.vo.MultiFactorAuthVO; import run.halo.app.model.vo.MultiFactorAuthVO;
import run.halo.app.service.UserService; import run.halo.app.service.UserService;
import run.halo.app.utils.HaloUtils;
import run.halo.app.utils.TwoFactorAuthUtils; import run.halo.app.utils.TwoFactorAuthUtils;
import run.halo.app.utils.ValidationUtils; import run.halo.app.utils.ValidationUtils;
@ -83,7 +83,8 @@ public class UserController {
String optAuthUrl = String optAuthUrl =
TwoFactorAuthUtils.generateOtpAuthUrl(user.getNickname(), mfaKey); TwoFactorAuthUtils.generateOtpAuthUrl(user.getNickname(), mfaKey);
String qrImageBase64 = "data:image/png;base64," 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); return new MultiFactorAuthVO(qrImageBase64, optAuthUrl, mfaKey, MFAType.TFA_TOTP);
} else { } else {
throw new BadRequestException("暂不支持的 MFA 认证的方式"); throw new BadRequestException("暂不支持的 MFA 认证的方式");
@ -99,16 +100,16 @@ public class UserController {
@DisableOnCondition @DisableOnCondition
public MultiFactorAuthVO updateMFAuth( public MultiFactorAuthVO updateMFAuth(
@RequestBody @Valid MultiFactorAuthParam multiFactorAuthParam, User user) { @RequestBody @Valid MultiFactorAuthParam multiFactorAuthParam, User user) {
if (StrUtil.isNotBlank(user.getMfaKey()) if (StringUtils.isNotBlank(user.getMfaKey())
&& MFAType.useMFA(multiFactorAuthParam.getMfaType())) { && MFAType.useMFA(multiFactorAuthParam.getMfaType())) {
return new MultiFactorAuthVO(MFAType.TFA_TOTP); return new MultiFactorAuthVO(MFAType.TFA_TOTP);
} else if (StrUtil.isBlank(user.getMfaKey()) } else if (StringUtils.isBlank(user.getMfaKey())
&& !MFAType.useMFA(multiFactorAuthParam.getMfaType())) { && !MFAType.useMFA(multiFactorAuthParam.getMfaType())) {
return new MultiFactorAuthVO(MFAType.NONE); return new MultiFactorAuthVO(MFAType.NONE);
} else { } else {
final String tfaKey = StrUtil.isNotBlank(user.getMfaKey()) ? user.getMfaKey() : final String mfaKey = StringUtils.isNotBlank(user.getMfaKey()) ? user.getMfaKey() :
multiFactorAuthParam.getMfaKey(); multiFactorAuthParam.getMfaKey();
TwoFactorAuthUtils.validateTFACode(tfaKey, multiFactorAuthParam.getAuthcode()); TwoFactorAuthUtils.validateTFACode(mfaKey, multiFactorAuthParam.getAuthcode());
} }
// update MFA key // update MFA key
User updateUser = userService User updateUser = userService

View File

@ -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.POST_PASSWORD_TEMPLATE;
import static run.halo.app.model.support.HaloConst.SUFFIX_FTL; import static run.halo.app.model.support.HaloConst.SUFFIX_FTL;
import cn.hutool.core.util.StrUtil;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -91,7 +90,7 @@ public class PostModel {
if (PostStatus.RECYCLE.equals(post.getStatus())) { if (PostStatus.RECYCLE.equals(post.getStatus())) {
// Articles in the recycle bin are not allowed to be accessed. // Articles in the recycle bin are not allowed to be accessed.
throw new NotFoundException("查询不到该文章的信息"); 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, // If the token is not empty, it means it is an admin request,
// then verify the token. // then verify the token.

View File

@ -1,6 +1,5 @@
package run.halo.app.core; package run.halo.app.core;
import cn.hutool.extra.servlet.ServletUtil;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Objects; 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.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import run.halo.app.utils.JsonUtils; import run.halo.app.utils.JsonUtils;
import run.halo.app.utils.ServletUtils;
/** /**
* @author johnniang * @author johnniang
@ -82,7 +82,7 @@ public class ControllerLogAop {
request.getRequestURL(), request.getRequestURL(),
request.getRequestURI(), request.getRequestURI(),
request.getMethod(), request.getMethod(),
ServletUtil.getClientIP(request)); ServletUtils.getClientIP(request));
if (args == null || !log.isDebugEnabled()) { if (args == null || !log.isDebugEnabled()) {
return; return;

View File

@ -1,12 +1,13 @@
package run.halo.app.core.freemarker.method; package run.halo.app.core.freemarker.method;
import cn.hutool.core.util.RandomUtil;
import freemarker.template.Configuration; import freemarker.template.Configuration;
import freemarker.template.SimpleNumber; import freemarker.template.SimpleNumber;
import freemarker.template.TemplateMethodModelEx; import freemarker.template.TemplateMethodModelEx;
import freemarker.template.TemplateModelException; import freemarker.template.TemplateModelException;
import java.util.List; import java.util.List;
import org.apache.commons.lang3.RandomUtils;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import run.halo.app.utils.HaloUtils;
/** /**
* Freemarker template random method. * Freemarker template random method.
@ -42,6 +43,6 @@ public class RandomMethod implements TemplateMethodModelEx {
SimpleNumber argTwo = (SimpleNumber) arguments.get(1); SimpleNumber argTwo = (SimpleNumber) arguments.get(1);
int start = argOne.getAsNumber().intValue(); int start = argOne.getAsNumber().intValue();
int end = argTwo.getAsNumber().intValue(); int end = argTwo.getAsNumber().intValue();
return RandomUtil.randomInt(start, end); return RandomUtils.nextInt(start, end);
} }
} }

View File

@ -2,7 +2,6 @@ package run.halo.app.core.freemarker.tag;
import static run.halo.app.model.support.HaloConst.URL_SEPARATOR; import static run.halo.app.model.support.HaloConst.URL_SEPARATOR;
import cn.hutool.core.util.PageUtil;
import freemarker.core.Environment; import freemarker.core.Environment;
import freemarker.template.Configuration; import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapperBuilder; 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.Pagination;
import run.halo.app.model.support.RainbowPage; import run.halo.app.model.support.RainbowPage;
import run.halo.app.service.OptionService; import run.halo.app.service.OptionService;
import run.halo.app.utils.HaloUtils;
/** /**
* @author ryanwang * @author ryanwang
@ -60,7 +60,7 @@ public class PaginationTagDirective implements TemplateDirectiveModel {
prevPageFullPath.append(optionService.getBlogBaseUrl()); 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<>(); List<RainbowPage> rainbowPages = new ArrayList<>();
StringBuilder fullPath = new StringBuilder(); StringBuilder fullPath = new StringBuilder();

View File

@ -1,7 +1,5 @@
package run.halo.app.core.freemarker.tag; 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.core.Environment;
import freemarker.template.Configuration; import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapperBuilder; import freemarker.template.DefaultObjectWrapperBuilder;
@ -11,8 +9,10 @@ import freemarker.template.TemplateException;
import freemarker.template.TemplateModel; import freemarker.template.TemplateModel;
import java.io.IOException; import java.io.IOException;
import java.util.Map; import java.util.Map;
import org.apache.commons.lang3.RandomUtils;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import run.halo.app.model.support.HaloConst; import run.halo.app.model.support.HaloConst;
import run.halo.app.utils.HaloUtils;
/** /**
* Freemarker custom tag of tools. * Freemarker custom tag of tools.
@ -41,12 +41,12 @@ public class ToolTagDirective implements TemplateDirectiveModel {
int total = Integer.parseInt(params.get("total").toString()); int total = Integer.parseInt(params.get("total").toString());
int display = Integer.parseInt(params.get("display").toString()); int display = Integer.parseInt(params.get("display").toString());
env.setVariable("numbers", env.setVariable("numbers",
builder.build().wrap(PageUtil.rainbow(page, total, display))); builder.build().wrap(HaloUtils.rainbow(page, total, display)));
break; break;
case "random": case "random":
int min = Integer.parseInt(params.get("min").toString()); int min = Integer.parseInt(params.get("min").toString());
int max = Integer.parseInt(params.get("max").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; break;
default: default:
break; break;

View File

@ -1,6 +1,5 @@
package run.halo.app.filter; package run.halo.app.filter;
import cn.hutool.extra.servlet.ServletUtil;
import java.io.IOException; import java.io.IOException;
import javax.servlet.FilterChain; import javax.servlet.FilterChain;
import javax.servlet.ServletException; import javax.servlet.ServletException;
@ -11,6 +10,7 @@ import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.filter.OncePerRequestFilter;
import run.halo.app.utils.ServletUtils;
/** /**
* Filter for logging. * Filter for logging.
@ -26,7 +26,7 @@ public class LogFilter extends OncePerRequestFilter {
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException { FilterChain filterChain) throws ServletException, IOException {
final String remoteAddr = ServletUtil.getClientIP(request); final String remoteAddr = ServletUtils.getClientIP(request);
log.debug("Starting url: [{}], method: [{}], ip: [{}]", log.debug("Starting url: [{}], method: [{}], ip: [{}]",
request.getRequestURL(), request.getRequestURL(),

View File

@ -1,7 +1,5 @@
package run.halo.app.listener.comment; 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.HashMap;
import java.util.Map; import java.util.Map;
import lombok.extern.slf4j.Slf4j; 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.SheetService;
import run.halo.app.service.ThemeService; import run.halo.app.service.ThemeService;
import run.halo.app.service.UserService; import run.halo.app.service.UserService;
import run.halo.app.utils.ValidationUtils;
/** /**
* PostComment event listener. * PostComment event listener.
@ -146,10 +145,10 @@ public class CommentEventListener {
Journal journal = journalService.getById(journalComment.getPostId()); Journal journal = journalService.getById(journalComment.getPostId());
StrBuilder url = new StrBuilder(optionService.getBlogBaseUrl()) StringBuilder url = new StringBuilder(optionService.getBlogBaseUrl())
.append("/") .append("/")
.append(optionService.getJournalsPrefix()); .append(optionService.getJournalsPrefix());
data.put("pageFullPath", url.toString()); data.put("pageFullPath", url);
data.put("pageTitle", journal.getCreateTime()); data.put("pageTitle", journal.getCreateTime());
data.put("author", journalComment.getAuthor()); data.put("author", journalComment.getAuthor());
data.put("content", journalComment.getContent()); data.put("content", journalComment.getContent());
@ -201,7 +200,7 @@ public class CommentEventListener {
PostComment baseComment = postCommentService.getById(postComment.getParentId()); PostComment baseComment = postCommentService.getById(postComment.getParentId());
if (StringUtils.isEmpty(baseComment.getEmail()) if (StringUtils.isEmpty(baseComment.getEmail())
&& !Validator.isEmail(baseComment.getEmail())) { && !ValidationUtils.isEmail(baseComment.getEmail())) {
return; return;
} }
@ -234,7 +233,7 @@ public class CommentEventListener {
SheetComment baseComment = sheetCommentService.getById(sheetComment.getParentId()); SheetComment baseComment = sheetCommentService.getById(sheetComment.getParentId());
if (StringUtils.isEmpty(baseComment.getEmail()) if (StringUtils.isEmpty(baseComment.getEmail())
&& !Validator.isEmail(baseComment.getEmail())) { && !ValidationUtils.isEmail(baseComment.getEmail())) {
return; return;
} }
@ -268,7 +267,7 @@ public class CommentEventListener {
journalCommentService.getById(journalComment.getParentId()); journalCommentService.getById(journalComment.getParentId());
if (StringUtils.isEmpty(baseComment.getEmail()) if (StringUtils.isEmpty(baseComment.getEmail())
&& !Validator.isEmail(baseComment.getEmail())) { && !ValidationUtils.isEmail(baseComment.getEmail())) {
return; return;
} }
@ -280,7 +279,7 @@ public class CommentEventListener {
Journal journal = journalService.getById(journalComment.getPostId()); Journal journal = journalService.getById(journalComment.getPostId());
StrBuilder url = new StrBuilder(optionService.getBlogBaseUrl()) StringBuilder url = new StringBuilder(optionService.getBlogBaseUrl())
.append("/") .append("/")
.append(optionService.getJournalsPrefix()); .append(optionService.getJournalsPrefix());
data.put("pageFullPath", url); data.put("pageFullPath", url);

View File

@ -2,7 +2,7 @@ package run.halo.app.model.enums;
/** /**
* @author zhixiang.yuan * @author zhixiang.yuan
* @since 2021/01/24 10:45:33 * @date 2021/01/24 10:45:33
*/ */
public enum EncryptTypeEnum { public enum EncryptTypeEnum {

View File

@ -1,6 +1,5 @@
package run.halo.app.security.handler; package run.halo.app.security.handler;
import cn.hutool.extra.servlet.ServletUtil;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException; import java.io.IOException;
import javax.servlet.http.HttpServletRequest; 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.model.support.BaseResponse;
import run.halo.app.utils.ExceptionUtils; import run.halo.app.utils.ExceptionUtils;
import run.halo.app.utils.JsonUtils; import run.halo.app.utils.JsonUtils;
import run.halo.app.utils.ServletUtils;
/** /**
* Default AuthenticationFailureHandler. * Default AuthenticationFailureHandler.
@ -32,7 +32,7 @@ public class DefaultAuthenticationFailureHandler implements AuthenticationFailur
@Override @Override
public void onFailure(HttpServletRequest request, HttpServletResponse response, public void onFailure(HttpServletRequest request, HttpServletResponse response,
AbstractHaloException exception) throws IOException { 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(), log.error("Authentication failure: [{}], status: [{}], data: [{}]", exception.getMessage(),
exception.getStatus(), exception.getErrorData()); exception.getStatus(), exception.getErrorData());

View File

@ -2,9 +2,6 @@ package run.halo.app.service.impl;
import static run.halo.app.model.support.HaloConst.DATABASE_PRODUCT_NAME; 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.File;
import java.io.IOException; import java.io.IOException;
import java.io.RandomAccessFile; import java.io.RandomAccessFile;
@ -15,6 +12,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisher;
import org.springframework.lang.NonNull; 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.service.UserService;
import run.halo.app.utils.HaloUtils; import run.halo.app.utils.HaloUtils;
import run.halo.app.utils.TwoFactorAuthUtils; import run.halo.app.utils.TwoFactorAuthUtils;
import run.halo.app.utils.ValidationUtils;
/** /**
* Admin service implementation. * Admin service implementation.
@ -98,7 +97,7 @@ public class AdminServiceImpl implements AdminService {
try { try {
// Get user by username or email // Get user by username or email
user = Validator.isEmail(username) user = ValidationUtils.isEmail(username)
? userService.getByEmailOfNonNull(username) : ? userService.getByEmailOfNonNull(username) :
userService.getByUsernameOfNonNull(username); userService.getByUsernameOfNonNull(username);
} catch (NotFoundException e) { } catch (NotFoundException e) {
@ -132,7 +131,7 @@ public class AdminServiceImpl implements AdminService {
// check authCode // check authCode
if (MFAType.useMFA(user.getMfaType())) { if (MFAType.useMFA(user.getMfaType())) {
if (StrUtil.isBlank(loginParam.getAuthcode())) { if (StringUtils.isBlank(loginParam.getAuthcode())) {
throw new BadRequestException("请输入两步验证码"); throw new BadRequestException("请输入两步验证码");
} }
TwoFactorAuthUtils.validateTFACode(user.getMfaKey(), loginParam.getAuthcode()); TwoFactorAuthUtils.validateTFACode(user.getMfaKey(), loginParam.getAuthcode());
@ -195,7 +194,7 @@ public class AdminServiceImpl implements AdminService {
} }
// Gets random code. // Gets random code.
String code = RandomUtil.randomNumbers(6); String code = RandomStringUtils.randomNumeric(6);
log.info("Got reset password code:{}", code); log.info("Got reset password code:{}", code);
@ -389,7 +388,7 @@ public class AdminServiceImpl implements AdminService {
boolean useMFA = true; boolean useMFA = true;
try { try {
final User user = Validator.isEmail(username) final User user = ValidationUtils.isEmail(username)
? userService.getByEmailOfNonNull(username) : ? userService.getByEmailOfNonNull(username) :
userService.getByUsernameOfNonNull(username); userService.getByUsernameOfNonNull(username);
useMFA = MFAType.useMFA(user.getMfaType()); useMFA = MFAType.useMFA(user.getMfaType());

View File

@ -1,10 +1,10 @@
package run.halo.app.service.impl; package run.halo.app.service.impl;
import cn.hutool.core.util.StrUtil;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import run.halo.app.model.entity.Category; import run.halo.app.model.entity.Category;
import run.halo.app.model.entity.Post; import run.halo.app.model.entity.Post;
@ -40,7 +40,7 @@ public class AuthenticationServiceImpl implements AuthenticationService {
public boolean postAuthentication(Post post, String password) { public boolean postAuthentication(Post post, String password) {
Set<String> accessPermissionStore = authorizationService.getAccessPermissionStore(); Set<String> accessPermissionStore = authorizationService.getAccessPermissionStore();
if (StrUtil.isNotBlank(post.getPassword())) { if (StringUtils.isNotBlank(post.getPassword())) {
if (accessPermissionStore.contains(AuthorizationService.buildPostToken(post.getId()))) { if (accessPermissionStore.contains(AuthorizationService.buildPostToken(post.getId()))) {
return true; return true;
} }
@ -86,7 +86,7 @@ public class AuthenticationServiceImpl implements AuthenticationService {
Category category = idToCategoryMap.get(categoryId); Category category = idToCategoryMap.get(categoryId);
if (StrUtil.isNotBlank(category.getPassword())) { if (StringUtils.isNotBlank(category.getPassword())) {
if (accessPermissionStore.contains( if (accessPermissionStore.contains(
AuthorizationService.buildCategoryToken(category.getId()))) { AuthorizationService.buildCategoryToken(category.getId()))) {
return true; return true;

View File

@ -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.DateTimeUtils.HORIZONTAL_LINE_DATETIME_FORMATTER;
import static run.halo.app.utils.FileUtils.checkDirectoryTraversal; 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.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException; import java.io.IOException;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.NoSuchFileException; import java.nio.file.NoSuchFileException;
import java.nio.file.Path; 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.ThemeSettingService;
import run.halo.app.service.UserService; import run.halo.app.service.UserService;
import run.halo.app.utils.DateTimeUtils; 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.HaloUtils;
import run.halo.app.utils.JsonUtils; import run.halo.app.utils.JsonUtils;
@ -105,6 +101,7 @@ import run.halo.app.utils.JsonUtils;
* @author johnniang * @author johnniang
* @author ryanwang * @author ryanwang
* @author Raremaa * @author Raremaa
* @author guqing
* @date 2019-04-26 * @date 2019-04-26
*/ */
@Service @Service
@ -209,7 +206,7 @@ public class BackupServiceImpl implements BackupService {
public BasePostDetailDTO importMarkdown(MultipartFile file) throws IOException { public BasePostDetailDTO importMarkdown(MultipartFile file) throws IOException {
// Read markdown content. // Read markdown content.
String markdown = IoUtil.read(file.getInputStream(), StandardCharsets.UTF_8); String markdown = FileUtils.readString(file.getInputStream());
// TODO sheet import // TODO sheet import
return postService.importMarkdown(markdown, file.getOriginalFilename()); return postService.importMarkdown(markdown, file.getOriginalFilename());
@ -222,7 +219,7 @@ public class BackupServiceImpl implements BackupService {
// Create zip path for halo zip // Create zip path for halo zip
String haloZipFileName = HALO_BACKUP_PREFIX String haloZipFileName = HALO_BACKUP_PREFIX
+ DateTimeUtils.format(LocalDateTime.now(), HORIZONTAL_LINE_DATETIME_FORMATTER) + DateTimeUtils.format(LocalDateTime.now(), HORIZONTAL_LINE_DATETIME_FORMATTER)
+ IdUtil.simpleUUID().hashCode() + ".zip"; + HaloUtils.simpleUUID().hashCode() + ".zip";
// Create halo zip file // Create halo zip file
Path haloZipFilePath = Paths.get(haloProperties.getBackupDir(), haloZipFileName); Path haloZipFilePath = Paths.get(haloProperties.getBackupDir(), haloZipFileName);
if (!Files.exists(haloZipFilePath.getParent())) { if (!Files.exists(haloZipFilePath.getParent())) {
@ -334,7 +331,7 @@ public class BackupServiceImpl implements BackupService {
public BackupDTO exportData() { public BackupDTO exportData() {
Map<String, Object> data = new HashMap<>(); Map<String, Object> data = new HashMap<>();
data.put("version", HaloConst.HALO_VERSION); 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("attachments", attachmentService.listAll());
data.put("categories", categoryService.listAll(true)); data.put("categories", categoryService.listAll(true));
data.put("comment_black_list", commentBlackListService.listAll()); data.put("comment_black_list", commentBlackListService.listAll());
@ -360,7 +357,7 @@ public class BackupServiceImpl implements BackupService {
try { try {
String haloDataFileName = HALO_DATA_EXPORT_PREFIX String haloDataFileName = HALO_DATA_EXPORT_PREFIX
+ DateTimeUtils.format(LocalDateTime.now(), HORIZONTAL_LINE_DATETIME_FORMATTER) + DateTimeUtils.format(LocalDateTime.now(), HORIZONTAL_LINE_DATETIME_FORMATTER)
+ IdUtil.simpleUUID().hashCode() + ".json"; + HaloUtils.simpleUUID().hashCode() + ".json";
Path haloDataFilePath = Paths.get(haloProperties.getDataExportDir(), haloDataFileName); Path haloDataFilePath = Paths.get(haloProperties.getDataExportDir(), haloDataFileName);
if (!Files.exists(haloDataFilePath.getParent())) { if (!Files.exists(haloDataFilePath.getParent())) {
@ -368,9 +365,7 @@ public class BackupServiceImpl implements BackupService {
} }
Path haloDataPath = Files.createFile(haloDataFilePath); Path haloDataPath = Files.createFile(haloDataFilePath);
FileWriter fileWriter = new FileWriter(haloDataPath.toFile(), CharsetUtil.UTF_8); FileUtils.writeStringToFile(haloDataPath.toFile(), JsonUtils.objectToJson(data));
fileWriter.write(JsonUtils.objectToJson(data));
return buildBackupDto(DATA_EXPORT_BASE_URI, haloDataPath); return buildBackupDto(DATA_EXPORT_BASE_URI, haloDataPath);
} catch (IOException e) { } catch (IOException e) {
throw new ServiceException("导出数据失败", e); throw new ServiceException("导出数据失败", e);
@ -420,7 +415,7 @@ public class BackupServiceImpl implements BackupService {
@Override @Override
public void importData(MultipartFile file) throws IOException { 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(); ObjectMapper mapper = JsonUtils.createDefaultJsonMapper();
TypeReference<HashMap<String, Object>> typeRef = TypeReference<HashMap<String, Object>> typeRef =
@ -534,7 +529,7 @@ public class BackupServiceImpl implements BackupService {
// Write files to the temporary directory // Write files to the temporary directory
String markdownFileTempPathName = String markdownFileTempPathName =
haloProperties.getBackupMarkdownDir() + IdUtil.simpleUUID().hashCode(); haloProperties.getBackupMarkdownDir() + HaloUtils.simpleUUID().hashCode();
for (PostMarkdownVO postMarkdownVo : postMarkdownList) { for (PostMarkdownVO postMarkdownVo : postMarkdownList) {
StringBuilder content = new StringBuilder(); StringBuilder content = new StringBuilder();
Boolean needFrontMatter = Boolean needFrontMatter =
@ -552,9 +547,7 @@ public class BackupServiceImpl implements BackupService {
Files.createDirectories(markdownFilePath.getParent()); Files.createDirectories(markdownFilePath.getParent());
} }
Path markdownDataPath = Files.createFile(markdownFilePath); Path markdownDataPath = Files.createFile(markdownFilePath);
FileWriter fileWriter = FileUtils.writeStringToFile(markdownDataPath.toFile(), content.toString());
new FileWriter(markdownDataPath.toFile(), CharsetUtil.UTF_8);
fileWriter.write(content.toString());
} catch (IOException e) { } catch (IOException e) {
throw new ServiceException("导出数据失败", e); throw new ServiceException("导出数据失败", e);
} }
@ -563,7 +556,7 @@ public class BackupServiceImpl implements BackupService {
// Create zip path // Create zip path
String markdownZipFileName = HALO_BACKUP_MARKDOWN_PREFIX String markdownZipFileName = HALO_BACKUP_MARKDOWN_PREFIX
+ DateTimeUtils.format(LocalDateTime.now(), HORIZONTAL_LINE_DATETIME_FORMATTER) + DateTimeUtils.format(LocalDateTime.now(), HORIZONTAL_LINE_DATETIME_FORMATTER)
+ IdUtil.simpleUUID().hashCode() + ".zip"; + HaloUtils.simpleUUID().hashCode() + ".zip";
// Create zip file // Create zip file
Path markdownZipFilePath = Path markdownZipFilePath =

View File

@ -2,7 +2,6 @@ package run.halo.app.service.impl;
import static org.springframework.data.domain.Sort.Direction.DESC; import static org.springframework.data.domain.Sort.Direction.DESC;
import cn.hutool.core.util.URLUtil;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; 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.UserService;
import run.halo.app.service.base.AbstractCrudService; import run.halo.app.service.base.AbstractCrudService;
import run.halo.app.service.base.BaseCommentService; import run.halo.app.service.base.BaseCommentService;
import run.halo.app.utils.HaloUtils;
import run.halo.app.utils.ServiceUtils; import run.halo.app.utils.ServiceUtils;
import run.halo.app.utils.ServletUtils; import run.halo.app.utils.ServletUtils;
import run.halo.app.utils.ValidationUtils; import run.halo.app.utils.ValidationUtils;
@ -328,7 +328,7 @@ public abstract class BaseCommentServiceImpl<COMMENT extends BaseComment>
} }
if (StringUtils.isNotEmpty(comment.getAuthorUrl())) { if (StringUtils.isNotEmpty(comment.getAuthorUrl())) {
comment.setAuthorUrl(URLUtil.normalize(comment.getAuthorUrl())); comment.setAuthorUrl(HaloUtils.normalizeUrl(comment.getAuthorUrl()));
} }
if (authentication != null) { if (authentication != null) {

View File

@ -2,8 +2,6 @@ package run.halo.app.service.impl;
import static run.halo.app.model.support.HaloConst.URL_SEPARATOR; 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 com.google.common.base.Objects;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
@ -15,6 +13,7 @@ import java.util.Optional;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.Page; 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.PostService;
import run.halo.app.service.base.AbstractCrudService; import run.halo.app.service.base.AbstractCrudService;
import run.halo.app.utils.BeanUtils; import run.halo.app.utils.BeanUtils;
import run.halo.app.utils.HaloUtils;
import run.halo.app.utils.ServiceUtils; 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()); category.setPassword(category.getPassword().trim());
} }
@ -298,7 +298,7 @@ public class CategoryServiceImpl extends AbstractCrudService<Category, Integer>
@Override @Override
public void refreshPostStatus(List<Integer> affectedPostIdList) { public void refreshPostStatus(List<Integer> affectedPostIdList) {
if (CollectionUtil.isEmpty(affectedPostIdList)) { if (CollectionUtils.isEmpty(affectedPostIdList)) {
return; return;
} }
@ -307,7 +307,7 @@ public class CategoryServiceImpl extends AbstractCrudService<Category, Integer>
post.setStatus(null); post.setStatus(null);
if (StrUtil.isNotBlank(post.getPassword())) { if (StringUtils.isNotBlank(post.getPassword())) {
post.setStatus(PostStatus.INTIMATE); post.setStatus(PostStatus.INTIMATE);
} else { } else {
postCategoryService.listByPostId(postId) postCategoryService.listByPostId(postId)
@ -368,7 +368,7 @@ public class CategoryServiceImpl extends AbstractCrudService<Category, Integer>
@Override @Override
public List<Category> filterEncryptCategory(List<Category> categories) { public List<Category> filterEncryptCategory(List<Category> categories) {
if (CollectionUtil.isEmpty(categories)) { if (CollectionUtils.isEmpty(categories)) {
return Collections.emptyList(); return Collections.emptyList();
} }
@ -399,7 +399,7 @@ public class CategoryServiceImpl extends AbstractCrudService<Category, Integer>
* @param categoryList category list * @param categoryList category list
*/ */
private void doFilterEncryptCategory(List<CategoryVO> categoryList) { private void doFilterEncryptCategory(List<CategoryVO> categoryList) {
if (CollectionUtil.isEmpty(categoryList)) { if (CollectionUtils.isEmpty(categoryList)) {
return; return;
} }
@ -423,7 +423,7 @@ public class CategoryServiceImpl extends AbstractCrudService<Category, Integer>
private void collectAllChild(List<Category> collectorList, private void collectAllChild(List<Category> collectorList,
List<CategoryVO> childrenList, List<CategoryVO> childrenList,
Boolean doNotCollectEncryptedCategory) { Boolean doNotCollectEncryptedCategory) {
if (CollectionUtil.isEmpty(childrenList)) { if (CollectionUtils.isEmpty(childrenList)) {
return; return;
} }
@ -439,7 +439,7 @@ public class CategoryServiceImpl extends AbstractCrudService<Category, Integer>
continue; continue;
} }
if (CollectionUtil.isNotEmpty(categoryVO.getChildren())) { if (HaloUtils.isNotEmpty(categoryVO.getChildren())) {
collectAllChild(collectorList, collectAllChild(collectorList,
categoryVO.getChildren(), doNotCollectEncryptedCategory); categoryVO.getChildren(), doNotCollectEncryptedCategory);
} }
@ -459,7 +459,7 @@ public class CategoryServiceImpl extends AbstractCrudService<Category, Integer>
List<CategoryVO> childrenList, List<CategoryVO> childrenList,
Integer categoryId, Integer categoryId,
Boolean doNotCollectEncryptedCategory) { Boolean doNotCollectEncryptedCategory) {
if (CollectionUtil.isEmpty(childrenList)) { if (CollectionUtils.isEmpty(childrenList)) {
return; return;
} }
@ -532,7 +532,7 @@ public class CategoryServiceImpl extends AbstractCrudService<Category, Integer>
public Category update(Category category) { public Category update(Category category) {
Category update = super.update(category); Category update = super.update(category);
if (StrUtil.isNotBlank(category.getPassword())) { if (StringUtils.isNotBlank(category.getPassword())) {
doEncryptPost(category); doEncryptPost(category);
} else { } else {
doDecryptPost(category); doDecryptPost(category);
@ -618,7 +618,7 @@ public class CategoryServiceImpl extends AbstractCrudService<Category, Integer>
.filter(postList -> !postList.isEmpty()) .filter(postList -> !postList.isEmpty())
.map(postList -> postList.stream() .map(postList -> postList.stream()
.filter(post -> StrUtil.isBlank(post.getPassword())) .filter(post -> StringUtils.isBlank(post.getPassword()))
.filter(post -> PostStatus.INTIMATE.equals(post.getStatus())) .filter(post -> PostStatus.INTIMATE.equals(post.getStatus()))
.map(Post::getId).collect(Collectors.toList())) .map(Post::getId).collect(Collectors.toList()))
@ -652,7 +652,7 @@ public class CategoryServiceImpl extends AbstractCrudService<Category, Integer>
Category category = idToCategoryMap.get(categoryId); Category category = idToCategoryMap.get(categoryId);
if (StrUtil.isNotBlank(category.getPassword())) { if (StringUtils.isNotBlank(category.getPassword())) {
return true; return true;
} }

View File

@ -1,6 +1,5 @@
package run.halo.app.service.impl; package run.halo.app.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -12,6 +11,7 @@ import org.springframework.data.domain.PageImpl;
import org.springframework.lang.NonNull; import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import run.halo.app.exception.NotFoundException; import run.halo.app.exception.NotFoundException;
import run.halo.app.model.dto.JournalDTO; import run.halo.app.model.dto.JournalDTO;
import run.halo.app.model.entity.Journal; import run.halo.app.model.entity.Journal;
@ -56,7 +56,7 @@ public class JournalCommentServiceImpl extends BaseCommentServiceImpl<JournalCom
public List<JournalCommentWithJournalVO> convertToWithJournalVo( public List<JournalCommentWithJournalVO> convertToWithJournalVo(
List<JournalComment> journalComments) { List<JournalComment> journalComments) {
if (CollectionUtil.isEmpty(journalComments)) { if (CollectionUtils.isEmpty(journalComments)) {
return Collections.emptyList(); return Collections.emptyList();
} }

View File

@ -2,7 +2,6 @@ package run.halo.app.service.impl;
import static run.halo.app.model.support.HaloConst.URL_SEPARATOR; import static run.halo.app.model.support.HaloConst.URL_SEPARATOR;
import cn.hutool.core.date.DateUtil;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; 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.OptionService;
import run.halo.app.service.PostCommentService; import run.halo.app.service.PostCommentService;
import run.halo.app.service.UserService; import run.halo.app.service.UserService;
import run.halo.app.utils.DateUtils;
import run.halo.app.utils.ServiceUtils; import run.halo.app.utils.ServiceUtils;
import run.halo.app.utils.ServletUtils; import run.halo.app.utils.ServletUtils;
@ -128,11 +128,11 @@ public class PostCommentServiceImpl extends BaseCommentServiceImpl<PostComment>
String archivesPrefix = optionService.getArchivesPrefix(); 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); 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); String dayString = day < 10 ? "0" + day : String.valueOf(day);
@ -153,14 +153,14 @@ public class PostCommentServiceImpl extends BaseCommentServiceImpl<PostComment>
fullPath.append("?p=") fullPath.append("?p=")
.append(post.getId()); .append(post.getId());
} else if (permalinkType.equals(PostPermalinkType.DATE)) { } else if (permalinkType.equals(PostPermalinkType.DATE)) {
fullPath.append(DateUtil.year(post.getCreateTime())) fullPath.append(DateUtils.year(post.getCreateTime()))
.append(URL_SEPARATOR) .append(URL_SEPARATOR)
.append(monthString) .append(monthString)
.append(URL_SEPARATOR) .append(URL_SEPARATOR)
.append(post.getSlug()) .append(post.getSlug())
.append(pathSuffix); .append(pathSuffix);
} else if (permalinkType.equals(PostPermalinkType.DAY)) { } else if (permalinkType.equals(PostPermalinkType.DAY)) {
fullPath.append(DateUtil.year(post.getCreateTime())) fullPath.append(DateUtils.year(post.getCreateTime()))
.append(URL_SEPARATOR) .append(URL_SEPARATOR)
.append(monthString) .append(monthString)
.append(URL_SEPARATOR) .append(URL_SEPARATOR)
@ -169,7 +169,7 @@ public class PostCommentServiceImpl extends BaseCommentServiceImpl<PostComment>
.append(post.getSlug()) .append(post.getSlug())
.append(pathSuffix); .append(pathSuffix);
} else if (permalinkType.equals(PostPermalinkType.YEAR)) { } else if (permalinkType.equals(PostPermalinkType.YEAR)) {
fullPath.append(DateUtil.year(post.getCreateTime())) fullPath.append(DateUtils.year(post.getCreateTime()))
.append(URL_SEPARATOR) .append(URL_SEPARATOR)
.append(post.getSlug()) .append(post.getSlug())
.append(pathSuffix); .append(pathSuffix);

View File

@ -3,9 +3,6 @@ package run.halo.app.service.impl;
import static org.springframework.data.domain.Sort.Direction.DESC; import static org.springframework.data.domain.Sort.Direction.DESC;
import static run.halo.app.model.support.HaloConst.URL_SEPARATOR; 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.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collection; 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.PostTagService;
import run.halo.app.service.TagService; import run.halo.app.service.TagService;
import run.halo.app.utils.DateUtils; import run.halo.app.utils.DateUtils;
import run.halo.app.utils.HaloUtils;
import run.halo.app.utils.MarkdownUtils; import run.halo.app.utils.MarkdownUtils;
import run.halo.app.utils.ServiceUtils; import run.halo.app.utils.ServiceUtils;
import run.halo.app.utils.SlugUtils; import run.halo.app.utils.SlugUtils;
@ -382,9 +380,9 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
for (String key : frontMatter.keySet()) { for (String key : frontMatter.keySet()) {
elementValue = frontMatter.get(key); elementValue = frontMatter.get(key);
for (String ele : elementValue) { for (String ele : elementValue) {
ele = StrUtil.strip(ele, "[", "]"); ele = HaloUtils.strip(ele, "[", "]");
ele = StrUtil.strip(ele, "\""); ele = StringUtils.strip(ele, "\"");
ele = StrUtil.strip(ele, "\'"); ele = StringUtils.strip(ele, "\'");
if ("".equals(ele)) { if ("".equals(ele)) {
continue; continue;
} }
@ -393,7 +391,7 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
post.setTitle(ele); post.setTitle(ele);
break; break;
case "date": case "date":
post.setCreateTime(DateUtil.parse(ele)); post.setCreateTime(DateUtils.parseDate(ele));
break; break;
case "permalink": case "permalink":
post.setSlug(ele); post.setSlug(ele);
@ -411,8 +409,8 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
Tag tag; Tag tag;
for (String tagName : ele.split(",")) { for (String tagName : ele.split(",")) {
tagName = tagName.trim(); tagName = tagName.trim();
tagName = StrUtil.strip(tagName, "\""); tagName = StringUtils.strip(tagName, "\"");
tagName = StrUtil.strip(tagName, "\'"); tagName = StringUtils.strip(tagName, "\'");
tag = tagService.getByName(tagName); tag = tagService.getByName(tagName);
if (null == tag) { if (null == tag) {
tag = new Tag(); tag = new Tag();
@ -427,8 +425,8 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
Integer lastCategoryId = null; Integer lastCategoryId = null;
for (String categoryName : ele.split(",")) { for (String categoryName : ele.split(",")) {
categoryName = categoryName.trim(); categoryName = categoryName.trim();
categoryName = StrUtil.strip(categoryName, "\""); categoryName = StringUtils.strip(categoryName, "\"");
categoryName = StrUtil.strip(categoryName, "\'"); categoryName = StringUtils.strip(categoryName, "\'");
Category category = categoryService.getByName(categoryName); Category category = categoryService.getByName(categoryName);
if (null == category) { if (null == category) {
category = new Category(); category = new Category();
@ -843,7 +841,7 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
// Create or update post // Create or update post
Boolean needEncrypt = Optional.ofNullable(categoryIds) Boolean needEncrypt = Optional.ofNullable(categoryIds)
.filter(CollectionUtil::isNotEmpty) .filter(HaloUtils::isNotEmpty)
.map(categoryIdSet -> { .map(categoryIdSet -> {
for (Integer categoryId : categoryIdSet) { for (Integer categoryId : categoryIdSet) {
if (categoryService.categoryHasEncrypt(categoryId)) { if (categoryService.categoryHasEncrypt(categoryId)) {
@ -996,11 +994,11 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
String archivesPrefix = optionService.getArchivesPrefix(); 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); 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); String dayString = day < 10 ? "0" + day : String.valueOf(day);
@ -1021,14 +1019,14 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
fullPath.append("?p=") fullPath.append("?p=")
.append(post.getId()); .append(post.getId());
} else if (permalinkType.equals(PostPermalinkType.DATE)) { } else if (permalinkType.equals(PostPermalinkType.DATE)) {
fullPath.append(DateUtil.year(post.getCreateTime())) fullPath.append(DateUtils.year(post.getCreateTime()))
.append(URL_SEPARATOR) .append(URL_SEPARATOR)
.append(monthString) .append(monthString)
.append(URL_SEPARATOR) .append(URL_SEPARATOR)
.append(post.getSlug()) .append(post.getSlug())
.append(pathSuffix); .append(pathSuffix);
} else if (permalinkType.equals(PostPermalinkType.DAY)) { } else if (permalinkType.equals(PostPermalinkType.DAY)) {
fullPath.append(DateUtil.year(post.getCreateTime())) fullPath.append(DateUtils.year(post.getCreateTime()))
.append(URL_SEPARATOR) .append(URL_SEPARATOR)
.append(monthString) .append(monthString)
.append(URL_SEPARATOR) .append(URL_SEPARATOR)
@ -1037,7 +1035,7 @@ public class PostServiceImpl extends BasePostServiceImpl<Post> implements PostSe
.append(post.getSlug()) .append(post.getSlug())
.append(pathSuffix); .append(pathSuffix);
} else if (permalinkType.equals(PostPermalinkType.YEAR)) { } else if (permalinkType.equals(PostPermalinkType.YEAR)) {
fullPath.append(DateUtil.year(post.getCreateTime())) fullPath.append(DateUtils.year(post.getCreateTime()))
.append(URL_SEPARATOR) .append(URL_SEPARATOR)
.append(post.getSlug()) .append(post.getSlug())
.append(pathSuffix); .append(pathSuffix);

View File

@ -1,6 +1,5 @@
package run.halo.app.service.impl; package run.halo.app.service.impl;
import cn.hutool.core.util.IdUtil;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.FileAlreadyExistsException; 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.model.support.StaticFile;
import run.halo.app.service.StaticStorageService; import run.halo.app.service.StaticStorageService;
import run.halo.app.utils.FileUtils; import run.halo.app.utils.FileUtils;
import run.halo.app.utils.HaloUtils;
/** /**
* StaticStorageService implementation class. * StaticStorageService implementation class.
@ -69,7 +69,7 @@ public class StaticStorageServiceImpl
pathStream.forEach(path -> { pathStream.forEach(path -> {
StaticFile staticFile = new StaticFile(); StaticFile staticFile = new StaticFile();
staticFile.setId(IdUtil.fastSimpleUUID()); staticFile.setId(HaloUtils.simpleUUID());
staticFile.setName(path.getFileName().toString()); staticFile.setName(path.getFileName().toString());
staticFile.setPath(path.toString()); staticFile.setPath(path.toString());
staticFile.setRelativePath( staticFile.setRelativePath(

View File

@ -1,6 +1,5 @@
package run.halo.app.service.impl; package run.halo.app.service.impl;
import cn.hutool.crypto.digest.BCrypt;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Optional; 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.repository.UserRepository;
import run.halo.app.service.UserService; import run.halo.app.service.UserService;
import run.halo.app.service.base.AbstractCrudService; import run.halo.app.service.base.AbstractCrudService;
import run.halo.app.utils.BCrypt;
import run.halo.app.utils.DateUtils; import run.halo.app.utils.DateUtils;
import run.halo.app.utils.HaloUtils; import run.halo.app.utils.HaloUtils;

View File

@ -1,6 +1,5 @@
package run.halo.app.task; package run.halo.app.task;
import cn.hutool.core.date.DateUtil;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import java.util.List; 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.model.properties.PostProperties;
import run.halo.app.service.OptionService; import run.halo.app.service.OptionService;
import run.halo.app.service.PostService; import run.halo.app.service.PostService;
import run.halo.app.utils.DateTimeUtils;
/** /**
* @author Wh1te * @author Wh1te
* @author guqing
* @date 2020-10-19 * @date 2020-10-19
*/ */
@Slf4j @Slf4j
@ -73,7 +74,7 @@ public class RecycledPostCleaningTask {
List<Post> recyclePost = postService.listAllBy(PostStatus.RECYCLE); List<Post> recyclePost = postService.listAllBy(PostStatus.RECYCLE);
LocalDateTime now = LocalDateTime.now(); LocalDateTime now = LocalDateTime.now();
List<Integer> ids = recyclePost.stream().filter(post -> { 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); long until = updateTime.until(now, ChronoUnit.HOURS);
return until >= expiredIn; return until >= expiredIn;
}).map(BasePost::getId).collect(Collectors.toList()); }).map(BasePost::getId).collect(Collectors.toList());

View File

@ -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 />
* &nbsp;&nbsp;&nbsp;&nbsp;System.out.println("It matches");<br />
* else<br />
* &nbsp;&nbsp;&nbsp;&nbsp;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));
}
}

View File

@ -1,10 +1,5 @@
package run.halo.app.utils; 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.Duration;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDate; import java.time.LocalDate;
@ -15,14 +10,34 @@ import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField; import java.time.temporal.ChronoField;
import java.time.temporal.Temporal; import java.time.temporal.Temporal;
import java.util.Date;
/** /**
* *
* *
* @author LeiXinXin * @author LeiXinXin
* @author guqing
* @date 2019/12/10 * @date 2019/12/10
*/ */
public class DateTimeUtils { 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 * {@link DateTimeFormatter}yyyyMMddHHmmssSSS
*/ */
@ -570,4 +585,14 @@ public class DateTimeUtils {
public static boolean isLessThanOrEqual(Temporal startInclusive, Temporal endInclusive) { public static boolean isLessThanOrEqual(Temporal startInclusive, Temporal endInclusive) {
return Duration.between(startInclusive, endInclusive).toNanos() <= 0; 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());
}
} }

View File

@ -1,7 +1,10 @@
package run.halo.app.utils; package run.halo.app.utils;
import java.text.ParseException;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.springframework.lang.NonNull; import org.springframework.lang.NonNull;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -10,9 +13,11 @@ import org.springframework.util.Assert;
* Date utilities. * Date utilities.
* *
* @author johnniang * @author johnniang
* @author guqing
* @date 3/18/19 * @date 3/18/19
*/ */
public class DateUtils { public class DateUtils {
private static final int FIRST_DAY_OF_WEEK = Calendar.MONDAY;
private DateUtils() { private DateUtils() {
} }
@ -88,4 +93,119 @@ public class DateUtils {
} }
return result; 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;
}
} }

View File

@ -1,10 +1,13 @@
package run.halo.app.utils; package run.halo.app.utils;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.DirectoryNotEmptyException; import java.nio.file.DirectoryNotEmptyException;
import java.nio.file.FileVisitResult; import java.nio.file.FileVisitResult;
import java.nio.file.Files; import java.nio.file.Files;
@ -512,4 +515,28 @@ public class FileUtils {
return tempDirectory; 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);
}
} }

View File

@ -2,15 +2,30 @@ package run.halo.app.utils;
import static run.halo.app.model.support.HaloConst.FILE_SEPARATOR; 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.InetAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID; import java.util.UUID;
import javax.imageio.ImageIO;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.lang.NonNull; import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import run.halo.app.model.support.HaloConst; import run.halo.app.model.support.HaloConst;
/** /**
@ -18,6 +33,7 @@ import run.halo.app.model.support.HaloConst;
* *
* @author ryanwang * @author ryanwang
* @author johnniang * @author johnniang
* @author guqing
* @date 2017-12-22 * @date 2017-12-22
*/ */
@Slf4j @Slf4j
@ -235,7 +251,7 @@ public class HaloUtils {
} }
/** /**
* Normalize url * Normalize url.
* *
* @param originalUrl original url * @param originalUrl original url
* @return normalized url. * @return normalized url.
@ -250,7 +266,40 @@ public class HaloUtils {
return originalUrl; 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 * @param content html document
* @return text before cleaned * @return text before cleaned
@ -280,4 +329,134 @@ public class HaloUtils {
} }
return content.replaceAll(RE_HTML_MARK, StringUtils.EMPTY); 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);
}
}
} }

View File

@ -1,6 +1,5 @@
package run.halo.app.utils; package run.halo.app.utils;
import cn.hutool.core.lang.Tuple;
import java.net.URI; import java.net.URI;
import java.security.KeyManagementException; import java.security.KeyManagementException;
import java.security.KeyStoreException; import java.security.KeyStoreException;
@ -71,15 +70,15 @@ public class HttpClientUtils {
final HttpClientBuilder httpClientBuilder) { final HttpClientBuilder httpClientBuilder) {
final String httpProxyEnv = System.getenv("http_proxy"); final String httpProxyEnv = System.getenv("http_proxy");
if (StringUtils.isNotBlank(httpProxyEnv)) { if (StringUtils.isNotBlank(httpProxyEnv)) {
final Tuple httpProxy = resolveHttpProxy(httpProxyEnv); final String[] httpProxy = resolveHttpProxy(httpProxyEnv);
final HttpHost httpHost = HttpHost.create(httpProxy.get(0)); final HttpHost httpHost = HttpHost.create(httpProxy[0]);
httpClientBuilder.setProxy(httpHost); httpClientBuilder.setProxy(httpHost);
if (httpProxy.getMembers().length == 3) { if (httpProxy.length == 3) {
//set proxy credentials //set proxy credentials
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider credentialsProvider
.setCredentials(new AuthScope(httpHost.getHostName(), httpHost.getPort()), .setCredentials(new AuthScope(httpHost.getHostName(), httpHost.getPort()),
new UsernamePasswordCredentials(httpProxy.get(1), httpProxy.get(2))); new UsernamePasswordCredentials(httpProxy[1], httpProxy[2]));
httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
} }
} }
@ -91,7 +90,7 @@ public class HttpClientUtils {
* @return resolved http proxy values; first is host(@nonNull), second is username(@nullable) * @return resolved http proxy values; first is host(@nonNull), second is username(@nullable)
* , third is password(@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); final URI proxyUri = URI.create(httpProxy);
int port = proxyUri.getPort(); int port = proxyUri.getPort();
if (port == -1) { if (port == -1) {
@ -115,9 +114,9 @@ public class HttpClientUtils {
username = usernamePassword; username = usernamePassword;
password = null; password = null;
} }
return new Tuple(hostUrl, username, password); return new String[] {hostUrl, username, password};
} else { } else {
return new Tuple(hostUrl); return new String[] {hostUrl};
} }
} }

View File

@ -1,8 +1,10 @@
package run.halo.app.utils; package run.halo.app.utils;
import cn.hutool.extra.servlet.ServletUtil; import java.util.Enumeration;
import java.util.Optional; import java.util.Optional;
import javax.servlet.http.HttpServletRequest; 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.NonNull;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.RequestContextHolder;
@ -12,6 +14,7 @@ import org.springframework.web.context.request.ServletRequestAttributes;
* Servlet utilities. * Servlet utilities.
* *
* @author johnniang * @author johnniang
* @author guqing
* @date 19-4-21 * @date 19-4-21
*/ */
public class ServletUtils { public class ServletUtils {
@ -39,7 +42,7 @@ public class ServletUtils {
*/ */
@Nullable @Nullable
public static String getRequestIp() { 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 @Nullable
public static String getHeaderIgnoreCase(String header) { public static String getHeaderIgnoreCase(String header) {
return getCurrentRequest().map(request -> ServletUtil.getHeaderIgnoreCase(request, header)) return getCurrentRequest().map(request -> ServletUtils.getHeaderIgnoreCase(request, header))
.orElse(null); .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>
* 1X-Forwarded-For
* 2X-Real-IP
* 3Proxy-Client-IP
* 4WL-Proxy-Client-IP
* </pre>
*
* <p>
* otherHeaderNamesHeader<br>
* 使IPHttpNginxIP
* </p>
*
* @param request {@link HttpServletRequest}
* @param otherHeaderNames HttpNginx
* @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>
* headerNamesHeader<br>
* 使IPHttpNginxIP
* </p>
*
* @param request {@link HttpServletRequest}
* @param headerNames HttpNginx
* @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;
}
} }

View File

@ -7,6 +7,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern;
import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException; import javax.validation.ConstraintViolationException;
import javax.validation.Path; import javax.validation.Path;
@ -26,6 +27,15 @@ import org.springframework.validation.FieldError;
*/ */
public class ValidationUtils { 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 static volatile Validator VALIDATOR;
private ValidationUtils() { private ValidationUtils() {
@ -146,4 +156,14 @@ public class ValidationUtils {
filedError -> errMap.put(filedError.getField(), filedError.getDefaultMessage())); filedError -> errMap.put(filedError.getField(), filedError.getDefaultMessage()));
return errMap; return errMap;
} }
/**
*
*
* @param email
* @return {@code true}, {@code false}
*/
public static boolean isEmail(String email) {
return EMAIL.matcher(email).matches();
}
} }

View File

@ -1,18 +1,482 @@
package run.halo.app.utils; 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 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; import org.junit.jupiter.api.Test;
/** /**
* BCrypt 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 johnniang
* @author guqing
* @date 3/28/19 * @date 3/28/19
*/ */
@Slf4j @Slf4j
class BcryptTest { 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 @Test
void cryptTest() { void cryptTest() {
String cryptPassword = BCrypt.hashpw("opentest", BCrypt.gensalt()); String cryptPassword = BCrypt.hashpw("opentest", BCrypt.gensalt());

View File

@ -1,6 +1,5 @@
package run.halo.app.utils; package run.halo.app.utils;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotNull;
@ -13,17 +12,21 @@ import java.time.LocalDateTime;
import java.time.LocalTime; import java.time.LocalTime;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.GregorianCalendar;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
/** /**
* DateTimeUtils * DateTimeUtils .
* *
* @author LeiXinXin * @author LeiXinXin
* @author guqing
* @date 2020/1/9 * @date 2020/1/9
*/ */
class DateTimeUtilsTest { class DateTimeUtilsTest {
/** /**
* * .
*/ */
@Test @Test
void nowTest() { void nowTest() {
@ -37,7 +40,7 @@ class DateTimeUtilsTest {
} }
/** /**
* * .
*/ */
@Test @Test
void formatTest() { void formatTest() {
@ -63,7 +66,7 @@ class DateTimeUtilsTest {
} }
/** /**
* * .
*/ */
@Test @Test
void plusTest() { void plusTest() {
@ -75,7 +78,6 @@ class DateTimeUtilsTest {
final LocalDateTime localDateTime1 = DateTimeUtils.plusOneMinute(now, localTime); final LocalDateTime localDateTime1 = DateTimeUtils.plusOneMinute(now, localTime);
assertEquals("2020-01-09T07:31", localDateTime1.toString()); assertEquals("2020-01-09T07:31", localDateTime1.toString());
final LocalDate date = LocalDate.of(2020, 1, 3); final LocalDate date = LocalDate.of(2020, 1, 3);
final LocalDateTime localDateTime2 = DateTimeUtils.plusOneMinute(date, localTime); final LocalDateTime localDateTime2 = DateTimeUtils.plusOneMinute(date, localTime);
assertEquals("2020-01-03T07:31", localDateTime2.toString()); assertEquals("2020-01-03T07:31", localDateTime2.toString());
@ -88,7 +90,7 @@ class DateTimeUtilsTest {
} }
/** /**
* LocalDateTime * LocalDateTime.
*/ */
@Test @Test
void parseTest() { void parseTest() {
@ -103,7 +105,7 @@ class DateTimeUtilsTest {
} }
/** /**
* * .
*/ */
@Test @Test
void minusTest() { void minusTest() {
@ -113,7 +115,7 @@ class DateTimeUtilsTest {
} }
/** /**
* Instant * Instant.
*/ */
@Test @Test
void toInstantTest() { void toInstantTest() {
@ -127,7 +129,7 @@ class DateTimeUtilsTest {
} }
/** /**
* 使 * 使.
*/ */
@Test @Test
void other() { void other() {
@ -163,4 +165,12 @@ class DateTimeUtilsTest {
final LocalDateTime time = DateTimeUtils.secondAndNanoSetZero(localDateTime); final LocalDateTime time = DateTimeUtils.secondAndNanoSetZero(localDateTime);
assertEquals("2020-01-05T06:40", time.toString()); 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());
}
} }

View File

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

View File

@ -1,10 +1,13 @@
package run.halo.app.utils; 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.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile; import java.io.RandomAccessFile;
import java.nio.file.FileAlreadyExistsException; import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files; import java.nio.file.Files;
@ -306,4 +309,12 @@ class FileUtilsTest {
assertTrue(Files.exists(target.resolve(".git"))); assertTrue(Files.exists(target.resolve(".git")));
assertEquals("test", Files.readString(target.resolve(".git").resolve("test.txt"))); 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));
}
} }

View File

@ -1,6 +1,7 @@
package run.halo.app.utils; 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.EmojiExtension;
import com.vladsch.flexmark.ext.emoji.EmojiImageType; import com.vladsch.flexmark.ext.emoji.EmojiImageType;
import com.vladsch.flexmark.ext.emoji.EmojiShortcutType; import com.vladsch.flexmark.ext.emoji.EmojiShortcutType;
@ -54,7 +55,7 @@ public class FootnoteTest {
+ "with continuation"; + "with continuation";
String s = renderHtml(markdown); String s = renderHtml(markdown);
System.out.println(s); 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" + " href=\"#fn1\">[1]</a></sup> embedded.</p>\n"
+ "<hr class=\"footnotes-sep\" />\n" + "<hr class=\"footnotes-sep\" />\n"
+ "<section class=\"footnotes\">\n" + "<section class=\"footnotes\">\n"
@ -78,7 +79,7 @@ public class FootnoteTest {
+ "[^another]: footnote text\n" + "[^another]: footnote text\n"
+ "with continuation"; + "with continuation";
String s = renderHtml(markdown); 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" + " href=\"#fn1\">[1]</a></sup> embedded.</p>\n"
+ "<hr class=\"footnotes-sep\" />\n" + "<hr class=\"footnotes-sep\" />\n"
+ "<section class=\"footnotes\">\n" + "<section class=\"footnotes\">\n"
@ -107,7 +108,7 @@ public class FootnoteTest {
+ "[^another]: footnote text with [^another] embedded footnote\n" + "[^another]: footnote text with [^another] embedded footnote\n"
+ "with continuation"; + "with continuation";
String s = renderHtml(markdown); 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" + " href=\"#fn1\">[1]</a></sup> embedded.</p>\n"
+ "<hr class=\"footnotes-sep\" />\n" + "<hr class=\"footnotes-sep\" />\n"
+ "<section class=\"footnotes\">\n" + "<section class=\"footnotes\">\n"
@ -142,7 +143,7 @@ public class FootnoteTest {
+ " - item 2\n" + " - item 2\n"
+ "."; + ".";
String s = renderHtml(markdown); 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" + "class=\"footnote-ref\"><a id=\"fnref1\" href=\"#fn1\">[1]</a></sup>.</p>\n"
+ "<hr class=\"footnotes-sep\" />\n" + "<hr class=\"footnotes-sep\" />\n"
+ "<section class=\"footnotes\">\n" + "<section class=\"footnotes\">\n"
@ -180,7 +181,7 @@ public class FootnoteTest {
+ " - item 2\n" + " - item 2\n"
+ "."; + ".";
String s = renderHtml(markdown); 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" + "<p>[ ^footnote]: This is the body of the footnote.<br />\n"
+ "with continuation text. Inline <em>italic</em> and<br />\n" + "with continuation text. Inline <em>italic</em> and<br />\n"
+ "<strong>bold</strong>.</p>\n" + "<strong>bold</strong>.</p>\n"
@ -213,7 +214,7 @@ public class FootnoteTest {
+ " - item 2\n" + " - item 2\n"
+ "."; + ".";
String s = renderHtml(markdown); 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" + "class=\"footnote-ref\"><a id=\"fnref1\" href=\"#fn1\">[1]</a></sup>.</p>\n"
+ "<hr class=\"footnotes-sep\" />\n" + "<hr class=\"footnotes-sep\" />\n"
+ "<section class=\"footnotes\">\n" + "<section class=\"footnotes\">\n"
@ -251,7 +252,7 @@ public class FootnoteTest {
+ " - item 2\n" + " - item 2\n"
+ "."; + ".";
String s = renderHtml(markdown); String s = renderHtml(markdown);
Assert.isTrue(StringUtils assertTrue(StringUtils
.equals(s, "<p>This paragraph has a footnote[^<strong>footnote</strong>].</p>\n")); .equals(s, "<p>This paragraph has a footnote[^<strong>footnote</strong>].</p>\n"));
} }
@ -275,7 +276,7 @@ public class FootnoteTest {
+ " - item 2\n" + " - item 2\n"
+ "."; + ".";
String s = renderHtml(markdown); 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 " + "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>" + "another<sup class=\"footnote-ref\"><a id=\"fnref2\" href=\"#fn2\">[2]</a></sup>"
+ ".</p>\n" + ".</p>\n"
@ -325,7 +326,7 @@ public class FootnoteTest {
+ " **bold**.\n" + " **bold**.\n"
+ "."; + ".";
String s = renderHtml(markdown); 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" + "class=\"footnote-ref\"><a id=\"fnref1\" href=\"#fn1\">[1]</a></sup>.</p>\n"
+ "<hr class=\"footnotes-sep\" />\n" + "<hr class=\"footnotes-sep\" />\n"
+ "<section class=\"footnotes\">\n" + "<section class=\"footnotes\">\n"

View File

@ -1,8 +1,11 @@
package run.halo.app.utils; package run.halo.app.utils;
import static org.junit.jupiter.api.Assertions.assertEquals; 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.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.List;
import java.util.stream.IntStream; import java.util.stream.IntStream;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomUtils; import org.apache.commons.lang3.RandomUtils;
@ -13,6 +16,7 @@ import org.junit.jupiter.api.Test;
* *
* @author johnniang * @author johnniang
* @author ryanwang * @author ryanwang
* @author guqing
* @date 2019-03-29 * @date 2019-03-29
*/ */
@Slf4j @Slf4j
@ -153,4 +157,11 @@ class HaloUtilsTest {
assertEquals("https://cn.gravatar.com/avatar?d=mm", assertEquals("https://cn.gravatar.com/avatar?d=mm",
HaloUtils.normalizeUrl("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")));
}
} }

View File

@ -3,7 +3,6 @@ package run.halo.app.utils;
import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import cn.hutool.core.lang.Tuple;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -15,20 +14,20 @@ public class HttpClientUtilsTest {
HttpClientUtils.class.getDeclaredMethod("resolveHttpProxy", String.class); HttpClientUtils.class.getDeclaredMethod("resolveHttpProxy", String.class);
resolveHttpProxy.setAccessible(true); resolveHttpProxy.setAccessible(true);
Tuple result = (Tuple) resolveHttpProxy.invoke(null, "http://127.0.0.1"); Object[] result = (Object[]) resolveHttpProxy.invoke(null, "http://127.0.0.1");
assertEquals(result.get(0), "http://127.0.0.1:80"); assertEquals("http://127.0.0.1:80", result[0]);
result = (Tuple) resolveHttpProxy.invoke(null, "https://127.0.0.1"); result = (Object[]) resolveHttpProxy.invoke(null, "https://127.0.0.1");
assertEquals(result.get(0), "https://127.0.0.1:443"); assertEquals("https://127.0.0.1:443", result[0]);
result = (Tuple) resolveHttpProxy.invoke(null, "https://127.0.0.1:123"); result = (Object[]) resolveHttpProxy.invoke(null, "https://127.0.0.1:123");
assertEquals(result.get(0), "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"); result = (Object[]) resolveHttpProxy.invoke(null, "https://u:p@127.0.0.1:123");
assertArrayEquals(result.getMembers(), new Object[] {"https://127.0.0.1:123", "u", "p"}); assertArrayEquals(new Object[] {"https://127.0.0.1:123", "u", "p"}, result);
result = (Tuple) resolveHttpProxy.invoke(null, "https://u@127.0.0.1"); result = (Object[]) resolveHttpProxy.invoke(null, "https://u@127.0.0.1");
assertArrayEquals(result.getMembers(), new Object[] {"https://127.0.0.1:443", "u", null}); assertArrayEquals(new Object[] {"https://127.0.0.1:443", "u", null}, result);
} }
} }

View File

@ -1,13 +1,16 @@
package run.halo.app.utils; 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 lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
/** /**
* @author zhixiang.yuan * @author zhixiang.yuan
* @since 2020/07/19 20:22:58 * @author guqing
* @date 2020/07/19 20:22:58
*/ */
@Slf4j @Slf4j
class MarkdownUtilsTest { class MarkdownUtilsTest {
@ -17,19 +20,19 @@ class MarkdownUtilsTest {
String markdown = "---\n" String markdown = "---\n"
+ "title: \"test remove\"\n" + "title: \"test remove\"\n"
+ "---"; + "---";
Assert.isTrue("".equals(MarkdownUtils.removeFrontMatter(markdown))); assertEquals("", MarkdownUtils.removeFrontMatter(markdown));
markdown = "---\n" markdown = "---\n"
+ "title: \"test remove\"\n" + "title: \"test remove\"\n"
+ "---" + "---"
+ "test"; + "test";
Assert.isTrue("test".equals(MarkdownUtils.removeFrontMatter(markdown))); assertEquals("test", MarkdownUtils.removeFrontMatter(markdown));
markdown = "---\n" markdown = "---\n"
+ "title: \"test remove\"\n" + "title: \"test remove\"\n"
+ "---" + "---"
+ "test---"; + "test---";
Assert.isTrue("test---".equals(MarkdownUtils.removeFrontMatter(markdown))); assertEquals("test---", MarkdownUtils.removeFrontMatter(markdown));
} }
@Test @Test
@ -37,7 +40,7 @@ class MarkdownUtilsTest {
String markdown1 = "驿外[^1]断桥边,寂寞开无主。已是黄昏独自愁,更着风和雨\n" String markdown1 = "驿外[^1]断桥边,寂寞开无主。已是黄昏独自愁,更着风和雨\n"
+ "[^1]: 驿指荒僻、冷清之地。驿驿站。"; + "[^1]: 驿指荒僻、冷清之地。驿驿站。";
String s1 = MarkdownUtils.renderHtml(markdown1); String s1 = MarkdownUtils.renderHtml(markdown1);
Assert.isTrue(StringUtils.isNotBlank(s1)); assertTrue(StringUtils.isNotBlank(s1));
String s1Expected = "<p>驿外<sup class=\"footnote-ref\"><a id=\"fnref1\" " String s1Expected = "<p>驿外<sup class=\"footnote-ref\"><a id=\"fnref1\" "
+ "href=\"#fn1\">[1]</a></sup>断桥边,寂寞开无主。已是黄昏独自愁,更着风和雨</p>\n" + "href=\"#fn1\">[1]</a></sup>断桥边,寂寞开无主。已是黄昏独自愁,更着风和雨</p>\n"
+ "<hr class=\"footnotes-sep\" />\n" + "<hr class=\"footnotes-sep\" />\n"
@ -49,7 +52,7 @@ class MarkdownUtilsTest {
+ "</li>\n" + "</li>\n"
+ "</ol>\n" + "</ol>\n"
+ "</section>\n"; + "</section>\n";
Assert.isTrue(StringUtils.equals(s1Expected, s1)); assertTrue(StringUtils.equals(s1Expected, s1));
String markdown2 = "Paragraph with a footnote reference[^1]\n" String markdown2 = "Paragraph with a footnote reference[^1]\n"
+ "[^1]: Footnote text added at the bottom of the document"; + "[^1]: Footnote text added at the bottom of the document";
@ -65,6 +68,6 @@ class MarkdownUtilsTest {
+ "</li>\n" + "</li>\n"
+ "</ol>\n" + "</ol>\n"
+ "</section>\n"; + "</section>\n";
Assert.isTrue(StringUtils.equals(s2Expected, s2)); assertTrue(StringUtils.equals(s2Expected, s2));
} }
} }

View File

@ -5,7 +5,7 @@ import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
/** /**
* Time unit test * Time unit test.
* *
* @author johnniang * @author johnniang
* @date 19-4-29 * @date 19-4-29

View File

@ -1,7 +1,9 @@
package run.halo.app.utils; package run.halo.app.utils;
import static org.junit.jupiter.api.Assertions.assertEquals; 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.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.Arrays; import java.util.Arrays;
import java.util.Comparator; import java.util.Comparator;
@ -55,6 +57,33 @@ class ValidationUtilsTest {
validateIteratorTest(exception.getConstraintViolations()); 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) { void validateIteratorTest(Set<? extends ConstraintViolation<?>> violations) {
assertEquals(2, violations.size()); assertEquals(2, violations.size());