diff --git a/.gitignore b/.gitignore index 8d68edc977c17516de25cc67d8d0a0e5f9bec179..d337347ea8287cacbd7c1049845eb79b550d40d6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,38 @@ +src/main/resources/application.yml +HELP.md .gradle -**/build/ -!src/**/build/ +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ -# Ignore Gradle GUI config -gradle-app.setting +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ -# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) -!gradle-wrapper.jar +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ -# Cache of project -.gradletasknamecache +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ -# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 -# gradle/wrapper/gradle-wrapper.properties +### VS Code ### +.vscode/ diff --git a/account-manager-sql.md b/account-manager-sql.md new file mode 100644 index 0000000000000000000000000000000000000000..3a9158b63986d6827a565cc31080ca10f0ba8bc5 --- /dev/null +++ b/account-manager-sql.md @@ -0,0 +1,74 @@ +# 数据库名称account_manager + +## 用户表 a_account + +- id ID +- username 用户名 +- nickname 昵称 +- phone 手机号 +- password 密码 +- sequence 顺序 +- wechat_id 微信唯一ID +- qq_open_id QQ开发ID +- img 头像 +- create_time 创建时间 +- update_time 修改时间 + +```sql +CREATE TABLE `account_manager`.`a_account` ( + `id` int UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '用户ID', + `username` varchar(255) NULL DEFAULT NULL COMMENT '用户名', + `nickname` varchar(255) NULL DEFAULT NULL COMMENT '昵称', + `phone` varchar(255) NULL DEFAULT NULL COMMENT '手机号', + `password` varchar(255) NULL DEFAULT NULL COMMENT '密码', + `sequence` varchar(255) NULL DEFAULT NULL COMMENT '顺序', + `wechat_id` varchar(255) NULL COMMENT '微信唯一ID', + `qq_open_id` varchar(255) NULL DEFAULT NULL COMMENT 'QQ开发ID', + `img` varchar(255) NULL COMMENT '头像', + `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`) +) COMMENT = '用户表'; +``` + +## 应用地址表 a_host + +- id ID +- host 应用地址 +- title 应用名称 +- create_time 创建时间 +- update_time 修改时间 + +```sql +CREATE TABLE `account_manager`.`a_host` ( + `id` int UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '应用地址', + `uid` int UNSIGNED NOT NULL COMMENT '用户ID', + `host` varchar(255) NULL DEFAULT NULL COMMENT '应用地址', + `title` varchar(255) NULL DEFAULT NULL COMMENT '应用名称', + `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`) +) COMMENT = '应用地址表'; +``` + +## 应用账号密码表 a_host_account + +- id ID +- host_id 应用地址ID +- username 用户名 +- password 密码 +- create_time 创建时间 +- update_time 修改时间 + +```sql +CREATE TABLE `account_manager`.`a_host_account` ( + `id` int UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '应用账号密码ID', + `uid` int UNSIGNED NOT NULL COMMENT '用户ID', + `host_id` int UNSIGNED NOT NULL COMMENT '应用地址ID', + `username` varchar(255) NULL DEFAULT NULL COMMENT '用户名', + `password` varchar(255) NULL DEFAULT NULL COMMENT '密码', + `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`) +) COMMENT = '应用账号密码表'; +``` \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000000000000000000000000000000000000..4688799ccce4ff45183f83fc83dc28968b6b35be --- /dev/null +++ b/build.gradle @@ -0,0 +1,37 @@ +plugins { + id 'org.springframework.boot' version '3.0.0-M2' + id 'io.spring.dependency-management' version '1.0.11.RELEASE' + id 'java' +} + +group = 'cc.cloudedu' +version = '1.0.0' +sourceCompatibility = '17' + +configurations { + compileOnly { + extendsFrom annotationProcessor + } +} + +repositories { + mavenCentral() + maven { url 'https://repo.spring.io/milestone' } +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'org.springframework.boot:spring-boot-starter-web' + annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' + implementation 'com.auth0:java-jwt:3.19.1' + compileOnly 'org.projectlombok:lombok' + runtimeOnly 'mysql:mysql-connector-java' + annotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.springframework.boot:spring-boot-starter-test' +} + +tasks.named('test') { + useJUnitPlatform() +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..41d9927a4d4fb3f96a785543079b8df6723c946b Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000000000000000000000000000000000..00e33edef6936b1ada8721cff42201db8c9d8675 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000000000000000000000000000000000000..1b6c787337ffb79f0e3cf8b1e9f00f680a959de1 --- /dev/null +++ b/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed 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 +# +# https://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. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +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 + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000000000000000000000000000000000000..ac1b06f93825db68fb0c0b5150917f340eaa5d02 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000000000000000000000000000000000000..3087306f33951a2d2f455f9581c43dfcc721dce7 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,8 @@ +pluginManagement { + repositories { + maven { url 'https://repo.spring.io/milestone' } + maven { url 'https://repo.spring.io/snapshot' } + gradlePluginPortal() + } +} +rootProject.name = 'account-manager' diff --git a/src/main/java/cc/cloudedu/accountmanager/AccountManagerApplication.java b/src/main/java/cc/cloudedu/accountmanager/AccountManagerApplication.java new file mode 100644 index 0000000000000000000000000000000000000000..a144f651912fc639a237a892cb49e6c2ed6a684e --- /dev/null +++ b/src/main/java/cc/cloudedu/accountmanager/AccountManagerApplication.java @@ -0,0 +1,17 @@ +package cc.cloudedu.accountmanager; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; + +/** + * @author fdg + */ +@SpringBootApplication(exclude = SecurityAutoConfiguration.class) +public class AccountManagerApplication { + + public static void main(String[] args) { + SpringApplication.run(AccountManagerApplication.class, args); + } + +} diff --git a/src/main/java/cc/cloudedu/accountmanager/common/AccountInfo.java b/src/main/java/cc/cloudedu/accountmanager/common/AccountInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..e96e0435ea1cbc78047b910babf3abee2b6d6b06 --- /dev/null +++ b/src/main/java/cc/cloudedu/accountmanager/common/AccountInfo.java @@ -0,0 +1,15 @@ +package cc.cloudedu.accountmanager.common; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +/** + * @author fdg + */ +@Getter +@Setter +@ToString +public class AccountInfo { + private Integer id; +} diff --git a/src/main/java/cc/cloudedu/accountmanager/common/AesCommon.java b/src/main/java/cc/cloudedu/accountmanager/common/AesCommon.java new file mode 100644 index 0000000000000000000000000000000000000000..a6f615e1dae6063b8d06846e3b8076abbf0e8e3e --- /dev/null +++ b/src/main/java/cc/cloudedu/accountmanager/common/AesCommon.java @@ -0,0 +1,25 @@ +package cc.cloudedu.accountmanager.common; + +import cc.cloudedu.accountmanager.config.AesConfig; +import cc.cloudedu.accountmanager.util.AesUtils; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Component; + +/** + * @author fdg + */ +@Component +@Configuration +@RequiredArgsConstructor +public class AesCommon { + private final AesConfig aesConfig; + + @Bean + public AesUtils init(){ + AesUtils aesUtils = new AesUtils(aesConfig); + + return aesUtils; + } +} diff --git a/src/main/java/cc/cloudedu/accountmanager/common/EduWebMvcConfig.java b/src/main/java/cc/cloudedu/accountmanager/common/EduWebMvcConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..531715721fe682dbb4cdbce0a8e0f2002d73318a --- /dev/null +++ b/src/main/java/cc/cloudedu/accountmanager/common/EduWebMvcConfig.java @@ -0,0 +1,22 @@ +package cc.cloudedu.accountmanager.common; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.util.List; + +/** + * @author fdg + */ +@Configuration +@RequiredArgsConstructor +public class EduWebMvcConfig implements WebMvcConfigurer { + private final LoginAccountHandler loginAccountHandler; + + @Override + public void addArgumentResolvers(List resolvers) { + resolvers.add(loginAccountHandler); + } +} diff --git a/src/main/java/cc/cloudedu/accountmanager/common/LoginAccount.java b/src/main/java/cc/cloudedu/accountmanager/common/LoginAccount.java new file mode 100644 index 0000000000000000000000000000000000000000..06b93d2ae5c19596c37db9a8f61842f053ae5818 --- /dev/null +++ b/src/main/java/cc/cloudedu/accountmanager/common/LoginAccount.java @@ -0,0 +1,14 @@ +package cc.cloudedu.accountmanager.common; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author fdg + */ +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface LoginAccount { +} diff --git a/src/main/java/cc/cloudedu/accountmanager/common/LoginAccountHandler.java b/src/main/java/cc/cloudedu/accountmanager/common/LoginAccountHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..f732b196940b636eb2df9c1b74838651d9d11672 --- /dev/null +++ b/src/main/java/cc/cloudedu/accountmanager/common/LoginAccountHandler.java @@ -0,0 +1,45 @@ +package cc.cloudedu.accountmanager.common; + +import cc.cloudedu.accountmanager.config.JwtAccountConfig; +import cc.cloudedu.accountmanager.enums.ResultEnum; +import cc.cloudedu.accountmanager.exception.ApiViewException; +import cc.cloudedu.accountmanager.util.JwtOperatorUtils; +import com.auth0.jwt.interfaces.DecodedJWT; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.MethodParameter; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; + +/** + * @author fdg + */ +@Configuration +@RequiredArgsConstructor +public class LoginAccountHandler implements HandlerMethodArgumentResolver { + private final JwtAccountConfig jwtAccountConfig; + + @Override + public boolean supportsParameter(MethodParameter parameter) { + return parameter.getParameterType().isAssignableFrom(AccountInfo.class) && parameter.hasParameterAnnotation(LoginAccount.class); + } + + @Override + public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws ApiViewException{ + String token = webRequest.getHeader(jwtAccountConfig.getHeaderKey()); + if (token == null || token.isEmpty()) { + throw new ApiViewException(ResultEnum.USER_LOGIN_EXPIRED); + }else { + DecodedJWT decodedJwt = JwtOperatorUtils.getFromToken(token, jwtAccountConfig.getSecret()); + + if (null == decodedJwt) { + throw new ApiViewException(ResultEnum.USER_LOGIN_EXPIRED); + } + AccountInfo accountInfo= new AccountInfo(); + accountInfo.setId(decodedJwt.getClaim("id").asInt()); + return accountInfo; + } + } +} diff --git a/src/main/java/cc/cloudedu/accountmanager/config/AesConfig.java b/src/main/java/cc/cloudedu/accountmanager/config/AesConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..8017962f25bb8d78f368634fbddb635a77fea064 --- /dev/null +++ b/src/main/java/cc/cloudedu/accountmanager/config/AesConfig.java @@ -0,0 +1,22 @@ +package cc.cloudedu.accountmanager.config; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +/** + * @author fdg + */ +@Getter +@Setter +@ToString +@Configuration +@ConfigurationProperties(prefix = "ase") +public class AesConfig { + private String key; + private Integer keySize; + private String algorithm; + private String rngAlgorithm; +} diff --git a/src/main/java/cc/cloudedu/accountmanager/config/JwtAccountConfig.java b/src/main/java/cc/cloudedu/accountmanager/config/JwtAccountConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..4bc06594ad8efcf36656cd59eb0c0bb1f11d07b3 --- /dev/null +++ b/src/main/java/cc/cloudedu/accountmanager/config/JwtAccountConfig.java @@ -0,0 +1,32 @@ +package cc.cloudedu.accountmanager.config; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +/** + * @author fdg + */ +@Getter +@Setter +@ToString +@Configuration +@ConfigurationProperties(prefix = "jwt.account") +public class JwtAccountConfig { + /** + * header key + */ + private String headerKey; + + /** + * secret + */ + private String secret; + + /** + * token的有效时间(毫秒),默认2周 + */ + private Long expiration = 120960000L; +} diff --git a/src/main/java/cc/cloudedu/accountmanager/controller/HostController.java b/src/main/java/cc/cloudedu/accountmanager/controller/HostController.java new file mode 100644 index 0000000000000000000000000000000000000000..1ba4370dee3273ebe4b68534a068d17cd07f6aa1 --- /dev/null +++ b/src/main/java/cc/cloudedu/accountmanager/controller/HostController.java @@ -0,0 +1,63 @@ +package cc.cloudedu.accountmanager.controller; + +import cc.cloudedu.accountmanager.common.AccountInfo; +import cc.cloudedu.accountmanager.common.LoginAccount; +import cc.cloudedu.accountmanager.dataobject.HostAccountDO; +import cc.cloudedu.accountmanager.enums.ResultEnum; +import cc.cloudedu.accountmanager.exception.ApiViewException; +import cc.cloudedu.accountmanager.form.CreateHostForm; +import cc.cloudedu.accountmanager.form.HostListForm; +import cc.cloudedu.accountmanager.server.HostService; +import cc.cloudedu.accountmanager.util.ResultVoUtils; +import cc.cloudedu.accountmanager.vo.ResultVO; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import java.util.Objects; + +/** + * @author fdg + */ +@Slf4j +@RestController +@RequestMapping("/host") +@RequiredArgsConstructor +public class HostController { + private final HostService hostService; + + @PostMapping("/list") + public ResultVO list(@LoginAccount AccountInfo accountInfo, @Valid @RequestBody HostListForm hostListForm, BindingResult bindingResult){ + if(accountInfo.getId()<0){ + log.error("[host/list]{}",accountInfo.getId()); + throw new ApiViewException(ResultEnum.USER_LOGIN_EXPIRED); + } + if (bindingResult.hasErrors()) { + log.error("[host/list]:参数错误{}", hostListForm.toString()); + throw new ApiViewException(ResultEnum.ACCOUNT_NOT_EXIST.getCode(), Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage()); + } + List list = hostService.list(accountInfo,hostListForm); + return ResultVoUtils.success(list); + } + + @PostMapping("/add") + public ResultVO add(@LoginAccount AccountInfo accountInfo, @Valid @RequestBody CreateHostForm createHostForm, BindingResult bindingResult){ + if(accountInfo.getId()<0){ + log.error("[host/add]{}",accountInfo.getId()); + throw new ApiViewException(ResultEnum.USER_LOGIN_EXPIRED); + } + if (bindingResult.hasErrors()) { + log.error("[host/add]:参数错误{}", createHostForm.toString()); + throw new ApiViewException(ResultEnum.ACCOUNT_NOT_EXIST.getCode(), Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage()); + } + HostAccountDO hostAccountDO = hostService.add(accountInfo,createHostForm); + return ResultVoUtils.success(hostAccountDO); + } + +} diff --git a/src/main/java/cc/cloudedu/accountmanager/controller/IndexController.java b/src/main/java/cc/cloudedu/accountmanager/controller/IndexController.java new file mode 100644 index 0000000000000000000000000000000000000000..89393a0dc286127a5ac905049b41de9568c7f951 --- /dev/null +++ b/src/main/java/cc/cloudedu/accountmanager/controller/IndexController.java @@ -0,0 +1,43 @@ +package cc.cloudedu.accountmanager.controller; + +import cc.cloudedu.accountmanager.util.AesUtils; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +/** + * @author fdg + */ +@RestController +@RequestMapping("/") +@RequiredArgsConstructor +public class IndexController { + private final AesUtils aesUtils; + + @RequestMapping(path = {"/","/index"},method = {RequestMethod.GET,RequestMethod.POST}) + public String index(){ + return "hello world!!!"; + } + + @GetMapping("/test") + public String test(){ + byte[] encrypt = aesUtils.encrypt("hello word".getBytes(StandardCharsets.UTF_8)); + + Base64.Encoder encoder = Base64.getEncoder(); + Base64.Decoder decoder = Base64.getDecoder(); + String s = encoder.encodeToString(encrypt); + System.out.println(s); + byte[] decrypt = aesUtils.decrypt(decoder.decode(s)); + System.out.println(new String(decrypt)); + + String hello_string = aesUtils.encrypt("hello string"); + String decrypt1 = aesUtils.decrypt(hello_string); + System.out.println(decrypt1); + return "test"; + } +} diff --git a/src/main/java/cc/cloudedu/accountmanager/controller/LoginController.java b/src/main/java/cc/cloudedu/accountmanager/controller/LoginController.java new file mode 100644 index 0000000000000000000000000000000000000000..32076551504336a6c5039e6f330ea168215b1c0b --- /dev/null +++ b/src/main/java/cc/cloudedu/accountmanager/controller/LoginController.java @@ -0,0 +1,67 @@ +package cc.cloudedu.accountmanager.controller; + +import cc.cloudedu.accountmanager.common.AccountInfo; +import cc.cloudedu.accountmanager.common.LoginAccount; +import cc.cloudedu.accountmanager.config.JwtAccountConfig; +import cc.cloudedu.accountmanager.dataobject.AccountDO; +import cc.cloudedu.accountmanager.enums.ResultEnum; +import cc.cloudedu.accountmanager.exception.ApiViewException; +import cc.cloudedu.accountmanager.form.AccountInfoForm; +import cc.cloudedu.accountmanager.server.AccountService; +import cc.cloudedu.accountmanager.util.JwtOperatorUtils; +import cc.cloudedu.accountmanager.util.ResultVoUtils; +import cc.cloudedu.accountmanager.vo.AccountInfoVO; +import cc.cloudedu.accountmanager.vo.ResultVO; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Objects; + +/** + * @author fdg + */ +@Slf4j +@RestController +@RequestMapping("/login") +@RequiredArgsConstructor +public class LoginController { + + public final JwtAccountConfig jwtAccountConfig; + + public final AccountService accountService; + + @PostMapping("/login") + public ResultVO login(@Valid @RequestBody AccountInfoForm accountInfoForm, BindingResult bindingResult) throws ApiViewException { + if (bindingResult.hasErrors()) { + log.error("[login]:参数错误{}", accountInfoForm.toString()); + throw new ApiViewException(ResultEnum.ACCOUNT_NOT_EXIST.getCode(), Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage()); + } + AccountDO accountDO = accountService.login(accountInfoForm); + AccountInfoVO accountInfoVO = new AccountInfoVO(); + accountInfoVO.setId(accountDO.getId()); + accountInfoVO.setUpdateTime(accountDO.getUpdateTime()); + AccountInfo accountInfo = new AccountInfo(); + accountInfo.setId(accountDO.getId()); + accountInfoVO.setToken(JwtOperatorUtils.generateToken(accountInfo,jwtAccountConfig.getSecret(),jwtAccountConfig.getExpiration())); + return ResultVoUtils.success(accountInfoVO); + } + + @PostMapping("/register") + public AccountDO register(@LoginAccount AccountInfo accountInfo, @Valid @RequestBody AccountInfoForm accountInfoForm, BindingResult bindingResult) throws ApiViewException { + if(accountInfo.getId()<0){ + log.error("[register]{}",accountInfo.getId()); + //throw new ApiViewException(ResultEnum.USER_LOGIN_EXPIRED); + } + if (bindingResult.hasErrors()) { + log.error("[register]:参数错误{}", accountInfoForm.toString()); + throw new ApiViewException(ResultEnum.ACCOUNT_NOT_EXIST.getCode(), Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage()); + } + return accountService.add(accountInfoForm); + } +} diff --git a/src/main/java/cc/cloudedu/accountmanager/dao/AccountDAO.java b/src/main/java/cc/cloudedu/accountmanager/dao/AccountDAO.java new file mode 100644 index 0000000000000000000000000000000000000000..dbf97a3dfc102ef50600b0a131384fe50807746d --- /dev/null +++ b/src/main/java/cc/cloudedu/accountmanager/dao/AccountDAO.java @@ -0,0 +1,22 @@ +package cc.cloudedu.accountmanager.dao; + +import cc.cloudedu.accountmanager.dataobject.AccountDO; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +/** + * @author fdg + */ +@Repository +public interface AccountDAO extends JpaRepository { + + /** + * 根据用户名查找密码 + * @param username + * @return + */ + Optional findFirstByUsername(String username); + +} diff --git a/src/main/java/cc/cloudedu/accountmanager/dao/HostAccountDAO.java b/src/main/java/cc/cloudedu/accountmanager/dao/HostAccountDAO.java new file mode 100644 index 0000000000000000000000000000000000000000..0282cfb14bfd45404fa7ba0de8501a1448c68ab3 --- /dev/null +++ b/src/main/java/cc/cloudedu/accountmanager/dao/HostAccountDAO.java @@ -0,0 +1,30 @@ +package cc.cloudedu.accountmanager.dao; + +import cc.cloudedu.accountmanager.dataobject.AccountDO; +import cc.cloudedu.accountmanager.dataobject.HostAccountDO; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +/** + * @author fdg + */ +@Repository +public interface HostAccountDAO extends JpaRepository { + /** + * 根据host查询账户 + * @param hostID + * @return + */ + Optional> findAllByHostId(Integer hostID); + + /** + * 当前uid的host账户信息 + * @param uid + * @param hostID + * @return + */ + Optional> findAllByUidAndHostId(Integer uid,Integer hostID); +} diff --git a/src/main/java/cc/cloudedu/accountmanager/dao/HostDAO.java b/src/main/java/cc/cloudedu/accountmanager/dao/HostDAO.java new file mode 100644 index 0000000000000000000000000000000000000000..abea4cc336741252f7aafe7bd23fab48ecde50f2 --- /dev/null +++ b/src/main/java/cc/cloudedu/accountmanager/dao/HostDAO.java @@ -0,0 +1,28 @@ +package cc.cloudedu.accountmanager.dao; + +import cc.cloudedu.accountmanager.dataobject.HostDO; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +/** + * @author fdg + */ +@Repository +public interface HostDAO extends JpaRepository { + /** + * 查询host + * @param host + * @return + */ + Optional findFirstByHost(String host); + + /** + * 当前uid的host + * @param uid user id + * @param host host + * @return host + */ + Optional findFirstByUidAndHost(Integer uid,String host); +} diff --git a/src/main/java/cc/cloudedu/accountmanager/dataobject/AccountDO.java b/src/main/java/cc/cloudedu/accountmanager/dataobject/AccountDO.java new file mode 100644 index 0000000000000000000000000000000000000000..5d0afecc2ca927a97850e80cc5e141d85c02c03b --- /dev/null +++ b/src/main/java/cc/cloudedu/accountmanager/dataobject/AccountDO.java @@ -0,0 +1,38 @@ +package cc.cloudedu.accountmanager.dataobject; + +import jakarta.persistence.*; +import lombok.Data; +import org.hibernate.annotations.Generated; +import org.hibernate.annotations.GenerationTime; + +import java.sql.Timestamp; + +/** + * @author fdg + */ +@Data +@Entity +@Table(name = "a_account") +public class AccountDO { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + private String username; + private String nickname; + private String phone; + private String password; + private String sequence; + @Column(name = "wechat_id") + private String wechatId; + @Column(name = "qq_open_id") + private String qqOpenId; + private String img; + + @Column(name="create_time",columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP",insertable = false,updatable = false) + @Generated(GenerationTime.INSERT) + private Timestamp createTime; + + @Column(name="update_time",columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP",insertable = false,updatable = false) + @Generated(GenerationTime.INSERT) + private Timestamp updateTime; +} diff --git a/src/main/java/cc/cloudedu/accountmanager/dataobject/HostAccountDO.java b/src/main/java/cc/cloudedu/accountmanager/dataobject/HostAccountDO.java new file mode 100644 index 0000000000000000000000000000000000000000..bdc00933486c13e30297f1a245bb3250d5f31dfb --- /dev/null +++ b/src/main/java/cc/cloudedu/accountmanager/dataobject/HostAccountDO.java @@ -0,0 +1,37 @@ +package cc.cloudedu.accountmanager.dataobject; + +import jakarta.persistence.*; +import lombok.Data; +import org.hibernate.annotations.Generated; +import org.hibernate.annotations.GenerationTime; + +import java.sql.Timestamp; + +/** + * @author fdg + */ +@Data +@Entity +@Table(name = "a_host_account") +public class HostAccountDO { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + private Integer uid; + + @Column(name = "host_id") + private Integer hostId; + + private String username; + + private String password; + + @Column(name="create_time",columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP",insertable = false,updatable = false) + @Generated(GenerationTime.INSERT) + private Timestamp createTime; + + @Column(name="update_time",columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP",insertable = false,updatable = false) + @Generated(GenerationTime.INSERT) + private Timestamp updateTime; +} diff --git a/src/main/java/cc/cloudedu/accountmanager/dataobject/HostDO.java b/src/main/java/cc/cloudedu/accountmanager/dataobject/HostDO.java new file mode 100644 index 0000000000000000000000000000000000000000..2d08a8063449da3da812f008613887873b367667 --- /dev/null +++ b/src/main/java/cc/cloudedu/accountmanager/dataobject/HostDO.java @@ -0,0 +1,34 @@ +package cc.cloudedu.accountmanager.dataobject; + +import jakarta.persistence.*; +import lombok.Data; +import org.hibernate.annotations.Generated; +import org.hibernate.annotations.GenerationTime; + +import java.sql.Timestamp; + +/** + * @author fdg + */ +@Data +@Entity +@Table(name = "a_host") +public class HostDO { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + private Integer uid; + + private String host; + + private String title; + + @Column(name="create_time",columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP",insertable = false,updatable = false) + @Generated(GenerationTime.INSERT) + private Timestamp createTime; + + @Column(name="update_time",columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP",insertable = false,updatable = false) + @Generated(GenerationTime.INSERT) + private Timestamp updateTime; +} diff --git a/src/main/java/cc/cloudedu/accountmanager/enums/ResultEnum.java b/src/main/java/cc/cloudedu/accountmanager/enums/ResultEnum.java new file mode 100644 index 0000000000000000000000000000000000000000..e1d4670f1c30375973e100ca19c5b3d0442be286 --- /dev/null +++ b/src/main/java/cc/cloudedu/accountmanager/enums/ResultEnum.java @@ -0,0 +1,63 @@ +package cc.cloudedu.accountmanager.enums; + +import lombok.Getter; + +/** + * @author fdg + */ + +@Getter +public enum ResultEnum { + + /** + * 成功 + */ + SUCCESS(0, "成功"), + + /** + * 用户名已存在 + */ + ACCOUNT_EXIST(10111,"用户名已存在"), + + /** + * 密码校验失败 + */ + PASSWORD_VERIFICATION_FAILED(10120,"密码校验失败"), + + /** + * 用户账户不存在 + */ + ACCOUNT_NOT_EXIST(10201, "用户账户不存在"), + + /** + * 用户密码错误 + */ + PASSWORD_ERROR(10210, "用户密码错误"), + /** + * 用户登录已过期 + */ + USER_LOGIN_EXPIRED(10230, "用户登录已过期"), + + /** + * RSA 签名错误 + */ + RSA_SIGN_ERROR(10341,"RSA签名错误"), + + /** + * 数据库服务出错 + */ + DATABASE_SERVICE_ERROR(30300,"数据库服务出错"), + + /** + * 查询为空 + */ + QUERY_IS_EMPTY(30310,"查询为空"); + private final int code; + + private final String message; + + ResultEnum(int code, String message) { + this.code = code; + this.message = message; + } +} diff --git a/src/main/java/cc/cloudedu/accountmanager/exception/AccountManagerExceptionHandler.java b/src/main/java/cc/cloudedu/accountmanager/exception/AccountManagerExceptionHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..37496c42073d590bd2ed8a2a95361eed7c056f82 --- /dev/null +++ b/src/main/java/cc/cloudedu/accountmanager/exception/AccountManagerExceptionHandler.java @@ -0,0 +1,19 @@ +package cc.cloudedu.accountmanager.exception; + +import cc.cloudedu.accountmanager.util.ResultVoUtils; +import cc.cloudedu.accountmanager.vo.ResultVO; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; + +/** + * @author fdg + */ +@ControllerAdvice +public class AccountManagerExceptionHandler { + @ResponseBody + @ExceptionHandler(value = ApiViewException.class) + public ResultVO apiViewHandler(ApiViewException e) { + return ResultVoUtils.error(e.getCode(), e.getMessage()); + } +} diff --git a/src/main/java/cc/cloudedu/accountmanager/exception/ApiViewException.java b/src/main/java/cc/cloudedu/accountmanager/exception/ApiViewException.java new file mode 100644 index 0000000000000000000000000000000000000000..a609173ca9a123b61bf436e9bd7b89d653a7cc47 --- /dev/null +++ b/src/main/java/cc/cloudedu/accountmanager/exception/ApiViewException.java @@ -0,0 +1,35 @@ +package cc.cloudedu.accountmanager.exception; + +import cc.cloudedu.accountmanager.enums.ResultEnum; + +import java.io.Serial; + +/** + * @author fdg + */ +public class ApiViewException extends RuntimeException{ + + @Serial + private static final long serialVersionUID = 8018946970696376052L; + + private int code; + + public ApiViewException(ResultEnum resultEnum) { + super(resultEnum.getMessage()); + + this.setCode(resultEnum.getCode()); + } + + public ApiViewException(int code, String message) { + super(message); + this.setCode(code); + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } +} diff --git a/src/main/java/cc/cloudedu/accountmanager/form/AccountInfoForm.java b/src/main/java/cc/cloudedu/accountmanager/form/AccountInfoForm.java new file mode 100644 index 0000000000000000000000000000000000000000..6942cad621632db299faccdb49bd2f200749af97 --- /dev/null +++ b/src/main/java/cc/cloudedu/accountmanager/form/AccountInfoForm.java @@ -0,0 +1,23 @@ +package cc.cloudedu.accountmanager.form; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +/** + * @author fdg + */ +@Getter +@Setter +@ToString +public class AccountInfoForm { + @NotNull(message="用户名必填") + @NotBlank(message="用户名必填") + private String username; + + @NotNull(message="密码必填") + @NotBlank(message="密码必填") + private String password; +} diff --git a/src/main/java/cc/cloudedu/accountmanager/form/CreateHostForm.java b/src/main/java/cc/cloudedu/accountmanager/form/CreateHostForm.java new file mode 100644 index 0000000000000000000000000000000000000000..f342b9c15b21a48ee244cf5c8791318d58480d2a --- /dev/null +++ b/src/main/java/cc/cloudedu/accountmanager/form/CreateHostForm.java @@ -0,0 +1,27 @@ +package cc.cloudedu.accountmanager.form; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +/** + * @author fdg + */ +@Getter +@Setter +@ToString +public class CreateHostForm { + @NotNull(message="HOST必填") + @NotBlank(message="HOST必填") + private String host; + @NotNull(message="用户名必填") + @NotBlank(message="用户名必填") + private String username; + @NotNull(message="密码必填") + @NotBlank(message="密码必填") + private String password; + + private String title; +} diff --git a/src/main/java/cc/cloudedu/accountmanager/form/HostListForm.java b/src/main/java/cc/cloudedu/accountmanager/form/HostListForm.java new file mode 100644 index 0000000000000000000000000000000000000000..17e7b51042560e9aadce1201b5ce07276603461d --- /dev/null +++ b/src/main/java/cc/cloudedu/accountmanager/form/HostListForm.java @@ -0,0 +1,19 @@ +package cc.cloudedu.accountmanager.form; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +/** + * @author fdg + */ +@Getter +@Setter +@ToString +public class HostListForm { + @NotNull(message="HOST必填") + @NotBlank(message="HOST必填") + private String host; +} diff --git a/src/main/java/cc/cloudedu/accountmanager/server/AccountService.java b/src/main/java/cc/cloudedu/accountmanager/server/AccountService.java new file mode 100644 index 0000000000000000000000000000000000000000..7b0eb2e22369c7c53c514e0950500b3249db948a --- /dev/null +++ b/src/main/java/cc/cloudedu/accountmanager/server/AccountService.java @@ -0,0 +1,24 @@ +package cc.cloudedu.accountmanager.server; + +import cc.cloudedu.accountmanager.dataobject.AccountDO; +import cc.cloudedu.accountmanager.form.AccountInfoForm; + +/** + * @author fdg + */ +public interface AccountService { + + /** + * 登录 + * @param accountInfoForm + * @return AccountDO + */ + AccountDO login(AccountInfoForm accountInfoForm); + + /** + * 新增 + * @param accountInfoForm + * @return AccountDO + */ + AccountDO add(AccountInfoForm accountInfoForm); +} diff --git a/src/main/java/cc/cloudedu/accountmanager/server/HostService.java b/src/main/java/cc/cloudedu/accountmanager/server/HostService.java new file mode 100644 index 0000000000000000000000000000000000000000..a5a85255e0d84db23fa1ace3a7f3f0b68e0e20f4 --- /dev/null +++ b/src/main/java/cc/cloudedu/accountmanager/server/HostService.java @@ -0,0 +1,29 @@ +package cc.cloudedu.accountmanager.server; + +import cc.cloudedu.accountmanager.common.AccountInfo; +import cc.cloudedu.accountmanager.dataobject.HostAccountDO; +import cc.cloudedu.accountmanager.form.CreateHostForm; +import cc.cloudedu.accountmanager.form.HostListForm; + +import java.util.List; + +/** + * @author fdg + */ +public interface HostService { + /** + * 查询list + * @param accountInfo uid + * @param hostListForm host + * @return list + */ + List list(AccountInfo accountInfo,HostListForm hostListForm); + + /** + * 添加 + * @param accountInfo uid + * @param createHostForm host account + * @return account + */ + HostAccountDO add(AccountInfo accountInfo, CreateHostForm createHostForm); +} diff --git a/src/main/java/cc/cloudedu/accountmanager/server/impl/AccountServiceImpl.java b/src/main/java/cc/cloudedu/accountmanager/server/impl/AccountServiceImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..af4a0b456312bd66b7f20b1545a8ee1e09caf86b --- /dev/null +++ b/src/main/java/cc/cloudedu/accountmanager/server/impl/AccountServiceImpl.java @@ -0,0 +1,59 @@ +package cc.cloudedu.accountmanager.server.impl; + +import cc.cloudedu.accountmanager.dao.AccountDAO; +import cc.cloudedu.accountmanager.dataobject.AccountDO; +import cc.cloudedu.accountmanager.enums.ResultEnum; +import cc.cloudedu.accountmanager.exception.ApiViewException; +import cc.cloudedu.accountmanager.form.AccountInfoForm; +import cc.cloudedu.accountmanager.server.AccountService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.stereotype.Service; + +import java.util.Optional; + +/** + * @author fdg + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class AccountServiceImpl implements AccountService { + + private final AccountDAO accountDAO; + + @Override + public AccountDO login(AccountInfoForm accountInfoForm) { + Optional accountDO = accountDAO.findFirstByUsername(accountInfoForm.getUsername()); + if(accountDO.isPresent()){ + AccountDO detail = accountDO.get(); + BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); + boolean matches = encoder.matches(accountInfoForm.getPassword(), detail.getPassword()); + if(matches){ + return detail; + }else { + log.error("[用户登录]:密码错误"); + throw new ApiViewException(ResultEnum.PASSWORD_ERROR); + } + }else{ + log.error("[用户登录]:用户名不存在"); + throw new ApiViewException(ResultEnum.ACCOUNT_NOT_EXIST); + } + } + + @Override + public AccountDO add(AccountInfoForm accountInfoForm) { + Optional accountDOOptional = accountDAO.findFirstByUsername(accountInfoForm.getUsername()); + if(accountDOOptional.isPresent()){ + log.error("[用户注册]:用户名已存在"); + throw new ApiViewException(ResultEnum.ACCOUNT_EXIST); + }else { + AccountDO accountDO = new AccountDO(); + accountDO.setUsername(accountInfoForm.getUsername()); + BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); + accountDO.setPassword(encoder.encode(accountInfoForm.getPassword())); + return accountDAO.save(accountDO); + } + } +} diff --git a/src/main/java/cc/cloudedu/accountmanager/server/impl/HostServiceImpl.java b/src/main/java/cc/cloudedu/accountmanager/server/impl/HostServiceImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..8faa53f999c96afe96a62e6bef0171790ef39dad --- /dev/null +++ b/src/main/java/cc/cloudedu/accountmanager/server/impl/HostServiceImpl.java @@ -0,0 +1,75 @@ +package cc.cloudedu.accountmanager.server.impl; + +import cc.cloudedu.accountmanager.common.AccountInfo; +import cc.cloudedu.accountmanager.dao.HostAccountDAO; +import cc.cloudedu.accountmanager.dao.HostDAO; +import cc.cloudedu.accountmanager.dataobject.HostAccountDO; +import cc.cloudedu.accountmanager.dataobject.HostDO; +import cc.cloudedu.accountmanager.enums.ResultEnum; +import cc.cloudedu.accountmanager.exception.ApiViewException; +import cc.cloudedu.accountmanager.form.CreateHostForm; +import cc.cloudedu.accountmanager.form.HostListForm; +import cc.cloudedu.accountmanager.server.HostService; +import cc.cloudedu.accountmanager.util.AesUtils; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * @author fdg + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class HostServiceImpl implements HostService { + private final HostDAO hostDAO; + private final HostAccountDAO hostAccountDAO; + private final AesUtils aesUtils; + + @Override + public List list(AccountInfo accountInfo,HostListForm hostListForm){ + Optional hostDO = hostDAO.findFirstByUidAndHost(accountInfo.getId(),hostListForm.getHost()); + if(hostDO.isPresent()){ + HostDO detail = hostDO.get(); + Optional> hostAccountList = hostAccountDAO.findAllByHostId(detail.getId()); + if(hostAccountList.isPresent()){ + return hostAccountList.stream().flatMap(Collection::stream).peek(hostAccountDO -> { + hostAccountDO.setUsername(aesUtils.decrypt(hostAccountDO.getUsername())); + hostAccountDO.setPassword(aesUtils.decrypt(hostAccountDO.getPassword())); + }).collect(Collectors.toList()); + }else { + log.error("[hostAccount]:查询账户信息"); + throw new ApiViewException(ResultEnum.QUERY_IS_EMPTY); + } + }else { + log.error("[host]:查询账户信息"); + throw new ApiViewException(ResultEnum.QUERY_IS_EMPTY); + } + } + + @Override + public HostAccountDO add(AccountInfo accountInfo,CreateHostForm createHostForm) { + Optional findHostDO = hostDAO.findFirstByUidAndHost(accountInfo.getId(),createHostForm.getHost()); + HostAccountDO hostAccountDO = new HostAccountDO(); + hostAccountDO.setUid(accountInfo.getId()); + hostAccountDO.setUsername(aesUtils.encrypt(createHostForm.getUsername())); + hostAccountDO.setPassword(aesUtils.encrypt(createHostForm.getPassword())); + if(findHostDO.isPresent()){ + hostAccountDO.setHostId(findHostDO.get().getId()); + }else { + HostDO hostDO = new HostDO(); + hostDO.setUid(accountInfo.getId()); + hostDO.setHost(createHostForm.getHost()); + hostDO.setTitle(createHostForm.getTitle()); + HostDO saveHost = hostDAO.save(hostDO); + hostAccountDO.setHostId(saveHost.getId()); + } + return hostAccountDAO.save(hostAccountDO); + } + +} diff --git a/src/main/java/cc/cloudedu/accountmanager/util/AccountEncryptUtils.java b/src/main/java/cc/cloudedu/accountmanager/util/AccountEncryptUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..a0ef78b4a0453c24cce4c29bbf6c39eca0fbab5d --- /dev/null +++ b/src/main/java/cc/cloudedu/accountmanager/util/AccountEncryptUtils.java @@ -0,0 +1,10 @@ +package cc.cloudedu.accountmanager.util; + +/** + * @author fdg + */ +public class AccountEncryptUtils { + + + +} diff --git a/src/main/java/cc/cloudedu/accountmanager/util/AesUtils.java b/src/main/java/cc/cloudedu/accountmanager/util/AesUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..91c9bbf04eecf89534302aff3d207b0a96469822 --- /dev/null +++ b/src/main/java/cc/cloudedu/accountmanager/util/AesUtils.java @@ -0,0 +1,92 @@ +package cc.cloudedu.accountmanager.util; + +import cc.cloudedu.accountmanager.config.AesConfig; +import cc.cloudedu.accountmanager.enums.ResultEnum; +import cc.cloudedu.accountmanager.exception.ApiViewException; + +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import java.nio.charset.StandardCharsets; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Base64; + +/** + * @author fdg + */ +public class AesUtils { + private final AesConfig aesConfig; + private final SecretKey secretKey; + private final Base64.Encoder encoder = Base64.getEncoder(); + private final Base64.Decoder decoder = Base64.getDecoder(); + + public AesUtils(AesConfig aesConfig) { + this.aesConfig = aesConfig; + this.secretKey=generateKey(aesConfig.getKey().getBytes(StandardCharsets.UTF_8)); + } + + public SecretKey generateKey(byte[] key) { + try { + // 创建安全随机数生成器 + SecureRandom random = SecureRandom.getInstance(aesConfig.getRngAlgorithm()); + + // 设置 密钥key的字节数组 作为安全随机数生成器的种子 + random.setSeed(key); + + // 创建 AES算法生成器 + KeyGenerator gen = KeyGenerator.getInstance(aesConfig.getAlgorithm()); + // 初始化算法生成器 + gen.init(aesConfig.getKeySize(), random); + return gen.generateKey(); + } catch (NoSuchAlgorithmException e) { + throw new ApiViewException(ResultEnum.DATABASE_SERVICE_ERROR); + } + } + + /** + * 数据加密: 明文 -> 密文 + */ + public byte[] encrypt(byte[] plainBytes) { + try { + // 获取 AES 密码器 + Cipher cipher = Cipher.getInstance(aesConfig.getAlgorithm()); + // 初始化密码器(加密模型) + cipher.init(Cipher.ENCRYPT_MODE, this.secretKey); + // 加密数据, 返回密文 + return cipher.doFinal(plainBytes); + } catch (Exception e) { + throw new ApiViewException(ResultEnum.DATABASE_SERVICE_ERROR); + } + + } + + /** + * 数据加密: 明文 -> 密文 + * @param plainString string + * @return string + */ + public String encrypt(String plainString){ + return encoder.encodeToString(encrypt(plainString.getBytes(StandardCharsets.UTF_8))); + } + + /** + * 数据解密: 密文 -> 明文 + */ + public byte[] decrypt(byte[] cipherBytes){ + try { + // 获取 AES 密码器 + Cipher cipher = Cipher.getInstance(aesConfig.getAlgorithm()); + // 初始化密码器(解密模型) + cipher.init(Cipher.DECRYPT_MODE, this.secretKey); + // 解密数据, 返回明文 + return cipher.doFinal(cipherBytes); + }catch (Exception e){ + throw new ApiViewException(ResultEnum.DATABASE_SERVICE_ERROR); + } + } + + public String decrypt(String cipherString){ + return new String(decrypt(decoder.decode(cipherString))); + } +} diff --git a/src/main/java/cc/cloudedu/accountmanager/util/JwtOperatorUtils.java b/src/main/java/cc/cloudedu/accountmanager/util/JwtOperatorUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..4b5f936986a675ccc5235987ce66bddbf4f002d8 --- /dev/null +++ b/src/main/java/cc/cloudedu/accountmanager/util/JwtOperatorUtils.java @@ -0,0 +1,51 @@ +package cc.cloudedu.accountmanager.util; + +import cc.cloudedu.accountmanager.common.AccountInfo; +import cc.cloudedu.accountmanager.enums.ResultEnum; +import cc.cloudedu.accountmanager.exception.ApiViewException; +import com.auth0.jwt.JWT; +import com.auth0.jwt.JWTVerifier; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.exceptions.JWTCreationException; +import com.auth0.jwt.exceptions.JWTVerificationException; +import com.auth0.jwt.interfaces.DecodedJWT; +import lombok.extern.slf4j.Slf4j; + +import java.util.Date; + +/** + * @author fdg + */ +@Slf4j +public class JwtOperatorUtils { + + public static String generateToken(AccountInfo accountInfo, String secret, Long expiration){ + try { + Algorithm algorithm = Algorithm.HMAC256(secret); + String token = JWT.create() + .withIssuer("fdg") + .withClaim("id",accountInfo.getId()) + .withIssuedAt(new Date()) + .withExpiresAt(new Date(System.currentTimeMillis()+expiration)) + .sign(algorithm); + return token; + } catch (JWTCreationException exception){ + log.error("[jwt]:获取token错误{}",exception.getMessage()); + throw new ApiViewException(ResultEnum.RSA_SIGN_ERROR.getCode(),ResultEnum.RSA_SIGN_ERROR.getMessage()); + } + } + + public static DecodedJWT getFromToken(String token, String secret){ + try { + Algorithm algorithm = Algorithm.HMAC256(secret); + JWTVerifier verifier = JWT.require(algorithm) + .withIssuer("fdg") + .build(); + DecodedJWT jwt = verifier.verify(token); + return jwt; + } catch (JWTVerificationException exception){ + log.error("[jwt]:解析token错误{}",exception.getMessage()); + throw new ApiViewException(ResultEnum.RSA_SIGN_ERROR.getCode(),ResultEnum.RSA_SIGN_ERROR.getMessage()); + } + } +} diff --git a/src/main/java/cc/cloudedu/accountmanager/util/ResultVoUtils.java b/src/main/java/cc/cloudedu/accountmanager/util/ResultVoUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..a2ccdcc75f887f57f34f3241f6cfc546d4d88d2b --- /dev/null +++ b/src/main/java/cc/cloudedu/accountmanager/util/ResultVoUtils.java @@ -0,0 +1,34 @@ +package cc.cloudedu.accountmanager.util; + +import cc.cloudedu.accountmanager.enums.ResultEnum; +import cc.cloudedu.accountmanager.vo.ResultVO; + +/** + * @author fdg + */ +public class ResultVoUtils { + public static ResultVO success(Object object) { + ResultVO resultVO = new ResultVO<>(); + resultVO.setData(object); + resultVO.setCode(0); + resultVO.setMsg("成功"); + return resultVO; + } + public static ResultVO success() { + return success(null); + } + + public static ResultVO error(int code, String msg) { + ResultVO resultVO = new ResultVO(); + resultVO.setCode(code); + resultVO.setMsg(msg); + return resultVO; + } + + public static ResultVO error(ResultEnum resultEnum) { + ResultVO resultVO = new ResultVO(); + resultVO.setCode(resultEnum.getCode()); + resultVO.setMsg(resultEnum.getMessage()); + return resultVO; + } +} diff --git a/src/main/java/cc/cloudedu/accountmanager/vo/AccountInfoVO.java b/src/main/java/cc/cloudedu/accountmanager/vo/AccountInfoVO.java new file mode 100644 index 0000000000000000000000000000000000000000..bf4dce448ea97d70bce3434578e06525a68389b5 --- /dev/null +++ b/src/main/java/cc/cloudedu/accountmanager/vo/AccountInfoVO.java @@ -0,0 +1,19 @@ +package cc.cloudedu.accountmanager.vo; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +import java.sql.Timestamp; + +/** + * @author fdg + */ +@Getter +@Setter +@ToString +public class AccountInfoVO { + private Integer id; + private String token; + private Timestamp updateTime; +} diff --git a/src/main/java/cc/cloudedu/accountmanager/vo/ResultVO.java b/src/main/java/cc/cloudedu/accountmanager/vo/ResultVO.java new file mode 100644 index 0000000000000000000000000000000000000000..32acfa6d82c6a71f4dea68a35a6ef6a36be9f329 --- /dev/null +++ b/src/main/java/cc/cloudedu/accountmanager/vo/ResultVO.java @@ -0,0 +1,22 @@ +package cc.cloudedu.accountmanager.vo; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +/** + * @author fdg + */ +@Setter +@Getter +@ToString +public class ResultVO { + /** 错误码. */ + private Integer code; + + /** 提示信息. */ + private String msg; + + /** 具体内容. */ + private T data; +} diff --git a/src/main/resources/application.yml.back b/src/main/resources/application.yml.back new file mode 100644 index 0000000000000000000000000000000000000000..d0778b7521e50cdd71ac6650571df1cbc34093f8 --- /dev/null +++ b/src/main/resources/application.yml.back @@ -0,0 +1,24 @@ +spring: + application: + name: account-manager-server + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + password: xxx + username: xxx + url: jdbc:mysql://localhost:3306/xxx + jackson: + time-zone: GMT+8 + jpa: + show-sql: true +server: + port: 8080 +jwt: + account: + header-key: authorization + secret: xx + expiration: 1209600000 +ase: + key: eyJpc3MiOiJmZGciLCJpZCI6MSwiZXhwIjoxNjUxNTA0OTYxLCJpYXQiOjE2NTAyOTUzNjF9 + key-size: 128 + algorithm: AES + rng-algorithm: SHA1PRNG diff --git a/src/test/java/cc/cloudedu/accountmanager/AccountManagerApplicationTests.java b/src/test/java/cc/cloudedu/accountmanager/AccountManagerApplicationTests.java new file mode 100644 index 0000000000000000000000000000000000000000..a2b8bdecbacea2fbd2cf59101cb379c6fd9e2989 --- /dev/null +++ b/src/test/java/cc/cloudedu/accountmanager/AccountManagerApplicationTests.java @@ -0,0 +1,13 @@ +package cc.cloudedu.accountmanager; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class AccountManagerApplicationTests { + + @Test + void contextLoads() { + } + +}