feat: Implement pagination and effective account validation for backdoor user login
This commit is contained in:
@@ -16,7 +16,6 @@ public class BackDoorLoginProperties
|
||||
private boolean enabled;
|
||||
private int maxUsers = DEFAULT_MAX_USERS;
|
||||
private String portalBasePath = "/shihezi";
|
||||
private String apiBasePath = "/api/shihezi";
|
||||
|
||||
public boolean isEnabled()
|
||||
{
|
||||
@@ -48,16 +47,6 @@ public class BackDoorLoginProperties
|
||||
this.portalBasePath = portalBasePath;
|
||||
}
|
||||
|
||||
public String getApiBasePath()
|
||||
{
|
||||
return normalizeBasePath(apiBasePath, "/api/shihezi");
|
||||
}
|
||||
|
||||
public void setApiBasePath(String apiBasePath)
|
||||
{
|
||||
this.apiBasePath = apiBasePath;
|
||||
}
|
||||
|
||||
private String normalizeBasePath(String value, String defaultValue)
|
||||
{
|
||||
if (value == null || value.trim().isEmpty())
|
||||
|
||||
@@ -32,6 +32,7 @@ public class BackDoorLoginController
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(BackDoorLoginController.class);
|
||||
private static final MediaType HTML_UTF8 = new MediaType("text", "html", StandardCharsets.UTF_8);
|
||||
private static final int PAGE_SIZE = 8;
|
||||
|
||||
private final BackDoorLoginService backDoorLoginService;
|
||||
private final BackDoorPageRenderer pageRenderer;
|
||||
@@ -49,11 +50,12 @@ public class BackDoorLoginController
|
||||
@GetMapping(value = "/backDoor", produces = "text/html;charset=UTF-8")
|
||||
public ResponseEntity<String> backDoor(
|
||||
@RequestParam(value = "id", required = false) String id,
|
||||
@RequestParam(value = "keyword", required = false) String keyword)
|
||||
@RequestParam(value = "keyword", required = false) String keyword,
|
||||
@RequestParam(value = "page", required = false) String page)
|
||||
{
|
||||
if (StringUtils.isBlank(id))
|
||||
{
|
||||
return renderUserList(keyword);
|
||||
return renderUserList(keyword, parsePage(page));
|
||||
}
|
||||
|
||||
Long userId;
|
||||
@@ -86,13 +88,14 @@ public class BackDoorLoginController
|
||||
}
|
||||
}
|
||||
|
||||
private ResponseEntity<String> renderUserList(String keyword)
|
||||
private ResponseEntity<String> renderUserList(String keyword, int page)
|
||||
{
|
||||
try
|
||||
{
|
||||
List<SysUser> users = backDoorLoginService.listAvailableUsers(keyword, properties.getMaxUsers());
|
||||
return html(HttpStatus.OK,
|
||||
pageRenderer.renderUserList(users, StringUtils.trimToEmpty(keyword), properties.getMaxUsers()));
|
||||
pageRenderer.renderUserList(users, StringUtils.trimToEmpty(keyword), properties.getMaxUsers(),
|
||||
page, PAGE_SIZE));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -101,6 +104,22 @@ public class BackDoorLoginController
|
||||
}
|
||||
}
|
||||
|
||||
private int parsePage(String page)
|
||||
{
|
||||
if (StringUtils.isBlank(page))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
try
|
||||
{
|
||||
return Math.max(1, Integer.parseInt(page.trim()));
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
private ResponseEntity<String> html(HttpStatus status, String body)
|
||||
{
|
||||
return ResponseEntity.status(status)
|
||||
@@ -109,7 +128,7 @@ public class BackDoorLoginController
|
||||
.header(HttpHeaders.PRAGMA, "no-cache")
|
||||
.header("Content-Security-Policy",
|
||||
"default-src 'none'; style-src 'unsafe-inline'; script-src 'unsafe-inline'; "
|
||||
+ "connect-src 'self'; img-src data:; form-action 'self'; base-uri 'none'; "
|
||||
+ "img-src data:; form-action 'self'; base-uri 'none'; "
|
||||
+ "frame-ancestors 'none'")
|
||||
.header("X-Content-Type-Options", "nosniff")
|
||||
.header("X-Frame-Options", "DENY")
|
||||
|
||||
@@ -17,6 +17,7 @@ import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StreamUtils;
|
||||
import org.springframework.web.util.HtmlUtils;
|
||||
import org.springframework.web.util.UriUtils;
|
||||
|
||||
/**
|
||||
* 渲染后门用户选择页和登录跳转页。
|
||||
@@ -38,14 +39,24 @@ public class BackDoorPageRenderer
|
||||
this.errorTemplate = loadTemplate("backdoor/error.html");
|
||||
}
|
||||
|
||||
public String renderUserList(List<SysUser> users, String keyword, int maxUsers)
|
||||
public String renderUserList(List<SysUser> users, String keyword, int maxUsers, int requestedPage, int pageSize)
|
||||
{
|
||||
List<SysUser> safeUsers = users == null ? Collections.emptyList() : users;
|
||||
int safePageSize = Math.max(1, pageSize);
|
||||
int totalPages = Math.max(1, (safeUsers.size() + safePageSize - 1) / safePageSize);
|
||||
int currentPage = Math.max(1, Math.min(requestedPage, totalPages));
|
||||
int fromIndex = Math.min((currentPage - 1) * safePageSize, safeUsers.size());
|
||||
int toIndex = Math.min(fromIndex + safePageSize, safeUsers.size());
|
||||
List<SysUser> pageUsers = safeUsers.subList(fromIndex, toIndex);
|
||||
|
||||
return userListTemplate
|
||||
.replace("@@ROWS@@", renderRows(safeUsers))
|
||||
.replace("@@ROWS@@", renderRows(pageUsers))
|
||||
.replace("@@KEYWORD@@", html(keyword))
|
||||
.replace("@@COUNT@@", String.valueOf(safeUsers.size()))
|
||||
.replace("@@MAX_USERS@@", String.valueOf(maxUsers));
|
||||
.replace("@@MAX_USERS@@", String.valueOf(maxUsers))
|
||||
.replace("@@CURRENT_PAGE@@", String.valueOf(currentPage))
|
||||
.replace("@@TOTAL_PAGES@@", String.valueOf(totalPages))
|
||||
.replace("@@PAGINATION@@", renderPagination(keyword, currentPage, totalPages));
|
||||
}
|
||||
|
||||
public String renderLogin(LoginSession session, BackDoorLoginProperties properties)
|
||||
@@ -58,7 +69,6 @@ public class BackDoorPageRenderer
|
||||
.replace("@@USER_NAME@@", html(displayName))
|
||||
.replace("@@TOKEN_JSON@@", javascriptString(session.getToken()))
|
||||
.replace("@@EXPIRE_AT@@", String.valueOf(expireAt))
|
||||
.replace("@@API_BASE_PATH_JSON@@", javascriptString(properties.getApiBasePath()))
|
||||
.replace("@@PORTAL_BASE_PATH_JSON@@", javascriptString(properties.getPortalBasePath()));
|
||||
}
|
||||
|
||||
@@ -94,6 +104,44 @@ public class BackDoorPageRenderer
|
||||
return rows.toString();
|
||||
}
|
||||
|
||||
private String renderPagination(String keyword, int currentPage, int totalPages)
|
||||
{
|
||||
StringBuilder pagination = new StringBuilder(512);
|
||||
appendPageLink(pagination, keyword, currentPage - 1, "上一页", currentPage == 1, false);
|
||||
for (int page = 1; page <= totalPages; page++)
|
||||
{
|
||||
appendPageLink(pagination, keyword, page, String.valueOf(page), false, page == currentPage);
|
||||
}
|
||||
appendPageLink(pagination, keyword, currentPage + 1, "下一页", currentPage == totalPages, false);
|
||||
return pagination.toString();
|
||||
}
|
||||
|
||||
private void appendPageLink(StringBuilder target, String keyword, int page, String label,
|
||||
boolean disabled, boolean current)
|
||||
{
|
||||
if (disabled)
|
||||
{
|
||||
target.append("<span class=\"page-link disabled\">").append(label).append("</span>");
|
||||
return;
|
||||
}
|
||||
if (current)
|
||||
{
|
||||
target.append("<span class=\"page-link current\">").append(label).append("</span>");
|
||||
return;
|
||||
}
|
||||
|
||||
String href = "?page=" + page;
|
||||
if (StringUtils.isNotBlank(keyword))
|
||||
{
|
||||
href += "&keyword=" + UriUtils.encodeQueryParam(keyword, StandardCharsets.UTF_8);
|
||||
}
|
||||
target.append("<a class=\"page-link\" href=\"")
|
||||
.append(html(href))
|
||||
.append("\">")
|
||||
.append(label)
|
||||
.append("</a>");
|
||||
}
|
||||
|
||||
private String maskPhone(String phone)
|
||||
{
|
||||
String value = StringUtils.trimToEmpty(phone);
|
||||
|
||||
@@ -63,6 +63,7 @@ public class BackDoorLoginService
|
||||
|
||||
SysUser user = userMapper.selectUserById(userId);
|
||||
validateUser(user);
|
||||
validateEffectiveAccount(user);
|
||||
|
||||
LoginUser loginUser = (LoginUser) userDetailsService.createLoginUser(user);
|
||||
loginService.recordLoginInfo(user.getUserId());
|
||||
@@ -92,6 +93,15 @@ public class BackDoorLoginService
|
||||
}
|
||||
}
|
||||
|
||||
private void validateEffectiveAccount(SysUser user)
|
||||
{
|
||||
Long effectiveUserId = userMapper.selectBackDoorEffectiveUserId(user.getUserName());
|
||||
if (!user.getUserId().equals(effectiveUserId))
|
||||
{
|
||||
throw new ServiceException("该账号存在更新记录,请从用户列表选择有效账号");
|
||||
}
|
||||
}
|
||||
|
||||
public static class LoginSession
|
||||
{
|
||||
private final SysUser user;
|
||||
|
||||
Reference in New Issue
Block a user