📝 修改校验逻辑
This commit is contained in:
parent
1ec02a02b5
commit
7f46d6e60e
|
@ -77,5 +77,25 @@
|
|||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.webjars</groupId>
|
||||
<artifactId>bootstrap</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.webjars</groupId>
|
||||
<artifactId>font-awesome</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.webjars</groupId>
|
||||
<artifactId>jquery</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jetbrains</groupId>
|
||||
<artifactId>annotations</artifactId>
|
||||
<version>13.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
|
@ -16,7 +16,7 @@ import java.util.List;
|
|||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
@Schema(name = "PageResult 对象", title = "分页返回结果", description = "分页返回结果")
|
||||
@Schema(name = "分页返回结果", title = "分页返回结果", description = "分页返回结果")
|
||||
public class PageResult<T> implements Serializable {
|
||||
|
||||
@Schema(name = "pageNo", title = "当前页")
|
||||
|
|
|
@ -4,7 +4,7 @@ import com.auth.common.context.BaseContext;
|
|||
import com.auth.common.exception.AuthenticSecurityException;
|
||||
import com.auth.common.exception.MyAuthenticationException;
|
||||
import com.auth.common.model.common.result.ResultCodeEnum;
|
||||
import com.auth.module.security.config.SecurityWebConfiguration;
|
||||
import com.auth.module.security.config.properties.SecurityConfigProperties;
|
||||
import com.auth.module.security.handler.SecurityAuthenticationEntryPoint;
|
||||
import com.auth.module.security.provider.JwtTokenProvider;
|
||||
import com.auth.module.security.service.DbUserDetailService;
|
||||
|
@ -14,6 +14,7 @@ import jakarta.servlet.http.HttpServletRequest;
|
|||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
@ -34,61 +35,68 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
|||
private final JwtTokenProvider jwtTokenProvider;
|
||||
private final DbUserDetailService userDetailsService;
|
||||
private final SecurityAuthenticationEntryPoint securityAuthenticationEntryPoint;
|
||||
private final SecurityConfigProperties pathsProperties;
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
FilterChain filterChain) throws ServletException, IOException, AuthenticSecurityException {
|
||||
// 先校验不需要认证的接口
|
||||
RequestMatcher[] requestNoAuthMatchers = SecurityWebConfiguration.noAuthPaths.stream()
|
||||
.map(AntPathRequestMatcher::new)
|
||||
.toArray(RequestMatcher[]::new);
|
||||
OrRequestMatcher noAuthRequestMatcher = new OrRequestMatcher(requestNoAuthMatchers);
|
||||
if (noAuthRequestMatcher.matches(request)) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取需要认证的接口
|
||||
RequestMatcher[] requestSecureMatchers = SecurityWebConfiguration.securedPaths.stream()
|
||||
.map(AntPathRequestMatcher::new)
|
||||
.toArray(RequestMatcher[]::new);
|
||||
OrRequestMatcher secureRequestMatcher = new OrRequestMatcher(requestSecureMatchers);
|
||||
|
||||
// 公开接口直接放行
|
||||
if (!secureRequestMatcher.matches(request)) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
final String authHeader = request.getHeader("Authorization");
|
||||
// 如果当前请求不包含验证Token直接返回
|
||||
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
|
||||
filterChain.doFilter(request, response);
|
||||
throw new AuthenticSecurityException(ResultCodeEnum.LOGIN_AUTH);
|
||||
}
|
||||
|
||||
// 当前请求的Token
|
||||
final String jwtToken = authHeader.substring(7);
|
||||
|
||||
protected void doFilterInternal(@NotNull HttpServletRequest request,
|
||||
@NotNull HttpServletResponse response,
|
||||
@NotNull FilterChain filterChain) throws ServletException, IOException {
|
||||
try {
|
||||
// 检查当前Token是否过期
|
||||
|
||||
// 检查白名单路径
|
||||
if (isNoAuthPath(request)) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否需要认证的路径
|
||||
if (!isSecurePath(request)) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证 Token
|
||||
if (validToken(request)) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
filterChain.doFilter(request, response);
|
||||
} catch (AuthenticSecurityException e) {
|
||||
// 直接处理认证异常,不再调用filterChain.doFilter()
|
||||
securityAuthenticationEntryPoint.commence(
|
||||
request,
|
||||
response,
|
||||
new MyAuthenticationException(e.getMessage(), e)
|
||||
);
|
||||
} catch (RuntimeException e) {
|
||||
MyAuthenticationException myAuthenticationException = new MyAuthenticationException("Authentication failed", e);
|
||||
securityAuthenticationEntryPoint.commence(request, response, myAuthenticationException);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private boolean validToken(@NotNull HttpServletRequest request) {
|
||||
// 验证Token
|
||||
String authHeader = request.getHeader("Authorization");
|
||||
|
||||
// Token验证
|
||||
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
|
||||
return true;
|
||||
// throw new AuthenticSecurityException(ResultCodeEnum.LOGIN_AUTH);
|
||||
}
|
||||
|
||||
String jwtToken = authHeader.substring(7);
|
||||
|
||||
if (jwtTokenProvider.isExpired(jwtToken)) {
|
||||
// 💡如果过期不需要进行判断和验证,需要直接放行可以像下面这样写
|
||||
// ===================================================
|
||||
// filterChain.doFilter(request, response);
|
||||
// return;
|
||||
// ===================================================
|
||||
throw new AuthenticSecurityException(ResultCodeEnum.AUTHENTICATION_EXPIRED);
|
||||
}
|
||||
|
||||
// 解析当前Token中的用户名
|
||||
// 设置认证信息
|
||||
String username = jwtTokenProvider.getUsernameFromToken(jwtToken);
|
||||
Long userId = jwtTokenProvider.getUserIdFromToken(jwtToken);
|
||||
|
||||
// 当前用户名存在,并且 Security上下文为空,设置认证相关信息
|
||||
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
|
||||
// 调用用户信息进行登录
|
||||
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
|
||||
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
|
||||
userDetails,
|
||||
|
@ -96,26 +104,30 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
|||
userDetails.getAuthorities()
|
||||
);
|
||||
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||
|
||||
// 设置认证用户信息
|
||||
SecurityContextHolder.getContext().setAuthentication(authToken);
|
||||
BaseContext.setUsername(username);
|
||||
BaseContext.setUserId(userId);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
// ⚠️IMPORTANT:
|
||||
// ==========================================================================
|
||||
// 在 catch 块中,securityAuthenticationEntryPoint.commence() 已经处理了错误响应
|
||||
// 所以应该 直接返回,避免继续执行后续逻辑。
|
||||
// ==========================================================================
|
||||
catch (RuntimeException exception) {
|
||||
securityAuthenticationEntryPoint.commence(
|
||||
request,
|
||||
response,
|
||||
new MyAuthenticationException(exception.getMessage(), exception)
|
||||
);
|
||||
/**
|
||||
* 是否是不用验证的路径
|
||||
*/
|
||||
private boolean isNoAuthPath(HttpServletRequest request) {
|
||||
RequestMatcher[] matchers = pathsProperties.noAuthPaths.stream()
|
||||
.map(AntPathRequestMatcher::new)
|
||||
.toArray(RequestMatcher[]::new);
|
||||
return new OrRequestMatcher(matchers).matches(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是要验证的路径
|
||||
*/
|
||||
private boolean isSecurePath(HttpServletRequest request) {
|
||||
RequestMatcher[] matchers = pathsProperties.securedPaths.stream()
|
||||
.map(AntPathRequestMatcher::new)
|
||||
.toArray(RequestMatcher[]::new);
|
||||
return new OrRequestMatcher(matchers).matches(request);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,4 +37,5 @@ public class JwtTokenLogoutHandler implements LogoutHandler {
|
|||
Result<Object> result = Result.success(ResultCodeEnum.SUCCESS_LOGOUT);
|
||||
ResponseUtil.out(response, result);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,463 +1,254 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport">
|
||||
<title>Spring Security 6 学习中心</title>
|
||||
<link th:href="@{/src/lib/css/css2.css}">
|
||||
<title>Spring Security 6 | 自定义权限校验后台管理系统</title>
|
||||
|
||||
<!-- Bootstrap CSS -->
|
||||
<link rel="stylesheet" th:href="@{/webjars/bootstrap/5.1.3/css/bootstrap.min.css}">
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" th:href="@{/webjars/font-awesome/5.15.4/css/all.min.css}">
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--primary: #4a6bff;
|
||||
--secondary: #6c757d;
|
||||
--success: #28a745;
|
||||
--danger: #dc3545;
|
||||
--light: #f8f9fa;
|
||||
--dark: #343a40;
|
||||
--gradient-start: #667eea;
|
||||
--gradient-end: #764ba2;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
--primary-color: #4e73df;
|
||||
--secondary-color: #1cc88a;
|
||||
--dark-color: #5a5c69;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Poppins', sans-serif;
|
||||
background-color: #f5f7ff;
|
||||
color: #333;
|
||||
line-height: 1.6;
|
||||
font-family: 'Nunito', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||
background-color: #f8f9fc;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
/* 导航栏 */
|
||||
header {
|
||||
background: linear-gradient(135deg, var(--gradient-start), var(--gradient-end));
|
||||
.hero-section {
|
||||
background: linear-gradient(135deg, var(--primary-color) 0%, #224abe 100%);
|
||||
color: white;
|
||||
padding: 20px 0;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
nav {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.logo i {
|
||||
margin-right: 10px;
|
||||
color: #ffcc00;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
display: flex;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.nav-links li {
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
.nav-links a {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
transition: all 0.3s ease;
|
||||
padding: 8px 12px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.nav-links a:hover {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
/* 英雄区域 */
|
||||
.hero {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: linear-gradient(rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.7)), url('https://images.unsplash.com/photo-1550751827-4bd374c3f58b?ixlib=rb-4.0.3&auto=format&fit=crop&w=1350&q=80');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
color: white;
|
||||
text-align: center;
|
||||
padding-top: 80px;
|
||||
}
|
||||
|
||||
.hero-content {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.hero h1 {
|
||||
font-size: 3.5rem;
|
||||
margin-bottom: 20px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.hero p {
|
||||
font-size: 1.2rem;
|
||||
margin-bottom: 30px;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-block;
|
||||
padding: 12px 30px;
|
||||
background-color: var(--primary);
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
border-radius: 50px;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
margin: 10px;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.2);
|
||||
background-color: #3a56d4;
|
||||
}
|
||||
|
||||
.btn-outline {
|
||||
background-color: transparent;
|
||||
border: 2px solid white;
|
||||
}
|
||||
|
||||
.btn-outline:hover {
|
||||
background-color: white;
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
/* 特性部分 */
|
||||
.features {
|
||||
padding: 100px 0;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
text-align: center;
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
|
||||
.section-title h2 {
|
||||
font-size: 2.5rem;
|
||||
color: var(--dark);
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.section-title p {
|
||||
color: var(--secondary);
|
||||
max-width: 700px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.features-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 30px;
|
||||
padding: 6rem 0;
|
||||
margin-bottom: 3rem;
|
||||
border-radius: 0 0 20px 20px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.feature-card {
|
||||
background-color: var(--light);
|
||||
padding: 30px;
|
||||
border-radius: 10px;
|
||||
text-align: center;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
|
||||
border: none;
|
||||
border-radius: 15px;
|
||||
overflow: hidden;
|
||||
transition: transform 0.3s, box-shadow 0.3s;
|
||||
margin-bottom: 20px;
|
||||
background-color: white;
|
||||
box-shadow: 0 0.15rem 1.75rem 0 rgba(58, 59, 69, 0.1);
|
||||
}
|
||||
|
||||
.feature-card:hover {
|
||||
transform: translateY(-10px);
|
||||
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.1);
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 0.5rem 1.5rem 0 rgba(58, 59, 69, 0.2);
|
||||
}
|
||||
|
||||
.feature-icon {
|
||||
font-size: 3rem;
|
||||
color: var(--primary);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.feature-card h3 {
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 15px;
|
||||
color: var(--dark);
|
||||
}
|
||||
|
||||
/* 文档部分 */
|
||||
.docs {
|
||||
padding: 100px 0;
|
||||
background-color: #f5f7ff;
|
||||
}
|
||||
|
||||
.docs-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
.doc-card {
|
||||
background-color: white;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.doc-card:hover {
|
||||
transform: translateY(-10px);
|
||||
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.doc-image {
|
||||
height: 200px;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.doc-card:nth-child(1) .doc-image {
|
||||
background-image: linear-gradient(rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.3)), url('https://images.unsplash.com/photo-1551288049-bebda4e38f71?ixlib=rb-4.0.3&auto=format&fit=crop&w=1350&q=80');
|
||||
}
|
||||
|
||||
.doc-card:nth-child(2) .doc-image {
|
||||
background-image: linear-gradient(rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.3)), url('https://images.unsplash.com/photo-1461749280684-dccba630e2f6?ixlib=rb-4.0.3&auto=format&fit=crop&w=1350&q=80');
|
||||
}
|
||||
|
||||
.doc-content {
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
.doc-content h3 {
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 15px;
|
||||
color: var(--dark);
|
||||
}
|
||||
|
||||
.doc-content p {
|
||||
color: var(--secondary);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* 页脚 */
|
||||
footer {
|
||||
background-color: var(--dark);
|
||||
color: white;
|
||||
padding: 60px 0 20px;
|
||||
}
|
||||
|
||||
.footer-content {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 40px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.footer-column h3 {
|
||||
font-size: 1.3rem;
|
||||
margin-bottom: 20px;
|
||||
position: relative;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.footer-column h3::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 50px;
|
||||
height: 2px;
|
||||
background-color: var(--primary);
|
||||
}
|
||||
|
||||
.footer-column p {
|
||||
opacity: 0.8;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.footer-links {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.footer-links li {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.footer-links a {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
opacity: 0.8;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.footer-links a:hover {
|
||||
opacity: 1;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.social-links {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.social-links a {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 50%;
|
||||
color: white;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.social-links a:hover {
|
||||
background-color: var(--primary);
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
|
||||
.footer-bottom {
|
||||
text-align: center;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||
opacity: 0.7;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.hero h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.hero p {
|
||||
font-size: 1rem;
|
||||
.btn-primary {
|
||||
background-color: var(--primary-color);
|
||||
border-color: var(--primary-color);
|
||||
padding: 0.5rem 1.5rem;
|
||||
border-radius: 50px;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
display: none;
|
||||
.btn-primary:hover {
|
||||
background-color: #2e59d9;
|
||||
border-color: #2653d4;
|
||||
}
|
||||
|
||||
.features-grid, .docs-cards {
|
||||
grid-template-columns: 1fr;
|
||||
.navbar {
|
||||
box-shadow: 0 0.15rem 1.75rem 0 rgba(58, 59, 69, 0.15);
|
||||
}
|
||||
|
||||
.footer {
|
||||
background-color: var(--dark-color);
|
||||
color: white;
|
||||
padding: 2rem 0;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.tech-badge {
|
||||
margin: 0.2rem;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- 导航栏 -->
|
||||
<header>
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-white sticky-top">
|
||||
<div class="container">
|
||||
<nav>
|
||||
<div class="logo">
|
||||
<i class="fas fa-shield-alt"></i>
|
||||
<span>Spring Security 6</span>
|
||||
</div>
|
||||
<ul class="nav-links">
|
||||
<li><a href="#features">特性</a></li>
|
||||
<li><a href="#docs">文档</a></li>
|
||||
<li><a href="/user" target="_blank">用户管理</a></li>
|
||||
<li><a href="/login" target="_blank">登录</a></li>
|
||||
<a class="navbar-brand fw-bold" href="#" style="color: var(--primary-color);">
|
||||
<i class="fas fa-shield-alt me-2"></i>Security Admin
|
||||
</a>
|
||||
<button class="navbar-toggler" data-bs-target="#navbarNav" data-bs-toggle="collapse" type="button">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav me-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="#">首页</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">功能</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">关于</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/doc.html" target="_blank">API文档</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/swagger-ui/index.html" target="_blank">Swagger UI</a>
|
||||
</li>
|
||||
<li class="nav-item ms-2">
|
||||
<a class="btn btn-primary" href="/login">登录系统 <i class="fas fa-sign-in-alt ms-1"></i></a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- 英雄区域 -->
|
||||
<section class="hero">
|
||||
<div class="container">
|
||||
<div class="hero-content">
|
||||
<h1>掌握 Spring Security 6</h1>
|
||||
<p>学习最强大的Java安全框架,保护您的应用程序免受现代安全威胁。Spring Security
|
||||
6提供了全面的身份验证和授权功能,让您的应用安全无忧。</p>
|
||||
<div>
|
||||
<a class="btn" href="#docs">开始学习</a>
|
||||
<a class="btn btn-outline" href="/doc.html" target="_blank">查看API文档</a>
|
||||
<section class="hero-section">
|
||||
<div class="container text-center">
|
||||
<h1 class="display-4 fw-bold mb-4">Spring Security 6 后台管理系统</h1>
|
||||
<p class="lead mb-5">基于Spring Security 6的自定义权限校验解决方案,提供安全、高效的后台管理体验</p>
|
||||
<div class="d-flex justify-content-center gap-3">
|
||||
<a class="btn btn-light btn-lg px-4" href="#features">探索功能 <i class="fas fa-arrow-down ms-2"></i></a>
|
||||
<a class="btn btn-outline-light btn-lg px-4" href="/login">立即体验 <i class="fas fa-rocket ms-2"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 主要特性 -->
|
||||
<section class="container mb-5" id="features">
|
||||
<div class="text-center mb-5">
|
||||
<h2 class="fw-bold">核心特性</h2>
|
||||
<p class="text-muted">Spring Security 6 提供的最新安全特性与自定义权限校验完美结合</p>
|
||||
</div>
|
||||
|
||||
<div class="row g-4">
|
||||
<div class="col-md-4">
|
||||
<div class="feature-card card h-100 p-4 text-center">
|
||||
<div class="feature-icon">
|
||||
<i class="fas fa-user-shield"></i>
|
||||
</div>
|
||||
<h4>自定义权限校验</h4>
|
||||
<p class="text-muted">基于Spring Security
|
||||
6的全新授权架构,实现灵活的自定义权限校验逻辑,满足复杂业务场景需求。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="feature-card card h-100 p-4 text-center">
|
||||
<div class="feature-icon">
|
||||
<i class="fas fa-lock"></i>
|
||||
</div>
|
||||
<h4>RBAC权限控制</h4>
|
||||
<p class="text-muted">基于角色的访问控制(RBAC),支持多级角色权限分配,精细到按钮级别的权限控制。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="feature-card card h-100 p-4 text-center">
|
||||
<div class="feature-icon">
|
||||
<i class="fas fa-bolt"></i>
|
||||
</div>
|
||||
<h4>高性能设计</h4>
|
||||
<p class="text-muted">优化权限校验流程,减少不必要的数据库查询,权限校验效率提升40%以上。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="feature-card card h-100 p-4 text-center">
|
||||
<div class="feature-icon">
|
||||
<i class="fas fa-mobile-alt"></i>
|
||||
</div>
|
||||
<h4>响应式设计</h4>
|
||||
<p class="text-muted">全面适配各种设备屏幕,从桌面到移动端,管理体验始终如一。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="feature-card card h-100 p-4 text-center">
|
||||
<div class="feature-icon">
|
||||
<i class="fas fa-chart-line"></i>
|
||||
</div>
|
||||
<h4>实时监控</h4>
|
||||
<p class="text-muted">系统安全事件实时监控,登录日志、操作日志完整记录,便于审计追踪。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="feature-card card h-100 p-4 text-center">
|
||||
<div class="feature-icon">
|
||||
<i class="fas fa-plug"></i>
|
||||
</div>
|
||||
<h4>API集成</h4>
|
||||
<p class="text-muted">完善的RESTful API设计,支持Swagger文档,方便与其他系统集成。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 特性部分 -->
|
||||
<section class="features" id="features">
|
||||
<!-- 技术栈 -->
|
||||
<section class="bg-light py-5 mb-5">
|
||||
<div class="container">
|
||||
<div class="section-title">
|
||||
<h2>Spring Security 6 核心特性</h2>
|
||||
<p>Spring Security 6引入了许多强大的新功能,使应用程序安全比以往任何时候都更简单、更强大。</p>
|
||||
</div>
|
||||
<div class="features-grid">
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">
|
||||
<i class="fas fa-user-lock"></i>
|
||||
</div>
|
||||
<h3>现代化的认证</h3>
|
||||
<p>支持OAuth 2.0、OpenID Connect、SAML 2.0等多种认证协议,满足现代应用的安全需求。</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">
|
||||
<i class="fas fa-key"></i>
|
||||
</div>
|
||||
<h3>强大的授权</h3>
|
||||
<p>细粒度的权限控制,支持方法级安全、领域对象安全等多种授权模式。</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">
|
||||
<i class="fas fa-shield-virus"></i>
|
||||
</div>
|
||||
<h3>防护机制</h3>
|
||||
<p>内置CSRF防护、点击劫持防护、内容安全策略等安全机制,保护应用免受常见攻击。</p>
|
||||
<div class="text-center mb-4">
|
||||
<h2 class="fw-bold">技术栈</h2>
|
||||
<p class="text-muted">基于最前沿的Java生态技术构建</p>
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-wrap justify-content-center">
|
||||
<span class="tech-badge badge bg-primary">Spring Security 6</span>
|
||||
<span class="tech-badge badge bg-success">Spring Boot 3</span>
|
||||
<span class="tech-badge badge bg-info">Java 17</span>
|
||||
<span class="tech-badge badge bg-warning text-dark">Thymeleaf</span>
|
||||
<span class="tech-badge badge bg-danger">JWT</span>
|
||||
<span class="tech-badge badge bg-secondary">MySQL</span>
|
||||
<span class="tech-badge badge bg-dark">Redis</span>
|
||||
<span class="tech-badge badge bg-primary">WebJars</span>
|
||||
<span class="tech-badge badge bg-success">Bootstrap 5</span>
|
||||
<span class="tech-badge badge bg-info">Font Awesome</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 文档部分 -->
|
||||
<section class="docs" id="docs">
|
||||
<div class="container">
|
||||
<div class="section-title">
|
||||
<h2>学习资源与文档</h2>
|
||||
<p>探索我们的文档和工具,快速掌握Spring Security 6的强大功能。</p>
|
||||
<!-- 系统截图 -->
|
||||
<section class="container mb-5">
|
||||
<div class="text-center mb-5">
|
||||
<h2 class="fw-bold">系统预览</h2>
|
||||
<p class="text-muted">直观易用的管理界面</p>
|
||||
</div>
|
||||
<div class="docs-cards">
|
||||
<div class="doc-card">
|
||||
<div class="doc-image"></div>
|
||||
<div class="doc-content">
|
||||
<h3>API 文档</h3>
|
||||
<p>详细的API参考文档,包含所有类、方法和配置选项的详细说明,帮助您充分利用Spring Security
|
||||
6的所有功能。</p>
|
||||
<a class="btn" href="/doc.html" target="_blank">查看API文档</a>
|
||||
|
||||
<div class="row g-4">
|
||||
<div class="col-md-6">
|
||||
<div class="card border-0 shadow-sm">
|
||||
<img alt="登录界面" class="card-img-top rounded"
|
||||
src="https://pure-admin.cn/assets/img/4.f2995f61.jpg">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">安全登录</h5>
|
||||
<p class="card-text">多因素认证支持,图形验证码,防止暴力破解。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="doc-card">
|
||||
<div class="doc-image"></div>
|
||||
<div class="doc-content">
|
||||
<h3>Swagger UI</h3>
|
||||
<p>交互式API文档,可以直接在浏览器中测试API端点,查看请求和响应示例,加快开发流程。</p>
|
||||
<a class="btn" href="/swagger-ui/index.html" target="_blank">访问Swagger UI</a>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card border-0 shadow-sm">
|
||||
<img alt="权限管理" class="card-img-top rounded" height="342.42px"
|
||||
src="https://pure-admin.cn/assets/img/rbac.078e7dea.jpg">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">权限管理</h5>
|
||||
<p class="card-text">可视化权限配置,角色与资源灵活关联。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -465,38 +256,54 @@
|
|||
</section>
|
||||
|
||||
<!-- 页脚 -->
|
||||
<footer>
|
||||
<footer class="footer">
|
||||
<div class="container">
|
||||
<div class="footer-content">
|
||||
<div class="footer-column">
|
||||
<h3>关于 Spring Security</h3>
|
||||
<p>Spring
|
||||
Security是一个功能强大且高度可定制的身份验证和访问控制框架,是保护基于Spring的应用程序的事实标准。</p>
|
||||
<div class="social-links">
|
||||
<a href="#"><i class="fab fa-github"></i></a>
|
||||
<a href="#"><i class="fab fa-twitter"></i></a>
|
||||
<a href="#"><i class="fab fa-stack-overflow"></i></a>
|
||||
<div class="row">
|
||||
<div class="col-md-4 mb-4 mb-md-0">
|
||||
<h5><i class="fas fa-shield-alt me-2"></i>Security Admin</h5>
|
||||
<p class="small text-white-50">基于Spring Security
|
||||
6的自定义权限校验后台管理系统,为企业级应用提供安全可靠的管理解决方案。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer-column">
|
||||
<h3>快速链接</h3>
|
||||
<ul class="footer-links">
|
||||
<li><a href="#features" target="_blank">核心特性</a></li>
|
||||
<li><a href="#docs" target="_blank">学习资源</a></li>
|
||||
<li><a href="/doc.html" target="_blank">API文档</a></li>
|
||||
<li><a href="/swagger-ui/index.html" target="_blank">Swagger UI</a></li>
|
||||
<div class="col-md-2 mb-4 mb-md-0">
|
||||
<h5>快速链接</h5>
|
||||
<ul class="list-unstyled">
|
||||
<li><a class="text-white-50" href="#">首页</a></li>
|
||||
<li><a class="text-white-50" href="/doc.html">API文档</a></li>
|
||||
<li><a class="text-white-50" href="/swagger-ui/index.html">Swagger UI</a></li>
|
||||
<li><a class="text-white-50" href="#">GitHub</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer-column">
|
||||
<h3>联系我们</h3>
|
||||
<p><i class="fas fa-envelope"></i> security@example.com</p>
|
||||
<p><i class="fas fa-globe"></i> www.springsecurity.org</p>
|
||||
<div class="col-md-3 mb-4 mb-md-0">
|
||||
<h5>技术资源</h5>
|
||||
<ul class="list-unstyled">
|
||||
<li><a class="text-white-50" href="https://spring.io/projects/spring-security">Spring Security</a>
|
||||
</li>
|
||||
<li><a class="text-white-50" href="https://spring.io/projects/spring-boot">Spring Boot</a></li>
|
||||
<li><a class="text-white-50" href="https://www.thymeleaf.org/">Thymeleaf</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<h5>联系我</h5>
|
||||
<ul class="list-unstyled">
|
||||
<li><i class="fas fa-envelope me-2"></i> <span
|
||||
class="text-white-50">1319900154@qq.com</span></li>
|
||||
<li><i class="fas fa-phone me-2"></i> <span class="text-white-50">+86 18900696469</span></li>
|
||||
</ul>
|
||||
<div class="mt-3">
|
||||
<a class="text-white me-3" href="https://github.com/BunnyMaster"><i class="fab fa-github fa-lg"></i></a>
|
||||
<a class="text-white me-3" href="#"><i class="fab fa-weixin fa-lg"></i></a>
|
||||
<a class="text-white" href="#"><i class="fab fa-weibo fa-lg"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer-bottom">
|
||||
<p>© 2023 Spring Security 学习中心. 保留所有权利.</p>
|
||||
</div>
|
||||
<hr class="my-4 bg-white-50">
|
||||
<div class="text-center small text-white-50">
|
||||
© 2025 Spring Security 6 Admin System. All rights reserved.
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- Bootstrap JS -->
|
||||
<script th:src="@{/webjars/bootstrap/5.1.3/js/bootstrap.bundle.min.js}"></script>
|
||||
</body>
|
||||
</html>
|
19
pom.xml
19
pom.xml
|
@ -49,6 +49,9 @@
|
|||
<dynamic.datasource.version>4.3.1</dynamic.datasource.version>
|
||||
<jackson-dataType.version>2.19.0</jackson-dataType.version>
|
||||
<quartz-scheduler.version>2.3.2</quartz-scheduler.version>
|
||||
<font-awesome.version>5.15.4</font-awesome.version>
|
||||
<bootstrap.version>5.1.3</bootstrap.version>
|
||||
<jquery.version>3.6.0</jquery.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
|
@ -164,6 +167,22 @@
|
|||
<artifactId>quartz</artifactId>
|
||||
<version>${quartz-scheduler.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.webjars</groupId>
|
||||
<artifactId>bootstrap</artifactId>
|
||||
<version>${bootstrap.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.webjars</groupId>
|
||||
<artifactId>font-awesome</artifactId>
|
||||
<version>${font-awesome.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.webjars</groupId>
|
||||
<artifactId>jquery</artifactId>
|
||||
<version>${jquery.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
|
Loading…
Reference in New Issue