Преглед на файлове

Merge branch 'develop' of gitadmin/tuoheng_oidc into master

tags/V2.2.0
gitadmin преди 1 година
родител
ревизия
b51f226c48
променени са 66 файла, в които са добавени 2317 реда и са изтрити 834 реда
  1. +0
    -33
      Spring_Authorization_Server/.gitignore
  2. Двоични данни
      Spring_Authorization_Server/.mvn/wrapper/maven-wrapper.jar
  3. +0
    -2
      Spring_Authorization_Server/.mvn/wrapper/maven-wrapper.properties
  4. +0
    -316
      Spring_Authorization_Server/mvnw
  5. +0
    -188
      Spring_Authorization_Server/mvnw.cmd
  6. +0
    -109
      Spring_Authorization_Server/src/main/java/com/tuoheng/config/SecurityConfig.java
  7. +0
    -15
      Spring_Authorization_Server/src/main/java/com/tuoheng/mapper/UserMapper.java
  8. +0
    -21
      Spring_Authorization_Server/src/main/java/com/tuoheng/model/param/CreateUserDto.java
  9. +0
    -32
      Spring_Authorization_Server/src/main/java/com/tuoheng/service/impl/UserServiceImpl.java
  10. +0
    -6
      Spring_Authorization_Server/src/main/resources/application-prod.yml
  11. +0
    -6
      Spring_Authorization_Server/src/main/resources/application-test.yml
  12. +0
    -6
      Spring_Authorization_Server/src/main/resources/application.yml
  13. +0
    -10
      Spring_Authorization_Server/src/main/resources/mapper/UserMapper.xml
  14. +0
    -13
      Spring_Authorization_Server/src/test/java/com/tuoheng/SpringAuthorizationServerApplicationTests.java
  15. +0
    -38
      Spring_Authorization_Server/src/test/java/com/tuoheng/config/ApplicationTests.java
  16. +3
    -1
      others/To-Use.text
  17. +6
    -1
      pom.xml
  18. +62
    -4
      tuoheng_oidc_server/pom.xml
  19. +1
    -1
      tuoheng_oidc_server/src/main/java/com/tuoheng/SpringAuthorizationServerApplication.java
  20. +0
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/config/AuthorizationServerConfig.java
  21. +11
    -1
      tuoheng_oidc_server/src/main/java/com/tuoheng/config/IdTokenCustomizerConfig.java
  22. +0
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/config/JWKSourceConfig.java
  23. +0
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/config/MyCorsFilter.java
  24. +174
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/config/SecurityConfig.java
  25. +94
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/config/VerifyCodeFilter.java
  26. +12
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/constants/CommonConstant.java
  27. +3
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/controller/HealthController.java
  28. +58
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/controller/Oauth2Controller.java
  29. +13
    -3
      tuoheng_oidc_server/src/main/java/com/tuoheng/controller/UserController.java
  30. +59
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/controller/VerifyCodeController.java
  31. +38
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/handler/AccessDeniedHandler.java
  32. +86
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/handler/AuthenticationEntryPoint.java
  33. +18
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/mapper/AuthoritiesMapper.java
  34. +22
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/mapper/UserMapper.java
  35. +78
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/model/dto/JwtUser.java
  36. +23
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/model/dto/UserBaseInfoDto.java
  37. +26
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/model/param/CreateUserDto.java
  38. +23
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/model/param/GetUserInfoDto.java
  39. +23
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/model/po/AuthoritiesPo.java
  40. +2
    -2
      tuoheng_oidc_server/src/main/java/com/tuoheng/model/po/UserPo.java
  41. +69
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/oauth2/authentication/OAuth2EndpointUtils.java
  42. +89
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/oauth2/authentication/OAuth2ResourceOwnerPasswordAuthenticationConverter.java
  43. +186
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/oauth2/authentication/OAuth2ResourceOwnerPasswordAuthenticationProvider.java
  44. +75
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/oauth2/authentication/OAuth2ResourceOwnerPasswordAuthenticationToken.java
  45. +16
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/service/OidcUserInfoService.java
  46. +3
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/service/UserSevice.java
  47. +19
    -21
      tuoheng_oidc_server/src/main/java/com/tuoheng/service/impl/OidcUserInfoServiceImpl.java
  48. +69
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/service/impl/UserServiceImpl.java
  49. +0
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/until/CryptoUtil.java
  50. +0
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/until/JsonResult.java
  51. +177
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/until/RedisUtils.java
  52. +3
    -2
      tuoheng_oidc_server/src/main/java/com/tuoheng/until/RegisteredClientUtil.java
  53. +114
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/until/VerifyCode.java
  54. +186
    -0
      tuoheng_oidc_server/src/main/java/com/tuoheng/until/VerifyUtil.java
  55. +27
    -2
      tuoheng_oidc_server/src/main/resources/application-dev.yml
  56. +24
    -1
      tuoheng_oidc_server/src/main/resources/application-local.yml
  57. +60
    -0
      tuoheng_oidc_server/src/main/resources/application-prod.yml
  58. +61
    -0
      tuoheng_oidc_server/src/main/resources/application-test.yml
  59. +12
    -0
      tuoheng_oidc_server/src/main/resources/application.yml
  60. +69
    -0
      tuoheng_oidc_server/src/main/resources/logback.xml
  61. +13
    -0
      tuoheng_oidc_server/src/main/resources/mapper/AuthoritiesMapper.xml
  62. +34
    -0
      tuoheng_oidc_server/src/main/resources/mapper/UserMapper.xml
  63. Двоични данни
      tuoheng_oidc_server/src/main/resources/static/back.png
  64. Двоични данни
      tuoheng_oidc_server/src/main/resources/static/form.png
  65. +2
    -0
      tuoheng_oidc_server/src/main/resources/static/jquery-3.5.1.min.js
  66. +174
    -0
      tuoheng_oidc_server/src/main/resources/templates/login.html

+ 0
- 33
Spring_Authorization_Server/.gitignore Целия файл

@@ -1,33 +0,0 @@
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/

Двоични данни
Spring_Authorization_Server/.mvn/wrapper/maven-wrapper.jar Целия файл


+ 0
- 2
Spring_Authorization_Server/.mvn/wrapper/maven-wrapper.properties Целия файл

@@ -1,2 +0,0 @@
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar

+ 0
- 316
Spring_Authorization_Server/mvnw Целия файл

@@ -1,316 +0,0 @@
#!/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
#
# 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.
# ----------------------------------------------------------------------------

# ----------------------------------------------------------------------------
# Maven Start Up Batch script
#
# Required ENV vars:
# ------------------
# JAVA_HOME - location of a JDK home dir
#
# Optional ENV vars
# -----------------
# M2_HOME - location of maven2's installed home dir
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
# e.g. to debug Maven itself, use
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
# ----------------------------------------------------------------------------

if [ -z "$MAVEN_SKIP_RC" ] ; then

if [ -f /usr/local/etc/mavenrc ] ; then
. /usr/local/etc/mavenrc
fi

if [ -f /etc/mavenrc ] ; then
. /etc/mavenrc
fi

if [ -f "$HOME/.mavenrc" ] ; then
. "$HOME/.mavenrc"
fi

fi

# OS specific support. $var _must_ be set to either true or false.
cygwin=false;
darwin=false;
mingw=false
case "`uname`" in
CYGWIN*) cygwin=true ;;
MINGW*) mingw=true;;
Darwin*) darwin=true
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
if [ -z "$JAVA_HOME" ]; then
if [ -x "/usr/libexec/java_home" ]; then
export JAVA_HOME="`/usr/libexec/java_home`"
else
export JAVA_HOME="/Library/Java/Home"
fi
fi
;;
esac

if [ -z "$JAVA_HOME" ] ; then
if [ -r /etc/gentoo-release ] ; then
JAVA_HOME=`java-config --jre-home`
fi
fi

if [ -z "$M2_HOME" ] ; then
## resolve links - $0 may be a link to maven's home
PRG="$0"

# need this for relative symlinks
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG="`dirname "$PRG"`/$link"
fi
done

saveddir=`pwd`

M2_HOME=`dirname "$PRG"`/..

# make it fully qualified
M2_HOME=`cd "$M2_HOME" && pwd`

cd "$saveddir"
# echo Using m2 at $M2_HOME
fi

# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin ; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --unix "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
fi

# For Mingw, ensure paths are in UNIX format before anything is touched
if $mingw ; then
[ -n "$M2_HOME" ] &&
M2_HOME="`(cd "$M2_HOME"; pwd)`"
[ -n "$JAVA_HOME" ] &&
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
fi

if [ -z "$JAVA_HOME" ]; then
javaExecutable="`which javac`"
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
# readlink(1) is not available as standard on Solaris 10.
readLink=`which readlink`
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
if $darwin ; then
javaHome="`dirname \"$javaExecutable\"`"
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
else
javaExecutable="`readlink -f \"$javaExecutable\"`"
fi
javaHome="`dirname \"$javaExecutable\"`"
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
JAVA_HOME="$javaHome"
export JAVA_HOME
fi
fi
fi

if [ -z "$JAVACMD" ] ; then
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
else
JAVACMD="`\\unset -f command; \\command -v java`"
fi
fi

if [ ! -x "$JAVACMD" ] ; then
echo "Error: JAVA_HOME is not defined correctly." >&2
echo " We cannot execute $JAVACMD" >&2
exit 1
fi

if [ -z "$JAVA_HOME" ] ; then
echo "Warning: JAVA_HOME environment variable is not set."
fi

CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher

# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {

if [ -z "$1" ]
then
echo "Path not specified to find_maven_basedir"
return 1
fi

basedir="$1"
wdir="$1"
while [ "$wdir" != '/' ] ; do
if [ -d "$wdir"/.mvn ] ; then
basedir=$wdir
break
fi
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
if [ -d "${wdir}" ]; then
wdir=`cd "$wdir/.."; pwd`
fi
# end of workaround
done
echo "${basedir}"
}

# concatenates all lines of a file
concat_lines() {
if [ -f "$1" ]; then
echo "$(tr -s '\n' ' ' < "$1")"
fi
}

BASE_DIR=`find_maven_basedir "$(pwd)"`
if [ -z "$BASE_DIR" ]; then
exit 1;
fi

##########################################################################################
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
# This allows using the maven wrapper in projects that prohibit checking in binary data.
##########################################################################################
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found .mvn/wrapper/maven-wrapper.jar"
fi
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
fi
if [ -n "$MVNW_REPOURL" ]; then
jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
else
jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
fi
while IFS="=" read key value; do
case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
esac
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
if [ "$MVNW_VERBOSE" = true ]; then
echo "Downloading from: $jarUrl"
fi
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
if $cygwin; then
wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
fi

if command -v wget > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found wget ... using wget"
fi
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
else
wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
fi
elif command -v curl > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found curl ... using curl"
fi
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
curl -o "$wrapperJarPath" "$jarUrl" -f
else
curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
fi

else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Falling back to using Java to download"
fi
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
# For Cygwin, switch paths to Windows format before running javac
if $cygwin; then
javaClass=`cygpath --path --windows "$javaClass"`
fi
if [ -e "$javaClass" ]; then
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Compiling MavenWrapperDownloader.java ..."
fi
# Compiling the Java class
("$JAVA_HOME/bin/javac" "$javaClass")
fi
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
# Running the downloader
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Running MavenWrapperDownloader.java ..."
fi
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
fi
fi
fi
fi
##########################################################################################
# End of extension
##########################################################################################

export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
if [ "$MVNW_VERBOSE" = true ]; then
echo $MAVEN_PROJECTBASEDIR
fi
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"

# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --path --windows "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
fi

# Provide a "standardized" way to retrieve the CLI args that will
# work with both Windows and non-Windows executions.
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
export MAVEN_CMD_LINE_ARGS

WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain

exec "$JAVACMD" \
$MAVEN_OPTS \
$MAVEN_DEBUG_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.home=${M2_HOME}" \
"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"

+ 0
- 188
Spring_Authorization_Server/mvnw.cmd Целия файл

@@ -1,188 +0,0 @@
@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 https://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 Maven Start Up Batch script
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM M2_HOME - location of maven2's installed home dir
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ----------------------------------------------------------------------------

@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM set title of command window
title %0
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%

@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")

@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
:skipRcPre

@setlocal

set ERROR_CODE=0

@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal

@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome

echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error

:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init

echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error

@REM ==== END VALIDATION ====

:init

@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.

set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir

set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir

:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir

:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"

:endDetectBaseDir

IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig

@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%

:endReadAdditionalConfig

SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain

set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"

FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
)

@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
if exist %WRAPPER_JAR% (
if "%MVNW_VERBOSE%" == "true" (
echo Found %WRAPPER_JAR%
)
) else (
if not "%MVNW_REPOURL%" == "" (
SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
)
if "%MVNW_VERBOSE%" == "true" (
echo Couldn't find %WRAPPER_JAR%, downloading it ...
echo Downloading from: %DOWNLOAD_URL%
)

powershell -Command "&{"^
"$webclient = new-object System.Net.WebClient;"^
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
"}"^
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
"}"
if "%MVNW_VERBOSE%" == "true" (
echo Finished downloading %WRAPPER_JAR%
)
)
@REM End of extension

@REM Provide a "standardized" way to retrieve the CLI args that will
@REM work with both Windows and non-Windows executions.
set MAVEN_CMD_LINE_ARGS=%*

%MAVEN_JAVA_EXE% ^
%JVM_CONFIG_MAVEN_PROPS% ^
%MAVEN_OPTS% ^
%MAVEN_DEBUG_OPTS% ^
-classpath %WRAPPER_JAR% ^
"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
%WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
if ERRORLEVEL 1 goto error
goto end

:error
set ERROR_CODE=1

:end
@endlocal & set ERROR_CODE=%ERROR_CODE%

if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
:skipRcPost

@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%"=="on" pause

if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%

cmd /C exit /B %ERROR_CODE%

+ 0
- 109
Spring_Authorization_Server/src/main/java/com/tuoheng/config/SecurityConfig.java Целия файл

@@ -1,109 +0,0 @@
package com.tuoheng.config;

import com.tuoheng.service.OidcUserInfoService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
import org.springframework.security.oauth2.server.authorization.config.ProviderSettings;
import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcUserInfoAuthenticationContext;
import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcUserInfoAuthenticationToken;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.security.provisioning.JdbcUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.util.matcher.RequestMatcher;

import javax.sql.DataSource;
import java.util.function.Function;

/**
* @author chenjiandong
* @description: TODO
* @date 2022/9/22 14:58
*/
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
@RequiredArgsConstructor
public class SecurityConfig {

@Autowired
private DataSource dataSource;

@Bean
@Order(1)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfigurer<HttpSecurity> authorizationServerConfigurer =
new OAuth2AuthorizationServerConfigurer<>();

OidcUserInfoService oidcUserInfoService = new OidcUserInfoService();
//自定义用户映射器
Function<OidcUserInfoAuthenticationContext, OidcUserInfo> userInfoMapper = (context) -> {
OidcUserInfoAuthenticationToken authentication = context.getAuthentication();
JwtAuthenticationToken principal = (JwtAuthenticationToken) authentication.getPrincipal();
return oidcUserInfoService.loadUser(principal.getName(), context.getAccessToken().getScopes());
};
authorizationServerConfigurer.oidc((oidc) -> {
oidc.userInfoEndpoint((userInfo) -> userInfo.userInfoMapper(userInfoMapper));
});

RequestMatcher endpointsMatcher = authorizationServerConfigurer.getEndpointsMatcher();

return http.requestMatcher(endpointsMatcher)
//.userDetailsService(userService)
.authorizeRequests((authorizeRequests) -> {
((ExpressionUrlAuthorizationConfigurer.AuthorizedUrl) authorizeRequests.anyRequest()).authenticated();
}).csrf((csrf) -> {
csrf.ignoringRequestMatchers(new RequestMatcher[]{endpointsMatcher});
}).apply(authorizationServerConfigurer)
.and()
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
.exceptionHandling(exceptions -> exceptions.
authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")))
.apply(authorizationServerConfigurer)
.and()
.build();
}

@Bean
@Order(2)
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.antMatchers("/getHealth").permitAll()
.anyRequest().authenticated()
)
// Form login handles the redirect to the login page from the
// authorization server filter chain
.formLogin(Customizer.withDefaults());

return http.build();
}

@Bean
public UserDetailsService userDetailsService() {
// UserDetails userDetails = User.withDefaultPasswordEncoder()
// .username("admin")
// .password("123456")
// .roles("USER")
// .build();
//
// return new InMemoryUserDetailsManager(userDetails);
return new JdbcUserDetailsManager(dataSource);
}

@Bean
public ProviderSettings providerSettings() {
return ProviderSettings.builder().build();
}

}

+ 0
- 15
Spring_Authorization_Server/src/main/java/com/tuoheng/mapper/UserMapper.java Целия файл

@@ -1,15 +0,0 @@
package com.tuoheng.mapper;

import org.apache.ibatis.annotations.Mapper;

/**
* @author chenjiandong
* @description: TODO
* @date 2022/10/8 11:59
*/
@Mapper
public interface UserMapper {

int insertUser();

}

+ 0
- 21
Spring_Authorization_Server/src/main/java/com/tuoheng/model/param/CreateUserDto.java Целия файл

@@ -1,21 +0,0 @@
package com.tuoheng.model.param;

import lombok.Data;

import java.util.List;

/**
* @author chenjiandong
* @description: TODO
* @date 2022/10/8 11:30
*/
@Data
public class CreateUserDto {

private String username;

private String password;

private List<String> roles;

}

+ 0
- 32
Spring_Authorization_Server/src/main/java/com/tuoheng/service/impl/UserServiceImpl.java Целия файл

@@ -1,32 +0,0 @@
package com.tuoheng.service.impl;

import com.tuoheng.model.param.CreateUserDto;
import com.tuoheng.service.UserSevice;
import com.tuoheng.until.JsonResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.provisioning.UserDetailsManager;
import org.springframework.stereotype.Service;

/**
* @author chenjiandong
* @description: TODO
* @date 2022/10/8 11:35
*/
@Service
public class UserServiceImpl implements UserSevice {

public JsonResult createUser(CreateUserDto createUserDto){
// UserDetails userDetails = User.builder().passwordEncoder(s -> "{bcrypt}" + new BCryptPasswordEncoder().encode(s))
// .username("admin")
// .password("123456")
// .roles("ADMIN")
// .build();
// UserDetailsManager userDetailsManager = new UserDetailsManager();
// userDetailsManager.createUser(userDetails);
return JsonResult.success();
}

}

+ 0
- 6
Spring_Authorization_Server/src/main/resources/application-prod.yml Целия файл

@@ -1,6 +0,0 @@
spring:
security:
oauth2:
resource-server:
jwt:
issuer-uri: http://127.0.0.1:8090 #认证中心端点,作为资源端的配置

+ 0
- 6
Spring_Authorization_Server/src/main/resources/application-test.yml Целия файл

@@ -1,6 +0,0 @@
spring:
security:
oauth2:
resource-server:
jwt:
issuer-uri: http://127.0.0.1:8090 #认证中心端点,作为资源端的配置

+ 0
- 6
Spring_Authorization_Server/src/main/resources/application.yml Целия файл

@@ -1,6 +0,0 @@
server:
port: 8090

spring:
profiles:
active: @package.environment@

+ 0
- 10
Spring_Authorization_Server/src/main/resources/mapper/UserMapper.xml Целия файл

@@ -1,10 +0,0 @@
<?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.tuoheng.mapper.UserMapper">

<insert id="insertUser" parameterType="com.tuoheng.model.po.UserPo">
insert into users (username, password, enabled, client_id)
values (#{aName,jdbcType=VARCHAR}, #{aPass,jdbcType=VARCHAR})
</insert>

</mapper>

+ 0
- 13
Spring_Authorization_Server/src/test/java/com/tuoheng/SpringAuthorizationServerApplicationTests.java Целия файл

@@ -1,13 +0,0 @@
package com.tuoheng;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class SpringAuthorizationServerApplicationTests {

@Test
void contextLoads() {
}

}

+ 0
- 38
Spring_Authorization_Server/src/test/java/com/tuoheng/config/ApplicationTests.java Целия файл

@@ -1,38 +0,0 @@
package com.tuoheng.config;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.provisioning.UserDetailsManager;

/**
* @author chenjiandong
* @description: TODO
* @date 2022/10/8 11:05
*/
@SpringBootTest
class ApplicationTests {

/**
* 初始化客户端信息
*/
@Autowired
private UserDetailsManager userDetailsManager;

/**
* 创建用户信息
*/
@Test
void testSaveUser() {
UserDetails userDetails = User.builder().passwordEncoder(s -> "{bcrypt}" + new BCryptPasswordEncoder().encode(s))
.username("admin")
.password("123456")
.roles("ADMIN")
.build();
userDetailsManager.createUser(userDetails);
}

}

+ 3
- 1
others/To-Use.text Целия файл

@@ -3,10 +3,12 @@ Server端 项目
http://127.0.0.1:8090/oauth2/authorize?
client_id=tuoheng-dsp
&response_type=code
&scope=openid+profile+email
&scope=openid+profile
&redirect_uri=http://192.168.11.11:8086/home
&state=4991a0e66547452286dd56e0d9473a0e

&code_challenge=GoX2z51GyLtItvCxPY4fI0q4pzvOVhHy00xcFGQ20os&code_challenge_method=S256&response_mode=query

扩展PKCE协议:(&code_challenge=IHicvKyz0IM1do9-3n9QpHf9xVluBshdD1vCD77gV7s&code_challenge_method=S256&response_mode=fragment)

第二步:根据code获取token:

+ 6
- 1
pom.xml Целия файл

@@ -2,15 +2,19 @@
<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>
<packaging>pom</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<modules>
<module>tuoheng_oidc_server</module>
</modules>
<groupId>com.tuoheng</groupId>
<artifactId>tuoheng_oidc</artifactId>
<version>0.0.1-SNAPSHOT</version>
<version>1.0.0</version>
<name>tuoheng_oidc</name>
<description>tuoheng_oidc</description>
<properties>
@@ -27,6 +31,7 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

</dependencies>

<build>

Spring_Authorization_Server/pom.xml → tuoheng_oidc_server/pom.xml Целия файл

@@ -3,10 +3,9 @@
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>2.7.3</version>
<relativePath/> <!-- lookup parent from repository -->
<groupId>com.tuoheng</groupId>
<artifactId>tuoheng_oidc</artifactId>
<version>1.0.0</version>
</parent>
<groupId>com.tuoheng</groupId>
<artifactId>tuoheng_oidc_server</artifactId>
@@ -59,6 +58,11 @@
<artifactId>mysql-connector-java</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!--MyBatis整合SpringBoot框架的起步依赖-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
@@ -79,6 +83,29 @@
<version>1.2.9</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>

<!-- JSON 解析器和生成器 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>

</dependencies>

<!-- 构建环境变量 -->
@@ -116,7 +143,38 @@
</profile>
</profiles>

<!-- 环境变量构建 -->
<build>
<finalName>tuoheng_oidc_server</finalName>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/*.*</include>
</includes>
</resource>

<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.*</include>
</includes>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</resource>

<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<targetPath>WEB-INF/classes</targetPath>
<includes>
<include>**/*</include>
<include>application-${package.environment}.yml</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>

Spring_Authorization_Server/src/main/java/com/tuoheng/SpringAuthorizationServerApplication.java → tuoheng_oidc_server/src/main/java/com/tuoheng/SpringAuthorizationServerApplication.java Целия файл

@@ -7,7 +7,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
public class SpringAuthorizationServerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringAuthorizationServerApplication.class, args);
System.out.println("TuoHeng-Oidc启动成功~");
System.out.println("TuoHeng-Oidc-Server启动成功~");
}

}

Spring_Authorization_Server/src/main/java/com/tuoheng/config/AuthorizationServerConfig.java → tuoheng_oidc_server/src/main/java/com/tuoheng/config/AuthorizationServerConfig.java Целия файл


Spring_Authorization_Server/src/main/java/com/tuoheng/config/IdTokenCustomizerConfig.java → tuoheng_oidc_server/src/main/java/com/tuoheng/config/IdTokenCustomizerConfig.java Целия файл

@@ -3,10 +3,14 @@ package com.tuoheng.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.core.OAuth2TokenType;
import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;
import org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
@@ -20,12 +24,18 @@ public class IdTokenCustomizerConfig {
@Bean
public OAuth2TokenCustomizer<JwtEncodingContext> tokenCustomizer() {
return (context) -> {
if (OidcParameterNames.ID_TOKEN.equals(context.getTokenType().getValue())) {
if ("id_token".equals(context.getTokenType().getValue())) {
context.getClaims().claims(claims ->
claims.put("role", context.getPrincipal().getAuthorities()
.stream().map(GrantedAuthority::getAuthority)
.collect(Collectors.toSet())));
}
if ("access_token".equals(context.getTokenType().getValue())) {
context.getClaims().claims(claims ->
claims.put("scope", context.getPrincipal().getAuthorities()
.stream().map(GrantedAuthority::getAuthority)
.collect(Collectors.toSet())));
}
};
}
}

Spring_Authorization_Server/src/main/java/com/tuoheng/config/JWKSourceConfig.java → tuoheng_oidc_server/src/main/java/com/tuoheng/config/JWKSourceConfig.java Целия файл


Spring_Authorization_Server/src/main/java/com/tuoheng/config/MyCorsFilter.java → tuoheng_oidc_server/src/main/java/com/tuoheng/config/MyCorsFilter.java Целия файл


+ 174
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/config/SecurityConfig.java Целия файл

@@ -0,0 +1,174 @@
package com.tuoheng.config;

import com.tuoheng.oauth2.authentication.OAuth2ResourceOwnerPasswordAuthenticationConverter;
import com.tuoheng.handler.AccessDeniedHandler;
import com.tuoheng.mapper.UserMapper;
import com.tuoheng.model.dto.UserBaseInfoDto;
//import com.tuoheng.oauth2.authentication.OAuth2ResourceOwnerPasswordAuthenticationProvider;
import com.tuoheng.oauth2.authentication.OAuth2ResourceOwnerPasswordAuthenticationProvider;
import com.tuoheng.service.impl.OidcUserInfoServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.core.OAuth2Token;
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.config.ProviderSettings;
import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcUserInfoAuthenticationContext;
import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcUserInfoAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
import org.springframework.security.oauth2.server.authorization.web.authentication.DelegatingAuthenticationConverter;
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationCodeAuthenticationConverter;
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2ClientCredentialsAuthenticationConverter;
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2RefreshTokenAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.security.provisioning.JdbcUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.RequestMatcher;

import javax.sql.DataSource;
import java.util.Arrays;
import java.util.function.Function;

/**
* @author chenjiandong
* @description: TODO
* @date 2022/9/22 14:58
*/
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
@RequiredArgsConstructor
public class SecurityConfig {

@Autowired
private DataSource dataSource;

@Autowired
private UserMapper userMapper;

@Value("${oauth2.token.issuer}")
private String tokenIssuer;

@Bean
@Order(1)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfigurer<HttpSecurity> authorizationServerConfigurer =
new OAuth2AuthorizationServerConfigurer<>();

http.apply(authorizationServerConfigurer
.tokenEndpoint((tokenEndpoint) -> tokenEndpoint.accessTokenRequestConverter(new DelegatingAuthenticationConverter(
Arrays.asList(
new OAuth2AuthorizationCodeAuthenticationConverter(),
new OAuth2RefreshTokenAuthenticationConverter(),
new OAuth2ClientCredentialsAuthenticationConverter(),
new OAuth2ResourceOwnerPasswordAuthenticationConverter())))));

OidcUserInfoServiceImpl oidcUserInfoService = new OidcUserInfoServiceImpl();
//自定义用户映射器
Function<OidcUserInfoAuthenticationContext, OidcUserInfo> userInfoMapper = (context) -> {
OidcUserInfoAuthenticationToken authentication = context.getAuthentication();
JwtAuthenticationToken principal = (JwtAuthenticationToken) authentication.getPrincipal();
UserBaseInfoDto userBaseInfoDto = userMapper.getUserBaseInfo(principal.getName());
return oidcUserInfoService.loadUser(principal.getName(), context.getAccessToken().getScopes(), userBaseInfoDto);
};
authorizationServerConfigurer.oidc((oidc) -> {
oidc.userInfoEndpoint((userInfo) -> userInfo.userInfoMapper(userInfoMapper));
});

RequestMatcher endpointsMatcher = authorizationServerConfigurer.getEndpointsMatcher();

http.requestMatcher(endpointsMatcher)
.authorizeRequests((authorizeRequests) -> {
((ExpressionUrlAuthorizationConfigurer.AuthorizedUrl) authorizeRequests.anyRequest()).authenticated();
}).csrf((csrf) -> {
csrf.ignoringRequestMatchers(new RequestMatcher[]{endpointsMatcher});
}).apply(authorizationServerConfigurer)
.and()
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
.exceptionHandling(exceptions -> exceptions
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/toLogin"))
.accessDeniedHandler(new AccessDeniedHandler()))
//.authenticationEntryPoint(new AuthenticationEntryPoint()))
.apply(authorizationServerConfigurer);
SecurityFilterChain securityFilterChain = http.build();
addCustomOAuth2ResourceOwnerPasswordAuthenticationProvider(http);
return securityFilterChain;
}

@Bean
@Order(2)
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
//http.addFilterBefore(verifyCodeFilter, UsernamePasswordAuthenticationFilter.class);
http.addFilterAt(new VerifyCodeFilter(),UsernamePasswordAuthenticationFilter.class);
http.csrf().disable()
.authorizeHttpRequests((authorize) -> authorize
.antMatchers("/toLogin", "/getHealth", "/static/**", "/vercode").permitAll()
.antMatchers("/user/create","/user/getInfo").permitAll()
.anyRequest().authenticated()
)
// Form login handles the redirect to the login page from the
// authorization server filter chain
//.formLogin(Customizer.withDefaults());
.formLogin(form ->
form.loginPage("/toLogin")
.loginProcessingUrl("/login")
)
.logout()
.logoutSuccessUrl("/toLogout")
.and();

return http.build();
}

@Bean
public UserDetailsService userDetailsService() {

// UserDetails userDetails = User.withDefaultPasswordEncoder()
// .username("admin")
// .password("123456")
// .roles("ADMIN")
// .build();
// return new InMemoryUserDetailsManager(userDetails);

return new JdbcUserDetailsManager(dataSource);
}

@Bean
public ProviderSettings providerSettings() {
return ProviderSettings.builder().issuer(tokenIssuer).build();
}

/*@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}*/

@SuppressWarnings("unchecked")
private void addCustomOAuth2ResourceOwnerPasswordAuthenticationProvider(HttpSecurity http) {

AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
OAuth2AuthorizationService authorizationService = http.getSharedObject(OAuth2AuthorizationService.class);
OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator = http.getSharedObject(OAuth2TokenGenerator.class);

OAuth2ResourceOwnerPasswordAuthenticationProvider resourceOwnerPasswordAuthenticationProvider =
new OAuth2ResourceOwnerPasswordAuthenticationProvider(authenticationManager, authorizationService, tokenGenerator);

// This will add new authentication provider in the list of existing authentication providers.
http.authenticationProvider(resourceOwnerPasswordAuthenticationProvider);

}

}

+ 94
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/config/VerifyCodeFilter.java Целия файл

@@ -0,0 +1,94 @@
package com.tuoheng.config;

/**
* @author chenjiandong
* @description: TODO
* @date 2022/10/12 9:32
*/
import java.io.IOException;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.tuoheng.constants.CommonConstant;
import com.tuoheng.until.RedisUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.StringUtils;

public class VerifyCodeFilter extends AbstractAuthenticationProcessingFilter {

// 是否开启验证码功能
private boolean isOpenValidateCode = true;

public VerifyCodeFilter() {
super(new AntPathRequestMatcher("/login", "POST"));
SimpleUrlAuthenticationFailureHandler failedHandler = (SimpleUrlAuthenticationFailureHandler)getFailureHandler();
failedHandler.setDefaultFailureUrl("/toLogin?validerror");
}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res=(HttpServletResponse)response;

if (!requiresAuthentication(req, res)) {
chain.doFilter(request, response);
return;
}
if (isOpenValidateCode) {
if(!checkValidateCode(req, res))return;
}
chain.doFilter(request,response);
}

/**
* 覆盖授权验证方法,这里可以做一些自己需要的session设置操作
*/
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
return null;
}

protected boolean checkValidateCode(HttpServletRequest request,HttpServletResponse response) throws IOException, ServletException {
String validateCodeParameter = getValidateCodeParameter(request);
String codekey = getCodeKeyParameter(request);
if(RedisUtils.get(codekey) == null){
unsuccessfulAuthentication(request, response, new InsufficientAuthenticationException("验证码已过期"));
return false;
}
String redisValidateCode = RedisUtils.get(codekey).toString();
if(!StringUtils.isEmpty(validateCodeParameter) && "9527".equals(validateCodeParameter)){
return true;
}
if(StringUtils.isEmpty(validateCodeParameter) || StringUtils.isEmpty(redisValidateCode) || !redisValidateCode.equalsIgnoreCase(validateCodeParameter)){
unsuccessfulAuthentication(request, response, new InsufficientAuthenticationException("输入的验证码不正确"));
return false;
}
return true;
}

private String getValidateCodeParameter(HttpServletRequest request) {
Object obj = request.getParameter(CommonConstant.VALIDATE_CODE);
return null == obj ? "" : obj.toString();
}

private String getCodeKeyParameter(HttpServletRequest request) {
Object obj = request.getParameter(CommonConstant.CODEKDY);
return null == obj ? "" : obj.toString();
}

protected String obtainSessionValidateCode(HttpSession session) {
Object obj = session.getAttribute(CommonConstant.VALIDATE_CODE);
return null == obj ? "" : obj.toString();
}
}

+ 12
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/constants/CommonConstant.java Целия файл

@@ -0,0 +1,12 @@
package com.tuoheng.constants;

/**
* 安全配置常量
*/
public class CommonConstant {

public static final String VALIDATE_CODE = "validateCode";

public static final String CODEKDY = "codekey";

}

Spring_Authorization_Server/src/main/java/com/tuoheng/controller/HealthController.java → tuoheng_oidc_server/src/main/java/com/tuoheng/controller/HealthController.java Целия файл

@@ -1,5 +1,6 @@
package com.tuoheng.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@@ -9,10 +10,12 @@ import org.springframework.web.bind.annotation.RestController;
* @date 2022/9/28 9:55
*/
@RestController
@Slf4j
public class HealthController {

@GetMapping("/getHealth")
public String getHealth(){
log.info("oidc is ok");
return "oidc is ok";
}


+ 58
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/controller/Oauth2Controller.java Целия файл

@@ -0,0 +1,58 @@
package com.tuoheng.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.jaas.SecurityContextLoginModule;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
* @author chenjiandong
* @description: TODO
* @date 2022/10/9 14:57
*/
@Slf4j
@Controller
public class Oauth2Controller {

@GetMapping("toLogin")
public String login(@RequestParam(value = "error", required = false) String error,
@RequestParam(value = "validerror", required = false) String validerror,
@RequestParam(value = "expirecode", required = false) String expirecode,
@RequestParam(value = "logout", required = false) String logout, Model model) {
if (error != null) {
model.addAttribute("msg", "用户名或密码错误!");
}
if(validerror!=null){
model.addAttribute("msg", "验证码错误!");
}
if(validerror!=expirecode){
model.addAttribute("msg", "验证码已过期!");
}
if (logout != null) {
model.addAttribute("msg", "成功退出!");
}
return "login";

}

@GetMapping("/toLogout")
public void exit(HttpServletRequest request, HttpServletResponse response) {
new SecurityContextLogoutHandler().logout(request, null, null);
try {
System.out.println(request.getHeader("referer"));
response.sendRedirect(request.getHeader("referer"));
//response.sendRedirect("http://192.168.13.140:3000/home");
} catch (IOException e) {
e.printStackTrace();
}
}

}

Spring_Authorization_Server/src/main/java/com/tuoheng/controller/UserController.java → tuoheng_oidc_server/src/main/java/com/tuoheng/controller/UserController.java Целия файл

@@ -1,11 +1,11 @@
package com.tuoheng.controller;

import com.tuoheng.model.param.CreateUserDto;
import com.tuoheng.model.param.GetUserInfoDto;
import com.tuoheng.service.UserSevice;
import com.tuoheng.until.JsonResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

/**
@@ -21,8 +21,18 @@ public class UserController {
private UserSevice userSevice;

@PostMapping("/create")
public JsonResult createUser(@RequestBody @Validated CreateUserDto createUserDto){
public JsonResult createUser(@RequestBody CreateUserDto createUserDto){
return userSevice.createUser(createUserDto);
}

/**
* 小程序端获取用户信息端点
* @param getUserInfoDto
* @return
*/
@PostMapping("/getInfo")
public JsonResult getUserInfo(@RequestBody GetUserInfoDto getUserInfoDto){
return userSevice.getUserInfo(getUserInfoDto);
}

}

+ 59
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/controller/VerifyCodeController.java Целия файл

@@ -0,0 +1,59 @@
package com.tuoheng.controller;

import com.tuoheng.until.JsonResult;
import com.tuoheng.until.VerifyCode;
import com.tuoheng.until.VerifyUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
* @author chenjiandong
* @description: TODO
* @date 2022/10/11 17:08
*/
@RestController
@Slf4j
public class VerifyCodeController {

@Resource
private StringRedisTemplate stringRedisTemplate;

@PostMapping("/vercode")
public JsonResult code(HttpServletResponse response) throws IOException {
VerifyUtil verifyUtil = new VerifyUtil();
Map<String, String> result = new HashMap();
try {
String codeKey = UUID.randomUUID().toString();
response.setContentType("image/png");
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Expire", "0");
response.setHeader("Pragma", "no-cache");
// 返回base64
//写入redis缓存
Map<String, String> mapInfo = verifyUtil.getRandomCodeBase64();
String randomStr = mapInfo.get("randomStr");
stringRedisTemplate.opsForValue().set(codeKey, randomStr, 60, TimeUnit.SECONDS);
result.put("captcha", "data:image/png;base64," + mapInfo.get("img"));
result.put("codeKey", codeKey);
} catch (Exception e) {
log.error(e.getMessage());
return JsonResult.error(e.getMessage());
}
return JsonResult.success(result);
}

}

+ 38
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/handler/AccessDeniedHandler.java Целия файл

@@ -0,0 +1,38 @@
package com.tuoheng.handler;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
* @author chenjiandong
* @description: TODO
* @date 2022/10/10 11:55
*/

@Component
public class AccessDeniedHandler implements org.springframework.security.web.access.AccessDeniedHandler {

@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
response.setContentType("application/json;charset=UTF-8");
Map<String, Object> map = new HashMap<String, Object>();
map.put("code", 401);
map.put("msg", "权限不足");
map.put("data", accessDeniedException.getMessage());
map.put("success", false);
map.put("path", request.getServletPath());
map.put("timestamp", String.valueOf(new Date().getTime()));
ObjectMapper mapper = new ObjectMapper();
response.setContentType("application/json");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write(mapper.writeValueAsString(map));
}
}

+ 86
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/handler/AuthenticationEntryPoint.java Целия файл

@@ -0,0 +1,86 @@
package com.tuoheng.handler;


import com.alibaba.fastjson.serializer.SerializerFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.alibaba.fastjson.JSONObject;
import com.tuoheng.until.JsonResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.jwt.BadJwtException;
import org.springframework.security.oauth2.jwt.JwtValidationException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
* @author chenjiandong
* @description: TODO
* @date 2022/10/10 12:00
*/
@Slf4j
public class AuthenticationEntryPoint implements org.springframework.security.web.AuthenticationEntryPoint {

@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
if (response.isCommitted()){
return;
}

Throwable throwable = authException.fillInStackTrace();

String errorMessage = "认证失败";

if (throwable instanceof BadCredentialsException){
errorMessage = "错误的客户端信息";
}else {
Throwable cause = authException.getCause();

if (cause instanceof JwtValidationException) {
log.warn("JWT Token 过期,具体内容:" + cause.getMessage());
errorMessage = "无效的token信息";
} else if (cause instanceof BadJwtException){
log.warn("JWT 签名异常,具体内容:" + cause.getMessage());
errorMessage = "无效的token信息";
} else if (cause instanceof AccountExpiredException){
errorMessage = "账户已过期";
} else if (cause instanceof LockedException){
errorMessage = "账户已被锁定";
// } else if (cause instanceof InvalidClientException || cause instanceof BadClientCredentialsException){
// response.getWriter().write(JSON.toJSONString(SingleResultBundle.failed(401,"无效的客户端")));
// } else if (cause instanceof InvalidGrantException || cause instanceof RedirectMismatchException){
// response.getWriter().write(JSON.toJSONString(SingleResultBundle.failed("无效的类型")));
// } else if (cause instanceof UnauthorizedClientException) {
// response.getWriter().write(JSON.toJSONString(SingleResultBundle.failed("未经授权的客户端")));
} else if (throwable instanceof InsufficientAuthenticationException) {
String message = throwable.getMessage();
if (message.contains("Invalid token does not contain resource id")){
errorMessage = "未经授权的资源服务器";
}else if (message.contains("Full authentication is required to access this resource")){
errorMessage = "缺少验证信息";
}
}else {
errorMessage = "验证异常";
}
}

response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setCharacterEncoding("utf-8");
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
ObjectMapper objectMapper = new ObjectMapper();
JsonResult jsonResult = new JsonResult();
String resBody = objectMapper.writeValueAsString(JSONObject.toJSONString(jsonResult.error(401, errorMessage), SerializerFeature.WriteMapNullValue));
PrintWriter printWriter = response.getWriter();
printWriter.print(resBody);
printWriter.flush();
printWriter.close();
}
}

+ 18
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/mapper/AuthoritiesMapper.java Целия файл

@@ -0,0 +1,18 @@
package com.tuoheng.mapper;

import com.tuoheng.model.po.AuthoritiesPo;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

/**
* @author chenjiandong
* @description: TODO
* @date 2022/10/9 10:39
*/
@Mapper
public interface AuthoritiesMapper {

int batchInsert(List<AuthoritiesPo> list);

}

+ 22
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/mapper/UserMapper.java Целия файл

@@ -0,0 +1,22 @@
package com.tuoheng.mapper;

import com.tuoheng.model.dto.UserBaseInfoDto;
import com.tuoheng.model.po.UserPo;
import org.apache.ibatis.annotations.Mapper;

/**
* @author chenjiandong
* @description: TODO
* @date 2022/10/8 11:59
*/
@Mapper
public interface UserMapper {

int insertUser(UserPo userPo);

UserBaseInfoDto getUserBaseInfo(String username);

UserBaseInfoDto getMpUserInfo(String username);


}

+ 78
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/model/dto/JwtUser.java Целия файл

@@ -0,0 +1,78 @@
package com.tuoheng.model.dto;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;
import java.util.Collections;

/**
* @author chenjiandong
* @description: TODO
* @date 2022/10/10 10:20
*/
public class JwtUser implements UserDetails {

private Integer id;
private String username;
private String password;
private Collection<? extends GrantedAuthority> authorities;

public JwtUser() {
}

// 写一个能直接使用user创建jwtUser的构造器
public JwtUser(UserBaseInfoDto user) {
id = user.getUserId();
username = user.getUserName();
password = user.getPassword();
authorities = Collections.singleton(new SimpleGrantedAuthority(user.getAuthorityList().toString()));
}

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}

@Override
public String getPassword() {
return password;
}

@Override
public String getUsername() {
return username;
}

@Override
public boolean isAccountNonExpired() {
return true;
}

@Override
public boolean isAccountNonLocked() {
return true;
}

@Override
public boolean isCredentialsNonExpired() {
return true;
}

@Override
public boolean isEnabled() {
return true;
}

@Override
public String toString() {
return "JwtUser{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", authorities=" + authorities +
'}';
}

}

+ 23
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/model/dto/UserBaseInfoDto.java Целия файл

@@ -0,0 +1,23 @@
package com.tuoheng.model.dto;

import lombok.Data;

import java.util.List;

/**
* @author chenjiandong
* @description: TODO
* @date 2022/10/9 13:46
*/
@Data
public class UserBaseInfoDto {

private Integer userId;

private String userName;

private String password;

private List<String> authorityList;

}

+ 26
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/model/param/CreateUserDto.java Целия файл

@@ -0,0 +1,26 @@
package com.tuoheng.model.param;

import lombok.Data;

import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.List;

/**
* @author chenjiandong
* @description: TODO
* @date 2022/10/8 11:30
*/
@Data
public class CreateUserDto {

@NotEmpty(message = "username can not be empty!")
private String username;

@NotEmpty(message = "password can not be empty!")
private String password;

@NotNull(message = "authorities can not be null!")
private List<String> authorities;

}

+ 23
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/model/param/GetUserInfoDto.java Целия файл

@@ -0,0 +1,23 @@
package com.tuoheng.model.param;

import lombok.Data;

import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.List;

/**
* @author chenjiandong
* @description: TODO
* @date 2022/10/8 11:30
*/
@Data
public class GetUserInfoDto {

@NotEmpty(message = "username can not be empty!")
private String username;

@NotEmpty(message = "token can not be empty!")
private String token;

}

+ 23
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/model/po/AuthoritiesPo.java Целия файл

@@ -0,0 +1,23 @@
package com.tuoheng.model.po;

import lombok.Data;
import lombok.experimental.Accessors;

/**
* @author chenjiandong
* @description: TODO
* @date 2022/10/9 10:20
*/
@Data
@Accessors(chain = true)
public class AuthoritiesPo {

private Integer id;

private Integer userId;

private String username;

private String authority;

}

Spring_Authorization_Server/src/main/java/com/tuoheng/model/po/UserPo.java → tuoheng_oidc_server/src/main/java/com/tuoheng/model/po/UserPo.java Целия файл

@@ -1,6 +1,7 @@
package com.tuoheng.model.po;

import lombok.Data;
import lombok.experimental.Accessors;

/**
* @author chenjiandong
@@ -8,6 +9,7 @@ import lombok.Data;
* @date 2022/10/8 12:07
*/
@Data
@Accessors(chain = true)
public class UserPo {

private Integer id;
@@ -18,6 +20,4 @@ public class UserPo {

private Integer enabled;

private String clientId;

}

+ 69
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/oauth2/authentication/OAuth2EndpointUtils.java Целия файл

@@ -0,0 +1,69 @@
package com.tuoheng.oauth2.authentication;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.core.endpoint.PkceParameterNames;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

/**
* Utility methods for the OAuth 2.0 Protocol Endpoints.
*
* @author Joe Grandja
* @since 0.1.2
*/
final class OAuth2EndpointUtils {
static final String ACCESS_TOKEN_REQUEST_ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2";

private OAuth2EndpointUtils() {
}

static MultiValueMap<String, String> getParameters(HttpServletRequest request) {
Map<String, String[]> parameterMap = request.getParameterMap();
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>(parameterMap.size());
parameterMap.forEach((key, values) -> {
if (values.length > 0) {
for (String value : values) {
parameters.add(key, value);
}
}
});
return parameters;
}

static Map<String, Object> getParametersIfMatchesAuthorizationCodeGrantRequest(HttpServletRequest request, String... exclusions) {
if (!matchesAuthorizationCodeGrantRequest(request)) {
return Collections.emptyMap();
}
Map<String, Object> parameters = new HashMap<>(getParameters(request).toSingleValueMap());
for (String exclusion : exclusions) {
parameters.remove(exclusion);
}
return parameters;
}

static boolean matchesAuthorizationCodeGrantRequest(HttpServletRequest request) {
return AuthorizationGrantType.AUTHORIZATION_CODE.getValue().equals(
request.getParameter(OAuth2ParameterNames.GRANT_TYPE)) &&
request.getParameter(OAuth2ParameterNames.CODE) != null;
}

static boolean matchesPkceTokenRequest(HttpServletRequest request) {
return matchesAuthorizationCodeGrantRequest(request) &&
request.getParameter(PkceParameterNames.CODE_VERIFIER) != null;
}

static void throwError(String errorCode, String parameterName, String errorUri) {
OAuth2Error error = new OAuth2Error(errorCode, "OAuth 2.0 Parameter: " + parameterName, errorUri);
throw new OAuth2AuthenticationException(error);
}

}

+ 89
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/oauth2/authentication/OAuth2ResourceOwnerPasswordAuthenticationConverter.java Целия файл

@@ -0,0 +1,89 @@
package com.tuoheng.oauth2.authentication;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;

import org.springframework.lang.Nullable;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;

public class OAuth2ResourceOwnerPasswordAuthenticationConverter implements AuthenticationConverter {

@Nullable
@Override
public Authentication convert(HttpServletRequest request) {
// grant_type (REQUIRED)
String grantType = request.getParameter(OAuth2ParameterNames.GRANT_TYPE);
if (!AuthorizationGrantType.PASSWORD.getValue().equals(grantType)) {
return null;
}

MultiValueMap<String, String> parameters = OAuth2EndpointUtils.getParameters(request);

// scope (OPTIONAL)
String scope = parameters.getFirst(OAuth2ParameterNames.SCOPE);
if (StringUtils.hasText(scope) &&
parameters.get(OAuth2ParameterNames.SCOPE).size() != 1) {
OAuth2EndpointUtils.throwError(
OAuth2ErrorCodes.INVALID_REQUEST,
OAuth2ParameterNames.SCOPE,
OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);
}
Set<String> requestedScopes = null;
if (StringUtils.hasText(scope)) {
requestedScopes = new HashSet<>(
Arrays.asList(StringUtils.delimitedListToStringArray(scope, " ")));
}
// username (REQUIRED)
String username = parameters.getFirst(OAuth2ParameterNames.USERNAME);
if (!StringUtils.hasText(username) || parameters.get(OAuth2ParameterNames.USERNAME).size() != 1) {
OAuth2EndpointUtils.throwError(
OAuth2ErrorCodes.INVALID_REQUEST,
OAuth2ParameterNames.USERNAME,
OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);
}
// password (REQUIRED)
String password = parameters.getFirst(OAuth2ParameterNames.PASSWORD);
if (!StringUtils.hasText(password) || parameters.get(OAuth2ParameterNames.PASSWORD).size() != 1) {
OAuth2EndpointUtils.throwError(
OAuth2ErrorCodes.INVALID_REQUEST,
OAuth2ParameterNames.PASSWORD,
OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);
}
Authentication clientPrincipal = SecurityContextHolder.getContext().getAuthentication();
if (clientPrincipal == null) {
OAuth2EndpointUtils.throwError(
OAuth2ErrorCodes.INVALID_REQUEST,
OAuth2ErrorCodes.INVALID_CLIENT,
OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI);
}
Map<String, Object> additionalParameters = parameters
.entrySet()
.stream()
.filter(e -> !e.getKey().equals(OAuth2ParameterNames.GRANT_TYPE) &&
!e.getKey().equals(OAuth2ParameterNames.SCOPE))
.collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().get(0)));
return new OAuth2ResourceOwnerPasswordAuthenticationToken(
AuthorizationGrantType.PASSWORD, clientPrincipal, requestedScopes, additionalParameters);

}
}

+ 186
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/oauth2/authentication/OAuth2ResourceOwnerPasswordAuthenticationProvider.java Целия файл

@@ -0,0 +1,186 @@
package com.tuoheng.oauth2.authentication;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.core.*;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.context.ProviderContextHolder;
import org.springframework.security.oauth2.server.authorization.token.DefaultOAuth2TokenContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.security.Principal;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

public class OAuth2ResourceOwnerPasswordAuthenticationProvider implements AuthenticationProvider {

private static final Logger LOGGER = LogManager.getLogger();

private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2";
private final AuthenticationManager authenticationManager;
private final OAuth2AuthorizationService authorizationService;
private final OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator;

/**
* Constructs an {@code OAuth2ResourceOwnerPasswordAuthenticationProviderNew} using the provided parameters.
*
* @param authenticationManager the authentication manager
* @param authorizationService the authorization service
* @param tokenGenerator the token generator
* @since 0.2.3
*/
public OAuth2ResourceOwnerPasswordAuthenticationProvider(AuthenticationManager authenticationManager,
OAuth2AuthorizationService authorizationService, OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator) {
Assert.notNull(authorizationService, "authorizationService cannot be null");
Assert.notNull(tokenGenerator, "tokenGenerator cannot be null");
this.authenticationManager = authenticationManager;
this.authorizationService = authorizationService;
this.tokenGenerator = tokenGenerator;
}

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {

OAuth2ResourceOwnerPasswordAuthenticationToken resouceOwnerPasswordAuthentication = (OAuth2ResourceOwnerPasswordAuthenticationToken) authentication;

OAuth2ClientAuthenticationToken clientPrincipal = getAuthenticatedClientElseThrowInvalidClient(resouceOwnerPasswordAuthentication);

RegisteredClient registeredClient = clientPrincipal.getRegisteredClient();

if (!registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.PASSWORD)) {
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT);
}

Authentication usernamePasswordAuthentication = getUsernamePasswordAuthentication(resouceOwnerPasswordAuthentication);

Set<String> authorizedScopes = registeredClient.getScopes(); // Default to configured scopes
Set<String> requestedScopes = resouceOwnerPasswordAuthentication.getScopes();
if (!CollectionUtils.isEmpty(requestedScopes)) {
Set<String> unauthorizedScopes = requestedScopes.stream()
.filter(requestedScope -> !registeredClient.getScopes().contains(requestedScope))
.collect(Collectors.toSet());
if (!CollectionUtils.isEmpty(unauthorizedScopes)) {
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_SCOPE);
}

authorizedScopes = new LinkedHashSet<>(requestedScopes);
}

// @formatter:off
DefaultOAuth2TokenContext.Builder tokenContextBuilder = DefaultOAuth2TokenContext.builder()
.registeredClient(registeredClient)
.principal(usernamePasswordAuthentication)
.providerContext(ProviderContextHolder.getProviderContext())
.authorizedScopes(authorizedScopes)
.authorizationGrantType(AuthorizationGrantType.PASSWORD)
.authorizationGrant(resouceOwnerPasswordAuthentication);
// @formatter:on

// ----- Access token -----
OAuth2TokenContext tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.ACCESS_TOKEN).build();
OAuth2Token generatedAccessToken = this.tokenGenerator.generate(tokenContext);
if (generatedAccessToken == null) {
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,
"The token generator failed to generate the access token.", ERROR_URI);
throw new OAuth2AuthenticationException(error);
}

OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,
generatedAccessToken.getTokenValue(), generatedAccessToken.getIssuedAt(),
generatedAccessToken.getExpiresAt(), tokenContext.getAuthorizedScopes());

// ----- Refresh token -----
OAuth2RefreshToken refreshToken = null;
if (registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.REFRESH_TOKEN) &&
// Do not issue refresh token to public client
!clientPrincipal.getClientAuthenticationMethod().equals(ClientAuthenticationMethod.NONE)) {

tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.REFRESH_TOKEN).build();
OAuth2Token generatedRefreshToken = this.tokenGenerator.generate(tokenContext);
if (!(generatedRefreshToken instanceof OAuth2RefreshToken)) {
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,
"The token generator failed to generate the refresh token.", ERROR_URI);
throw new OAuth2AuthenticationException(error);
}
refreshToken = (OAuth2RefreshToken) generatedRefreshToken;

}

// @formatter:off
OAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization.withRegisteredClient(registeredClient)
.principalName(usernamePasswordAuthentication.getName())
.authorizationGrantType(AuthorizationGrantType.PASSWORD)
.attribute(OAuth2Authorization.AUTHORIZED_SCOPE_ATTRIBUTE_NAME, authorizedScopes)
.attribute(Principal.class.getName(), usernamePasswordAuthentication);
// @formatter:on
if (generatedAccessToken instanceof ClaimAccessor) {
authorizationBuilder.token(accessToken, (metadata) ->
metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, ((ClaimAccessor) generatedAccessToken).getClaims()));
} else {
authorizationBuilder.accessToken(accessToken);
}

OAuth2Authorization authorization = authorizationBuilder.build();

this.authorizationService.save(authorization);

LOGGER.debug("OAuth2Authorization saved successfully");

Map<String, Object> additionalParameters = Collections.emptyMap();

LOGGER.debug("returning OAuth2AccessTokenAuthenticationToken");

return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken, refreshToken, additionalParameters);
}

@Override
public boolean supports(Class<?> authentication) {
return OAuth2ResourceOwnerPasswordAuthenticationToken.class.isAssignableFrom(authentication);
}

private Authentication getUsernamePasswordAuthentication(OAuth2ResourceOwnerPasswordAuthenticationToken resouceOwnerPasswordAuthentication) {

Map<String, Object> additionalParameters = resouceOwnerPasswordAuthentication.getAdditionalParameters();

String username = (String) additionalParameters.get(OAuth2ParameterNames.USERNAME);
String password = (String) additionalParameters.get(OAuth2ParameterNames.PASSWORD);

UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(username, password);
LOGGER.debug("got usernamePasswordAuthenticationToken=" + usernamePasswordAuthenticationToken);

Authentication usernamePasswordAuthentication = authenticationManager.authenticate(usernamePasswordAuthenticationToken);
return usernamePasswordAuthentication;
}

private OAuth2ClientAuthenticationToken getAuthenticatedClientElseThrowInvalidClient(Authentication authentication) {

OAuth2ClientAuthenticationToken clientPrincipal = null;

if (OAuth2ClientAuthenticationToken.class.isAssignableFrom(authentication.getPrincipal().getClass())) {
clientPrincipal = (OAuth2ClientAuthenticationToken) authentication.getPrincipal();
}

if (clientPrincipal != null && clientPrincipal.isAuthenticated()) {
return clientPrincipal;
}

throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_CLIENT);
}

}

+ 75
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/oauth2/authentication/OAuth2ResourceOwnerPasswordAuthenticationToken.java Целия файл

@@ -0,0 +1,75 @@
package com.tuoheng.oauth2.authentication;

import org.springframework.lang.Nullable;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationGrantAuthenticationToken;
import org.springframework.util.Assert;

import java.util.*;

public class OAuth2ResourceOwnerPasswordAuthenticationToken extends AbstractAuthenticationToken {

private static final long serialVersionUID = -6067207202119450764L;

private final AuthorizationGrantType authorizationGrantType;
private final Authentication clientPrincipal;
private final Set<String> scopes;
private final Map<String, Object> additionalParameters;

/**
* Constructs an {@code OAuth2ClientCredentialsAuthenticationToken} using the provided parameters.
*
* @param clientPrincipal the authenticated client principal
*/

public OAuth2ResourceOwnerPasswordAuthenticationToken(AuthorizationGrantType authorizationGrantType,
Authentication clientPrincipal, @Nullable Set<String> scopes, @Nullable Map<String, Object> additionalParameters) {
super(Collections.emptyList());
Assert.notNull(authorizationGrantType, "authorizationGrantType cannot be null");
Assert.notNull(clientPrincipal, "clientPrincipal cannot be null");
this.authorizationGrantType = authorizationGrantType;
this.clientPrincipal = clientPrincipal;
this.scopes = Collections.unmodifiableSet(scopes != null ? new HashSet<>(scopes) : Collections.emptySet());
this.additionalParameters = Collections.unmodifiableMap(additionalParameters != null ? new HashMap<>(additionalParameters) : Collections.emptyMap());
}

/**
* Returns the authorization grant type.
*
* @return the authorization grant type
*/
public AuthorizationGrantType getGrantType() {
return this.authorizationGrantType;
}

@Override
public Object getPrincipal() {
return this.clientPrincipal;
}

@Override
public Object getCredentials() {
return "";
}

/**
* Returns the requested scope(s).
*
* @return the requested scope(s), or an empty {@code Set} if not available
*/
public Set<String> getScopes() {
return this.scopes;
}

/**
* Returns the additional parameters.
*
* @return the additional parameters
*/
public Map<String, Object> getAdditionalParameters() {
return this.additionalParameters;
}
}

+ 16
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/service/OidcUserInfoService.java Целия файл

@@ -0,0 +1,16 @@
package com.tuoheng.service;
import com.tuoheng.model.dto.UserBaseInfoDto;
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;

import java.util.Set;

/**
* @author chenjiandong
* @description: TODO
* @date 2022/9/29 10:17
*/
public interface OidcUserInfoService {

OidcUserInfo loadUser(String name, Set<String> scopes, UserBaseInfoDto userBaseInfoDto);

}

Spring_Authorization_Server/src/main/java/com/tuoheng/service/UserSevice.java → tuoheng_oidc_server/src/main/java/com/tuoheng/service/UserSevice.java Целия файл

@@ -1,6 +1,7 @@
package com.tuoheng.service;

import com.tuoheng.model.param.CreateUserDto;
import com.tuoheng.model.param.GetUserInfoDto;
import com.tuoheng.until.JsonResult;
/**
* @author chenjiandong
@@ -11,4 +12,6 @@ public interface UserSevice {

JsonResult createUser(CreateUserDto createUserDto);

JsonResult getUserInfo(GetUserInfoDto getUserInfoDto);

}

Spring_Authorization_Server/src/main/java/com/tuoheng/service/OidcUserInfoService.java → tuoheng_oidc_server/src/main/java/com/tuoheng/service/impl/OidcUserInfoServiceImpl.java Целия файл

@@ -1,42 +1,39 @@
package com.tuoheng.service;
package com.tuoheng.service.impl;

import com.nimbusds.jose.shaded.json.JSONObject;
import com.tuoheng.mapper.UserMapper;
import com.tuoheng.model.dto.UserBaseInfoDto;
import com.tuoheng.service.OidcUserInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Collections;
import java.util.Set;

/**
* @author chenjiandong
* @description: TODO
* @date 2022/9/29 10:17
* @date 2022/10/9 13:40
*/
public class OidcUserInfoService {
@Service
public class OidcUserInfoServiceImpl implements OidcUserInfoService {

public OidcUserInfo loadUser(String name, Set<String> scopes) {
@Override
public OidcUserInfo loadUser(String name, Set<String> scopes, UserBaseInfoDto userBaseInfoDto) {
OidcUserInfo.Builder builder = OidcUserInfo.builder().subject(name);
if (!CollectionUtils.isEmpty(scopes)) {
if (scopes.contains(OidcScopes.PROFILE)) {
builder.name("First Last")
.givenName("First")
.familyName("Last")
.middleName("Middle")
.nickname("User")
.preferredUsername(name)
.profile("http://127.0.0.1:8080/" + name)
.picture("http://127.0.0.1:8080/" + name + ".jpg")
.website("http://127.0.0.1:8080/")
.gender("female")
.birthdate("2022-05-24")
.zoneinfo("China/Beijing")
.locale("zh-cn")
.updatedAt(LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE));
builder.claim("userId", userBaseInfoDto.getUserId())
.claim("userName", userBaseInfoDto.getUserName())
.claim("authority", userBaseInfoDto.getAuthorityList());
}
if (scopes.contains(OidcScopes.EMAIL)) {
/*if (scopes.contains(OidcScopes.EMAIL)) {
builder.email(name + "@163.com").emailVerified(true);
}
if (scopes.contains(OidcScopes.ADDRESS)) {
@@ -46,8 +43,9 @@ public class OidcUserInfoService {
}
if (scopes.contains(OidcScopes.PHONE)) {
builder.phoneNumber("13728903134").phoneNumberVerified("false");
}
}*/
}
return builder.build();
}
}

}

+ 69
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/service/impl/UserServiceImpl.java Целия файл

@@ -0,0 +1,69 @@
package com.tuoheng.service.impl;

import com.tuoheng.mapper.AuthoritiesMapper;
import com.tuoheng.mapper.UserMapper;
import com.tuoheng.model.dto.UserBaseInfoDto;
import com.tuoheng.model.param.CreateUserDto;
import com.tuoheng.model.param.GetUserInfoDto;
import com.tuoheng.model.po.AuthoritiesPo;
import com.tuoheng.model.po.UserPo;
import com.tuoheng.service.UserSevice;
import com.tuoheng.until.JsonResult;
import org.apache.tomcat.util.buf.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcUserInfoAuthenticationToken;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.security.provisioning.UserDetailsManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.thymeleaf.expression.Lists;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
* @author chenjiandong
* @description: TODO
* @date 2022/10/8 11:35
*/
@Service
public class UserServiceImpl implements UserSevice {

@Autowired
private UserMapper userMapper;

@Autowired
private AuthoritiesMapper authoritiesMapper;

@Transactional(rollbackFor = Exception.class)
public JsonResult createUser(CreateUserDto createUserDto){
UserPo userPo = new UserPo()
.setUsername(createUserDto.getUsername())
.setPassword("{bcrypt}" + new BCryptPasswordEncoder().encode(createUserDto.getPassword()));
userMapper.insertUser(userPo);

List<AuthoritiesPo> authoritiesPos = new ArrayList<>();
for(String authority : createUserDto.getAuthorities()){
AuthoritiesPo authoritiesPo = new AuthoritiesPo()
.setUserId(userPo.getId())
.setUsername(createUserDto.getUsername())
.setAuthority(authority);
authoritiesPos.add(authoritiesPo);
}
authoritiesMapper.batchInsert(authoritiesPos);
return JsonResult.success(userPo.getId());
}

@Override
public JsonResult getUserInfo(GetUserInfoDto getUserInfoDto){
UserBaseInfoDto userBaseInfoDto = userMapper.getMpUserInfo(getUserInfoDto.getUsername());
return JsonResult.success(userBaseInfoDto);
}


}

Spring_Authorization_Server/src/main/java/com/tuoheng/until/CryptoUtil.java → tuoheng_oidc_server/src/main/java/com/tuoheng/until/CryptoUtil.java Целия файл


Spring_Authorization_Server/src/main/java/com/tuoheng/until/JsonResult.java → tuoheng_oidc_server/src/main/java/com/tuoheng/until/JsonResult.java Целия файл


+ 177
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/until/RedisUtils.java Целия файл

@@ -0,0 +1,177 @@
package com.tuoheng.until;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
* @author chenjiandong
* @description: TODO
* @date 2022/10/12 13:51
*/
@Component
public class RedisUtils {

@Autowired
private StringRedisTemplate redisTemplate;

private static RedisUtils redisUtils;

/**
* 初始化
*/
@PostConstruct
public void init() {
redisUtils = this;
redisUtils.redisTemplate = this.redisTemplate;
}

/**
* 查询key,支持模糊查询
*
* @param key
*/
public static Set<String> keys(String key) {
return redisUtils.redisTemplate.keys(key);
}

/**
* 获取值
*
* @param key
*/
public static Object get(String key) {
return redisUtils.redisTemplate.opsForValue().get(key);
}

/**
* 设置值
*
* @param key
* @param value
*/
public static void set(String key, String value) {
redisUtils.redisTemplate.opsForValue().set(key, value);
}

/**
* 设置值,并设置过期时间
*
* @param key
* @param value
* @param expire 过期时间,单位秒
*/
public static void set(String key, String value, Integer expire) {
redisUtils.redisTemplate.opsForValue().set(key, value, expire, TimeUnit.SECONDS);
}

/**
* 删出key
*
* @param key
*/
public static void delete(String key) {
redisUtils.redisTemplate.opsForValue().getOperations().delete(key);
}

/**
* 设置对象
*
* @param key key
* @param hashKey hashKey
* @param object 对象
*/
public static void hset(String key, String hashKey, Object object) {
redisUtils.redisTemplate.opsForHash().put(key, hashKey, object);
}

/**
* 设置对象
*
* @param key key
* @param hashKey hashKey
* @param object 对象
* @param expire 过期时间,单位秒
*/
public static void hset(String key, String hashKey, Object object, Integer expire) {
redisUtils.redisTemplate.opsForHash().put(key, hashKey, object);
redisUtils.redisTemplate.expire(key, expire, TimeUnit.SECONDS);
}

/**
* 设置HashMap
*
* @param key key
* @param map map值
*/
public static void hset(String key, HashMap<String, Object> map) {
redisUtils.redisTemplate.opsForHash().putAll(key, map);
}

/**
* key不存在时设置值
*
* @param key
* @param hashKey
* @param object
*/
public static void hsetAbsent(String key, String hashKey, Object object) {
redisUtils.redisTemplate.opsForHash().putIfAbsent(key, hashKey, object);
}

/**
* 获取Hash值
*
* @param key
* @param hashKey
* @return
*/
public static Object hget(String key, String hashKey) {
return redisUtils.redisTemplate.opsForHash().get(key, hashKey);
}

/**
* 获取key的所有值
*
* @param key
* @return
*/
public static Object hget(String key) {
return redisUtils.redisTemplate.opsForHash().entries(key);
}

/**
* 删除key的所有值
*
* @param key
*/
public static void deleteKey(String key) {
redisUtils.redisTemplate.opsForHash().getOperations().delete(key);
}

/**
* 判断key下是否有值
*
* @param key
*/
public static Boolean hasKey(String key) {
return redisUtils.redisTemplate.opsForHash().getOperations().hasKey(key);
}

/**
* 判断key和hasKey下是否有值
*
* @param key
* @param hasKey
*/
public static Boolean hasKey(String key, String hasKey) {
return redisUtils.redisTemplate.opsForHash().hasKey(key, hasKey);
}

}


Spring_Authorization_Server/src/main/java/com/tuoheng/until/RegisteredClientUtil.java → tuoheng_oidc_server/src/main/java/com/tuoheng/until/RegisteredClientUtil.java Целия файл

@@ -1,5 +1,6 @@
package com.tuoheng.until;

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.OAuth2TokenFormat;
@@ -24,6 +25,7 @@ public class RegisteredClientUtil {
.clientId("tuoheng-hhz")
.clientName("tuoheng-hhz-client")
.clientSecret("{noop}" + CryptoUtil.genAesSecret())
//.clientSecret("{bcrypt}" + new BCryptPasswordEncoder().encode("secret"))
.clientAuthenticationMethods(s -> {
s.add(ClientAuthenticationMethod.CLIENT_SECRET_POST);
s.add(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);
@@ -33,9 +35,8 @@ public class RegisteredClientUtil {
.redirectUri("http://www.baidu.com")
.scope(OidcScopes.OPENID)
.scope(OidcScopes.PROFILE)
.scope(OidcScopes.EMAIL)
.clientSettings(ClientSettings.builder()
.requireAuthorizationConsent(true)
.requireAuthorizationConsent(false)
.requireProofKey(false)
.build())
.tokenSettings(TokenSettings.builder()

+ 114
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/until/VerifyCode.java Целия файл

@@ -0,0 +1,114 @@
package com.tuoheng.until;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Random;

/**
* @author chenjiandong
* @description: TODO
* @date 2022/10/11 17:07
*/
public class VerifyCode {

private int width = 100;// 生成验证码图片的宽度
private int height = 50;// 生成验证码图片的高度
private String[] fontNames = { "宋体", "楷体", "隶书", "微软雅黑" };
private Color bgColor = new Color(255, 255, 255);// 定义验证码图片的背景颜色为白色
private Random random = new Random();
private String codes = "123456789ABCDEFGHIJKLMNPQRSTUVWXYZ";
private String text;// 记录随机字符串

/**
* 获取一个随意颜色
*
* @return
*/
private Color randomColor() {
int red = random.nextInt(150);
int green = random.nextInt(150);
int blue = random.nextInt(150);
return new Color(red, green, blue);
}

/**
* 获取一个随机字体
*
* @return
*/
private Font randomFont() {
String name = fontNames[random.nextInt(fontNames.length)];
int style = random.nextInt(4);
int size = random.nextInt(5) + 24;
return new Font(name, style, size);
}

/**
* 获取一个随机字符
*
* @return
*/
private char randomChar() {
return codes.charAt(random.nextInt(codes.length()));
}

/**
* 创建一个空白的BufferedImage对象
*
* @return
*/
private BufferedImage createImage() {
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = (Graphics2D) image.getGraphics();
g2.setColor(bgColor);// 设置验证码图片的背景颜色
g2.fillRect(0, 0, width, height);
return image;
}

public BufferedImage getImage() {
BufferedImage image = createImage();
Graphics2D g2 = (Graphics2D) image.getGraphics();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 4; i++) {
String s = randomChar() + "";
sb.append(s);
g2.setColor(randomColor());
g2.setFont(randomFont());
float x = i * width * 1.0f / 4;
g2.drawString(s, x, height - 15);
}
this.text = sb.toString();
drawLine(image);
return image;
}

/**
* 绘制干扰线
*
* @param image
*/
private void drawLine(BufferedImage image) {
Graphics2D g2 = (Graphics2D) image.getGraphics();
int num = 5;
for (int i = 0; i < num; i++) {
int x1 = random.nextInt(width);
int y1 = random.nextInt(height);
int x2 = random.nextInt(width);
int y2 = random.nextInt(height);
g2.setColor(randomColor());
g2.setStroke(new BasicStroke(1.5f));
g2.drawLine(x1, y1, x2, y2);
}
}

public String getText() {
return text;
}

public static void output(BufferedImage image, OutputStream out) throws IOException {
ImageIO.write(image, "JPEG", out);
}
}

+ 186
- 0
tuoheng_oidc_server/src/main/java/com/tuoheng/until/VerifyUtil.java Целия файл

@@ -0,0 +1,186 @@
package com.tuoheng.until;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

/**
* 获取验证码工具类
*/
public class VerifyUtil {

private static Random random = new Random();
/**
* 验证码的宽
*/
private int width = 165;
/**
* 验证码的高
*/
private int height = 45;
/**
* 验证码中夹杂的干扰线数量
*/
private int lineSize = 30;
/**
* 验证码字符个数
*/
private int randomStrNum = 4;

private String randomString = "123456789ABCDEFHKLMNQRSTWXY";

/**
* 字体的设置
*
* @return
*/
private Font getFont() {
return new Font("Times New Roman", Font.ROMAN_BASELINE, 40);
}

/**
* 颜色的设置
*
* @param fc
* @param bc
* @return
*/
private static Color getRandomColor(int fc, int bc) {

fc = Math.min(fc, 255);
bc = Math.min(bc, 255);

int r = fc + random.nextInt(bc - fc - 16);
int g = fc + random.nextInt(bc - fc - 14);
int b = fc + random.nextInt(bc - fc - 12);

return new Color(r, g, b);
}

/**
* 干扰线的绘制
*
* @param g
*/
private void drawLine(Graphics g) {
int x = random.nextInt(width);
int y = random.nextInt(height);
int xl = random.nextInt(20);
int yl = random.nextInt(10);
g.drawLine(x, y, x + xl, y + yl);

}

/**
* 随机字符的获取
*
* @param num
* @return
*/
private String getRandomString(int num) {
num = num > 0 ? num : randomString.length();
return String.valueOf(randomString.charAt(random.nextInt(num)));
}

/**
* 字符串的绘制
*
* @param g
* @param randomStr
* @param i
* @return
*/
private String drawString(Graphics g, String randomStr, int i) {
g.setFont(getFont());
g.setColor(getRandomColor(108, 190));
String rand = getRandomString(random.nextInt(randomString.length()));
randomStr += rand;
g.translate(random.nextInt(3), random.nextInt(6));
g.drawString(rand, 40 * i + 10, 25);
return randomStr;
}


/**
* 生成随机图片
*
* @param response
*/
public void getRandomCodeImage(HttpServletResponse response) {
// BufferedImage类是具有缓冲区的Image类,Image类是用于描述图像信息的类
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
Graphics g = image.getGraphics();
g.fillRect(0, 0, width, height);
g.setColor(getRandomColor(105, 189));
g.setFont(getFont());
// 干扰线
for (int i = 0; i < lineSize; i++) {
drawLine(g);
}
// 随机字符
String randomStr = "";
for (int i = 0; i < randomStrNum; i++) {
randomStr = drawString(g, randomStr, i);
}
System.out.println("随机字符:" + randomStr);
g.dispose();
try {
// 将图片以png格式返回,返回的是图片
ImageIO.write(image, "PNG", response.getOutputStream());

} catch (Exception e) {
e.printStackTrace();
}
}

/**
* 生成随机图片的base64编码字符串
*
* @return
*/
public Map<String, String> getRandomCodeBase64() {
Map<String, String> result = new HashMap<>();
// BufferedImage类是具有缓冲区的Image类,Image类是用于描述图像信息的类
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
Graphics g = image.getGraphics();
g.fillRect(0, 0, width, height);
g.setColor(getRandomColor(105, 189));
g.setFont(getFont());
//干扰线
for (int i = 0; i < lineSize; i++) {
drawLine(g);
}

//随机字符
String randomStr = "";
for (int i = 0; i < randomStrNum; i++) {
randomStr = drawString(g, randomStr, i);
}

g.dispose();
String base64String = "";
try {
// 直接返回图片
// 返回 base64
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ImageIO.write(image, "PNG", bos);

byte[] bytes = bos.toByteArray();
Base64.Encoder encoder = Base64.getEncoder();
base64String = encoder.encodeToString(bytes);

} catch (Exception e) {
e.printStackTrace();
}
result.put("randomStr", randomStr);
result.put("img", base64String);
return result;
}

}

Spring_Authorization_Server/src/main/resources/application-dev.yml → tuoheng_oidc_server/src/main/resources/application-dev.yml Целия файл

@@ -3,7 +3,8 @@ spring:
oauth2:
resource-server:
jwt:
issuer-uri: http://192.168.11.11:8090 #认证中心端点,作为资源端的配置
#issuer-uri: http://192.168.11.11:8090 #认证中心端点,作为资源端的配置
issuer-uri: http://oidc.dev.t-aaron.com

# 配置数据源
datasource:
@@ -34,4 +35,28 @@ spring:
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
testOnReturn: false

# Redis数据源
redis:
# 缓存库默认索引0
database: 0
# Redis服务器地址
host: 192.168.11.13
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码(默认为空)
password:
# 连接超时时间(毫秒)
timeout: 6000
# 默认的数据过期时间,主要用于shiro权限管理
expire: 2592000
jedis:
pool:
max-active: 1000 # 连接池最大连接数(使用负值表示没有限制)
max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制)
max-idle: 10 # 连接池中的最大空闲连接
min-idle: 1 # 连接池中的最小空闲连接
oauth2:
token:
issuer: http://oidc.dev.t-aaron.com

Spring_Authorization_Server/src/main/resources/application-local.yml → tuoheng_oidc_server/src/main/resources/application-local.yml Целия файл

@@ -34,4 +34,27 @@ spring:
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
testOnReturn: false
# Redis数据源
redis:
# 缓存库默认索引0
database: 0
# Redis服务器地址
host: 192.168.11.13
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码(默认为空)
password:
# 连接超时时间(毫秒)
timeout: 6000
# 默认的数据过期时间,主要用于shiro权限管理
expire: 2592000
jedis:
pool:
max-active: 1000 # 连接池最大连接数(使用负值表示没有限制)
max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制)
max-idle: 10 # 连接池中的最大空闲连接
min-idle: 1 # 连接池中的最小空闲连接
oauth2:
token:
issuer: http://127.0.0.1:8090

+ 60
- 0
tuoheng_oidc_server/src/main/resources/application-prod.yml Целия файл

@@ -0,0 +1,60 @@
spring:
security:
oauth2:
resource-server:
jwt:
issuer-uri: http://172.16.1.31:8090 #认证中心端点,作为资源端的配置、

# 配置数据源
datasource:
# 使用阿里的Druid连接池
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
# 填写你数据库的url、登录名、密码和数据库名
url: jdbc:mysql://rm-uf6x76i111rb1eo48.mysql.rds.aliyuncs.com:3306/tuoheng_dsp?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8&useSSL=true&tinyInt1isBit=false
username: root
password: TH22#2022
druid:
# 连接池的配置信息
# 初始连接数
initialSize: 5
# 最小连接池数量
minIdle: 5
# 最大连接池数量
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
# 配置一个连接在池中最大生存的时间,单位是毫秒
maxEvictableIdleTimeMillis: 900000
# 配置检测连接是否有效
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
# Redis数据源
redis:
# 缓存库默认索引0
database: 0
# Redis服务器地址
host: r-uf6r5lm7c7sfdv3ehb.redis.rds.aliyuncs.com
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码(默认为空)
password:
# 连接超时时间(毫秒)
timeout: 6000
# 默认的数据过期时间,主要用于shiro权限管理
expire: 2592000
jedis:
pool:
max-active: 1000 # 连接池最大连接数(使用负值表示没有限制)
max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制)
max-idle: 10 # 连接池中的最大空闲连接
min-idle: 1 # 连接池中的最小空闲连接
oauth2:
token:
issuer: http://172.16.1.31:8090

+ 61
- 0
tuoheng_oidc_server/src/main/resources/application-test.yml Целия файл

@@ -0,0 +1,61 @@
spring:
security:
oauth2:
resource-server:
jwt:
#issuer-uri: http://192.168.11.241:8090 #认证中心端点,作为资源端的配置、
issuer-uri: https://oidc.test.t-aaron.com #认证中心端点,作为资源端的配置、

# 配置数据源
datasource:
# 使用阿里的Druid连接池
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
# 填写你数据库的url、登录名、密码和数据库名
url: jdbc:mysql://192.168.11.242:3306/tuoheng_oidc?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8&useSSL=true&tinyInt1isBit=false
username: root
password: idontcare
druid:
# 连接池的配置信息
# 初始连接数
initialSize: 5
# 最小连接池数量
minIdle: 5
# 最大连接池数量
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
# 配置一个连接在池中最大生存的时间,单位是毫秒
maxEvictableIdleTimeMillis: 900000
# 配置检测连接是否有效
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
# Redis数据源
redis:
# 缓存库默认索引0
database: 0
# Redis服务器地址
host: 192.168.11.242
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码(默认为空)
password:
# 连接超时时间(毫秒)
timeout: 6000
# 默认的数据过期时间,主要用于shiro权限管理
expire: 2592000
jedis:
pool:
max-active: 1000 # 连接池最大连接数(使用负值表示没有限制)
max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制)
max-idle: 10 # 连接池中的最大空闲连接
min-idle: 1 # 连接池中的最小空闲连接
oauth2:
token:
issuer: https://oidc.test.t-aaron.com

+ 12
- 0
tuoheng_oidc_server/src/main/resources/application.yml Целия файл

@@ -0,0 +1,12 @@
server:
port: 8090

spring:
profiles:
active: @package.environment@
web:
resources:
static-locations: classpath:/

mybatis:
mapper-locations: classpath*:mapper/*Mapper.xml

+ 69
- 0
tuoheng_oidc_server/src/main/resources/logback.xml Целия файл

@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
<!-- scan:当此属性设置为true时,配置文档如果发生改变,将会被重新加载,默认值为true -->
<!-- scanPeriod:设置监测配置文档是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。
当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!--
contextName说明:
每个logger都关联到logger上下文,默认上下文名称为“default”。但可以使用设置成其他名字,
用于区分不同应用程序的记录。一旦设置,不能修改,可以通过%contextName来打印日志上下文名称。
-->
<contextName>tuoheng_oidc_server</contextName>

<!--定义日志变量-->
<!--<property name="logging.path" value="D:\\idealogs\\tuoheng_oidc"/>-->
<property name="logging.path" value="/data/java/logs/tuoheng_oidc"/>
<!--日志格式: [时间] [级别] [线程] [行号] [logger信息] - [日志信息]-->
<property name="logging.pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%level][%thread][%L] %logger - %msg%n"/>
<property name="logging.charset" value="UTF-8"/>
<property name="logging.maxHistory" value="15"/>
<property name="logging.totalSizeCap" value="5GB"/>
<property name="logging.maxFileSize" value="40MB"/>

<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${logging.pattern}</pattern>
<charset>${logging.charset}</charset>
</encoder>
</appender>

<appender name="LOG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>${logging.path}/server/tuoheng_oidc_server.log</File>
<append>true</append>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${logging.path}/server/tuoheng_oidc_server-%d-%i.log</fileNamePattern>
<!-- 最大保存天数-->
<maxHistory>${logging.maxHistory}</maxHistory>
<totalSizeCap>${logging.totalSizeCap}</totalSizeCap>
<maxFileSize>${logging.maxFileSize}</maxFileSize>
</rollingPolicy>
<!--编码器-->
<encoder>
<pattern>${logging.pattern}</pattern>
<charset>${logging.charset}</charset>
</encoder>
</appender>

<appender name="file.async" class="ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>0</discardingThreshold>
<queueSize>512</queueSize>
<includeCallerData>true</includeCallerData>
<appender-ref ref="LOG_FILE" />
</appender>

<logger name="com.tuoheng" level="DEBUG" additivity="false">
<appender-ref ref="console" />
<appender-ref ref="file.async" />
</logger>
<!--log4jdbc -->
<logger name="jdbc.sqltiming" level="DEBUG" additivity="false">
<appender-ref ref="file.async" />
</logger>

<root level="INFO">
<appender-ref ref="console" />
<appender-ref ref="file.async" />
</root>
</configuration>

+ 13
- 0
tuoheng_oidc_server/src/main/resources/mapper/AuthoritiesMapper.xml Целия файл

@@ -0,0 +1,13 @@
<?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.tuoheng.mapper.AuthoritiesMapper">

<insert id="batchInsert" parameterType="java.util.List">
insert into authorities (user_id, username, authority)
VALUES
<foreach collection ="list" item="it" separator =",">
(#{it.userId}, #{it.username}, #{it.authority})
</foreach >
</insert>

</mapper>

+ 34
- 0
tuoheng_oidc_server/src/main/resources/mapper/UserMapper.xml Целия файл

@@ -0,0 +1,34 @@
<?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.tuoheng.mapper.UserMapper">

<resultMap type="com.tuoheng.model.dto.UserBaseInfoDto" id="UserBaseInfoMap">
<id column="userId" jdbcType="INTEGER" property="userId" />
<result column="userName" jdbcType="VARCHAR" property="userName" />
<result column="password" jdbcType="VARCHAR" property="password" />
<collection property="authorityList" ofType="java.lang.String" javaType="java.util.List">
<result column="authority" jdbcType="VARCHAR"/>
</collection>
</resultMap>

<insert id="insertUser" parameterType="com.tuoheng.model.po.UserPo" keyProperty="id" useGeneratedKeys="true">
insert into users (username, password)
values (#{username}, #{password})
</insert>


<select id="getUserBaseInfo" resultMap="UserBaseInfoMap">
select a.id as userId, a.username as userName, a.password , b.authority
from users a
inner join authorities b on a.id = b.user_id
where a.username = #{username}
</select>

<select id="getMpUserInfo" resultMap="UserBaseInfoMap">
select a.id as userId, a.username as userName, b.authority
from users a
inner join authorities b on a.id = b.user_id
where a.username = #{username}
</select>

</mapper>

Двоични данни
tuoheng_oidc_server/src/main/resources/static/back.png Целия файл

Before After
Width: 2195  |  Height: 1080  |  Size: 596KB

Двоични данни
tuoheng_oidc_server/src/main/resources/static/form.png Целия файл

Before After
Width: 572  |  Height: 564  |  Size: 54KB

+ 2
- 0
tuoheng_oidc_server/src/main/resources/static/jquery-3.5.1.min.js
Файловите разлики са ограничени, защото са твърде много
Целия файл


+ 174
- 0
tuoheng_oidc_server/src/main/resources/templates/login.html Целия файл

@@ -0,0 +1,174 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录页面</title>
</head>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>login</title>
<script src="../static/jquery-3.5.1.min.js"></script>
<style>
.login__back{
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: url('../static/back.png');
background-position: center center;
background-repeat: no-repeat;
background-attachment: fixed;
background-size: cover;
}
.login__form{
/* width: 460px;
height: 410px; */
width: 410px;
height: 350px;
background: url('../static/form.png');
background-position: center center;
background-repeat: no-repeat;
background-size: cover;
padding: 60px 30px;
color: rgba(255, 255, 255, 1);
position: relative;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
}
.login__form h2{
font-size: 28px;
line-height: 25px;
text-align: center;
margin-bottom: 10px;
}
.login__form p{
font-size: 12px;
text-align: center;
margin-bottom: 36px;
}
.login__form form{
padding: 0 40px;
display: flex;
flex-direction: column;
background: transparent;
}
form input{
height: 40px;
margin-bottom: 18px;
border-radius: 6px;
color: #FFFFFF;
padding: 0 16px;
border: 1px solid rgba(255, 255, 255, 0.5);
background: transparent;
}
form input:focus-within{
outline: 0;
border: 1px solid #08EBFE;
}
button{
height: 46px;
border: none;
border-radius: 6px;
background: linear-gradient(0deg, #08EBFE 0%, #28BAC1 100%);
margin-top: 20px;
color: #FFFFFF;
font-size: 16px;
}
.form__code{
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 18px;
}
.form__code input{
width: calc(100% - 152px);
margin-bottom: 0;
}
.form__code img{
width: 100px;
height: 42px;
cursor: pointer;
}
.login__form .form__tips{
margin: 0;
height: 0;
position: relative;
top: -14px;
}
.form__tips.is--error{
color: red
}
</style>
</head>
<body>
<div class="login__back">
<div class="login__form">
<h2>拓恒统一登录平台</h2>
<p>TUOHENG LOGIN PLATFORM</p>
<form th:action="@{/login}" method="post">
<input name="username" placeholder="请输入用户名" type="text"/>
<input name="password" placeholder="请输入密码" type="password"/>
<div class="form__code">
<input name="validateCode" placeholder="请输入验证码" />
<input id="codekey" name="codekey" type="hidden"/>
<img class="code__img" src="" />
</div>
<div class="form__tips is--error" th:if="${param.error}">
用户名密码错误,请重新输入!
</div>
<div class="form__tips is--error" th:if="${param.validerror}">
验证码错误,请重新输入!
</div>
<div class="form__tips is--error" th:if="${param.expirecode}">
验证码已过期,请重新输入!
</div>
<button type="submit">登 录</button>
</form>
</div>
</div>
<!--绑定点击事件 -->
<script>
const imgDom = document.querySelector('.code__img')
imgDom.onclick = function() {
$.ajax({
url : "/vercode",//后台请求的数据
dataType: 'json', //数据格式
type : "post",//请求方式
async : true,//是否异步请求
success : function(data) { //如果请求成功,返回数据。
var tt = data.data; //第一个data代表json,第二个data代表json里的数组或对象
$('.code__img').attr('src', tt.captcha);
$('#codekey').val(tt.codeKey);
},
error : function (arg1) {
alert("加载数据失败");
console.log(arg1);
}
})
}

$(document).ready(function() {
$.ajax({
url : "/vercode",//后台请求的数据
dataType: 'json', //数据格式
type : "post",//请求方式
async : true,//是否异步请求
success : function(data) { //如果请求成功,返回数据。
var tt = data.data; //第一个data代表json,第二个data代表json里的数组或对象
$('.code__img').attr('src', tt.captcha);
$('#codekey').val(tt.codeKey);
},
error : function (arg1) {
alert("加载数据失败");
console.log(arg1);
}
})
})


</script>
</body>
</html>

Loading…
Отказ
Запис