first commit

This commit is contained in:
萌狼蓝天 2024-09-22 17:07:22 +08:00
commit 5fd4a86a93
31 changed files with 2748 additions and 0 deletions

33
.gitignore vendored Normal file
View File

@ -0,0 +1,33 @@
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/

19
.mvn/wrapper/maven-wrapper.properties vendored Normal file
View File

@ -0,0 +1,19 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
wrapperVersion=3.3.2
distributionType=only-script
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip

26
README.md Normal file
View File

@ -0,0 +1,26 @@
## 参考资料
sa-token 文档https://sa-token.cc/doc.html#/
Knife4j 文档 https://doc.xiaominfo.com/
雪花算法 https://github.com/yitter/idgenerator/tree/master/Java
lambok:
```
@Data 标签生成getter/setter toString()等方法
@NonNull : 让你不在担忧并且爱上NullPointerException
@CleanUp : 自动资源管理不用再在finally中添加资源的close方法
@Setter/@Getter : 自动生成set和get方法
@ToString : 自动生成toString方法
@EqualsAndHashcode : 从对象的字段中生成hashCode和equals的实现
@NoArgsConstructor/@RequiredArgsConstructor/@AllArgsConstructor
自动生成构造方法
@Data : 自动生成set/get方法toString方法equals方法hashCode方法不带参数的构造方法
@Value : 用于注解final类
@Builder : 产生复杂的构建器api类
@SneakyThrows : 异常处理(谨慎使用)
@Synchronized : 同步方法安全的转化
@Getter(lazy=true) :
@Log : 支持各种logger对象使用时用对应的注解@Log4j
```

1186
logs/app.log Normal file

File diff suppressed because it is too large Load Diff

259
mvnw vendored Normal file
View File

@ -0,0 +1,259 @@
#!/bin/sh
# ----------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Apache Maven Wrapper startup batch script, version 3.3.2
#
# Optional ENV vars
# -----------------
# JAVA_HOME - location of a JDK home dir, required when download maven via java source
# MVNW_REPOURL - repo url base for downloading maven distribution
# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
# ----------------------------------------------------------------------------
set -euf
[ "${MVNW_VERBOSE-}" != debug ] || set -x
# OS specific support.
native_path() { printf %s\\n "$1"; }
case "$(uname)" in
CYGWIN* | MINGW*)
[ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
native_path() { cygpath --path --windows "$1"; }
;;
esac
# set JAVACMD and JAVACCMD
set_java_home() {
# For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
if [ -n "${JAVA_HOME-}" ]; then
if [ -x "$JAVA_HOME/jre/sh/java" ]; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
JAVACCMD="$JAVA_HOME/jre/sh/javac"
else
JAVACMD="$JAVA_HOME/bin/java"
JAVACCMD="$JAVA_HOME/bin/javac"
if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
return 1
fi
fi
else
JAVACMD="$(
'set' +e
'unset' -f command 2>/dev/null
'command' -v java
)" || :
JAVACCMD="$(
'set' +e
'unset' -f command 2>/dev/null
'command' -v javac
)" || :
if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
return 1
fi
fi
}
# hash string like Java String::hashCode
hash_string() {
str="${1:-}" h=0
while [ -n "$str" ]; do
char="${str%"${str#?}"}"
h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
str="${str#?}"
done
printf %x\\n $h
}
verbose() { :; }
[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
die() {
printf %s\\n "$1" >&2
exit 1
}
trim() {
# MWRAPPER-139:
# Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
# Needed for removing poorly interpreted newline sequences when running in more
# exotic environments such as mingw bash on Windows.
printf "%s" "${1}" | tr -d '[:space:]'
}
# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
while IFS="=" read -r key value; do
case "${key-}" in
distributionUrl) distributionUrl=$(trim "${value-}") ;;
distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
esac
done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties"
[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties"
case "${distributionUrl##*/}" in
maven-mvnd-*bin.*)
MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
*AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
:Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
:Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
:Linux*x86_64*) distributionPlatform=linux-amd64 ;;
*)
echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
distributionPlatform=linux-amd64
;;
esac
distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
;;
maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
esac
# apply MVNW_REPOURL and calculate MAVEN_HOME
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
distributionUrlName="${distributionUrl##*/}"
distributionUrlNameMain="${distributionUrlName%.*}"
distributionUrlNameMain="${distributionUrlNameMain%-bin}"
MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
exec_maven() {
unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
}
if [ -d "$MAVEN_HOME" ]; then
verbose "found existing MAVEN_HOME at $MAVEN_HOME"
exec_maven "$@"
fi
case "${distributionUrl-}" in
*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
esac
# prepare tmp dir
if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
trap clean HUP INT TERM EXIT
else
die "cannot create temp dir"
fi
mkdir -p -- "${MAVEN_HOME%/*}"
# Download and Install Apache Maven
verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
verbose "Downloading from: $distributionUrl"
verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
# select .zip or .tar.gz
if ! command -v unzip >/dev/null; then
distributionUrl="${distributionUrl%.zip}.tar.gz"
distributionUrlName="${distributionUrl##*/}"
fi
# verbose opt
__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
# normalize http auth
case "${MVNW_PASSWORD:+has-password}" in
'') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
esac
if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
verbose "Found wget ... using wget"
wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
verbose "Found curl ... using curl"
curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
elif set_java_home; then
verbose "Falling back to use Java to download"
javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
cat >"$javaSource" <<-END
public class Downloader extends java.net.Authenticator
{
protected java.net.PasswordAuthentication getPasswordAuthentication()
{
return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
}
public static void main( String[] args ) throws Exception
{
setDefault( new Downloader() );
java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
}
}
END
# For Cygwin/MinGW, switch paths to Windows format before running javac and java
verbose " - Compiling Downloader.java ..."
"$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
verbose " - Running Downloader.java ..."
"$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
fi
# If specified, validate the SHA-256 sum of the Maven distribution zip file
if [ -n "${distributionSha256Sum-}" ]; then
distributionSha256Result=false
if [ "$MVN_CMD" = mvnd.sh ]; then
echo "Checksum validation is not supported for maven-mvnd." >&2
echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
exit 1
elif command -v sha256sum >/dev/null; then
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then
distributionSha256Result=true
fi
elif command -v shasum >/dev/null; then
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
distributionSha256Result=true
fi
else
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
exit 1
fi
if [ $distributionSha256Result = false ]; then
echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
exit 1
fi
fi
# unzip and move
if command -v unzip >/dev/null; then
unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
else
tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
fi
printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url"
mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
clean || :
exec_maven "$@"

149
mvnw.cmd vendored Normal file
View File

@ -0,0 +1,149 @@
<# : batch portion
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Apache Maven Wrapper startup batch script, version 3.3.2
@REM
@REM Optional ENV vars
@REM MVNW_REPOURL - repo url base for downloading maven distribution
@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
@REM ----------------------------------------------------------------------------
@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
@SET __MVNW_CMD__=
@SET __MVNW_ERROR__=
@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
@SET PSModulePath=
@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
)
@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
@SET __MVNW_PSMODULEP_SAVE=
@SET __MVNW_ARG0_NAME__=
@SET MVNW_USERNAME=
@SET MVNW_PASSWORD=
@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*)
@echo Cannot start maven from wrapper >&2 && exit /b 1
@GOTO :EOF
: end batch / begin powershell #>
$ErrorActionPreference = "Stop"
if ($env:MVNW_VERBOSE -eq "true") {
$VerbosePreference = "Continue"
}
# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
if (!$distributionUrl) {
Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
}
switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
"maven-mvnd-*" {
$USE_MVND = $true
$distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
$MVN_CMD = "mvnd.cmd"
break
}
default {
$USE_MVND = $false
$MVN_CMD = $script -replace '^mvnw','mvn'
break
}
}
# apply MVNW_REPOURL and calculate MAVEN_HOME
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
if ($env:MVNW_REPOURL) {
$MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" }
$distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')"
}
$distributionUrlName = $distributionUrl -replace '^.*/',''
$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain"
if ($env:MAVEN_USER_HOME) {
$MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain"
}
$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
exit $?
}
if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
}
# prepare tmp dir
$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
trap {
if ($TMP_DOWNLOAD_DIR.Exists) {
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
}
}
New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
# Download and Install Apache Maven
Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
Write-Verbose "Downloading from: $distributionUrl"
Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
$webclient = New-Object System.Net.WebClient
if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
$webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
}
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
# If specified, validate the SHA-256 sum of the Maven distribution zip file
$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
if ($distributionSha256Sum) {
if ($USE_MVND) {
Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
}
Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
}
}
# unzip and move
Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null
try {
Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
} catch {
if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
Write-Error "fail to move MAVEN_HOME"
}
} finally {
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
}
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"

136
pom.xml Normal file
View File

@ -0,0 +1,136 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.guaiguailang</groupId>
<artifactId>harmony-life-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>harmony-life-server</name>
<description>harmony-life-server</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-core</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter-test</artifactId>
<version>3.0.3</version>
<scope>test</scope>
</dependency>
<!--springdoc 官方Starter-->
<!-- <dependency>-->
<!-- <groupId>org.springdoc</groupId>-->
<!-- <artifactId>springdoc-openapi-ui</artifactId>-->
<!-- <version>1.7.0</version>-->
<!-- </dependency>-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>4.5.0</version>
</dependency>
<!-- 雪花算法-->
<dependency>
<groupId>com.github.yitter</groupId>
<artifactId>yitter-idgenerator</artifactId>
<version>1.0.6</version>
</dependency>
<!-- Sa-Token 权限认证在线文档https://sa-token.cc -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.31.0</version>
</dependency>
<!-- 提供Redis连接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- Sa-Token 整合 Redis (使用 jackson 序列化方式) -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-redis-jackson</artifactId>
<version>1.38.0</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- 日志-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.8</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,45 @@
package com.guaiguailang.harmony;
import com.github.yitter.contract.IdGeneratorOptions;
import com.github.yitter.idgen.YitIdHelper;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class HarmonyLifeServerApplication {
public static void main(String[] args) {
// SpringApplication.run(HarmonyLifeServerApplication.class, args);
// 启动 Spring Boot 应用
ConfigurableApplicationContext context = SpringApplication.run(HarmonyLifeServerApplication.class, args);
// 初始化雪花算法
Short work_space = context.getEnvironment().getProperty("Unique_Worker_Id", Short.class);
IdGeneratorOptions options = new IdGeneratorOptions(work_space);
// 保存参数务必调用否则参数设置不生效
YitIdHelper.setIdGenerator(options);
// 初始化后在任何需要生成ID的地方调用以下方法
// long newId = YitIdHelper.nextId();
// 获取服务器端口
int port = context.getEnvironment().getProperty("server.port", Integer.class);
// 获取服务器上下文路径默认为"/"
String contextPath = context.getEnvironment().getProperty("server.servlet.context-path", "/");
if (contextPath.equals("/")) {
contextPath = "";
}
System.out.println("\n\n\n^_^ 青山埋忠骨,烟巷葬伟杰 ORZ···");
System.out.println("\n工 作 区 "+work_space+" (请保证工作区全局唯一)");
// 输出项目访问地址
System.out.println("\n访 问 地 址 : http://localhost:" + port + contextPath);
// 输出 Swagger 文档地址
System.out.println("\nSwagger 文档地址: http://localhost:" + port + contextPath + "/swagger-ui.html");
System.out.println("Knife4j 文档地址: http://localhost:" + port + contextPath + "/doc.html");
System.out.println("\n^_^ 终于……终于跑起来了! HD,不要……不要小看我们的羁绊啊!!! ORZ···\n\n\n");
}
}

View File

@ -0,0 +1,18 @@
package com.guaiguailang.harmony.config;
import cn.dev33.satoken.util.SaResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
@Slf4j
//全局异常拦截器统一返回给前端的格式
public class GlobalExceptionHandler {
// 全局异常拦截
@ExceptionHandler
public SaResult handlerException(Exception e) {
log.error(e.getMessage());
return SaResult.error(e.getMessage());
}
}

View File

@ -0,0 +1,26 @@
package com.guaiguailang.harmony.config;
import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.stp.StpUtil;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* sa-token 注解鉴权拦截 注册
*/
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {
// 注册 Sa-Token 拦截器打开注解式鉴权功能
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册 Sa-Token 拦截器打开注解式鉴权功能
registry.addInterceptor(new SaInterceptor()).addPathPatterns("/**");
// 注册 Sa-Token 拦截器校验规则为 StpUtil.checkLogin() 登录校验
registry.addInterceptor(new SaInterceptor(handle -> StpUtil.checkLogin()))
.addPathPatterns("/**")
.excludePathPatterns("/user/doLogin")
.excludePathPatterns("/doc.html")
.excludePathPatterns("/swagger-ui.html");
}
}

View File

@ -0,0 +1,69 @@
package com.guaiguailang.harmony.config;
import cn.dev33.satoken.stp.StpInterface;
import com.guaiguailang.harmony.mapper.AuthMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* 自定义权限加载接口实现类
*/
@Slf4j
@Component // 保证此类被 SpringBoot 扫描完成 Sa-Token 的自定义权限验证扩展
public class StpInterfaceImpl implements StpInterface {
@Autowired
AuthMapper authMapper;
/**
* 返回一个账号所拥有的权限码集合
*/
@Override
public List<String> getPermissionList(Object loginId, String loginType) {
// Long uid = (Long)loginId;//得到用戶id
// List<String> list = new ArrayList<String>();
// List<SystemPermission> systemPermission = authMapper.getUserPermissionList(uid);
// for(SystemPermission permission : systemPermission){
// List<SystemAction> systemAction = authMapper.getPermissionActionList(permission.getPermissionId());
// for(SystemAction action : systemAction){
// list.add(permission.getPermissionId()+"."+action.getTag());
// }
// }
// return list;
// 性能优化
Long uid = (Long) loginId; // 得到用戶id
List<String> permissionList = new ArrayList<>();
// 使用更高效的SQL查询获取所有权限及其动作
List<Map<String, Object>> allPermissionsAndActions = authMapper.getAllPermissionsAndActionsByUserId(uid);
for (Map<String, Object> permissionAction : allPermissionsAndActions) {
String permissionId = (String) permissionAction.get("permission_id");
String actionTag = (String) permissionAction.get("action_tag");
if (permissionId != null && actionTag != null) {
permissionList.add(permissionId + "." + actionTag);
}
}
log.info("用户{}权限:{}", uid.toString(), permissionList.toString());
return permissionList;
}
/**
* 返回一个账号所拥有的角色标识集合 (权限与角色可分开校验)
*/
@Override
public List<String> getRoleList(Object loginId, String loginType) {
Long uid = (Long)loginId;//得到用戶id
List<String> list = new ArrayList<String>();
list.add(authMapper.getUserById(uid).getRoleId());
log.info("用户{}角色:{}", uid.toString(), list.toString());
return list;
}
}

View File

@ -0,0 +1,72 @@
package com.guaiguailang.harmony.controller;
import com.guaiguailang.harmony.domain.dto.ParamLogin;
import com.guaiguailang.harmony.domain.vo.ResponseResult;
import com.guaiguailang.harmony.service.AuthService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@Tag(name="系统认证接口")
@RequestMapping("/auth")
public class AuthController {
@Autowired
private final AuthService authService;
// 为什么加个final使用构造器注入 AuthService这在 Spring Boot 中是推荐的做法
/*
使用构造器注入Constructor Injection Spring Boot 中是一种推荐的做法主要有以下几个原因和好处
1. 强制依赖
构造器注入确保了依赖项在对象创建时就必须存在这意味着任何依赖项都必须在构造对象时就提供这种方式可以避免运行时的 NullPointerException空指针异常因为依赖项在对象创建时就已经被初始化
2. 不可变性
构造器注入有助于创建不可变对象Immutable Objects一旦对象通过构造器接收了其所需的依赖项这些依赖项就不能被改变这有助于提高代码的可预测性和安全性
3. 清晰性
构造器注入使得依赖关系非常明确当查看类的构造器时可以立即看到该类依赖哪些组件这提高了代码的可读性和可维护性
4. 测试友好
构造器注入使得单元测试变得更容易可以通过构造器直接传入模拟对象Mock Objects而不需要依赖框架提供的 setter 方法或其他注入方式这使得测试代码更加简洁明了
5. 减少副作用
构造器注入减少了类内部的副作用与使用 setter 注入相比构造器注入避免了在对象创建之后通过 setter 方法来修改其状态的情况从而减少了潜在的副作用
6. 生命周期管理
构造器注入有助于 Spring 容器更好地管理 bean 的生命周期依赖项在构造器中声明后Spring 容器可以在创建 bean 时就注入这些依赖项从而确保 bean 在使用前就已经处于完全初始化的状态
*/
// 哥们你在这做面试题呢我只是开发过程中记录一下这些是我逝去的青春
public AuthController(AuthService authService) {
this.authService = authService;
}
@Operation(
summary = "用户登录 账号登录",
description = "用户通过账号密码进行登录",
tags = {"系统认证接口"},
requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ParamLogin.class))
),
responses = {
@ApiResponse(
responseCode = "200",
description = "成功返回登录结果",
content = @Content(
mediaType = "application/json",
schema = @Schema(
implementation = ResponseResult.class,
example = "{ \"msg\": \"登录成功\", \"data\": {\"token\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\", \"userId\":1}, \"code\":200 }"
)
)
),
@ApiResponse(responseCode = "400", description = "请求参数错误"),
@ApiResponse(responseCode = "401", description = "未授权"),
@ApiResponse(responseCode = "500", description = "内部服务器错误")
}
)
@PostMapping("/login")
public ResponseEntity loginByAccount(@RequestBody ParamLogin loginParam){
return ResponseEntity.ok(authService.loginByAccount(loginParam));
}
}

View File

@ -0,0 +1,4 @@
domain 文件夹包含了
entity持久化对象 映射数据库数据表的字段
dtoData Transfer Object数据传输对象 接收前端参数的对象
voValue Object值对象 响应值对象

View File

@ -0,0 +1,20 @@
package com.guaiguailang.harmony.domain.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
public class ParamLogin {
@Schema(description = "用户名",
implementation = String.class,
example = "账号是用户名username不是昵称name",
requiredMode = Schema.RequiredMode.REQUIRED)
private String username;
@Schema(description = "密码",
implementation = String.class,
example = "密码是经过MD5加密的",
requiredMode = Schema.RequiredMode.REQUIRED)
private String password;
}

View File

@ -0,0 +1,57 @@
package com.guaiguailang.harmony.domain.entity;
public class SystemAction {
private long id;
private String tag;
private String describe;
private long defaultCheck;
private long status;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getTag() {
return tag;
}
public void setTag(String tag) {
this.tag = tag;
}
public String getDescribe() {
return describe;
}
public void setDescribe(String describe) {
this.describe = describe;
}
public long getDefaultCheck() {
return defaultCheck;
}
public void setDefaultCheck(long defaultCheck) {
this.defaultCheck = defaultCheck;
}
public long getStatus() {
return status;
}
public void setStatus(long status) {
this.status = status;
}
}

View File

@ -0,0 +1,77 @@
package com.guaiguailang.harmony.domain.entity;
public class SystemMenu {
private long id;
private String key;
private String name;
private String component;
private String redirect;
private String icon;
private long status;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getComponent() {
return component;
}
public void setComponent(String component) {
this.component = component;
}
public String getRedirect() {
return redirect;
}
public void setRedirect(String redirect) {
this.redirect = redirect;
}
public String getIcon() {
return icon;
}
public void setIcon(String icon) {
this.icon = icon;
}
public long getStatus() {
return status;
}
public void setStatus(long status) {
this.status = status;
}
}

View File

@ -0,0 +1,47 @@
package com.guaiguailang.harmony.domain.entity;
public class SystemPermission {
private long id;
private String permissionId;
private String permissionName;
private long status;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getPermissionId() {
return permissionId;
}
public void setPermissionId(String permissionId) {
this.permissionId = permissionId;
}
public String getPermissionName() {
return permissionName;
}
public void setPermissionName(String permissionName) {
this.permissionName = permissionName;
}
public long getStatus() {
return status;
}
public void setStatus(long status) {
this.status = status;
}
}

View File

@ -0,0 +1,12 @@
package com.guaiguailang.harmony.domain.entity;
import lombok.Data;
@Data
public class SystemPermissionAction {
private long id;
private String actionTag;
private String permissionId;
private long status;
}

View File

@ -0,0 +1,47 @@
package com.guaiguailang.harmony.domain.entity;
public class SystemRole {
private long id;
private String roleName;
private String roleId;
private long status;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getRoleId() {
return roleId;
}
public void setRoleId(String roleId) {
this.roleId = roleId;
}
public long getStatus() {
return status;
}
public void setStatus(long status) {
this.status = status;
}
}

View File

@ -0,0 +1,47 @@
package com.guaiguailang.harmony.domain.entity;
public class SystemRolePermission {
private long id;
private String roleId;
private String permissionId;
private long status;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getRoleId() {
return roleId;
}
public void setRoleId(String roleId) {
this.roleId = roleId;
}
public String getPermissionId() {
return permissionId;
}
public void setPermissionId(String permissionId) {
this.permissionId = permissionId;
}
public long getStatus() {
return status;
}
public void setStatus(long status) {
this.status = status;
}
}

View File

@ -0,0 +1,21 @@
package com.guaiguailang.harmony.domain.entity;
import lombok.Data;
@Data
public class UserInfo {
private long id;
private String name;
private String username;
private String password;
private String avatar;
private String status;
private String telephone;
private String email;
private java.sql.Timestamp createTime;
private String merchantCode;
private long deleted;
private String roleId;
private long creatorId;
}

View File

@ -0,0 +1,52 @@
package com.guaiguailang.harmony.domain.vo;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ResponseResult<T> {
private Integer code; // 消息代码不填写默认1
private String msg; // 消息内容不填写默认"success"
private T data; // 数据内容可以为空
public static <T> ResponseResult<T> success(T data) {
return success(data, "success");
}
public static <T> ResponseResult<T> success(T data, String msg) {
return ResponseResult.<T>builder()
.code(1)
.msg(msg)
.data(data)
.build();
}
public static <T> ResponseResult<T> error(String msg) {
return ResponseResult.<T>builder()
.code(-1) //这里假设-1代表错误
.msg(msg)
.build();
}
public static <T> ResponseResult<T> info(Integer code, String msg, T data) {
return ResponseResult.<T>builder()
.code(code)
.msg(msg)
.data(data)
.build();
}
public static <T> ResponseResult<T> info(Integer code, T data) {
return info(code, "info", data);
}
public static <T> ResponseResult<T> info(Integer code) {
return info(code, "info", null);
}
public static <T> ResponseResult<T> info(Integer code, String msg) {
return info(code, msg, null);
}
}

View File

@ -0,0 +1,29 @@
package com.guaiguailang.harmony.mapper;
import com.guaiguailang.harmony.domain.entity.SystemAction;
import com.guaiguailang.harmony.domain.entity.SystemPermission;
import com.guaiguailang.harmony.domain.entity.SystemRole;
import com.guaiguailang.harmony.domain.entity.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
import java.util.Map;
@Mapper
public interface AuthMapper {
// 有没有想过注解写SQL的方式好不好对比xml写SQL的方式哪种更好
// 与其说好不好不如说 合不合适简单SQL写注解方便又省事复杂SQL写xml高效且清晰就这样
@Select("SELECT * from user_info where id=#{id} AND status=1")
UserInfo getUserById(@Param("id") Long id);
List<SystemPermission> getUserPermissionList(@Param("id") Long id);
List<SystemAction> getPermissionActionList(String permissionId);
List<Map<String, Object>> getAllPermissionsAndActionsByUserId(Long uid);
@Select("SELECT * from user_info where username=#{username} AND status=1")
UserInfo getUserByAccount(String username);
}

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.guaiguailang.harmony.mapper.AuthMapper">
<!-- SQL for getting user permissions -->
<select id="getUserPermissionList" parameterType="long" resultType="com.guaiguailang.harmony.domain.entity.SystemPermission">
SELECT *
FROM system_role_permission srp
INNER JOIN system_permission sp ON srp.permission_id = sp.id
WHERE srp.role_id =
(SELECT role_id FROM user_info WHERE id = #{id} AND deleted = 0)
AND srp.status = 1
</select>
<!-- SQL for getting actions by permission id -->
<select id="getPermissionActionList" parameterType="string" resultType="com.guaiguailang.harmony.domain.entity.SystemAction">
SELECT sa.*
FROM system_permission_action spa
LEFT JOIN system_action sa ON spa.action_tag = sa.tag
WHERE spa.permission_id = #{permissionId} AND spa.status = 1 AND sa.status = 1
</select>
<!-- SQL for getting all permissions and actions by user id -->
<select id="getAllPermissionsAndActionsByUserId" parameterType="long" resultType="map">
SELECT sp.permission_id, sa.tag
FROM user_info ui
JOIN system_role_permission srp ON ui.role_id = srp.role_id
JOIN system_permission sp ON srp.permission_id = sp.permission_id
JOIN system_permission_action spa ON sp.permission_id = spa.permission_id
JOIN system_action sa ON spa.action_tag = sa.tag
WHERE ui.id = #{userId} AND ui.deleted = 0 AND srp.status = 1 AND spa.status = 1 AND sa.status = 1
</select>
</mapper>

View File

@ -0,0 +1,9 @@
package com.guaiguailang.harmony.service;
import com.guaiguailang.harmony.domain.dto.ParamLogin;
import com.guaiguailang.harmony.domain.vo.ResponseResult;
import org.springframework.http.ResponseEntity;
public interface AuthService {
ResponseResult loginByAccount(ParamLogin loginParam);
}

View File

@ -0,0 +1,42 @@
package com.guaiguailang.harmony.service.impl;
import com.guaiguailang.harmony.domain.dto.ParamLogin;
import com.guaiguailang.harmony.domain.entity.UserInfo;
import com.guaiguailang.harmony.domain.vo.ResponseResult;
import com.guaiguailang.harmony.mapper.AuthMapper;
import com.guaiguailang.harmony.service.AuthService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
@Service
public class AuthServiceImpl implements AuthService {
@Autowired
private final AuthMapper authMapper;
public AuthServiceImpl(AuthMapper authMapper) {
this.authMapper = authMapper;
}
@Override
public ResponseResult loginByAccount(ParamLogin loginParam) {
// 根据账号查询用户
UserInfo userInfo = authMapper.getUserByAccount(loginParam.getUsername());
if (userInfo == null) {
return ResponseResult.error("用户不存在");//提示 用户不存在
}
// 验证密码
// 执行登录
// 保存登录信息
// 保存登录缓存
// 构建返回值对象
return null;
}
}

View File

@ -0,0 +1,89 @@
package com.guaiguailang.harmony.utils;
import lombok.experimental.UtilityClass;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
@UtilityClass
public class PasswordEncryptor {
// 盐的长度这里设置为16字节
private static final int SALT_LENGTH = 16;
// SecureRandom用于生成安全的随机数
private final SecureRandom secureRandom =new SecureRandom();
// 生成随机的盐
private byte[] generateSalt() {
byte[] salt = new byte[SALT_LENGTH];
secureRandom.nextBytes(salt);
return salt;
}
// 哈希密码并附带盐一起存储
public String hashPassword(String password) throws NoSuchAlgorithmException {
// 生成随机的盐
byte[] salt = generateSalt();
// 使用盐对密码进行哈希处理
byte[] hash = hash(password.getBytes(StandardCharsets.UTF_8), salt);
// 将盐和哈希值合并存储
byte[] saltedHash = new byte[salt.length + hash.length];
System.arraycopy(salt, 0, saltedHash, 0, salt.length);
System.arraycopy(hash, 0, saltedHash, salt.length, hash.length);
// 使用Base64对合并后的数据进行编码便于存储和传输
return Base64.getEncoder().encodeToString(saltedHash);
}
// 使用SHA-256算法和盐对输入数据进行哈希处理
private byte[] hash(byte[] input, byte[] salt) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(salt);
return md.digest(input);
}
// 验证密码是否正确
public boolean verifyPassword(String storedSaltedHash, String passwordToVerify) throws NoSuchAlgorithmException {
// 对存储的Base64编码的盐和哈希值进行解码
byte[] saltedHash = Base64.getDecoder().decode(storedSaltedHash);
// 从解码后的数据中提取盐
byte[] salt = new byte[SALT_LENGTH];
System.arraycopy(saltedHash, 0, salt, 0, salt.length);
// 从解码后的数据中提取哈希值
byte[] hash = new byte[saltedHash.length - salt.length];
System.arraycopy(saltedHash, salt.length, hash, 0, hash.length);
// 使用提取的盐对用户输入的密码进行哈希处理
byte[] computedHash = hash(passwordToVerify.getBytes(StandardCharsets.UTF_8), salt);
// 比较计算出的哈希值与存储的哈希值是否一致
return MessageDigest.isEqual(computedHash, hash);
}
// 主函数用于测试
public static void main(String[] args) {
String password = "userPassword123";
try {
// 对密码进行哈希处理并附带盐一起存储
String encryptedPassword = hashPassword(password);
System.out.println("加密后的密码(包含盐): " + encryptedPassword);
// 验证密码是否正确
boolean isVerified = verifyPassword(encryptedPassword, password);
System.out.println("密码验证结果: " + isVerified);
// 使用错误的密码进行验证
boolean isWrongPasswordVerified = verifyPassword(encryptedPassword, "wrongPassword");
System.out.println("错误密码验证结果: " + isWrongPasswordVerified);
} catch (NoSuchAlgorithmException e) {
System.err.println("找不到哈希算法: " + e.getMessage());
}
}
}

View File

@ -0,0 +1,67 @@
spring.application.name=harmony-life-server
spring.application.title=He Sheng
spring.application.version=1.0.0
# 日志
logging.config=classpath:logback-spring.xml
# Server Info Setting
server.port=8080
server.servlet.context-path=/
server.servlet.session.timeout=3600
# Spring Boot Info Setting
spring.main.banner-mode=log
spring.banner.charset=utf-8
spring.banner.location=classpath:banner.txt
# MySQL Setting
spring.datasource.url=jdbc:mysql://localhost:3306/harmonylife?serverTimezone=UTC&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# MyBatis Setting
mybatis.type-aliases-package=com.guaiguailang.harmony.domain
mybatis.mapper-locations=classpath:mapper/*.xml
# Redis Configuration
spring.data.redis.host=localhost
spring.data.redis.port=6379
spring.data.redis.database=0
spring.data.redis.password=
spring.data.redis.lettuce.pool.max-active= 8
spring.data.redis.lettuce.pool.max-wait=-1
spring.data.redis.lettuce.pool.max-idle=8
spring.data.redis.lettuce.pool.min-idle=0
# Spring Session with Redis
spring.session.store-type=redis
spring.session.timeout=1800000
# springdoc-openapi
springdoc.swagger-ui.path=/swagger-ui.html
springdoc.swagger-ui.tags-sorter=alpha
springdoc.swagger-ui.operations-sorter=alpha
springdoc.api-docs.path=/v3/api-docs
springdoc.group-configs[0].group=main
springdoc.group-configs[0].paths-to-match=/**
springdoc.group-configs[0].packages-to-scan=com.guaiguailang.harmony.controller
# knife4j
knife4j.enable=true
knife4j.setting.language=zh_cn
knife4j.setting.enable-open-api=true
knife4j.setting.enable-swagger-models=true
# 雪花算法
Unique_Worker_Id= 1
############## Sa-Token 配置 (文档: https://sa-token.cc) ##############
# token 名称(同时也是 cookie 名称)
sa-token.token-name=xrilang
# token 有效期(单位:秒) 默认30天-1 代表永久有效
sa-token.timeout=2592000
# token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结
sa-token.active-timeout=-1
# 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录)
sa-token.is-concurrent=true
# 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token
sa-token.is-share=true
# token 风格默认可取值uuid、simple-uuid、random-32、random-64、random-128、tik
sa-token.token-style=uuid
# 是否输出操作日志
sa-token.is-log=true

View File

@ -0,0 +1,13 @@
██╗ ██╗██████╗ ██╗██╗ █████╗ ███╗ ██╗ ██████╗
╚██╗██╔╝██╔══██╗██║██║ ██╔══██╗████╗ ██║██╔════╝
╚███╔╝ ██████╔╝██║██║ ███████║██╔██╗ ██║██║ ███╗
██╔██╗ ██╔══██╗██║██║ ██╔══██║██║╚██╗██║██║ ██║
██╔╝ ██╗██║ ██║██║███████╗██║ ██║██║ ╚████║╚██████╔╝
╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝
^_^ 服务启动成功 ${spring.application.title}
# 项目名称:${spring.application.name}
# 项目版本:${spring.application.version}
# 框架版本:${spring-boot.version}
# 最后更新于 2024年 9月 25日

View File

@ -0,0 +1,27 @@
<configuration>
<include resource="org/springframework/boot/logging/logback/initial.xml"/>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>logs/app.log</file>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT"/>
<appender-ref ref="FILE"/>
</root>
<!-- 更细粒度的日志控制 -->
<logger name="com.guaiguailang.harmony" level="DEBUG" additivity="false">
<appender-ref ref="STDOUT"/>
<appender-ref ref="FILE"/>
</logger>
</configuration>

View File

@ -0,0 +1,15 @@
package com.guaiguailang.harmony;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.xml.transform.Result;
@SpringBootTest
class HarmonyLifeServerApplicationTests {
@Test
void contextLoads() {
}
}