├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src └── main ├── java └── org │ └── xueliang │ └── springsecuritystudy │ ├── Constants.java │ ├── DefaultException.java │ ├── api │ ├── AuthApi.java │ ├── BaseApi.java │ ├── admin │ │ └── AdminApi.java │ ├── dba │ │ └── DBAApi.java │ └── user │ │ └── UserApi.java │ ├── config │ ├── ExceptionHandler.java │ ├── SecurityConfig.java │ ├── WebAppConfig.java │ └── WebAppInitializer.java │ ├── model │ ├── Authority.java │ ├── JSONResponse.java │ └── User.java │ ├── security │ ├── RestAccessDeniedHandler.java │ ├── RestAuthenticationEntryPoint.java │ ├── RestAuthenticationFailureHandler.java │ ├── RestAuthenticationSuccessHandler.java │ └── RestLogoutSuccessHandler.java │ └── service │ └── MyUserDetailsService.java ├── resources ├── config.properties └── log4j2.xml └── webapp ├── META-INF └── MANIFEST.MF ├── admin └── index.html ├── dba └── index.html ├── index.html ├── static └── js │ └── common.js ├── user └── index.html └── welcome.html /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/eclipse,gradle 3 | 4 | ### Eclipse ### 5 | 6 | .metadata 7 | bin/ 8 | tmp/ 9 | *.tmp 10 | *.bak 11 | *.swp 12 | *~.nib 13 | local.properties 14 | .settings/ 15 | .loadpath 16 | .recommenders 17 | 18 | # Eclipse Core 19 | .project 20 | 21 | # External tool builders 22 | .externalToolBuilders/ 23 | 24 | # Locally stored "Eclipse launch configurations" 25 | *.launch 26 | 27 | # PyDev specific (Python IDE for Eclipse) 28 | *.pydevproject 29 | 30 | # CDT-specific (C/C++ Development Tooling) 31 | .cproject 32 | 33 | # JDT-specific (Eclipse Java Development Tools) 34 | .classpath 35 | 36 | # Java annotation processor (APT) 37 | .factorypath 38 | 39 | # PDT-specific (PHP Development Tools) 40 | .buildpath 41 | 42 | # sbteclipse plugin 43 | .target 44 | 45 | # Tern plugin 46 | .tern-project 47 | 48 | # TeXlipse plugin 49 | .texlipse 50 | 51 | # STS (Spring Tool Suite) 52 | .springBeans 53 | 54 | # Code Recommenders 55 | .recommenders/ 56 | 57 | ### Gradle ### 58 | .gradle 59 | /build/ 60 | 61 | # Ignore Gradle GUI config 62 | gradle-app.setting 63 | 64 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 65 | !gradle-wrapper.jar 66 | 67 | # Cache of project 68 | .gradletasknamecache 69 | 70 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 71 | # gradle/wrapper/gradle-wrapper.properties 72 | 73 | # End of https://www.gitignore.io/api/eclipse,gradle 74 | 75 | /logs 76 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 薛亮 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # springsecuritystudy 2 | RESTful API using Spring Security. 3 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * This build file was auto generated by running the Gradle 'init' task 3 | * by 'XueLiang' at '17-2-24 上午9:55' with Gradle 3.0 4 | * 5 | * This generated file contains a sample Java project to get you started. 6 | * For more details take a look at the Java Quickstart chapter in the Gradle 7 | * user guide available at https://docs.gradle.org/3.0/userguide/tutorial_java_projects.html 8 | */ 9 | 10 | // Apply the java plugin to add support for Java 11 | apply plugin: 'java' 12 | apply plugin: 'eclipse' 13 | apply plugin: 'war' 14 | apply plugin: 'org.akhikhl.gretty' 15 | 16 | buildscript { 17 | repositories { 18 | maven { 19 | url "https://plugins.gradle.org/m2/" 20 | } 21 | } 22 | dependencies { 23 | classpath "org.akhikhl.gretty:gretty:1.4.0" 24 | } 25 | } 26 | 27 | gretty { 28 | port = 8080 29 | servletContainer = 'jetty9' 30 | contextPath = '/' 31 | debugSuspend = false 32 | // ... many more properties 33 | } 34 | 35 | compileJava { 36 | sourceCompatibility = 1.8 37 | targetCompatibility = 1.8 38 | } 39 | 40 | // In this section you declare where to find the dependencies of your project 41 | repositories { 42 | mavenLocal() 43 | maven { url 'http://repo1.xueliang.org/repository/maven-public/'} 44 | maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' } 45 | mavenCentral() 46 | jcenter() 47 | maven { url 'https://repo.spring.io/libs-snapshot/' } 48 | } 49 | 50 | tasks.withType(JavaCompile) { 51 | options.encoding = "UTF-8" 52 | } 53 | 54 | // In this section you declare the dependencies for your production and test code 55 | dependencies { 56 | def springVersion = '4.3.6.RELEASE' 57 | def springSecurityVersion = '4.2.1.RELEASE' 58 | def log4jVersion = '2.8'; 59 | 60 | compile "org.springframework:spring-web:$springVersion" 61 | compile "org.springframework:spring-webmvc:$springVersion" 62 | compile "org.springframework.security:spring-security-web:$springSecurityVersion" 63 | compile "org.springframework.security:spring-security-config:$springSecurityVersion" 64 | 65 | compile "org.apache.logging.log4j:log4j-core:$log4jVersion" 66 | compile "org.apache.logging.log4j:log4j-api:$log4jVersion" 67 | 68 | compile "org.json:json:20160810" 69 | compile "com.fasterxml.jackson.core:jackson-databind:2.9.0.pr1" 70 | 71 | testCompile 'junit:junit:4.12' 72 | } 73 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.daemon=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liangzai-cool/springsecuritystudy/9e5b10e0759041acca413a9ed0e89ebfcc32fea6/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Feb 24 09:54:59 CST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.0-bin.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn ( ) { 37 | echo "$*" 38 | } 39 | 40 | die ( ) { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 165 | if [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]]; then 166 | cd "$(dirname "$0")" 167 | fi 168 | 169 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 170 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * This settings file was auto generated by the Gradle buildInit task 3 | * by 'XueLiang' at '17-2-24 上午9:55' with Gradle 3.0 4 | * 5 | * The settings file is used to specify which projects to include in your build. 6 | * In a single project build this file can be empty or even removed. 7 | * 8 | * Detailed information about configuring a multi-project build in Gradle can be found 9 | * in the user guide at https://docs.gradle.org/3.0/userguide/multi_project_builds.html 10 | */ 11 | 12 | /* 13 | // To declare projects as part of a multi-project build use the 'include' method 14 | include 'shared' 15 | include 'api' 16 | include 'services:webservice' 17 | */ 18 | 19 | rootProject.name = 'springsecuritystudy' 20 | -------------------------------------------------------------------------------- /src/main/java/org/xueliang/springsecuritystudy/Constants.java: -------------------------------------------------------------------------------- 1 | package org.xueliang.springsecuritystudy; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.xueliang.springsecuritystudy.model.Authority; 7 | import org.xueliang.springsecuritystudy.model.User; 8 | 9 | public class Constants { 10 | 11 | public static final List userList = new ArrayList(){ 12 | 13 | private static final long serialVersionUID = 1L; 14 | { 15 | User user = new User(); 16 | user.setUsername("myuser"); 17 | user.setPassword("$2a$10$r6V8qEOWr.4TZxdMCNRweOfeD3uxhEMd3Ey7obEhJsh7F53S3c8l6"); // myuserpass 18 | user.setAuthorities(new ArrayList(){ 19 | private static final long serialVersionUID = 1L; 20 | { 21 | add(new Authority("USER")); 22 | } 23 | }); 24 | 25 | User admin = new User(); 26 | admin.setUsername("myadmin"); 27 | admin.setPassword("$2a$10$UoD6b1MX7cTKwPjia34n4eJ1Q7eD4c.rBFbM826tPdqGWs/bh5Xfm"); // myadminpass 28 | admin.setAuthorities(new ArrayList(){ 29 | private static final long serialVersionUID = 1L; 30 | { 31 | add(new Authority("ADMIN")); 32 | } 33 | }); 34 | 35 | User dba = new User(); 36 | dba.setUsername("mydba"); 37 | dba.setPassword("$2a$10$31Vufh14KaO40xqRKlJvkeBIboOtPNjqJdKXJuEuxAjMwCqhECL8a"); // mydbapass 38 | dba.setAuthorities(new ArrayList(){ 39 | private static final long serialVersionUID = 1L; 40 | { 41 | add(new Authority("DBA")); 42 | } 43 | }); 44 | 45 | add(user); 46 | add(admin); 47 | add(dba); 48 | } 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/org/xueliang/springsecuritystudy/DefaultException.java: -------------------------------------------------------------------------------- 1 | package org.xueliang.springsecuritystudy; 2 | 3 | public class DefaultException extends Exception { 4 | 5 | private static final long serialVersionUID = 6304732905146351972L; 6 | 7 | private String errorCode; 8 | 9 | public static enum Error { 10 | user_not_login("用户未登录"), 11 | server_internal_error("服务器内部错误"), 12 | invalid_parameter("无效参数"); 13 | 14 | private String message; 15 | 16 | Error(String message){ 17 | this.message = message; 18 | } 19 | 20 | public String getMessage() { 21 | return this.message; 22 | } 23 | } 24 | 25 | public DefaultException(Error error) { 26 | this(error.name(), error.message); 27 | } 28 | 29 | public DefaultException(String code, String message) { 30 | super(message); 31 | this.errorCode = code; 32 | } 33 | 34 | public String getErrorCode() { 35 | return errorCode; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/org/xueliang/springsecuritystudy/api/AuthApi.java: -------------------------------------------------------------------------------- 1 | package org.xueliang.springsecuritystudy.api; 2 | 3 | import javax.servlet.http.HttpServletRequest; 4 | 5 | import org.springframework.security.core.context.SecurityContextHolder; 6 | import org.springframework.security.web.csrf.CsrfToken; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.bind.annotation.RestController; 9 | import org.xueliang.springsecuritystudy.model.JSONResponse; 10 | 11 | @RestController 12 | @RequestMapping(value = "/api/auth/") 13 | public class AuthApi extends BaseApi { 14 | 15 | @RequestMapping(value="csrf-token") 16 | public JSONResponse getCsrfToken(HttpServletRequest request) { 17 | JSONResponse jsonResponse = new JSONResponse(); 18 | CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName()); 19 | String token = csrfToken.getToken(); 20 | jsonResponse.addMsg("csrfToken", token); 21 | return jsonResponse; 22 | } 23 | 24 | @RequestMapping("whoami") 25 | public JSONResponse whoami() { 26 | JSONResponse jsonResponse = new JSONResponse(); 27 | String username = SecurityContextHolder.getContext().getAuthentication().getName(); 28 | jsonResponse.addMsg("username", username); 29 | return jsonResponse; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/org/xueliang/springsecuritystudy/api/BaseApi.java: -------------------------------------------------------------------------------- 1 | package org.xueliang.springsecuritystudy.api; 2 | 3 | public class BaseApi { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/org/xueliang/springsecuritystudy/api/admin/AdminApi.java: -------------------------------------------------------------------------------- 1 | package org.xueliang.springsecuritystudy.api.admin; 2 | 3 | import org.springframework.web.bind.annotation.RequestMapping; 4 | import org.springframework.web.bind.annotation.RestController; 5 | import org.xueliang.springsecuritystudy.api.BaseApi; 6 | import org.xueliang.springsecuritystudy.model.JSONResponse; 7 | 8 | /** 9 | * 普通管理员接口 10 | * @author XueLiang 11 | * 2017年3月2日 下午11:01:45 12 | */ 13 | @RestController 14 | @RequestMapping("/api/admin/") 15 | public class AdminApi extends BaseApi { 16 | 17 | @RequestMapping({"", "/"}) 18 | public JSONResponse visiteAdmin() { 19 | JSONResponse jsonResponse = new JSONResponse(); 20 | jsonResponse.addMsg("say", "this comes from admin api"); 21 | return jsonResponse; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/org/xueliang/springsecuritystudy/api/dba/DBAApi.java: -------------------------------------------------------------------------------- 1 | package org.xueliang.springsecuritystudy.api.dba; 2 | 3 | import org.springframework.web.bind.annotation.RequestMapping; 4 | import org.springframework.web.bind.annotation.RestController; 5 | import org.xueliang.springsecuritystudy.api.BaseApi; 6 | import org.xueliang.springsecuritystudy.model.JSONResponse; 7 | 8 | /** 9 | * 数据库管理员接口 10 | * @author XueLiang 11 | * 2017年3月2日 下午11:01:17 12 | */ 13 | @RestController 14 | @RequestMapping("/api/dba/") 15 | public class DBAApi extends BaseApi { 16 | 17 | @RequestMapping({"", "/"}) 18 | public JSONResponse visiteAdmin() { 19 | JSONResponse jsonResponse = new JSONResponse(); 20 | jsonResponse.addMsg("say", "this comes from dba api"); 21 | return jsonResponse; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/org/xueliang/springsecuritystudy/api/user/UserApi.java: -------------------------------------------------------------------------------- 1 | package org.xueliang.springsecuritystudy.api.user; 2 | 3 | import org.springframework.web.bind.annotation.RequestMapping; 4 | import org.springframework.web.bind.annotation.RestController; 5 | import org.xueliang.springsecuritystudy.api.BaseApi; 6 | import org.xueliang.springsecuritystudy.model.JSONResponse; 7 | 8 | /** 9 | * 用户接口 10 | * @author XueLiang 11 | * 2017年3月2日 下午11:00:56 12 | */ 13 | @RestController 14 | @RequestMapping("/api/user/") 15 | public class UserApi extends BaseApi { 16 | 17 | @RequestMapping({"", "/"}) 18 | public JSONResponse visiteAdmin() { 19 | JSONResponse jsonResponse = new JSONResponse(); 20 | jsonResponse.addMsg("say", "this comes from user api"); 21 | return jsonResponse; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/org/xueliang/springsecuritystudy/config/ExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package org.xueliang.springsecuritystudy.config; 2 | 3 | import java.io.IOException; 4 | import java.io.PrintWriter; 5 | 6 | import javax.servlet.http.HttpServletRequest; 7 | import javax.servlet.http.HttpServletResponse; 8 | 9 | import org.apache.logging.log4j.LogManager; 10 | import org.apache.logging.log4j.Logger; 11 | import org.springframework.stereotype.Component; 12 | import org.springframework.web.servlet.ModelAndView; 13 | import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver; 14 | import org.xueliang.springsecuritystudy.DefaultException; 15 | import org.xueliang.springsecuritystudy.model.JSONResponse; 16 | 17 | @Component("exceptionHandler") 18 | public class ExceptionHandler extends DefaultHandlerExceptionResolver { 19 | 20 | private static final Logger LOGGER = LogManager.getLogger(ExceptionHandler.class); 21 | 22 | @Override 23 | protected ModelAndView doResolveException(HttpServletRequest request, 24 | HttpServletResponse response, Object handler, Exception ex) { 25 | LOGGER.error("error", ex); 26 | JSONResponse jsonResponse = new JSONResponse(1); 27 | jsonResponse.addError(DefaultException.Error.server_internal_error); 28 | try { 29 | response.setCharacterEncoding("UTF-8"); 30 | response.addHeader("Content-Type", "application/json;charset=UTF-8"); 31 | PrintWriter writer = response.getWriter(); 32 | writer.print(jsonResponse); 33 | writer.flush(); 34 | writer.close(); 35 | } catch (IOException e) { 36 | LOGGER.error("write response error", e); 37 | } 38 | return null; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/org/xueliang/springsecuritystudy/config/SecurityConfig.java: -------------------------------------------------------------------------------- 1 | package org.xueliang.springsecuritystudy.config; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.beans.factory.annotation.Value; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.security.authentication.dao.DaoAuthenticationProvider; 8 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 9 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 10 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 11 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 12 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 13 | import org.springframework.security.crypto.password.PasswordEncoder; 14 | import org.xueliang.springsecuritystudy.security.RestAccessDeniedHandler; 15 | import org.xueliang.springsecuritystudy.security.RestAuthenticationEntryPoint; 16 | import org.xueliang.springsecuritystudy.security.RestAuthenticationFailureHandler; 17 | import org.xueliang.springsecuritystudy.security.RestAuthenticationSuccessHandler; 18 | import org.xueliang.springsecuritystudy.security.RestLogoutSuccessHandler; 19 | import org.xueliang.springsecuritystudy.service.MyUserDetailsService; 20 | 21 | @Configuration 22 | @EnableWebSecurity 23 | public class SecurityConfig extends WebSecurityConfigurerAdapter { 24 | 25 | @Value("${api.csrftoken}") 26 | private String csrfTokenApi; 27 | 28 | @Value("${api.login}") 29 | private String loginApi; 30 | 31 | @Value("${api.logout}") 32 | private String logoutApi; 33 | 34 | @Autowired 35 | private MyUserDetailsService userDetailsService; 36 | 37 | @Autowired 38 | private PasswordEncoder passwordEncoder; 39 | 40 | @Override 41 | protected void configure(HttpSecurity http) throws Exception { 42 | http.authorizeRequests().antMatchers(csrfTokenApi).permitAll() 43 | .antMatchers("/api/user/**").access("hasAuthority('USER')") 44 | .antMatchers("/api/admin/**").access("hasAuthority('ADMIN')") 45 | .antMatchers("/api/dba/**").access("hasAuthority('DBA')") 46 | .antMatchers("/api/**").fullyAuthenticated() 47 | .and().formLogin().loginProcessingUrl(loginApi) 48 | .successHandler(new RestAuthenticationSuccessHandler()) 49 | .failureHandler(new RestAuthenticationFailureHandler()) 50 | .and().rememberMe().rememberMeParameter("remember").rememberMeCookieName("remember") 51 | .and().sessionManagement().maximumSessions(1).maxSessionsPreventsLogin(true) 52 | .and().and().logout().logoutUrl(logoutApi).logoutSuccessHandler(new RestLogoutSuccessHandler()) 53 | .deleteCookies( "JSESSIONID" ).invalidateHttpSession(true) 54 | .and().exceptionHandling().authenticationEntryPoint(new RestAuthenticationEntryPoint()) 55 | .accessDeniedHandler(new RestAccessDeniedHandler()); 56 | } 57 | 58 | @Autowired 59 | public void configureGlobalSecurity(AuthenticationManagerBuilder auth, DaoAuthenticationProvider daoAuthenticationProvider) throws Exception { 60 | auth.authenticationProvider(daoAuthenticationProvider); 61 | } 62 | 63 | @Bean 64 | public PasswordEncoder passwordEncoder() { 65 | return new BCryptPasswordEncoder(11); 66 | } 67 | 68 | @Bean 69 | public DaoAuthenticationProvider daoAuthenticationProvider() { 70 | DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider(); 71 | daoAuthenticationProvider.setHideUserNotFoundExceptions(false); 72 | daoAuthenticationProvider.setUserDetailsService(userDetailsService); 73 | daoAuthenticationProvider.setPasswordEncoder(passwordEncoder); 74 | return daoAuthenticationProvider; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/org/xueliang/springsecuritystudy/config/WebAppConfig.java: -------------------------------------------------------------------------------- 1 | package org.xueliang.springsecuritystudy.config; 2 | 3 | import java.util.Collections; 4 | import java.util.Locale; 5 | 6 | import javax.annotation.PostConstruct; 7 | 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.ComponentScan; 11 | import org.springframework.context.annotation.Configuration; 12 | import org.springframework.context.annotation.PropertySource; 13 | import org.springframework.context.support.ReloadableResourceBundleMessageSource; 14 | import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; 15 | import org.springframework.web.accept.ContentNegotiationManager; 16 | import org.springframework.web.servlet.config.annotation.EnableWebMvc; 17 | import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; 18 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 19 | import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; 20 | 21 | @Configuration 22 | @EnableWebMvc 23 | @ComponentScan(basePackages = "org.xueliang.springsecuritystudy") 24 | @PropertySource({"classpath:config.properties"}) 25 | public class WebAppConfig extends WebMvcConfigurerAdapter { 26 | 27 | @PostConstruct 28 | public void init() { 29 | Locale.setDefault(Locale.SIMPLIFIED_CHINESE); 30 | } 31 | 32 | @Bean 33 | @Autowired 34 | public RequestMappingHandlerAdapter requestMappingHandlerAdapter(MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter, ContentNegotiationManager mvcContentNegotiationManager) { 35 | RequestMappingHandlerAdapter requestMappingHandlerAdapter = new RequestMappingHandlerAdapter(); 36 | requestMappingHandlerAdapter.setMessageConverters(Collections.singletonList(mappingJackson2HttpMessageConverter)); 37 | requestMappingHandlerAdapter.setContentNegotiationManager(mvcContentNegotiationManager); 38 | return requestMappingHandlerAdapter; 39 | } 40 | 41 | @Bean 42 | public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() { 43 | return new MappingJackson2HttpMessageConverter(); 44 | } 45 | 46 | @Bean 47 | public ReloadableResourceBundleMessageSource messageSource() { 48 | ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); 49 | messageSource.setBasename("classpath:org/springframework/security/messages"); 50 | messageSource.setDefaultEncoding("UTF-8"); 51 | messageSource.setUseCodeAsDefaultMessage(true); 52 | return messageSource; 53 | } 54 | 55 | /** 56 | * 设置欢迎页 57 | * 相当于web.xml中的 welcome-file-list > welcome-file 58 | */ 59 | @Override 60 | public void addViewControllers(ViewControllerRegistry registry) { 61 | registry.addRedirectViewController("/", "/index.html"); 62 | } 63 | } -------------------------------------------------------------------------------- /src/main/java/org/xueliang/springsecuritystudy/config/WebAppInitializer.java: -------------------------------------------------------------------------------- 1 | package org.xueliang.springsecuritystudy.config; 2 | 3 | import javax.servlet.Filter; 4 | import javax.servlet.ServletContext; 5 | import javax.servlet.ServletException; 6 | 7 | import org.springframework.security.web.session.HttpSessionEventPublisher; 8 | import org.springframework.web.filter.CharacterEncodingFilter; 9 | import org.springframework.web.filter.DelegatingFilterProxy; 10 | import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; 11 | 12 | /** 13 | * 基于注解的/WEB-INF/web.xml 依赖 servlet 3.0 14 | * 15 | * @author XueLiang 16 | * @date 2017年2月24日 下午3:38:28 17 | * @version 1.0 18 | */ 19 | public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { 20 | 21 | @Override 22 | public void onStartup(ServletContext servletContext) throws ServletException { 23 | servletContext.addFilter("springSecurityFilterChain", new DelegatingFilterProxy("springSecurityFilterChain")) 24 | .addMappingForUrlPatterns(null, false, "/api/*"); 25 | 26 | // 静态资源映射 27 | servletContext.getServletRegistration("default").addMapping("/static/*", "*.html", "*.ico"); 28 | 29 | servletContext.addListener(HttpSessionEventPublisher.class); 30 | super.onStartup(servletContext); 31 | } 32 | 33 | @Override 34 | protected Class[] getRootConfigClasses() { 35 | return new Class[] { WebAppConfig.class }; 36 | } 37 | 38 | @Override 39 | protected Class[] getServletConfigClasses() { 40 | return null; 41 | } 42 | 43 | @Override 44 | protected String[] getServletMappings() { 45 | return new String[] { "/" }; 46 | } 47 | 48 | @Override 49 | protected Filter[] getServletFilters() { 50 | return new Filter[] { new CharacterEncodingFilter("UTF-8", true) }; 51 | } 52 | } -------------------------------------------------------------------------------- /src/main/java/org/xueliang/springsecuritystudy/model/Authority.java: -------------------------------------------------------------------------------- 1 | package org.xueliang.springsecuritystudy.model; 2 | 3 | import org.springframework.security.core.GrantedAuthority; 4 | 5 | public class Authority implements GrantedAuthority { 6 | 7 | private static final long serialVersionUID = 1L; 8 | 9 | private String authority; 10 | 11 | public Authority() { } 12 | public Authority(String authority) { 13 | this.setAuthority(authority); 14 | } 15 | 16 | @Override 17 | public String getAuthority() { 18 | return this.authority; 19 | } 20 | public void setAuthority(String authority) { 21 | this.authority = authority; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/org/xueliang/springsecuritystudy/model/JSONResponse.java: -------------------------------------------------------------------------------- 1 | package org.xueliang.springsecuritystudy.model; 2 | 3 | import java.util.Date; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | import org.json.JSONObject; 8 | import org.xueliang.springsecuritystudy.DefaultException.Error; 9 | 10 | public class JSONResponse { 11 | 12 | private String redirectUrl; 13 | private int code; 14 | private Map msg = new HashMap(); 15 | private Map error = new HashMap(); 16 | 17 | public JSONResponse(int code){ 18 | this.code = code; 19 | } 20 | 21 | public JSONResponse() { } 22 | public JSONResponse(Map msg){ 23 | this.msg = msg; 24 | } 25 | 26 | public JSONResponse(int code, Map msg){ 27 | this.code = code; 28 | this.msg = msg; 29 | } 30 | 31 | public int getCode() { 32 | return code ; 33 | } 34 | public void setCode(int code) { 35 | this.code = code; 36 | } 37 | 38 | public Map getMsg() { 39 | return msg; 40 | } 41 | public void setMsg(Map msg) { 42 | this.msg = msg; 43 | } 44 | public Object getMsg(String key){ 45 | return this.msg.get(key); 46 | } 47 | public void addMsg(String key, Object content){ 48 | this.msg.put(key, content); 49 | } 50 | public void removeMsg(String key){ 51 | this.msg.remove(key); 52 | } 53 | 54 | public Map getError() { 55 | return error; 56 | } 57 | public void setError(Map error) { 58 | this.error = error; 59 | } 60 | public Object getError(String key){ 61 | return this.error.get(key); 62 | } 63 | public void addError(String key, Object content){ 64 | this.code = 1; 65 | this.error.put(key, content); 66 | } 67 | public void addError(Error error){ 68 | this.code = 1; 69 | this.error.put(error.name(), error.getMessage()); 70 | } 71 | public void removeError(String key){ 72 | this.error.remove(key); 73 | } 74 | 75 | public boolean getIsOK() { 76 | return error.isEmpty(); 77 | } 78 | public String getRedirectUrl() { 79 | return redirectUrl; 80 | } 81 | public void setRedirectUrl(String redirectUrl) { 82 | this.redirectUrl = redirectUrl; 83 | } 84 | 85 | @Override 86 | public String toString() { 87 | JSONObject json = new JSONObject(this); 88 | json.put("serverTime", new Date()); 89 | return json.toString(); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/org/xueliang/springsecuritystudy/model/User.java: -------------------------------------------------------------------------------- 1 | package org.xueliang.springsecuritystudy.model; 2 | 3 | import java.util.Collection; 4 | import java.util.List; 5 | 6 | import org.springframework.security.core.GrantedAuthority; 7 | import org.springframework.security.core.userdetails.UserDetails; 8 | 9 | public class User implements UserDetails { 10 | 11 | private static final long serialVersionUID = 1L; 12 | 13 | private String username; 14 | private String password; 15 | private List authorities; 16 | 17 | @Override 18 | public String getUsername() { 19 | return username; 20 | } 21 | public void setUsername(String username) { 22 | this.username = username; 23 | } 24 | 25 | @Override 26 | public String getPassword() { 27 | return password; 28 | } 29 | public void setPassword(String password) { 30 | this.password = password; 31 | } 32 | 33 | @Override 34 | public Collection getAuthorities() { 35 | return this.authorities; 36 | } 37 | public void setAuthorities(List authorities) { 38 | this.authorities = authorities; 39 | } 40 | 41 | @Override 42 | public boolean isAccountNonExpired() { 43 | return true; 44 | } 45 | @Override 46 | public boolean isAccountNonLocked() { 47 | return true; 48 | } 49 | 50 | @Override 51 | public boolean isCredentialsNonExpired() { 52 | return true; 53 | } 54 | @Override 55 | public boolean isEnabled() { 56 | return true; 57 | } 58 | 59 | /** 60 | * Returns {@code true} if the supplied object is a {@code User} instance with the 61 | * same {@code username} value. 62 | *

63 | * In other words, the objects are equal if they have the same username, representing 64 | * the same principal. 65 | */ 66 | @Override 67 | public boolean equals(Object rhs) { 68 | if (rhs instanceof User) { 69 | return username.equals(((User) rhs).username); 70 | } 71 | return false; 72 | } 73 | 74 | /** 75 | * Returns the hashcode of the {@code username}. 76 | */ 77 | @Override 78 | public int hashCode() { 79 | return username.hashCode(); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/org/xueliang/springsecuritystudy/security/RestAccessDeniedHandler.java: -------------------------------------------------------------------------------- 1 | package org.xueliang.springsecuritystudy.security; 2 | 3 | import java.io.IOException; 4 | import java.io.PrintWriter; 5 | 6 | import javax.servlet.ServletException; 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.servlet.http.HttpServletResponse; 9 | 10 | import org.apache.logging.log4j.LogManager; 11 | import org.apache.logging.log4j.Logger; 12 | import org.springframework.security.access.AccessDeniedException; 13 | import org.springframework.security.web.access.AccessDeniedHandler; 14 | import org.xueliang.springsecuritystudy.DefaultException; 15 | import org.xueliang.springsecuritystudy.model.JSONResponse; 16 | 17 | /** 18 | * 拒绝访问 19 | * @author XueLiang 20 | * @date 2017年3月1日 上午11:07:10 21 | * @version 1.0 22 | */ 23 | public class RestAccessDeniedHandler implements AccessDeniedHandler { 24 | 25 | private static final Logger LOGGER = LogManager.getLogger(RestAccessDeniedHandler.class); 26 | 27 | @Override 28 | public void handle(HttpServletRequest request, HttpServletResponse response, 29 | AccessDeniedException accessDeniedException) throws IOException, ServletException { 30 | LOGGER.warn("Authentication Failed: " + accessDeniedException.getMessage()); 31 | JSONResponse jsonResponse = new JSONResponse(); 32 | jsonResponse.addError(DefaultException.Error.invalid_parameter.name(), accessDeniedException.getMessage()); 33 | response.addHeader("Content-type", "application/json; charset=UTF-8"); 34 | response.setCharacterEncoding("UTF-8"); 35 | PrintWriter printWriter = response.getWriter(); 36 | printWriter.write(jsonResponse.toString()); 37 | printWriter.flush(); 38 | printWriter.close(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/org/xueliang/springsecuritystudy/security/RestAuthenticationEntryPoint.java: -------------------------------------------------------------------------------- 1 | package org.xueliang.springsecuritystudy.security; 2 | 3 | import java.io.IOException; 4 | import java.io.PrintWriter; 5 | 6 | import javax.servlet.ServletException; 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.servlet.http.HttpServletResponse; 9 | 10 | import org.apache.logging.log4j.LogManager; 11 | import org.apache.logging.log4j.Logger; 12 | import org.springframework.context.support.MessageSourceAccessor; 13 | import org.springframework.security.core.AuthenticationException; 14 | import org.springframework.security.core.SpringSecurityMessageSource; 15 | import org.springframework.security.web.AuthenticationEntryPoint; 16 | import org.xueliang.springsecuritystudy.DefaultException; 17 | import org.xueliang.springsecuritystudy.model.JSONResponse; 18 | 19 | /** 20 | * 这个入口点其实仅仅是被ExceptionTranslationFilter引用 21 | * 由此入口决定redirect、forward的操作 22 | * @author XueLiang 23 | * @date 2017年3月1日 上午10:20:39 24 | * @version 1.0 25 | */ 26 | public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint { 27 | 28 | private static final Logger LOGGER = LogManager.getLogger(RestAuthenticationEntryPoint.class); 29 | 30 | protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor(); 31 | 32 | @Override 33 | public void commence(HttpServletRequest request, HttpServletResponse response, 34 | AuthenticationException authException) throws IOException, ServletException { 35 | LOGGER.warn("Authentication Failed: " + authException.getMessage()); 36 | JSONResponse jsonResponse = new JSONResponse(); 37 | jsonResponse.addError(DefaultException.Error.invalid_parameter.name(), messages.getMessage("AbstractAccessDecisionManager.accessDenied")); 38 | response.addHeader("Content-type", "application/json; charset=UTF-8"); 39 | response.setCharacterEncoding("UTF-8"); 40 | PrintWriter printWriter = response.getWriter(); 41 | printWriter.write(jsonResponse.toString()); 42 | printWriter.flush(); 43 | printWriter.close(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/org/xueliang/springsecuritystudy/security/RestAuthenticationFailureHandler.java: -------------------------------------------------------------------------------- 1 | package org.xueliang.springsecuritystudy.security; 2 | 3 | import java.io.IOException; 4 | import java.io.PrintWriter; 5 | 6 | import javax.servlet.ServletException; 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.servlet.http.HttpServletResponse; 9 | 10 | import org.apache.logging.log4j.LogManager; 11 | import org.apache.logging.log4j.Logger; 12 | import org.springframework.security.core.AuthenticationException; 13 | import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; 14 | import org.xueliang.springsecuritystudy.DefaultException; 15 | import org.xueliang.springsecuritystudy.model.JSONResponse; 16 | 17 | public class RestAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler { 18 | 19 | private static final Logger LOGGER = LogManager.getLogger(RestAuthenticationFailureHandler.class); 20 | 21 | @Override 22 | public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, 23 | AuthenticationException exception) throws IOException, ServletException { 24 | LOGGER.info("auth failure!"); 25 | JSONResponse jsonResponse = new JSONResponse(); 26 | jsonResponse.addError(DefaultException.Error.invalid_parameter.name(), exception.getMessage()); 27 | response.addHeader("Content-type", "application/json; charset=UTF-8"); 28 | response.setCharacterEncoding("UTF-8"); 29 | PrintWriter printWriter = response.getWriter(); 30 | printWriter.write(jsonResponse.toString()); 31 | printWriter.flush(); 32 | printWriter.close(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/xueliang/springsecuritystudy/security/RestAuthenticationSuccessHandler.java: -------------------------------------------------------------------------------- 1 | package org.xueliang.springsecuritystudy.security; 2 | 3 | import java.io.IOException; 4 | import java.io.PrintWriter; 5 | 6 | import javax.servlet.ServletException; 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.servlet.http.HttpServletResponse; 9 | 10 | import org.apache.logging.log4j.LogManager; 11 | import org.apache.logging.log4j.Logger; 12 | import org.springframework.security.core.Authentication; 13 | import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; 14 | import org.xueliang.springsecuritystudy.model.JSONResponse; 15 | 16 | public class RestAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { 17 | 18 | private static final Logger LOGGER = LogManager.getLogger(RestAuthenticationSuccessHandler.class); 19 | 20 | @Override 21 | public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, 22 | Authentication authentication) throws IOException, ServletException { 23 | LOGGER.info("auth success!"); 24 | JSONResponse jsonResponse = new JSONResponse(); 25 | jsonResponse.addMsg("result", "SUCCESS"); 26 | PrintWriter printWriter = response.getWriter(); 27 | printWriter.write(jsonResponse.toString()); 28 | printWriter.flush(); 29 | printWriter.close(); 30 | clearAuthenticationAttributes(request); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/org/xueliang/springsecuritystudy/security/RestLogoutSuccessHandler.java: -------------------------------------------------------------------------------- 1 | package org.xueliang.springsecuritystudy.security; 2 | 3 | import java.io.IOException; 4 | import java.io.PrintWriter; 5 | 6 | import javax.servlet.ServletException; 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.servlet.http.HttpServletResponse; 9 | 10 | import org.apache.logging.log4j.LogManager; 11 | import org.apache.logging.log4j.Logger; 12 | import org.springframework.security.core.Authentication; 13 | import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler; 14 | import org.xueliang.springsecuritystudy.model.JSONResponse; 15 | 16 | public class RestLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler { 17 | 18 | private static final Logger LOGGER = LogManager.getLogger(RestLogoutSuccessHandler.class); 19 | 20 | @Override 21 | public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) 22 | throws IOException, ServletException { 23 | LOGGER.info("logout success!"); 24 | JSONResponse jsonResponse = new JSONResponse(); 25 | jsonResponse.addMsg("result", "success"); 26 | PrintWriter printWriter = response.getWriter(); 27 | printWriter.write(jsonResponse.toString()); 28 | printWriter.flush(); 29 | printWriter.close(); 30 | // response.sendRedirect("/index.html"); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/org/xueliang/springsecuritystudy/service/MyUserDetailsService.java: -------------------------------------------------------------------------------- 1 | package org.xueliang.springsecuritystudy.service; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.security.core.userdetails.UserDetails; 6 | import org.springframework.security.core.userdetails.UserDetailsService; 7 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 8 | import org.springframework.stereotype.Service; 9 | import org.xueliang.springsecuritystudy.Constants; 10 | import org.xueliang.springsecuritystudy.model.User; 11 | 12 | @Service 13 | public class MyUserDetailsService implements UserDetailsService { 14 | 15 | @Override 16 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 17 | List userList = Constants.userList; 18 | for (int i = 0, len = userList.size(); i < len; i++) { 19 | User user = userList.get(i); 20 | if (user.getUsername().equals(username)) { 21 | return user; 22 | } 23 | } 24 | throw new UsernameNotFoundException("用户不存在!"); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/resources/config.properties: -------------------------------------------------------------------------------- 1 | api.csrftoken=/api/auth/csrf-token 2 | api.login=/api/auth/login 3 | api.logout=/api/auth/logout -------------------------------------------------------------------------------- /src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | springsecuritystudy 5 | logs/${protjectname}/${protjectname} 6 | %d %-5p [%c]\: %L - %m%n 7 | 8 | 9 | 10 | 11 | 12 | 13 | ${pattern} 14 | 15 | 16 | 17 | 18 | 19 | ${pattern} 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/main/webapp/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Class-Path: 3 | 4 | -------------------------------------------------------------------------------- /src/main/webapp/admin/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Admin page 5 | 6 | 7 | 8 |

Webcome! This is Admin page!

9 |

10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/main/webapp/dba/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | DBA page 5 | 6 | 7 |

Webcome! This is DBA page!

8 |

9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/main/webapp/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 登录 6 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 |
23 |
24 | 25 | 66 | 67 | -------------------------------------------------------------------------------- /src/main/webapp/static/js/common.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var $ = (selector) => document.querySelector(selector); 3 | // get csrf token 4 | var token = ''; 5 | fetch('/api/auth/csrf-token', {credentials: 'include'}).then(function(r) { 6 | return r.json(); 7 | }).then(function(response) { 8 | if (response.isOK) { 9 | token = response.msg.csrfToken; 10 | } 11 | }).then(function() { 12 | 13 | // 获取信息 14 | var apiUrl = document.body.getAttribute('data-api'); 15 | fetch(apiUrl + '?_csrf=' + token, {credentials: 'include', method: 'POST'}).then(function(r){ 16 | return r.json() 17 | }).then(function(response){ 18 | if (response.isOK) { 19 | var msg = response.msg; 20 | for (var key in msg) { 21 | $('.' + key).innerHTML = msg[key]; 22 | } 23 | $('.logout').style.display = 'inline'; 24 | /* alert('username: ' + user.username + '\n' + 'nickname: ' + user.nickname); */ 25 | } else { 26 | var errors = []; 27 | for (var key in response.error) { 28 | errors.push(response.error[key]); 29 | } 30 | alert(errors.join('\n')); 31 | location.href = '/welcome.html'; 32 | } 33 | }).catch(function(error) { 34 | console.info(error); 35 | }); 36 | 37 | $('.logout').addEventListener('click', function(event) { 38 | fetch('/api/auth/logout?_csrf=' + token, {credentials: 'include', method: 'POST'}).then(function(r){ 39 | return r.json(); 40 | }).then(function(response) { 41 | if (response.isOK) { 42 | alert('退出登录成功!'); 43 | location.href = '/index.html'; 44 | } 45 | }); 46 | }); 47 | }); 48 | })(); -------------------------------------------------------------------------------- /src/main/webapp/user/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | User page 5 | 6 | 7 |

Webcome! This is User page!

8 |

9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/main/webapp/welcome.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WELCOME! 6 | 7 | 8 | USER 9 | ADMIN 10 | DBA 11 | 25 | 26 | --------------------------------------------------------------------------------