mirror of
https://github.com/getrebuild/rebuild.git
synced 2025-03-12 15:11:42 +08:00
be (#707)
* be: tips * enh: h5NoKill * enh: session Keep 60s for H5 * enh: H5 session setMaxInactiveInterval * ImageMaker --------- Co-authored-by: devezhao <zhaofang123@gmail.com>
This commit is contained in:
parent
d47dfb007f
commit
d68cce0673
10 changed files with 232 additions and 117 deletions
|
@ -24,21 +24,13 @@ import com.rebuild.core.privileges.bizz.Department;
|
|||
import com.rebuild.core.privileges.bizz.User;
|
||||
import com.rebuild.core.service.approval.FlowNode;
|
||||
import com.rebuild.core.support.RebuildConfiguration;
|
||||
import com.rebuild.utils.CommonsUtils;
|
||||
import com.rebuild.utils.ImageMaker;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.commons.lang.math.RandomUtils;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.Principal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
@ -305,95 +297,28 @@ public class UserHelper {
|
|||
return users;
|
||||
}
|
||||
|
||||
private static final Color[] RB_COLORS = new Color[]{
|
||||
new Color(66, 133, 244),
|
||||
new Color(52, 168, 83),
|
||||
new Color(251, 188, 5),
|
||||
new Color(234, 67, 53),
|
||||
new Color(155, 82, 222),
|
||||
new Color(22, 168, 143),
|
||||
};
|
||||
|
||||
/**
|
||||
* 生成用户头像
|
||||
*
|
||||
* @param name
|
||||
* @param forceMake
|
||||
* @return
|
||||
* @see ImageMaker
|
||||
*/
|
||||
public static File generateAvatar(String name, boolean forceMake) {
|
||||
if (StringUtils.isBlank(name)) name = "RB";
|
||||
|
||||
File avatar = RebuildConfiguration.getFileOfData("avatar-" + name + "29.jpg");
|
||||
if (avatar.exists()) {
|
||||
File avatarFile = RebuildConfiguration.getFileOfData("avatar-" + name + "29.jpg");
|
||||
if (avatarFile.exists()) {
|
||||
if (forceMake) {
|
||||
FileUtils.deleteQuietly(avatar);
|
||||
FileUtils.deleteQuietly(avatarFile);
|
||||
} else {
|
||||
return avatar;
|
||||
return avatarFile;
|
||||
}
|
||||
}
|
||||
|
||||
if (name.length() > 2) name = name.substring(name.length() - 2);
|
||||
name = name.toUpperCase();
|
||||
|
||||
BufferedImage bi = new BufferedImage(200, 200, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D g2d = (Graphics2D) bi.getGraphics();
|
||||
|
||||
g2d.setColor(RB_COLORS[RandomUtils.nextInt(RB_COLORS.length)]);
|
||||
g2d.fillRect(0, 0, bi.getWidth(), bi.getHeight());
|
||||
|
||||
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
|
||||
|
||||
try {
|
||||
final Font font = createFont();
|
||||
g2d.setFont(font);
|
||||
g2d.setColor(Color.WHITE);
|
||||
FontMetrics fontMetrics = g2d.getFontMetrics(font);
|
||||
int x = fontMetrics.stringWidth(name);
|
||||
g2d.drawString(name, (200 - x) / 2, 128);
|
||||
g2d.setColor(new Color(0, 0, 0, 1));
|
||||
g2d.drawString("wbr", 0, 62);
|
||||
g2d.dispose();
|
||||
|
||||
try (FileOutputStream fos = new FileOutputStream(avatar)) {
|
||||
ImageIO.write(bi, "png", fos);
|
||||
fos.flush();
|
||||
}
|
||||
|
||||
} catch (Throwable ex) {
|
||||
log.warn("Cannot make font-avatar : {}", name, ex);
|
||||
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = CommonsUtils.getStreamOfRes("/web" + DEFAULT_AVATAR);
|
||||
|
||||
bi = ImageIO.read(is);
|
||||
try (FileOutputStream fos = new FileOutputStream(avatar)) {
|
||||
ImageIO.write(bi, "png", fos);
|
||||
fos.flush();
|
||||
}
|
||||
|
||||
} catch (IOException ignored) {
|
||||
IOUtils.closeQuietly(is);
|
||||
}
|
||||
}
|
||||
|
||||
return avatar;
|
||||
}
|
||||
|
||||
private static Font createFont() {
|
||||
File fontFile = RebuildConfiguration.getFileOfData("SourceHanSansK-Regular.ttf");
|
||||
if (fontFile.exists()) {
|
||||
try {
|
||||
Font font = Font.createFont(Font.TRUETYPE_FONT, fontFile);
|
||||
font = font.deriveFont((float) 81.0);
|
||||
return font;
|
||||
} catch (Throwable ex) {
|
||||
log.warn("Cannot create Font: SourceHanSansK-Regular.ttf", ex);
|
||||
}
|
||||
}
|
||||
// Use default
|
||||
return new Font(Font.SERIF, Font.BOLD, (int) (float) 81.0);
|
||||
ImageMaker.makeAvatar(name, avatarFile);
|
||||
return avatarFile;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
145
src/main/java/com/rebuild/utils/ImageMaker.java
Normal file
145
src/main/java/com/rebuild/utils/ImageMaker.java
Normal file
|
@ -0,0 +1,145 @@
|
|||
package com.rebuild.utils;
|
||||
|
||||
import com.rebuild.core.RebuildException;
|
||||
import com.rebuild.core.privileges.UserHelper;
|
||||
import com.rebuild.core.support.RebuildConfiguration;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang.math.RandomUtils;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* @author Zixin
|
||||
* @since 2023/1/8
|
||||
*/
|
||||
@Slf4j
|
||||
public class ImageMaker {
|
||||
|
||||
// 颜色
|
||||
public static final Color[] RB_COLORS = new Color[]{
|
||||
new Color(66, 133, 244),
|
||||
new Color(52, 168, 83),
|
||||
new Color(251, 188, 5),
|
||||
new Color(234, 67, 53),
|
||||
new Color(155, 82, 222),
|
||||
new Color(22, 168, 143),
|
||||
};
|
||||
|
||||
/**
|
||||
* 生成LOGO(效果不佳暂不用)
|
||||
*
|
||||
* @param text
|
||||
* @param color
|
||||
* @param dest
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
public static void makeLogo(String text, Color color, File dest) {
|
||||
BufferedImage bi = new BufferedImage(300, 60, BufferedImage.TYPE_INT_ARGB);
|
||||
Graphics2D g2d = (Graphics2D) bi.getGraphics();
|
||||
g2d.setComposite(AlphaComposite.Clear);
|
||||
g2d.fillRect(0, 0, bi.getWidth(), bi.getHeight());
|
||||
g2d.setComposite(AlphaComposite.SrcOver);
|
||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
|
||||
Color textColor = color == null ? RB_COLORS[RandomUtils.nextInt(RB_COLORS.length)] : color;
|
||||
|
||||
FileUtils.deleteQuietly(dest);
|
||||
try {
|
||||
final Font font = createFont(63f);
|
||||
g2d.setFont(font);
|
||||
g2d.setColor(textColor);
|
||||
FontMetrics fontMetrics = g2d.getFontMetrics(font);
|
||||
int x = fontMetrics.stringWidth(text);
|
||||
g2d.drawString(text, (300 - x) / 2, 60 - 6);
|
||||
|
||||
try (FileOutputStream fos = new FileOutputStream(dest)) {
|
||||
ImageIO.write(bi, "png", fos);
|
||||
fos.flush();
|
||||
}
|
||||
|
||||
} catch (Throwable ex) {
|
||||
throw new RebuildException("Cannot make logo", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成头像
|
||||
*
|
||||
* @param name
|
||||
* @param dest
|
||||
*/
|
||||
public static void makeAvatar(String name, File dest) {
|
||||
if (name.length() > 2) name = name.substring(name.length() - 2);
|
||||
name = name.toUpperCase();
|
||||
|
||||
BufferedImage bi = new BufferedImage(200, 200, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D g2d = (Graphics2D) bi.getGraphics();
|
||||
|
||||
g2d.setColor(RB_COLORS[RandomUtils.nextInt(RB_COLORS.length)]);
|
||||
g2d.fillRect(0, 0, bi.getWidth(), bi.getHeight());
|
||||
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB);
|
||||
|
||||
FileUtils.deleteQuietly(dest);
|
||||
try {
|
||||
final Font font = createFont(81f);
|
||||
g2d.setFont(font);
|
||||
g2d.setColor(Color.WHITE);
|
||||
FontMetrics fontMetrics = g2d.getFontMetrics(font);
|
||||
int x = fontMetrics.stringWidth(name);
|
||||
g2d.drawString(name, (200 - x) / 2, 128);
|
||||
g2d.setColor(new Color(0, 0, 0, 1));
|
||||
g2d.drawString("wbr", 0, 62);
|
||||
g2d.dispose();
|
||||
|
||||
try (FileOutputStream fos = new FileOutputStream(dest)) {
|
||||
ImageIO.write(bi, "png", fos);
|
||||
fos.flush();
|
||||
}
|
||||
|
||||
} catch (Throwable ex) {
|
||||
log.warn("Cannot make font-avatar : {}", name, ex);
|
||||
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = CommonsUtils.getStreamOfRes("/web" + UserHelper.DEFAULT_AVATAR);
|
||||
bi = ImageIO.read(is);
|
||||
try (FileOutputStream fos = new FileOutputStream(dest)) {
|
||||
ImageIO.write(bi, "png", fos);
|
||||
fos.flush();
|
||||
}
|
||||
|
||||
} catch (IOException ignored) {
|
||||
IOUtils.closeQuietly(is);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字体
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
static Font createFont(float size) {
|
||||
File fontFile = RebuildConfiguration.getFileOfData("SourceHanSansK-Regular.ttf");
|
||||
if (fontFile.exists()) {
|
||||
try {
|
||||
Font font = Font.createFont(Font.TRUETYPE_FONT, fontFile);
|
||||
font = font.deriveFont(size);
|
||||
return font;
|
||||
} catch (Throwable ex) {
|
||||
log.warn("Cannot create Font: SourceHanSansK-Regular.ttf", ex);
|
||||
}
|
||||
}
|
||||
// Use default
|
||||
return new Font(Font.SERIF, Font.BOLD, (int) size);
|
||||
}
|
||||
}
|
|
@ -107,14 +107,15 @@ public class OnlineSessionStore implements HttpSessionListener {
|
|||
|
||||
/**
|
||||
* @param request
|
||||
* @param h5NoKill
|
||||
*/
|
||||
public void storeLoginSuccessed(HttpServletRequest request) {
|
||||
public void storeLoginSuccessed(HttpServletRequest request, boolean h5NoKill) {
|
||||
HttpSession s = request.getSession();
|
||||
Object loginUser = s.getAttribute(WebUtils.CURRENT_USER);
|
||||
Assert.notNull(loginUser, "No login user found in session!");
|
||||
|
||||
if (!RebuildConfiguration.getBool(ConfigurationItem.MultipleSessions)) {
|
||||
HttpSession previous = getSession((ID) loginUser);
|
||||
HttpSession previous = h5NoKill ? null : getSession((ID) loginUser);
|
||||
if (previous != null) {
|
||||
log.warn("Kill previous session : {} ({})", previous.getId(), loginUser);
|
||||
|
||||
|
|
|
@ -176,7 +176,10 @@ public class RebuildWebInterceptor implements AsyncHandlerInterceptor, InstallSt
|
|||
|
||||
@Override
|
||||
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
|
||||
// Notings
|
||||
// v3.6 H5 session 时间
|
||||
if (AppUtils.isRbMobile(request)) {
|
||||
request.getSession().setMaxInactiveInterval(60 * 5);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -59,6 +59,36 @@ public class LoginAction extends BaseController {
|
|||
protected static final String PREFIX_2FA = "2FA:";
|
||||
protected static final String PREFIX_ALT = "ALT:";
|
||||
|
||||
/**
|
||||
* @param request
|
||||
* @param response
|
||||
* @param user
|
||||
* @param autoLogin
|
||||
* @return
|
||||
*/
|
||||
protected Integer loginSuccessed(HttpServletRequest request, HttpServletResponse response, ID user, boolean autoLogin) {
|
||||
return loginSuccessed(request, response, user, autoLogin, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param request
|
||||
* @param response
|
||||
* @param user
|
||||
* @return
|
||||
*/
|
||||
protected Map<String, Object> loginSuccessedH5(HttpServletRequest request, HttpServletResponse response, ID user) {
|
||||
Map<String, Object> resMap = new HashMap<>();
|
||||
|
||||
Integer ed = loginSuccessed(request, response, user, false, true);
|
||||
if (ed != null) resMap.put("passwdExpiredDays", ed);
|
||||
|
||||
String authToken = AuthTokenManager.generateAccessToken(user);
|
||||
resMap.put("authToken", authToken);
|
||||
|
||||
request.getSession().invalidate();
|
||||
return resMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录成功
|
||||
*
|
||||
|
@ -66,9 +96,10 @@ public class LoginAction extends BaseController {
|
|||
* @param response
|
||||
* @param user
|
||||
* @param autoLogin
|
||||
* @param fromH5
|
||||
* @return 密码过期时间(如有)
|
||||
*/
|
||||
protected Integer loginSuccessed(HttpServletRequest request, HttpServletResponse response, ID user, boolean autoLogin) {
|
||||
private Integer loginSuccessed(HttpServletRequest request, HttpServletResponse response, ID user, boolean autoLogin, boolean fromH5) {
|
||||
// 自动登录
|
||||
if (autoLogin) {
|
||||
final String altToken = CodecUtils.randomCode(60);
|
||||
|
@ -82,7 +113,7 @@ public class LoginAction extends BaseController {
|
|||
|
||||
ServletUtils.setSessionAttribute(request, WebUtils.CURRENT_USER, user);
|
||||
ServletUtils.setSessionAttribute(request, SK_USER_THEME, KVStorage.getCustomValue("THEME." + user));
|
||||
Application.getSessionStore().storeLoginSuccessed(request);
|
||||
Application.getSessionStore().storeLoginSuccessed(request, fromH5);
|
||||
|
||||
// 头像缓存
|
||||
ServletUtils.setSessionAttribute(request, UserAvatar.SK_DAVATAR, System.currentTimeMillis());
|
||||
|
@ -109,27 +140,8 @@ public class LoginAction extends BaseController {
|
|||
}
|
||||
|
||||
/**
|
||||
* 登录成功 H5
|
||||
* 登录日志
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
* @param user
|
||||
* @return
|
||||
*/
|
||||
protected Map<String, Object> loginSuccessedH5(HttpServletRequest request, HttpServletResponse response, ID user) {
|
||||
Map<String, Object> resMap = new HashMap<>();
|
||||
|
||||
Integer ed = loginSuccessed(request, response, user, false);
|
||||
if (ed != null) resMap.put("passwdExpiredDays", ed);
|
||||
|
||||
String authToken = AuthTokenManager.generateAccessToken(user);
|
||||
resMap.put("authToken", authToken);
|
||||
|
||||
request.getSession().invalidate();
|
||||
return resMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param request
|
||||
* @param user
|
||||
*/
|
||||
|
|
|
@ -28,11 +28,10 @@
|
|||
<td data-id="StorageURL" th:data-value="${storageAccount == null ? '' : storageAccount[3]}">[[${storageAccount == null ? bundle.L('未设置') : storageAccount[3]}]]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
[[${bundle.L('存储空间')}]]
|
||||
<p>[[${bundle.L('存储空间变更需你自行迁移原有数据')}]]</p>
|
||||
<td>[[${bundle.L('存储空间')}]]</td>
|
||||
<td data-id="StorageBucket" th:data-value="${storageAccount == null ? '' : storageAccount[2]}" th:data-form-text="${bundle.L('存储空间变更需你自行迁移原有数据')}">
|
||||
[[${storageAccount == null ? bundle.L('未设置') : storageAccount[2]}]]
|
||||
</td>
|
||||
<td data-id="StorageBucket" th:data-value="${storageAccount == null ? '' : storageAccount[2]}">[[${storageAccount == null ? bundle.L('未设置') : storageAccount[2]}]]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>[[${bundle.L('密钥 AK')}]]</td>
|
||||
|
|
|
@ -21,6 +21,9 @@
|
|||
.smtp tr.smtp-show {
|
||||
display: table-row;
|
||||
}
|
||||
.smtp td[data-id='MailAddr'] > p {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -62,11 +65,10 @@
|
|||
<td data-id="MailPassword" th:data-value="${mailAccount == null ? '' : mailAccount[1]}">[[${mailAccount == null ? bundle.L('未设置') : mailAccount[1]}]]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
[[${bundle.L('发件人地址')}]]
|
||||
<p class="smtp-hide">[[${bundle.L('地址域名需与 SUBMAIL 中配置的域名匹配')}]]</p>
|
||||
<td>[[${bundle.L('发件人地址')}]]</td>
|
||||
<td data-id="MailAddr" th:data-value="${mailAccount == null ? '' : mailAccount[2]}" th:data-form-text="${bundle.L('地址域名需与 SUBMAIL 中配置的域名匹配')}">
|
||||
[[${mailAccount == null ? bundle.L('未设置') : mailAccount[2]}]]
|
||||
</td>
|
||||
<td data-id="MailAddr" th:data-value="${mailAccount == null ? '' : mailAccount[2]}">[[${mailAccount == null ? bundle.L('未设置') : mailAccount[2]}]]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>[[${bundle.L('发件人名称')}]]</td>
|
||||
|
|
|
@ -105,7 +105,7 @@
|
|||
<i class="logo-img white"></i>
|
||||
<b th:title="${bundle.L('还原')}"><span class="zmdi zmdi-replay"></span></b>
|
||||
</a>
|
||||
<p class="mt-2 text-dark hide">[[${bundle.L('请分别上传深色与白色 LOGO,透明背景,建议尺寸 300 × 60')}]]</p>
|
||||
<p class="mt-2 text-dark hide">[[${bundle.L('请分别上传深色与白色 LOGO,透明背景,建议尺寸 300 × 60')}]]<a class="ml-2 J_logo-gen hide" href="###genlogo"><i class="icon mdi mdi-pencil-ruler"></i> 制作 LOGO</a></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
|
|
@ -191,6 +191,11 @@ const _toggleImage = function (el, init) {
|
|||
_$imgCurrent.find('>i').css('background-image', `url(${rb.baseUrl}/assets/img/s.gif)`)
|
||||
changeValue({ target: { name: _$imgCurrent.data('id'), value: '' } })
|
||||
})
|
||||
$img
|
||||
.find('.J_logo-gen')
|
||||
.removeAttr('title')
|
||||
.off('click')
|
||||
.on('click', () => {})
|
||||
}
|
||||
|
||||
class DlgMM extends RbAlert {
|
||||
|
|
23
src/test/java/com/rebuild/utils/ImageMakerTest.java
Normal file
23
src/test/java/com/rebuild/utils/ImageMakerTest.java
Normal file
|
@ -0,0 +1,23 @@
|
|||
package com.rebuild.utils;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
*/
|
||||
class ImageMakerTest {
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Test
|
||||
void makeLogo() {
|
||||
File tmp = new File(FileUtils.getTempDirectory(), "logo.png");
|
||||
ImageMaker.makeLogo("锐昉科技", null, tmp);
|
||||
System.out.println(tmp);
|
||||
}
|
||||
|
||||
@Test
|
||||
void makeAvatar() {
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue