🎉 init-阶段二

This commit is contained in:
Bunny 2025-07-11 13:48:10 +08:00
parent 6579c15627
commit ba6ab4b1ee
43 changed files with 2594 additions and 3 deletions

View File

@ -1,4 +1,4 @@
package com.spring.service.security.impl;
package com.spring.service.impl;
import com.spring.service.security.LoginService;
import lombok.RequiredArgsConstructor;

View File

@ -20,8 +20,9 @@
<modules>
<module>official</module>
<module>step-1</module>
<module>step-2</module>
</modules>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source>

View File

@ -1,4 +1,4 @@
package com.spring.service.security.impl;
package com.spring.service.impl;
import com.spring.service.security.LoginService;
import lombok.RequiredArgsConstructor;

View File

View File

@ -0,0 +1,27 @@
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.mall</groupId>
<artifactId>spring-security</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>step-2</artifactId>
<description>学习步骤1,不前后端分离的项目</description>
<packaging>jar</packaging>
<name>step-2</name>
<url>https://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<java.version>17</java.version>
</properties>
<dependencies>
</dependencies>
</project>

View File

@ -0,0 +1,11 @@
package com.spring;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringSecurityStep1Application {
public static void main(String[] args) {
SpringApplication.run(SpringSecurityStep1Application.class, args);
}
}

View File

@ -0,0 +1,22 @@
package com.spring.bean.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Schema(name = "LoginRequest", title = "LoginRequest登录参数", description = "登录请求参数")
public class LoginRequest {
@Schema(name = "username", title = "用户名")
private String username;
@Schema(name = "password", description = "密码")
private String password;
}

View File

@ -0,0 +1 @@
package com.spring.bean.vo;

View File

@ -0,0 +1,34 @@
package com.spring.bean.vo.result;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
/**
* 封装分页查询结果
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Schema(name = "PageResult 对象", title = "分页返回结果", description = "分页返回结果")
public class PageResult<T> implements Serializable {
@Schema(name = "pageNo", title = "当前页")
private Long pageNo;
@Schema(name = "pageSize", title = "每页记录数")
private Long pageSize;
@Schema(name = "total", title = "总记录数")
private Long total;
@Schema(name = "list", title = "当前页数据集合")
private List<T> list;
}

View File

@ -0,0 +1,173 @@
package com.spring.bean.vo.result;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result<T> {
// 状态码
private Integer code;
// 返回消息
private String message;
// 返回数据
private T data;
/**
* * 自定义返回体
*
* @param data 返回体
* @return Result<T>
*/
protected static <T> Result<T> build(T data) {
Result<T> result = new Result<>();
result.setData(data);
return result;
}
/**
* * 自定义返回体使用ResultCodeEnum构建
*
* @param body 返回体
* @param codeEnum 返回状态码
* @return Result<T>
*/
public static <T> Result<T> build(T body, ResultCodeEnum codeEnum) {
Result<T> result = build(body);
result.setCode(codeEnum.getCode());
result.setMessage(codeEnum.getMessage());
return result;
}
/**
* * 自定义返回体
*
* @param body 返回体
* @param code 返回状态码
* @param message 返回消息
* @return Result<T>
*/
public static <T> Result<T> build(T body, Integer code, String message) {
Result<T> result = build(body);
result.setCode(code);
result.setMessage(message);
result.setData(null);
return result;
}
/**
* * 操作成功
*
* @return Result<T>
*/
public static <T> Result<T> success() {
return success(null, ResultCodeEnum.SUCCESS);
}
/**
* * 操作成功
*
* @param data baseCategory1List
*/
public static <T> Result<T> success(T data) {
return build(data, ResultCodeEnum.SUCCESS);
}
/**
* * 操作成功-状态码
*
* @param codeEnum 状态码
*/
public static <T> Result<T> success(ResultCodeEnum codeEnum) {
return success(null, codeEnum);
}
/**
* * 操作成功-自定义返回数据和状态码
*
* @param data 返回体
* @param codeEnum 状态码
*/
public static <T> Result<T> success(T data, ResultCodeEnum codeEnum) {
return build(data, codeEnum);
}
/**
* * 操作失败-自定义返回数据和状态码
*
* @param data 返回体
* @param message 错误信息
*/
public static <T> Result<T> success(T data, String message) {
return build(data, 200, message);
}
/**
* * 操作失败-自定义返回数据和状态码
*
* @param data 返回体
* @param code 状态码
* @param message 错误信息
*/
public static <T> Result<T> success(T data, Integer code, String message) {
return build(data, code, message);
}
/**
* * 操作失败
*/
public static <T> Result<T> error() {
return Result.build(null);
}
/**
* * 操作失败-自定义返回数据
*
* @param data 返回体
*/
public static <T> Result<T> error(T data) {
return build(data, ResultCodeEnum.FAIL);
}
/**
* * 操作失败-状态码
*
* @param codeEnum 状态码
*/
public static <T> Result<T> error(ResultCodeEnum codeEnum) {
return build(null, codeEnum);
}
/**
* * 操作失败-自定义返回数据和状态码
*
* @param data 返回体
* @param codeEnum 状态码
*/
public static <T> Result<T> error(T data, ResultCodeEnum codeEnum) {
return build(data, codeEnum);
}
/**
* * 操作失败-自定义返回数据和状态码
*
* @param data 返回体
* @param code 状态码
* @param message 错误信息
*/
public static <T> Result<T> error(T data, Integer code, String message) {
return build(data, code, message);
}
/**
* * 操作失败-自定义返回数据和状态码
*
* @param data 返回体
* @param message 错误信息
*/
public static <T> Result<T> error(T data, String message) {
return build(null, 500, message);
}
}

View File

@ -0,0 +1,90 @@
package com.spring.bean.vo.result;
import lombok.Getter;
/**
* 统一返回结果状态信息类
*/
@Getter
public enum ResultCodeEnum {
// 成功操作 200
SUCCESS(200, "操作成功"),
LOAD_FINISHED(200, "加载完成"),
ADD_SUCCESS(200, "添加成功"),
UPDATE_SUCCESS(200, "修改成功"),
DELETE_SUCCESS(200, "删除成功"),
SORT_SUCCESS(200, "排序成功"),
SUCCESS_UPLOAD(200, "上传成功"),
SUCCESS_LOGOUT(200, "退出成功"),
EMAIL_CODE_REFRESH(200, "邮箱验证码已刷新"),
EMAIL_CODE_SEND_SUCCESS(200, "邮箱验证码已发送"),
// 验证错误 201
USERNAME_OR_PASSWORD_NOT_EMPTY(201, "用户名或密码不能为空"),
EMAIL_CODE_NOT_EMPTY(201, "邮箱验证码不能为空"),
SEND_EMAIL_CODE_NOT_EMPTY(201, "请先发送邮箱验证码"),
EMAIL_CODE_NOT_MATCHING(201, "邮箱验证码不匹配"),
LOGIN_ERROR(500, "账号或密码错误"),
LOGIN_ERROR_USERNAME_PASSWORD_NOT_EMPTY(201, "登录信息不能为空"),
GET_BUCKET_EXCEPTION(201, "获取文件信息失败"),
SEND_MAIL_CODE_ERROR(201, "邮件发送失败"),
EMAIL_CODE_EMPTY(201, "邮箱验证码过期或不存在"),
EMAIL_EXIST(201, "邮箱已存在"),
REQUEST_IS_EMPTY(201, "请求数据为空"),
DATA_TOO_LARGE(201, "请求数据为空"),
UPDATE_NEW_PASSWORD_SAME_AS_OLD_PASSWORD(201, "新密码与密码相同"),
// 数据相关 206
ILLEGAL_REQUEST(206, "非法请求"),
REPEAT_SUBMIT(206, "重复提交"),
DATA_ERROR(206, "数据异常"),
EMAIL_USER_TEMPLATE_IS_EMPTY(206, "邮件模板为空"),
EMAIL_TEMPLATE_IS_EMPTY(206, "邮件模板为空"),
EMAIL_USER_IS_EMPTY(206, "关联邮件用户配置为空"),
DATA_EXIST(206, "数据已存在"),
DATA_NOT_EXIST(206, "数据不存在"),
ALREADY_USER_EXCEPTION(206, "用户已存在"),
USER_IS_EMPTY(206, "用户不存在"),
FILE_NOT_EXIST(206, "文件不存在"),
NEW_PASSWORD_SAME_OLD_PASSWORD(206, "新密码不能和旧密码相同"),
MISSING_TEMPLATE_FILES(206, "缺少模板文件"),
// 身份过期 208
LOGIN_AUTH(208, "请先登陆"),
AUTHENTICATION_EXPIRED(208, "身份验证过期"),
SESSION_EXPIRATION(208, "会话过期"),
// 209
THE_SAME_USER_HAS_LOGGED_IN(209, "相同用户已登录"),
// 提示错误
UPDATE_ERROR(216, "修改失败"),
URL_ENCODE_ERROR(216, "URL编码失败"),
ILLEGAL_CALLBACK_REQUEST_ERROR(217, "非法回调请求"),
FETCH_USERINFO_ERROR(219, "获取用户信息失败"),
ILLEGAL_DATA_REQUEST(219, "非法数据请求"),
CLASS_NOT_FOUND(219, "类名不存在"),
ADMIN_ROLE_CAN_NOT_DELETED(219, "无法删除admin角色"),
ROUTER_RANK_NEED_LARGER_THAN_THE_PARENT(219, "设置路由等级需要大于或等于父级的路由等级"),
// 无权访问 403
FAIL_NO_ACCESS_DENIED(403, "无权访问"),
FAIL_NO_ACCESS_DENIED_USER_OFFLINE(403, "用户强制下线"),
TOKEN_PARSING_FAILED(403, "token解析失败"),
FAIL_NO_ACCESS_DENIED_USER_LOCKED(403, "该账户已封禁"),
// 系统错误 500
UNKNOWN_EXCEPTION(500, "服务异常"),
SERVICE_ERROR(500, "服务异常"),
UPLOAD_ERROR(500, "上传失败"),
FAIL(500, "失败"),
;
private final Integer code;
private final String message;
ResultCodeEnum(Integer code, String message) {
this.code = code;
this.message = message;
}
}

View File

@ -0,0 +1,49 @@
package com.spring.config;
import io.swagger.v3.oas.models.ExternalDocumentation;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import lombok.extern.slf4j.Slf4j;
import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@Slf4j
public class Knife4jConfig {
@Value("${server.port}")
private String port;
@Bean
public OpenAPI openAPI() {
String url = "http://localhost:" + port;
// 作者等信息
Contact contact = new Contact().name("Bunny").email("1319900154@qq.com").url(url);
// 使用协议
License license = new License().name("MIT").url("https://mit-license.org");
// 相关信息
Info info = new Info().title("Bunny-Admin")
.contact(contact).license(license)
.description("Bunny代码生成器")
.summary("Bunny的代码生成器")
.termsOfService(url)
.version("v1.0.0");
return new OpenAPI().info(info).externalDocs(new ExternalDocumentation());
}
@Bean
public GroupedOpenApi all() {
return GroupedOpenApi.builder().group("全部请求接口").pathsToMatch("/api/**").build();
}
@Bean
public GroupedOpenApi security() {
return GroupedOpenApi.builder().group("security接口").pathsToMatch("/api/security/**").build();
}
}

View File

@ -0,0 +1,23 @@
package com.spring.controller;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Tag(name = "检查接口", description = "检查当前用户的权限信息")
@RestController
@RequestMapping("/api/security")
public class CheckController {
@GetMapping("/current-user")
public Authentication getCurrentUser() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
System.out.println("Current user: " + auth.getName());
System.out.println("Authorities: " + auth.getAuthorities());
return auth;
}
}

View File

@ -0,0 +1,18 @@
package com.spring.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class LoginController {
@GetMapping("")
public String indexPage() {
return "index";
}
@GetMapping("/login-page")
public String showLoginPage() {
return "login";
}
}

View File

@ -0,0 +1,65 @@
package com.spring.security.config;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
@Configuration
public class SecurityConfiguration {
/**
* 添加内存用户
*
* @return {@link ConditionalOnMissingBean}
*/
@Bean
@ConditionalOnMissingBean(UserDetailsService.class)
InMemoryUserDetailsManager inMemoryUserDetailsManager(PasswordEncoder passwordEncoder) {
// 使用注入的密码加密器进行密码加密
String generatedPassword = passwordEncoder.encode("123456");
// 创建用户 权限为只读
UserDetails bunny = User.withUsername("bunny").password(generatedPassword).roles("USER").authorities("read").build();
// 管理员可以查看全部
UserDetails admin = User.withUsername("admin").password(generatedPassword).roles("ADMIN").authorities("all", "read").build();
// 返回内存中的用户
return new InMemoryUserDetailsManager(bunny, admin);
}
/**
* 配置密码编码器Bean
*
* <p>Spring Security提供了多种密码编码器实现推荐使用BCryptPasswordEncoder作为默认选择</p>
*
* <p>特点</p>
* <ul>
* <li>BCryptPasswordEncoder - 使用bcrypt强哈希算法自动加盐是当前最推荐的密码编码器</li>
* <li>Argon2PasswordEncoder - 使用Argon2算法抗GPU/ASIC攻击但需要更多内存</li>
* <li>SCryptPasswordEncoder - 使用scrypt算法内存密集型抗硬件攻击</li>
* <li>Pbkdf2PasswordEncoder - 使用PBKDF2算法较老但广泛支持</li>
* </ul>
*
* <p>注意不推荐使用MD5等弱哈希算法Spring官方也不推荐自定义弱密码编码器</p>
*
* @return PasswordEncoder 密码编码器实例
* @see BCryptPasswordEncoder
*/
@Bean
public PasswordEncoder passwordEncoder() {
// 其他编码器示例根据需求选择一种:
// return new Argon2PasswordEncoder(16, 32, 1, 1 << 14, 2);
// return new SCryptPasswordEncoder();
// return new Pbkdf2PasswordEncoder("secret", 185000, 256);
// 实际项目中只需返回一个密码编码器
return new BCryptPasswordEncoder();
}
}

View File

@ -0,0 +1,65 @@
package com.spring.security.config;
import com.spring.security.handler.SecurityAccessDeniedHandler;
import com.spring.security.handler.SecurityAuthenticationEntryPoint;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.web.SecurityFilterChain;
@EnableMethodSecurity
@EnableWebSecurity
@Configuration
public class SecurityWebConfiguration {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
String[] permitAllUrls = {
"/", "/doc.html/**",
"/webjars/**", "/images/**", ".well-known/**", "favicon.ico", "/error/**",
"/swagger-ui/**", "/v3/api-docs/**"
};
http.authorizeHttpRequests(authorizeRequests ->
// 访问路径为 /api 时需要进行认证
authorizeRequests
.requestMatchers(permitAllUrls).permitAll()
.requestMatchers("/api/security/**").permitAll()
.requestMatchers(HttpMethod.GET, "/api/anonymous/**").anonymous()
// 会自动变成 ROLE_ADMIN
// .requestMatchers("/api/**").hasRole("ADMIN")
.requestMatchers("/api/**").hasAnyAuthority("all", "read")
)
.formLogin(loginPage -> loginPage
// 自定义登录页路径
.loginPage("/login-page")
// 处理登录的URL默认就是/login
.loginProcessingUrl("/login")
// 登录成功跳转
.defaultSuccessUrl("/")
// 登录失败跳转
.failureUrl("/login-page?error=true")
.permitAll()
)
// 使用默认的登录
// .formLogin(Customizer.withDefaults())
// 禁用表单登录
// .formLogin(AbstractHttpConfigurer::disable)
.logout(logout -> logout
.logoutSuccessUrl("/login-page?logout=true")
.permitAll()
)
.csrf(AbstractHttpConfigurer::disable)
.exceptionHandling(configurer -> configurer
.accessDeniedHandler(new SecurityAccessDeniedHandler())
.authenticationEntryPoint(new SecurityAuthenticationEntryPoint())
)
;
return http.build();
}
}

View File

@ -0,0 +1,28 @@
package com.spring.security.handler;
import com.alibaba.fastjson2.JSON;
import com.spring.bean.vo.result.Result;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import java.io.IOException;
@Slf4j
public class SecurityAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
log.error("CustomerAccessDeniedHandler:{}", accessDeniedException.getLocalizedMessage());
Result<Object> result = Result.error(accessDeniedException.getMessage());
Object json = JSON.toJSON(result);
// 返回响应
response.setContentType("application/json;charset=UTF-8");
response.getWriter().println(json);
}
}

View File

@ -0,0 +1,29 @@
package com.spring.security.handler;
import com.alibaba.fastjson2.JSON;
import com.spring.bean.vo.result.Result;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import java.io.IOException;
@Slf4j
public class SecurityAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
log.error("CustomerAccessDeniedHandler:{}", authException.getLocalizedMessage());
Result<Object> result = Result.error(authException.getMessage());
Object json = JSON.toJSON(result);
// 返回响应
response.setContentType("application/json;charset=UTF-8");
response.getWriter().println(json);
}
}

View File

@ -0,0 +1,52 @@
package com.spring.security.password;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;
import java.util.HexFormat;
/**
* <h1>MD5密码编码器实现</h1>
*
* <strong>安全警告</strong>此类使用MD5算法进行密码哈希已不再安全不推荐用于生产环境
*
* <p>MD5算法因其计算速度快且易受彩虹表攻击而被认为不安全即使密码哈希本身是单向的
* 但现代计算能力使得暴力破解和预先计算的彩虹表攻击变得可行</p>
*
* <p>Spring Security推荐使用BCryptPBKDF2Argon2或Scrypt等自适应单向函数替代MD5</p>
*
* @see PasswordEncoder
* 一般仅用于遗留系统兼容新系统应使用更安全的密码编码器
*/
public class MD5PasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence rawPassword) {
if (rawPassword == null) {
throw new IllegalArgumentException("原始密码不能为null");
}
byte[] md5Digest = DigestUtils.md5Digest(rawPassword.toString().getBytes());
return HexFormat.of().formatHex(md5Digest);
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
if (rawPassword == null) {
throw new IllegalArgumentException("原始密码不能为null");
}
if (!StringUtils.hasText(encodedPassword)) {
return false;
}
return encodedPassword.equalsIgnoreCase(encode(rawPassword));
}
@Override
public boolean upgradeEncoding(String encodedPassword) {
// MD5已不安全始终返回true建议升级到更安全的算法
return true;
}
}

View File

@ -0,0 +1,35 @@
package com.spring.security.service;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class CustomUserDetailsService implements UserDetailsService {
private final PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 1. 这里应该根据username从数据库或其他存储中查询用户信息
// 以下是模拟数据实际应用中应从数据库查询
// 2. 如果用户不存在抛出UsernameNotFoundException
if (!"bunny".equalsIgnoreCase(username)) {
throw new UsernameNotFoundException("User not found: " + username);
}
// 3. 构建UserDetails对象返回
return User.builder()
.username(username) // 使用传入的用户名
.password(passwordEncoder.encode("123456")) // 密码应该已经加密存储这里仅为示例
.roles("USER") // 角色会自动添加ROLE_前缀
.authorities("read", "write") // 添加具体权限
.build();
}
}

View File

@ -0,0 +1,4 @@
package com.spring.service;
public interface LoginService {
}

View File

@ -0,0 +1,11 @@
package com.spring.service.impl;
import com.spring.service.LoginService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class LoginServiceImpl implements LoginService {
}

View File

@ -0,0 +1,7 @@
bunny:
master:
host: rm-bp12z6hlv46vi6g8mro.mysql.rds.aliyuncs.com
port: 3306
database: bunny_test
username: bunny_test
password: "Test1234"

View File

@ -0,0 +1,34 @@
server:
port: 8772
spring:
application:
name: spring-security
profiles:
active: dev
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://${bunny.master.host}:${bunny.master.port}/${bunny.master.database}?serverTimezone=GMT%2B8&useSSL=false&characterEncoding=utf-8&allowPublicKeyRetrieval=true
username: ${bunny.master.username}
password: ${bunny.master.password}
hikari:
maximum-pool-size: 20
connection-timeout: 30000
jackson:
date-format: yyyy-MM-dd HH:mm:ss
mybatis-plus:
mapper-locations: classpath:/mapper/*.xml
global-config:
db-config:
id-type: auto
logic-delete-value: 1
logic-not-delete-value: 0
logging:
level:
com.spring: debug
root: info

View File

@ -0,0 +1,11 @@
____ ____ _ _
| __ ) _ _ _ __ _ __ _ _ / ___|| |_ _ _ __| |_ _
| _ \| | | | '_ \| '_ \| | | | \___ \| __| | | |/ _` | | | |
| |_) | |_| | | | | | | | |_| | ___) | |_| |_| | (_| | |_| |
|____/ \__,_|_| |_|_| |_|\__, | |____/ \__|\__,_|\__,_|\__, |
|___/ |___/
Service Name${spring.application.name}
SpringBoot Version: ${spring-boot.version}${spring-boot.formatted-version}
Spring Active${spring.profiles.active}

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<contextName>logback</contextName>
<!-- 格式化 年--日 输出 -->
<timestamp key="datetime" datePattern="yyyy-MM-dd"/>
<!-- 引入Spring属性 -->
<springProperty name="APP_NAME" source="spring.application.name" defaultValue="application"/>
<!--编码-->
<property name="ENCODING" value="UTF-8"/>
<!-- 控制台日志 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!-- 临界值过滤器 -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUG</level>
</filter>
<encoder>
<pattern>
%cyan([%thread %d{yyyy-MM-dd HH:mm:ss}]) %yellow(%-5level) %green(%logger{100}).%boldRed(%method)-%boldMagenta(%line)-%blue(%msg%n)
</pattern>
<charset>${ENCODING}</charset>
</encoder>
</appender>
<!-- 文件日志 -->
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>logs/${APP_NAME}/${datetime}.log</file>
<append>true</append>
<encoder>
<pattern>%date{yyyy-MM-dd HH:mm:ss} [%-5level] %thread %file:%line %logger %msg%n</pattern>
<charset>${ENCODING}</charset>
</encoder>
</appender>
<!-- 让SpringBoot内部日志ERROR级别 减少日志输出 -->
<logger name="org.springframework" level="ERROR" additivity="false">
<appender-ref ref="STOUT"/>
</logger>
<!-- 让mybatis整合包日志ERROR 减少日志输出 -->
<logger name="org.mybatis" level="ERROR" additivity="false">
<appender-ref ref="STOUT"/>
</logger>
<!-- 让ibatis 日志ERROR 减少日志输出 -->
<logger name="org.apache.ibatis" level="ERROR" additivity="false">
<appender-ref ref="STOUT"/>
</logger>
<!-- 让 tomcat包打印日志 日志ERROR 减少日志输出 -->
<logger name="org.apache" level="ERROR" additivity="false">
<appender-ref ref="STOUT"/>
</logger>
<!-- 我们自己开发的程序为DEBUG -->
<logger name="cn.bunny" level="DEBUG" additivity="false">
<appender-ref ref="STOUT"/>
</logger>
<logger name="com.baomidou" level="ERROR" additivity="false">
<appender-ref ref="STOUT"/>
</logger>
<!-- 根日志记录器INFO级别 -->
<root level="DEBUG">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</root>
</configuration>

View File

@ -0,0 +1,108 @@
/* devanagari */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 300;
font-display: swap;
src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLDz8Z11lFc-K.woff2) format('woff2');
unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, U+A8E0-A8FF, U+11B00-11B09;
}
/* latin-ext */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 300;
font-display: swap;
src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLDz8Z1JlFc-K.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 300;
font-display: swap;
src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLDz8Z1xlFQ.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* devanagari */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(https://fonts.gstatic.com/s/poppins/v23/pxiEyp8kv8JHgFVrJJbecmNE.woff2) format('woff2');
unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, U+A8E0-A8FF, U+11B00-11B09;
}
/* latin-ext */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(https://fonts.gstatic.com/s/poppins/v23/pxiEyp8kv8JHgFVrJJnecmNE.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(https://fonts.gstatic.com/s/poppins/v23/pxiEyp8kv8JHgFVrJJfecg.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* devanagari */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLEj6Z11lFc-K.woff2) format('woff2');
unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, U+A8E0-A8FF, U+11B00-11B09;
}
/* latin-ext */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLEj6Z1JlFc-K.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLEj6Z1xlFQ.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* devanagari */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLCz7Z11lFc-K.woff2) format('woff2');
unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, U+A8E0-A8FF, U+11B00-11B09;
}
/* latin-ext */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLCz7Z1JlFc-K.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Poppins';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(https://fonts.gstatic.com/s/poppins/v23/pxiByp8kv8JHgFVrLCz7Z1xlFQ.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

View File

@ -0,0 +1,69 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>400 错误 - phpstudy</title>
<meta content="" name="keywords">
<meta content="" name="description">
<meta content="webkit" name="renderer">
<meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<meta content="black" name="apple-mobile-web-app-status-bar-style">
<meta content="yes" name="apple-mobile-web-app-capable">
<meta content="telephone=no" name="format-detection">
<meta CONTENT="no-cache" HTTP-EQUIV="pragma">
<meta CONTENT="no-store, must-revalidate" HTTP-EQUIV="Cache-Control">
<meta CONTENT="Wed, 26 Feb 1997 08:21:57 GMT" HTTP-EQUIV="expires">
<meta CONTENT="0" HTTP-EQUIV="expires">
<style>
body {
font: 16px arial, 'Microsoft Yahei', 'Hiragino Sans GB', sans-serif;
}
h1 {
margin: 0;
color: #3a87ad;
font-size: 26px;
}
.content {
width: 45%;
margin: 0 auto;
}
.content > div {
margin-top: 200px;
padding: 20px;
background: #d9edf7;
border-radius: 12px;
}
.content dl {
color: #2d6a88;
line-height: 40px;
}
.content div div {
padding-bottom: 20px;
text-align: center;
}
</style>
</head>
<body>
<div class="content">
<div>
<h1>HTTP 400 - Bad Request</h1>
<dl>
<dt>错误说明:因为错误的语法导致服务器无法理解请求信息。</dt>
<dt>原因1客户端发起的请求不符合服务器对请求的某些限制或者请求本身存在一定的错误。</dt>
<dd>解决办法:</dd>
<dd>链接中有特殊字符或者链接长度过长导致,请对应修改.</dd>
<dt>原因2request header 或者 cookie 过大所引起</dt>
<dd>解决办法:</dd>
<dd>crtl+shift+delete 快捷键清除cookie.</dd>
</dl>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,69 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>403 错误 - phpstudy</title>
<meta content="" name="keywords">
<meta content="" name="description">
<meta content="webkit" name="renderer">
<meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<meta content="black" name="apple-mobile-web-app-status-bar-style">
<meta content="yes" name="apple-mobile-web-app-capable">
<meta content="telephone=no" name="format-detection">
<meta CONTENT="no-cache" HTTP-EQUIV="pragma">
<meta CONTENT="no-store, must-revalidate" HTTP-EQUIV="Cache-Control">
<meta CONTENT="Wed, 26 Feb 1997 08:21:57 GMT" HTTP-EQUIV="expires">
<meta CONTENT="0" HTTP-EQUIV="expires">
<style>
body {
font: 16px arial, 'Microsoft Yahei', 'Hiragino Sans GB', sans-serif;
}
h1 {
margin: 0;
color: #3a87ad;
font-size: 26px;
}
.content {
width: 45%;
margin: 0 auto;
}
.content > div {
margin-top: 200px;
padding: 20px;
background: #d9edf7;
border-radius: 12px;
}
.content dl {
color: #2d6a88;
line-height: 40px;
}
.content div div {
padding-bottom: 20px;
text-align: center;
}
</style>
</head>
<body>
<div class="content">
<div>
<h1>403 - Forbidden 禁止访问: 访问被拒绝</h1>
<dl>
<dt>错误说明:禁止访问,服务器拒绝访问</dt>
<dt>原因1未找到默认的索引文件</dt>
<dd>解决办法:</dd>
<dd>IIS中【启用默认内容文档】选项中将默认打开文档修改为程序首页文件格式index.html或者index.php</dd>
<dt>原因2文件夹安全权限导致</dt>
<dd>解决办法:</dd>
<dd>程序文件-右击-属性-安全-Users-修改为读取和执行权限</dd>
</dl>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,78 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>404 错误 - phpstudy</title>
<meta content="" name="keywords">
<meta content="" name="description">
<meta content="webkit" name="renderer">
<meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<meta content="black" name="apple-mobile-web-app-status-bar-style">
<meta content="yes" name="apple-mobile-web-app-capable">
<meta content="telephone=no" name="format-detection">
<meta CONTENT="no-cache" HTTP-EQUIV="pragma">
<meta CONTENT="no-store, must-revalidate" HTTP-EQUIV="Cache-Control">
<meta CONTENT="Wed, 26 Feb 1997 08:21:57 GMT" HTTP-EQUIV="expires">
<meta CONTENT="0" HTTP-EQUIV="expires">
<style>
body {
font: 16px arial, 'Microsoft Yahei', 'Hiragino Sans GB', sans-serif;
}
h1 {
margin: 0;
color: #3a87ad;
font-size: 26px;
}
.content {
width: 45%;
margin: 0 auto;
}
.content > div {
margin-top: 50px;
padding: 20px;
background: #d9edf7;
border-radius: 12px;
}
.content dl {
color: #2d6a88;
line-height: 40px;
}
.content div div {
padding-bottom: 20px;
text-align: center;
}
</style>
</head>
<body>
<div class="content">
<div>
<h1>404 - Page Not Found 未找到</h1>
<dl>
<dt>错误说明:请求的页面不存在</dt>
<dt>原因1访问的文档权限不够</dt>
<dd>解决办法:</dd>
<dd>修改文件权限为755windos系统修改目录权限为可写可读。</dd>
<dt>原因2防火墙的原因</dt>
<dd>解决办法:</dd>
<dd>先关闭让防火墙通过WWW服务。</dd>
<dt>原因3站点根目录无默认访问文件</dt>
<dd>解决办法:</dd>
<dd>在根目录中创建index.html或者创建index.php。</dd>
<dt>原因4站点配置目录不正确</dt>
<dd>解决办法:</dd>
<dd>将网站应用程序复制到站点目录中,或者修改站点配置目录指定到应用程序目录中。</dd>
<dt>原因5站点使用了伪静态</dt>
<dd>解决办法:</dd>
<dd>将伪静态规则删除,或者重新编写正确的伪静态规则,或关闭伪静态配置。</dd>
</dl>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,64 @@
<!DOCTYPE html>
<html lang="zh-cmn-Hans">
<head>
<meta charset="UTF-8">
<title>500 - 服务器错误</title>
<meta content="width=device-width, maximum-scale=1, initial-scale=1" name="viewport"/>
<style>
html, body {
height: 100%;
}
body {
color: #333;
margin: auto;
padding: 1em;
display: table;
user-select: none;
box-sizing: border-box;
font: lighter 20px "微软雅黑";
}
a {
color: #3498db;
text-decoration: none;
}
h1 {
margin-top: 0;
font-size: 3.5em;
}
main {
margin: 0 auto;
text-align: center;
display: table-cell;
vertical-align: middle;
}
.btn {
color: #fff;
padding: .75em 1em;
background: #3498db;
border-radius: 1.5em;
display: inline-block;
transition: opacity .3s, transform .3s;
}
.btn:hover {
transform: scale(1.1);
}
.btn:active {
opacity: .7;
}
</style>
</head>
<body>
<main>
<h1>:'(</h1>
<p>服务器开小差啦!管理员正在修理中...</p>
<p>还请阁下静候站点恢复~</p>
</main>
</body>
</html>

View File

@ -0,0 +1,66 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>501 错误 - phpstudy</title>
<meta content="" name="keywords">
<meta content="" name="description">
<meta content="webkit" name="renderer">
<meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<meta content="black" name="apple-mobile-web-app-status-bar-style">
<meta content="yes" name="apple-mobile-web-app-capable">
<meta content="telephone=no" name="format-detection">
<meta CONTENT="no-cache" HTTP-EQUIV="pragma">
<meta CONTENT="no-store, must-revalidate" HTTP-EQUIV="Cache-Control">
<meta CONTENT="Wed, 26 Feb 1997 08:21:57 GMT" HTTP-EQUIV="expires">
<meta CONTENT="0" HTTP-EQUIV="expires">
<style>
body {
font: 16px arial, 'Microsoft Yahei', 'Hiragino Sans GB', sans-serif;
}
h1 {
margin: 0;
color: #3a87ad;
font-size: 26px;
}
.content {
width: 45%;
margin: 0 auto;
}
.content > div {
margin-top: 200px;
padding: 20px;
background: #d9edf7;
border-radius: 12px;
}
.content dl {
color: #2d6a88;
line-height: 40px;
}
.content div div {
padding-bottom: 20px;
text-align: center;
}
</style>
</head>
<body>
<div class="content">
<div>
<h1>HTTP 501 - Not Implemented</h1>
<dl>
<dt>错误说明:服务器没有相应的执行动作来完成当前请求。</dt>
<dt>原因1Web 服务器不支持实现此请求所需的功能</dt>
<dd>解决办法:</dd>
<dd>可以用来HttpWebRequest指定一个UserAgent来试试的有时候你可以换电脑来测试一下的。</dd>
</dl>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,80 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>502 错误 - phpstudy</title>
<meta content="" name="keywords">
<meta content="" name="description">
<meta content="webkit" name="renderer">
<meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<meta content="black" name="apple-mobile-web-app-status-bar-style">
<meta content="yes" name="apple-mobile-web-app-capable">
<meta content="telephone=no" name="format-detection">
<meta CONTENT="no-cache" HTTP-EQUIV="pragma">
<meta CONTENT="no-store, must-revalidate" HTTP-EQUIV="Cache-Control">
<meta CONTENT="Wed, 26 Feb 1997 08:21:57 GMT" HTTP-EQUIV="expires">
<meta CONTENT="0" HTTP-EQUIV="expires">
<style>
body {
font: 16px arial, 'Microsoft Yahei', 'Hiragino Sans GB', sans-serif;
}
h1 {
margin: 0;
color: #3a87ad;
font-size: 26px;
}
.content {
width: 45%;
margin: 0 auto;
}
.content > div {
margin-top: 50px;
padding: 20px;
background: #d9edf7;
border-radius: 12px;
}
.content dl {
color: #2d6a88;
line-height: 40px;
}
.content div div {
padding-bottom: 20px;
text-align: center;
}
</style>
</head>
<body>
<div class="content">
<div>
<h1>HTTP 502 - Bad Gateway 没有响应</h1>
<dl>
<dt>错误说明坏的网关http向后端节点请求没有响应</dt>
<dt>原因1DNS 缓冲</dt>
<dd>解决办法:</dd>
<dd>在dos窗口运行 ipconfig /flushdns该命令会刷新DNS缓冲。</dd>
<dt>原因2浏览器代理</dt>
<dd>解决办法:</dd>
<dd>关掉代理。</dd>
<dt>原因3dns 被劫持了即使使用国外的dns也会被劫持</dt>
<dd>解决办法:</dd>
<dd>
去掉VPN服务器的DNS。切换另外的dns。在windows系统中可以在本地网络连接的属性中去掉默认的dns选用国外的dns比如google的或opendns。
</dd>
<dt>原因4php执行超时</dt>
<dd>解决办法:</dd>
<dd>修改/usr/local/php/etc/php.ini 将max_execution_time 改为300。</dd>
<dt>原因5nginx等待时间超时</dt>
<dd>解决办法:</dd>
<dd>适当增加nginx.conf配置文件中FastCGI的timeout时间。</dd>
</dl>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,69 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>503 错误 - phpstudy</title>
<meta content="" name="keywords">
<meta content="" name="description">
<meta content="webkit" name="renderer">
<meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<meta content="black" name="apple-mobile-web-app-status-bar-style">
<meta content="yes" name="apple-mobile-web-app-capable">
<meta content="telephone=no" name="format-detection">
<meta CONTENT="no-cache" HTTP-EQUIV="pragma">
<meta CONTENT="no-store, must-revalidate" HTTP-EQUIV="Cache-Control">
<meta CONTENT="Wed, 26 Feb 1997 08:21:57 GMT" HTTP-EQUIV="expires">
<meta CONTENT="0" HTTP-EQUIV="expires">
<style>
body {
font: 16px arial, 'Microsoft Yahei', 'Hiragino Sans GB', sans-serif;
}
h1 {
margin: 0;
color: #3a87ad;
font-size: 26px;
}
.content {
width: 45%;
margin: 0 auto;
}
.content > div {
margin-top: 200px;
padding: 20px;
background: #d9edf7;
border-radius: 12px;
}
.content dl {
color: #2d6a88;
line-height: 40px;
}
.content div div {
padding-bottom: 20px;
text-align: center;
}
</style>
</head>
<body>
<div class="content">
<div>
<h1>HTTP 503 - Service Unavailable 服务不可用</h1>
<dl>
<dt>错误说明:服务当前不可用</dt>
<dt>原因1服务不可用状态</dt>
<dd>解决办法:</dd>
<dd>服务器或许就是正在维护或者暂停了,你可以联系一下服务器空间商。</dd>
<dt>原因2程序占用资源太多</dt>
<dd>解决办法:</dd>
<dd>通过设置应用程序池把账户改为NetworkService即可解决。</dd>
</dl>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,81 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>504 错误 - phpstudy</title>
<meta content="" name="keywords">
<meta content="" name="description">
<meta content="webkit" name="renderer">
<meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<meta content="black" name="apple-mobile-web-app-status-bar-style">
<meta content="yes" name="apple-mobile-web-app-capable">
<meta content="telephone=no" name="format-detection">
<meta CONTENT="no-cache" HTTP-EQUIV="pragma">
<meta CONTENT="no-store, must-revalidate" HTTP-EQUIV="Cache-Control">
<meta CONTENT="Wed, 26 Feb 1997 08:21:57 GMT" HTTP-EQUIV="expires">
<meta CONTENT="0" HTTP-EQUIV="expires">
<style>
body {
font: 16px arial, 'Microsoft Yahei', 'Hiragino Sans GB', sans-serif;
}
h1 {
margin: 0;
color: #3a87ad;
font-size: 26px;
}
.content {
width: 45%;
margin: 0 auto;
}
.content > div {
margin-top: 50px;
padding: 20px;
background: #d9edf7;
border-radius: 12px;
}
.content dl {
color: #2d6a88;
line-height: 40px;
}
.content div div {
padding-bottom: 20px;
text-align: center;
}
</style>
</head>
<body>
<div class="content">
<div>
<h1>HTTP 504 - Gateway Timeout 网关超时</h1>
<dl>
<dt>错误说明:网关超时,服务器响应时间,达到超出设定的范围</dt>
<dt>原因1后端电脑之间 IP 通讯缓慢而产生</dt>
<dd>解决办法:</dd>
<dd>如果您的 Web 服务器由某一网站托管, 只有负责那个网站设置的人员才能解决这个问题。</dd>
<dt>原因2由于nginx默认的fastcgi进程响应的缓冲区太小造成的错误</dt>
<dd>解决办法:</dd>
<dd>一般默认的fastcgi进程响应的缓冲区是8K这时可以设置大一点在nginx.conf里加入fastcgi_buffers 8
128k这表示设置fastcgi缓冲区为8块128k大小的空间。当然如果在进行某一项即时的操作, 可能需要nginx的超时参数调大点,
例如设置成60秒:send_timeout 60;经过这两个参数的调整一般不会再提示“504 Gateway Time-out”错误问题基本解决。
</dd>
<dt>原因3PHP环境的配置问题</dt>
<dd>解决办法:</dd>
<dd>更改php-fpm的几处配置 把max_children由之前的10改为现在的30这样就可以保证有充足的php-cgi进程可以被使用
把request_terminate_timeout由之前的0s改为60s这样php-cgi进程 处理脚本的超时时间就是60秒可以防止进程都被挂起提高利用效率。
接着再更改nginx的几个配置项减少FastCGI的请求次数尽量维持buffers不变 fastcgi_buffers由 4 64k 改为 2
256k fastcgi_buffer_size 由 64k 改为 128K fastcgi_busy_buffers_size 由 128K 改为 256K
fastcgi_temp_file_write_size 由 128K 改为 256K。 重新加载php-fpm和nginx的配置再次测试如果没有出现“504
Gateway Time-out”错误问题解决。
</dd>
</dl>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,72 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>505 错误 - phpstudy</title>
<meta content="" name="keywords">
<meta content="" name="description">
<meta content="webkit" name="renderer">
<meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<meta content="black" name="apple-mobile-web-app-status-bar-style">
<meta content="yes" name="apple-mobile-web-app-capable">
<meta content="telephone=no" name="format-detection">
<meta CONTENT="no-cache" HTTP-EQUIV="pragma">
<meta CONTENT="no-store, must-revalidate" HTTP-EQUIV="Cache-Control">
<meta CONTENT="Wed, 26 Feb 1997 08:21:57 GMT" HTTP-EQUIV="expires">
<meta CONTENT="0" HTTP-EQUIV="expires">
<style>
body {
font: 16px arial, 'Microsoft Yahei', 'Hiragino Sans GB', sans-serif;
}
h1 {
margin: 0;
color: #3a87ad;
font-size: 26px;
}
.content {
width: 45%;
margin: 0 auto;
}
.content > div {
margin-top: 200px;
padding: 20px;
background: #d9edf7;
border-radius: 12px;
}
.content dl {
color: #2d6a88;
line-height: 40px;
}
.content div div {
padding-bottom: 20px;
text-align: center;
}
</style>
</head>
<body>
<div class="content">
<div>
<h1>HTTP 505 - HTTP Version Not Supported</h1>
<dl>
<dt>错误说明HTTP 版本不受支持。</dt>
<dt>原因1您的 Web 服务器不支持,或拒绝支持客户端(如您的浏览器)在发送给服务器的 HTTP 请求数据流中指定的 HTTP
协议版本
</dt>
<dd>解决办法:</dd>
<dd>升级您的 Web 服务器软件。</dd>
<dt>原因2http请求格式的错误</dt>
<dd>解决办法:</dd>
<dd>对照一下自己的代码从打印的信息中终于找到问题所在。可能在请求后面多加了一个空格。http协议真是很严格了。
</dd>
</dl>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,66 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>506 错误 - phpstudy</title>
<meta content="" name="keywords">
<meta content="" name="description">
<meta content="webkit" name="renderer">
<meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<meta content="black" name="apple-mobile-web-app-status-bar-style">
<meta content="yes" name="apple-mobile-web-app-capable">
<meta content="telephone=no" name="format-detection">
<meta CONTENT="no-cache" HTTP-EQUIV="pragma">
<meta CONTENT="no-store, must-revalidate" HTTP-EQUIV="Cache-Control">
<meta CONTENT="Wed, 26 Feb 1997 08:21:57 GMT" HTTP-EQUIV="expires">
<meta CONTENT="0" HTTP-EQUIV="expires">
<style>
body {
font: 16px arial, 'Microsoft Yahei', 'Hiragino Sans GB', sans-serif;
}
h1 {
margin: 0;
color: #3a87ad;
font-size: 26px;
}
.content {
width: 45%;
margin: 0 auto;
}
.content > div {
margin-top: 200px;
padding: 20px;
background: #d9edf7;
border-radius: 12px;
}
.content dl {
color: #2d6a88;
line-height: 40px;
}
.content div div {
padding-bottom: 20px;
text-align: center;
}
</style>
</head>
<body>
<div class="content">
<div>
<h1>HTTP 506 - Variant Also Negotiates</h1>
<dl>
<dt>错误说明:</dt>
<dt>原因1服务器存在内部配置错误</dt>
<dd>解决办法:</dd>
<dd>被请求的协商变元资源被配置为在透明内容协商中使用自己,因此在一个协商处理中不是一个合适的重点。</dd>
</dl>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,66 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>507 错误 - phpstudy</title>
<meta content="" name="keywords">
<meta content="" name="description">
<meta content="webkit" name="renderer">
<meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<meta content="black" name="apple-mobile-web-app-status-bar-style">
<meta content="yes" name="apple-mobile-web-app-capable">
<meta content="telephone=no" name="format-detection">
<meta CONTENT="no-cache" HTTP-EQUIV="pragma">
<meta CONTENT="no-store, must-revalidate" HTTP-EQUIV="Cache-Control">
<meta CONTENT="Wed, 26 Feb 1997 08:21:57 GMT" HTTP-EQUIV="expires">
<meta CONTENT="0" HTTP-EQUIV="expires">
<style>
body {
font: 16px arial, 'Microsoft Yahei', 'Hiragino Sans GB', sans-serif;
}
h1 {
margin: 0;
color: #3a87ad;
font-size: 26px;
}
.content {
width: 45%;
margin: 0 auto;
}
.content > div {
margin-top: 200px;
padding: 20px;
background: #d9edf7;
border-radius: 12px;
}
.content dl {
color: #2d6a88;
line-height: 40px;
}
.content div div {
padding-bottom: 20px;
text-align: center;
}
</style>
</head>
<body>
<div class="content">
<div>
<h1>HTTP 507 - Insufficient Storage</h1>
<dl>
<dt>错误说明:</dt>
<dt>原因1服务器无法存储完成请求所必须的内容</dt>
<dd>解决办法:</dd>
<dd>这个状况被认为是临时的。WebDAV (RFC 4918)。</dd>
</dl>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,66 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>509 错误 - phpstudy</title>
<meta content="" name="keywords">
<meta content="" name="description">
<meta content="webkit" name="renderer">
<meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<meta content="black" name="apple-mobile-web-app-status-bar-style">
<meta content="yes" name="apple-mobile-web-app-capable">
<meta content="telephone=no" name="format-detection">
<meta CONTENT="no-cache" HTTP-EQUIV="pragma">
<meta CONTENT="no-store, must-revalidate" HTTP-EQUIV="Cache-Control">
<meta CONTENT="Wed, 26 Feb 1997 08:21:57 GMT" HTTP-EQUIV="expires">
<meta CONTENT="0" HTTP-EQUIV="expires">
<style>
body {
font: 16px arial, 'Microsoft Yahei', 'Hiragino Sans GB', sans-serif;
}
h1 {
margin: 0;
color: #3a87ad;
font-size: 26px;
}
.content {
width: 45%;
margin: 0 auto;
}
.content > div {
margin-top: 200px;
padding: 20px;
background: #d9edf7;
border-radius: 12px;
}
.content dl {
color: #2d6a88;
line-height: 40px;
}
.content div div {
padding-bottom: 20px;
text-align: center;
}
</style>
</head>
<body>
<div class="content">
<div>
<h1>HTTP 509 - Bandwidth Limit Exceeded</h1>
<dl>
<dt>错误说明:</dt>
<dt>原因1网站流量已经超出您所购买的方案限制即服务器达到带宽限制</dt>
<dd>解决办法:</dd>
<dd>1.升级方案 2.等到下个月后流量重新计算,网站即可正常浏览。</dd>
</dl>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,66 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>510 错误 - phpstudy</title>
<meta content="" name="keywords">
<meta content="" name="description">
<meta content="webkit" name="renderer">
<meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport">
<meta content="black" name="apple-mobile-web-app-status-bar-style">
<meta content="yes" name="apple-mobile-web-app-capable">
<meta content="telephone=no" name="format-detection">
<meta CONTENT="no-cache" HTTP-EQUIV="pragma">
<meta CONTENT="no-store, must-revalidate" HTTP-EQUIV="Cache-Control">
<meta CONTENT="Wed, 26 Feb 1997 08:21:57 GMT" HTTP-EQUIV="expires">
<meta CONTENT="0" HTTP-EQUIV="expires">
<style>
body {
font: 16px arial, 'Microsoft Yahei', 'Hiragino Sans GB', sans-serif;
}
h1 {
margin: 0;
color: #3a87ad;
font-size: 26px;
}
.content {
width: 45%;
margin: 0 auto;
}
.content > div {
margin-top: 200px;
padding: 20px;
background: #d9edf7;
border-radius: 12px;
}
.content dl {
color: #2d6a88;
line-height: 40px;
}
.content div div {
padding-bottom: 20px;
text-align: center;
}
</style>
</head>
<body>
<div class="content">
<div>
<h1>HTTP 510 - Not Extended</h1>
<dl>
<dt>错误说明:</dt>
<dt>原因1获取资源所需要的策略并没有被满足</dt>
<dd>解决办法:</dd>
<dd>需要请求有额外的扩展内容,服务器才能处理请求。</dd>
</dl>
</div>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -0,0 +1,503 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<title>Spring Security 6 学习中心</title>
<link th:href="@{/css/css2.css}">
<!-- 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;
}
body {
font-family: 'Poppins', sans-serif;
background-color: #f5f7ff;
color: #333;
line-height: 1.6;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
/* 导航栏 */
header {
background: linear-gradient(135deg, var(--gradient-start), var(--gradient-end));
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;
}
.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);
}
.feature-card:hover {
transform: translateY(-10px);
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.1);
}
.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;
}
.hero p {
font-size: 1rem;
}
.nav-links {
display: none;
}
.features-grid, .docs-cards {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<!-- 导航栏 -->
<header>
<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" 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>
<li><a href="/login-page" target="_blank">登录</a></li>
</ul>
</nav>
</div>
</header>
<!-- 英雄区域 -->
<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>
</div>
</div>
</div>
</section>
<!-- 特性部分 -->
<section class="features" id="features">
<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>
</div>
</div>
</section>
<!-- 文档部分 -->
<section class="docs" id="docs">
<div class="container">
<div class="section-title">
<h2>学习资源与文档</h2>
<p>探索我们的文档和工具快速掌握Spring Security 6的强大功能。</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>
</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>
</div>
</div>
</section>
<!-- 页脚 -->
<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>
</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>
</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>
</div>
<div class="footer-bottom">
<p>&copy; 2023 Spring Security 学习中心. 保留所有权利.</p>
</div>
</div>
</footer>
</body>
</html>

View File

@ -0,0 +1,206 @@
<!DOCTYPE html>
<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>登录 | 您的应用名称</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}">
<!-- 自定义CSS -->
<style>
:root {
--primary-color: #4e73df;
--secondary-color: #f8f9fc;
--accent-color: #2e59d9;
--text-color: #5a5c69;
}
body {
background-color: var(--secondary-color);
height: 100vh;
display: flex;
align-items: center;
}
.login-container {
width: 100%;
max-width: 450px;
margin: 0 auto;
padding: 2rem;
background: white;
border-radius: 10px;
box-shadow: 0 0.15rem 1.75rem 0 rgba(58, 59, 69, 0.15);
}
.login-header {
text-align: center;
margin-bottom: 2rem;
}
.login-header img {
height: 80px;
margin-bottom: 1rem;
}
.login-header h1 {
color: var(--primary-color);
font-weight: 600;
font-size: 1.8rem;
}
.form-control {
padding: 0.75rem 1rem;
border-radius: 0.35rem;
}
.form-control:focus {
border-color: var(--primary-color);
box-shadow: 0 0 0 0.25rem rgba(78, 115, 223, 0.25);
}
.btn-login {
background-color: var(--primary-color);
border: none;
padding: 0.75rem;
font-weight: 600;
width: 100%;
transition: all 0.3s;
}
.btn-login:hover {
background-color: var(--accent-color);
}
.input-group-text {
background-color: #f8f9fa;
}
.divider {
position: relative;
margin: 1.5rem 0;
text-align: center;
}
.divider::before {
content: "";
position: absolute;
top: 50%;
left: 0;
right: 0;
height: 1px;
background-color: #e3e6f0;
z-index: 1;
}
.divider-text {
display: inline-block;
padding: 0 1rem;
background-color: white;
position: relative;
z-index: 2;
color: #b7b9cc;
font-size: 0.85rem;
}
.footer-links {
text-align: center;
margin-top: 1.5rem;
font-size: 0.9rem;
}
.footer-links a {
color: var(--text-color);
text-decoration: none;
margin: 0 0.5rem;
}
.footer-links a:hover {
color: var(--primary-color);
text-decoration: underline;
}
.alert {
border-radius: 0.35rem;
}
</style>
</head>
<body>
<div class="container">
<div class="login-container">
<div class="login-header">
<!-- 替换为你的logo -->
<img alt="Logo" class="img-fluid" src="/favicon.ico">
<h1>欢迎回来</h1>
<p class="text-muted">请登录您的账户</p>
</div>
<!-- 错误消息显示 -->
<div class="alert alert-danger alert-dismissible fade show" role="alert" th:if="${param.error}">
<i class="fas fa-exclamation-circle me-2"></i>
<span th:text="#{login.error}">无效的用户名或密码</span>
<button aria-label="Close" class="btn-close" data-bs-dismiss="alert" type="button"></button>
</div>
<div class="alert alert-success alert-dismissible fade show" role="alert" th:if="${param.logout}">
<i class="fas fa-check-circle me-2"></i>
<span th:text="#{login.logout}">您已成功登出</span>
<button aria-label="Close" class="btn-close" data-bs-dismiss="alert" type="button"></button>
</div>
<!-- 登录表单 -->
<form method="post" th:action="@{/login}">
<div class="mb-3">
<label class="form-label" for="username">用户名</label>
<div class="input-group">
<span class="input-group-text"><i class="fas fa-user"></i></span>
<input autofocus class="form-control" id="username" name="username" placeholder="请输入用户名"
required type="text">
</div>
</div>
<div class="mb-3">
<label class="form-label" for="password">密码</label>
<div class="input-group">
<span class="input-group-text"><i class="fas fa-lock"></i></span>
<input class="form-control" id="password" name="password" placeholder="请输入密码" required
type="password" value="123456">
</div>
</div>
<div class="mb-3 form-check">
<input class="form-check-input" id="remember-me" name="remember-me" type="checkbox">
<label class="form-check-label" for="remember-me">记住我</label>
</div>
<button class="btn btn-primary btn-login mb-3" type="submit">
<i class="fas fa-sign-in-alt me-2"></i> 登录
</button>
<div class="text-center mb-3">
<a class="text-decoration-none" th:href="@{/forgot-password}">忘记密码?</a>
</div>
<div class="divider">
<span class="divider-text"></span>
</div>
<div class="text-center">
<p class="text-muted">还没有账户? <a class="text-decoration-none" th:href="@{/register}">注册</a></p>
</div>
</form>
<div class="footer-links">
<a href="#">隐私政策</a>
<a href="#">使用条款</a>
<a href="#">帮助中心</a>
</div>
</div>
</div>
</body>
<!-- Bootstrap JS Bundle with Popper -->
<script th:src="@{/webjars/bootstrap/5.1.3/js/bootstrap.bundle.min.js}"></script>
</html>