├── web-security ├── settings.gradle ├── src │ ├── main │ │ ├── resources │ │ │ ├── static │ │ │ │ ├── test.txt │ │ │ │ └── images │ │ │ │ │ └── web-security-ignoring-test-file.png │ │ │ ├── application-test.yml │ │ │ ├── application-debug.yml │ │ │ ├── application-postgres.yml │ │ │ ├── application.yml │ │ │ ├── db │ │ │ │ ├── data.sql │ │ │ │ ├── README.md │ │ │ │ └── schema.sql │ │ │ └── templates │ │ │ │ └── index.html │ │ ├── vue │ │ │ ├── common │ │ │ │ ├── css │ │ │ │ │ ├── footer.scss │ │ │ │ │ └── common.scss │ │ │ │ ├── common.js │ │ │ │ ├── blank.vue │ │ │ │ └── mixins │ │ │ │ │ └── common-mixin.js │ │ │ ├── page │ │ │ │ ├── main.vue │ │ │ │ ├── login.vue │ │ │ │ └── index.vue │ │ │ ├── store │ │ │ │ └── store.js │ │ │ ├── index.js │ │ │ ├── App.vue │ │ │ ├── README.md │ │ │ └── route │ │ │ │ └── route.js │ │ └── java │ │ │ └── kr │ │ │ └── kdev │ │ │ └── demo │ │ │ ├── repository │ │ │ └── AbstractRepository.java │ │ │ ├── service │ │ │ ├── AbstractService.java │ │ │ ├── RepositoryService.java │ │ │ ├── UserService.java │ │ │ └── AuthenticationService.java │ │ │ ├── bean │ │ │ ├── CurrentUser.java │ │ │ ├── UserState.java │ │ │ ├── Repository.java │ │ │ └── User.java │ │ │ ├── WebSecurityApplication.java │ │ │ ├── config │ │ │ ├── WebConfig.java │ │ │ └── SecurityConfig.java │ │ │ ├── api │ │ │ ├── UserApi.java │ │ │ ├── LoginApi.java │ │ │ ├── BaseApi.java │ │ │ └── RepositoryApi.java │ │ │ ├── expression │ │ │ └── RepositoryChecker.java │ │ │ ├── controller │ │ │ └── IndexController.java │ │ │ └── interceptor │ │ │ └── UserStateInjectionInterceptor.java │ └── test │ │ └── java │ │ └── kr │ │ └── kdev │ │ └── demo │ │ ├── WebSecurityApplicationTests.java │ │ ├── context │ │ └── WithMockAdmin.java │ │ ├── config │ │ ├── ServiceTestConfigurer.java │ │ ├── ServiceTest.java │ │ └── ApiTestConfigurer.java │ │ ├── api │ │ ├── LoginApiTests.java │ │ └── UserApiTests.java │ │ ├── util │ │ └── RequestMatcherTests.java │ │ └── service │ │ ├── RepositoryServiceTests.java │ │ └── UserServiceTests.java ├── .eslintrc.js ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── postcss.config.js ├── .babelrc.js ├── .gitignore ├── build.gradle ├── package.json ├── gradlew.bat ├── webpack.config.js ├── gradlew └── README.md ├── web-security-session ├── settings.gradle ├── src │ ├── main │ │ ├── resources │ │ │ ├── application-embedded-redis.yml │ │ │ ├── static │ │ │ │ └── images │ │ │ │ │ └── max-sessions-prevents-login.PNG │ │ │ ├── application-debug.yml │ │ │ ├── templates │ │ │ │ └── index.html │ │ │ └── application.yml │ │ └── java │ │ │ └── kr │ │ │ └── kdev │ │ │ └── demo │ │ │ ├── controller │ │ │ └── IndexController.java │ │ │ ├── WebSecurityApplication.java │ │ │ ├── api │ │ │ ├── UserApi.java │ │ │ └── BaseApi.java │ │ │ └── config │ │ │ ├── SessionConfig.java │ │ │ └── SecurityConfig.java │ └── test │ │ └── java │ │ └── kr │ │ └── kdev │ │ └── demo │ │ └── WebSecurityApplicationTests.java ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── .gitignore ├── build.gradle ├── gradlew.bat ├── README.md └── gradlew ├── web-security-oauth-server ├── settings.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── src │ ├── main │ │ ├── resources │ │ │ ├── static │ │ │ │ └── images │ │ │ │ │ ├── token-endpoint-jwt.PNG │ │ │ │ │ ├── oauth-approval-scopes.PNG │ │ │ │ │ ├── token-endpoint-request-post.PNG │ │ │ │ │ └── token-endpoint-not-allowed-get.PNG │ │ │ ├── application-debug.yml │ │ │ ├── application.yml │ │ │ ├── db │ │ │ │ ├── user.json │ │ │ │ └── client.json │ │ │ ├── messages │ │ │ │ └── messages_ko_KR.yml │ │ │ └── templates │ │ │ │ └── index.ftl │ │ └── java │ │ │ └── kr │ │ │ └── kdev │ │ │ └── demo │ │ │ ├── service │ │ │ ├── AbstractService.java │ │ │ ├── AuthenticationService.java │ │ │ ├── UserService.java │ │ │ └── ClientService.java │ │ │ ├── controller │ │ │ └── IndexController.java │ │ │ ├── WebSecurityApplication.java │ │ │ ├── config │ │ │ ├── SessionConfig.java │ │ │ └── WebConfig.java │ │ │ ├── bean │ │ │ ├── CurrentUser.java │ │ │ ├── User.java │ │ │ └── Client.java │ │ │ ├── endpoint │ │ │ └── JwkSetEndpoint.java │ │ │ ├── interceptor │ │ │ └── PrincipalInjectionInterceptor.java │ │ │ └── api │ │ │ ├── BaseApi.java │ │ │ └── UserApi.java │ └── test │ │ └── java │ │ └── kr │ │ └── kdev │ │ └── demo │ │ └── WebSecurityApplicationTests.java ├── .gitignore ├── build.gradle ├── gradlew.bat └── gradlew ├── web-security-oauth-kakao-login ├── settings.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── src │ ├── main │ │ ├── resources │ │ │ ├── static │ │ │ │ └── images │ │ │ │ │ ├── kakao-app.PNG │ │ │ │ │ ├── oauth-login-form.PNG │ │ │ │ │ ├── kakao-app-information.PNG │ │ │ │ │ ├── kakao-app-redirect-uri.PNG │ │ │ │ │ ├── kakao-app-client-secret.PNG │ │ │ │ │ ├── kakao-app-settings-user.PNG │ │ │ │ │ ├── kakao-app-setting-platform.PNG │ │ │ │ │ ├── oauth-login-authentication-debug.PNG │ │ │ │ │ └── kakao-app-settings-user-redirect-uri.PNG │ │ │ ├── application-debug.yml │ │ │ ├── templates │ │ │ │ └── index.html │ │ │ └── application.yml │ │ └── java │ │ │ └── kr │ │ │ └── kdev │ │ │ └── demo │ │ │ ├── WebSecurityApplication.java │ │ │ ├── controller │ │ │ └── IndexController.java │ │ │ ├── bean │ │ │ └── KakaoOAuth2User.java │ │ │ ├── api │ │ │ ├── BaseApi.java │ │ │ └── UserApi.java │ │ │ └── config │ │ │ └── SecurityConfig.java │ └── test │ │ └── java │ │ └── kr │ │ └── kdev │ │ └── demo │ │ └── WebSecurityApplicationTests.java ├── .gitignore ├── build.gradle ├── gradlew.bat ├── gradlew └── README.md ├── .gitignore └── README.md /web-security/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'demo' 2 | -------------------------------------------------------------------------------- /web-security-session/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'demo' 2 | -------------------------------------------------------------------------------- /web-security-oauth-server/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'demo' 2 | -------------------------------------------------------------------------------- /web-security-oauth-kakao-login/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'demo' 2 | -------------------------------------------------------------------------------- /web-security/src/main/resources/static/test.txt: -------------------------------------------------------------------------------- 1 | You can download test.txt because web security ignore static locations. -------------------------------------------------------------------------------- /web-security/src/main/resources/application-test.yml: -------------------------------------------------------------------------------- 1 | logging: 2 | level: 3 | org.springframework.test.context: ERROR -------------------------------------------------------------------------------- /web-security-session/src/main/resources/application-embedded-redis.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | redis: 3 | host: localhost 4 | port: 6380 -------------------------------------------------------------------------------- /web-security/.eslintrc.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | extends: ['plugin:vue/recommended'], 4 | rules: { 5 | 6 | } 7 | } -------------------------------------------------------------------------------- /web-security/src/main/vue/common/css/footer.scss: -------------------------------------------------------------------------------- 1 | #sticky-footer { 2 | position: fixed; 3 | bottom: 0; 4 | width: 100%; 5 | } -------------------------------------------------------------------------------- /web-security/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kdevkr/spring-demo-security/HEAD/web-security/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /web-security-session/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kdevkr/spring-demo-security/HEAD/web-security-session/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /web-security/src/main/java/kr/kdev/demo/repository/AbstractRepository.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.repository; 2 | 3 | public abstract class AbstractRepository { 4 | } 5 | -------------------------------------------------------------------------------- /web-security/src/main/vue/page/main.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /web-security-oauth-server/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kdevkr/spring-demo-security/HEAD/web-security-oauth-server/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /web-security-oauth-kakao-login/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kdevkr/spring-demo-security/HEAD/web-security-oauth-kakao-login/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /web-security-oauth-kakao-login/src/main/resources/static/images/kakao-app.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kdevkr/spring-demo-security/HEAD/web-security-oauth-kakao-login/src/main/resources/static/images/kakao-app.PNG -------------------------------------------------------------------------------- /web-security/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('postcss-cssnext'), 4 | require('precss'), 5 | require('autoprefixer'), 6 | require('cssnano') 7 | ] 8 | } -------------------------------------------------------------------------------- /web-security-oauth-server/src/main/resources/static/images/token-endpoint-jwt.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kdevkr/spring-demo-security/HEAD/web-security-oauth-server/src/main/resources/static/images/token-endpoint-jwt.PNG -------------------------------------------------------------------------------- /web-security/src/main/resources/static/images/web-security-ignoring-test-file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kdevkr/spring-demo-security/HEAD/web-security/src/main/resources/static/images/web-security-ignoring-test-file.png -------------------------------------------------------------------------------- /web-security-oauth-kakao-login/src/main/resources/static/images/oauth-login-form.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kdevkr/spring-demo-security/HEAD/web-security-oauth-kakao-login/src/main/resources/static/images/oauth-login-form.PNG -------------------------------------------------------------------------------- /web-security-oauth-server/src/main/resources/static/images/oauth-approval-scopes.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kdevkr/spring-demo-security/HEAD/web-security-oauth-server/src/main/resources/static/images/oauth-approval-scopes.PNG -------------------------------------------------------------------------------- /web-security-session/src/main/resources/static/images/max-sessions-prevents-login.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kdevkr/spring-demo-security/HEAD/web-security-session/src/main/resources/static/images/max-sessions-prevents-login.PNG -------------------------------------------------------------------------------- /web-security-oauth-kakao-login/src/main/resources/static/images/kakao-app-information.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kdevkr/spring-demo-security/HEAD/web-security-oauth-kakao-login/src/main/resources/static/images/kakao-app-information.PNG -------------------------------------------------------------------------------- /web-security-oauth-kakao-login/src/main/resources/static/images/kakao-app-redirect-uri.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kdevkr/spring-demo-security/HEAD/web-security-oauth-kakao-login/src/main/resources/static/images/kakao-app-redirect-uri.PNG -------------------------------------------------------------------------------- /web-security-oauth-server/src/main/resources/static/images/token-endpoint-request-post.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kdevkr/spring-demo-security/HEAD/web-security-oauth-server/src/main/resources/static/images/token-endpoint-request-post.PNG -------------------------------------------------------------------------------- /web-security-oauth-kakao-login/src/main/resources/static/images/kakao-app-client-secret.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kdevkr/spring-demo-security/HEAD/web-security-oauth-kakao-login/src/main/resources/static/images/kakao-app-client-secret.PNG -------------------------------------------------------------------------------- /web-security-oauth-kakao-login/src/main/resources/static/images/kakao-app-settings-user.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kdevkr/spring-demo-security/HEAD/web-security-oauth-kakao-login/src/main/resources/static/images/kakao-app-settings-user.PNG -------------------------------------------------------------------------------- /web-security-oauth-kakao-login/src/main/resources/static/images/kakao-app-setting-platform.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kdevkr/spring-demo-security/HEAD/web-security-oauth-kakao-login/src/main/resources/static/images/kakao-app-setting-platform.PNG -------------------------------------------------------------------------------- /web-security-oauth-server/src/main/resources/static/images/token-endpoint-not-allowed-get.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kdevkr/spring-demo-security/HEAD/web-security-oauth-server/src/main/resources/static/images/token-endpoint-not-allowed-get.PNG -------------------------------------------------------------------------------- /web-security/src/main/resources/application-debug.yml: -------------------------------------------------------------------------------- 1 | logging: 2 | level: 3 | org: 4 | springframework: 5 | web: 6 | servlet: 7 | mvc: INFO 8 | 9 | spring: 10 | http: 11 | log-request-details: true -------------------------------------------------------------------------------- /web-security-oauth-kakao-login/src/main/resources/static/images/oauth-login-authentication-debug.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kdevkr/spring-demo-security/HEAD/web-security-oauth-kakao-login/src/main/resources/static/images/oauth-login-authentication-debug.PNG -------------------------------------------------------------------------------- /web-security-session/src/main/resources/application-debug.yml: -------------------------------------------------------------------------------- 1 | logging: 2 | level: 3 | org: 4 | springframework: 5 | web: 6 | servlet: 7 | mvc: INFO 8 | 9 | spring: 10 | http: 11 | log-request-details: true -------------------------------------------------------------------------------- /web-security-oauth-server/src/main/resources/application-debug.yml: -------------------------------------------------------------------------------- 1 | logging: 2 | level: 3 | org: 4 | springframework: 5 | web: 6 | servlet: 7 | mvc: INFO 8 | 9 | spring: 10 | http: 11 | log-request-details: true -------------------------------------------------------------------------------- /web-security-oauth-kakao-login/src/main/resources/application-debug.yml: -------------------------------------------------------------------------------- 1 | logging: 2 | level: 3 | org: 4 | springframework: 5 | web: 6 | servlet: 7 | mvc: INFO 8 | 9 | spring: 10 | http: 11 | log-request-details: true -------------------------------------------------------------------------------- /web-security-oauth-kakao-login/src/main/resources/static/images/kakao-app-settings-user-redirect-uri.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kdevkr/spring-demo-security/HEAD/web-security-oauth-kakao-login/src/main/resources/static/images/kakao-app-settings-user-redirect-uri.PNG -------------------------------------------------------------------------------- /web-security/.babelrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: ['syntax-dynamic-import'], 3 | presets: [ 4 | [ 5 | '@babel/preset-env', 6 | { 7 | modules: false 8 | } 9 | ] 10 | ], 11 | } -------------------------------------------------------------------------------- /web-security/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /web-security-session/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /web-security/src/main/vue/store/store.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | Vue.use(Vuex) 5 | 6 | export default new Vuex.Store({ 7 | state: { 8 | 9 | }, 10 | mutations: { 11 | 12 | }, 13 | actions: { 14 | 15 | } 16 | }) -------------------------------------------------------------------------------- /web-security-oauth-server/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /web-security-oauth-kakao-login/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /web-security/src/main/java/kr/kdev/demo/service/AbstractService.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.service; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | public abstract class AbstractService { 7 | protected Logger LOG = LoggerFactory.getLogger(getClass()); 8 | } 9 | -------------------------------------------------------------------------------- /web-security-oauth-server/src/main/java/kr/kdev/demo/service/AbstractService.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.service; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | public abstract class AbstractService { 7 | protected Logger LOG = LoggerFactory.getLogger(getClass()); 8 | } 9 | -------------------------------------------------------------------------------- /web-security/src/main/resources/application-postgres.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | datasource: 3 | driver-class-name: org.postgresql.Driver 4 | hikari: 5 | username: kdevkr 6 | password: password 7 | maximum-pool-size: 100 8 | minimum-idle: 50 9 | url: 'jdbc:postgresql://localhost:5432/demo' -------------------------------------------------------------------------------- /web-security/src/main/vue/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App' 3 | import '~/vue/common/common' 4 | 5 | import store from '~/vue/store/store' 6 | import router from '~/vue/route/route' 7 | 8 | new Vue({ 9 | router, 10 | store, 11 | el: '#app', 12 | render: h => h(App) 13 | }) -------------------------------------------------------------------------------- /web-security/src/test/java/kr/kdev/demo/WebSecurityApplicationTests.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class WebSecurityApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /web-security-session/src/test/java/kr/kdev/demo/WebSecurityApplicationTests.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class WebSecurityApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /web-security-oauth-server/src/test/java/kr/kdev/demo/WebSecurityApplicationTests.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class WebSecurityApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /web-security-oauth-kakao-login/src/test/java/kr/kdev/demo/WebSecurityApplicationTests.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class WebSecurityApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /web-security/src/main/vue/common/common.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import BootstrapVue from 'bootstrap-vue' 3 | import Vuelidate from 'vuelidate' 4 | import axios from 'axios' 5 | import '~/vue/common/css/common.scss' 6 | 7 | Vue.use(BootstrapVue) 8 | Vue.use(Vuelidate) 9 | 10 | axios.defaults.withCredentials = true 11 | Vue.prototype.$http = axios 12 | window.$http = axios -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | 5 | ### STS ### 6 | .apt_generated 7 | .classpath 8 | .factorypath 9 | .project 10 | .settings 11 | .springBeans 12 | bin/ 13 | 14 | ### IntelliJ IDEA ### 15 | .idea 16 | *.iws 17 | *.iml 18 | *.ipr 19 | 20 | ### NetBeans ### 21 | nbproject/private/ 22 | build/ 23 | nbbuild/ 24 | dist/ 25 | nbdist/ 26 | .nb-gradle/ -------------------------------------------------------------------------------- /web-security/src/main/vue/common/css/common.scss: -------------------------------------------------------------------------------- 1 | @import '~bootstrap'; 2 | @import '~bootstrap-vue'; 3 | @import 'footer.scss'; 4 | 5 | .form-group { 6 | &.text-value { 7 | .form-row { 8 | .col { 9 | padding-top: calc(.375rem + 1px); 10 | padding-bottom: calc(.375rem + 1px); 11 | } 12 | } 13 | } 14 | } 15 | 16 | .login-form { 17 | max-width: 350px; 18 | } -------------------------------------------------------------------------------- /web-security/src/main/java/kr/kdev/demo/bean/CurrentUser.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.bean; 2 | 3 | import org.springframework.security.core.annotation.AuthenticationPrincipal; 4 | 5 | import java.lang.annotation.*; 6 | 7 | @Target({ ElementType.PARAMETER, ElementType.ANNOTATION_TYPE }) 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @Documented 10 | @AuthenticationPrincipal 11 | public @interface CurrentUser { 12 | } -------------------------------------------------------------------------------- /web-security/src/test/java/kr/kdev/demo/context/WithMockAdmin.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.context; 2 | 3 | import org.springframework.security.test.context.support.WithMockUser; 4 | 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | 8 | @Retention(RetentionPolicy.RUNTIME) 9 | @WithMockUser(value = "admin", roles = "ADMIN") 10 | public @interface WithMockAdmin { 11 | } 12 | -------------------------------------------------------------------------------- /web-security-oauth-server/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | freemarker: 3 | suffix: .ftl 4 | 5 | resources: 6 | static-locations: 'classpath:/META-INF/resources/, classpath:/resources/, classpath:/static/, classpath:/public/, classpath:/META-INF/resources/webjars' 7 | 8 | server: 9 | forward-headers-strategy: NATIVE 10 | 11 | logging: 12 | level: 13 | kr: 14 | kdev: 15 | demo: DEBUG -------------------------------------------------------------------------------- /web-security/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | freemarker: 3 | suffix: .html 4 | 5 | resources: 6 | static-locations: 'classpath:/META-INF/resources/, classpath:/resources/, classpath:/static/, classpath:/public/, classpath:/dist/, classpath:/META-INF/resources/webjars' 7 | datasource: 8 | driver-class-name: org.h2.Driver 9 | 10 | logging: 11 | level: 12 | kr: 13 | kdev: 14 | demo: DEBUG 15 | -------------------------------------------------------------------------------- /web-security-session/src/main/java/kr/kdev/demo/controller/IndexController.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.controller; 2 | 3 | import org.springframework.stereotype.Controller; 4 | import org.springframework.ui.Model; 5 | import org.springframework.web.bind.annotation.GetMapping; 6 | 7 | 8 | @Controller 9 | public class IndexController { 10 | 11 | @GetMapping("/") 12 | public String index(Model model) { 13 | return "index"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /web-security-oauth-server/src/main/java/kr/kdev/demo/controller/IndexController.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.controller; 2 | 3 | import org.springframework.stereotype.Controller; 4 | import org.springframework.ui.Model; 5 | import org.springframework.web.bind.annotation.GetMapping; 6 | 7 | 8 | @Controller 9 | public class IndexController { 10 | 11 | @GetMapping("/") 12 | public String index(Model model) { 13 | return "index"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /web-security/src/main/vue/App.vue: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /web-security-session/src/main/resources/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Spring Demo Security 8 | 9 | 10 |
11 |
Hello World
12 |
13 | 14 | -------------------------------------------------------------------------------- /web-security/src/main/java/kr/kdev/demo/bean/UserState.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.bean; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.Locale; 6 | import java.util.TimeZone; 7 | 8 | @Data 9 | public class UserState { 10 | private String userId = ""; 11 | private String name = ""; 12 | private String role = ""; 13 | private TimeZone timeZone = TimeZone.getDefault(); 14 | private Locale locale = Locale.getDefault(); 15 | private String sessionId = ""; 16 | } 17 | -------------------------------------------------------------------------------- /web-security/src/main/vue/common/blank.vue: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 19 | -------------------------------------------------------------------------------- /web-security/src/main/resources/db/data.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO users (id, password, name, role) VALUES ('admin', '{bcrypt}$2a$12$ry/T4SyQyiNpaWbadf9sne3Cko..q92Oh2klkCMv4XB1qG6cy8iaG', 'admin', 'ADMIN'); 2 | INSERT INTO users (id, password, name, role) VALUES ('user', '{bcrypt}$2a$12$ry/T4SyQyiNpaWbadf9sne3Cko..q92Oh2klkCMv4XB1qG6cy8iaG', 'user', 'USER'); 3 | 4 | INSERT INTO users_authority (user_id, authority) 5 | SELECT user_id, 'REPOSITORY_CREATE' FROM users where id = 'user' ON CONFLICT (user_id, authority) DO NOTHING; -------------------------------------------------------------------------------- /web-security-session/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | freemarker: 3 | suffix: .html 4 | 5 | resources: 6 | static-locations: 'classpath:/META-INF/resources/, classpath:/resources/, classpath:/static/, classpath:/public/, classpath:/META-INF/resources/webjars' 7 | session: 8 | store-type: redis 9 | 10 | 11 | logging: 12 | level: 13 | kr: 14 | kdev: 15 | demo: DEBUG 16 | server: 17 | servlet: 18 | session: 19 | timeout: 60m 20 | persistent: true -------------------------------------------------------------------------------- /web-security-session/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/** 6 | !**/src/test/** 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | 17 | ### IntelliJ IDEA ### 18 | .idea 19 | *.iws 20 | *.iml 21 | *.ipr 22 | out/ 23 | 24 | ### NetBeans ### 25 | /nbproject/private/ 26 | /nbbuild/ 27 | /dist/ 28 | /nbdist/ 29 | /.nb-gradle/ 30 | 31 | ### VS Code ### 32 | .vscode/ 33 | -------------------------------------------------------------------------------- /web-security-oauth-server/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/** 6 | !**/src/test/** 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | 17 | ### IntelliJ IDEA ### 18 | .idea 19 | *.iws 20 | *.iml 21 | *.ipr 22 | out/ 23 | 24 | ### NetBeans ### 25 | /nbproject/private/ 26 | /nbbuild/ 27 | /dist/ 28 | /nbdist/ 29 | /.nb-gradle/ 30 | 31 | ### VS Code ### 32 | .vscode/ 33 | -------------------------------------------------------------------------------- /web-security-oauth-kakao-login/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/** 6 | !**/src/test/** 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | 17 | ### IntelliJ IDEA ### 18 | .idea 19 | *.iws 20 | *.iml 21 | *.ipr 22 | out/ 23 | 24 | ### NetBeans ### 25 | /nbproject/private/ 26 | /nbbuild/ 27 | /dist/ 28 | /nbdist/ 29 | /.nb-gradle/ 30 | 31 | ### VS Code ### 32 | .vscode/ 33 | 34 | application-kakao.yml -------------------------------------------------------------------------------- /web-security/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/** 6 | !**/src/test/** 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | 17 | ### IntelliJ IDEA ### 18 | .idea 19 | *.iws 20 | *.iml 21 | *.ipr 22 | out/ 23 | 24 | ### NetBeans ### 25 | /nbproject/private/ 26 | /nbbuild/ 27 | /dist/ 28 | /nbdist/ 29 | /.nb-gradle/ 30 | 31 | ### VS Code ### 32 | .vscode/ 33 | 34 | node_modules/ 35 | src/main/resources/dist/ -------------------------------------------------------------------------------- /web-security/src/main/java/kr/kdev/demo/bean/Repository.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.bean; 2 | 3 | import lombok.Data; 4 | 5 | import javax.validation.constraints.NotBlank; 6 | 7 | /** 8 | * 저장소 9 | */ 10 | @Data 11 | public class Repository { 12 | private String id; 13 | @NotBlank private String name; 14 | private String description; 15 | private boolean isPublic = true; 16 | 17 | private String ownerId; 18 | 19 | private long created = System.currentTimeMillis(); 20 | private long modified = System.currentTimeMillis(); 21 | } 22 | -------------------------------------------------------------------------------- /web-security/src/main/java/kr/kdev/demo/WebSecurityApplication.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; 6 | 7 | @SpringBootApplication 8 | public class WebSecurityApplication extends SpringBootServletInitializer { 9 | 10 | public static void main(String[] args) { 11 | SpringApplication.run(WebSecurityApplication.class, args); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /web-security/src/main/vue/common/mixins/common-mixin.js: -------------------------------------------------------------------------------- 1 | export default { 2 | mixins: [], 3 | data() { 4 | // this.getState() 5 | 6 | return { 7 | state: window.state 8 | } 9 | }, 10 | methods: { 11 | getState() { 12 | async function state() { 13 | let response = await $http.get('/api/state') 14 | return response 15 | } 16 | 17 | state().then((res) => { 18 | this.state = res.data 19 | }) 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /web-security-session/src/main/java/kr/kdev/demo/WebSecurityApplication.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; 6 | 7 | @SpringBootApplication 8 | public class WebSecurityApplication extends SpringBootServletInitializer { 9 | 10 | public static void main(String[] args) { 11 | SpringApplication.run(WebSecurityApplication.class, args); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /web-security/src/main/resources/db/README.md: -------------------------------------------------------------------------------- 1 | ## PostgreSQL with Docker 2 | ```sh 3 | # install postgres container 4 | docker run -p 5432:5432 --name postgres -e POSTGRES_PASSWORD=postgres -d postgres:9.6 5 | 6 | # connect postgres container using bash 7 | docker exec -i -t postgres /bin/bash 8 | 9 | # change to postgres user 10 | su - postgres 11 | 12 | # 13 | psql 14 | 15 | # create database 16 | create database demo; 17 | 18 | # create user and grant 19 | create user kdevkr with encrypted password 'password'; 20 | grant all privileges on database demo to kdevkr; 21 | ``` -------------------------------------------------------------------------------- /web-security-oauth-server/src/main/java/kr/kdev/demo/WebSecurityApplication.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; 6 | 7 | @SpringBootApplication 8 | public class WebSecurityApplication extends SpringBootServletInitializer { 9 | 10 | public static void main(String[] args) { 11 | SpringApplication.run(WebSecurityApplication.class, args); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /web-security-oauth-kakao-login/src/main/java/kr/kdev/demo/WebSecurityApplication.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; 6 | 7 | @SpringBootApplication 8 | public class WebSecurityApplication extends SpringBootServletInitializer { 9 | 10 | public static void main(String[] args) { 11 | SpringApplication.run(WebSecurityApplication.class, args); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /web-security-oauth-kakao-login/src/main/resources/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Spring Demo Security 8 | 9 | 10 |
11 |

${authentication.name}

12 |

13 | ${authentication.principal} 14 |

15 |
16 | 17 | -------------------------------------------------------------------------------- /web-security-oauth-server/src/main/java/kr/kdev/demo/config/SessionConfig.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.security.core.session.SessionRegistry; 6 | import org.springframework.security.core.session.SessionRegistryImpl; 7 | 8 | @Configuration(proxyBeanMethods = false) 9 | public class SessionConfig { 10 | 11 | @Bean 12 | public SessionRegistry sessionRegistry() { 13 | return new SessionRegistryImpl(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /web-security-oauth-server/src/main/java/kr/kdev/demo/bean/CurrentUser.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.bean; 2 | 3 | import org.springframework.security.core.annotation.AuthenticationPrincipal; 4 | 5 | import java.lang.annotation.Documented; 6 | import java.lang.annotation.ElementType; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | import java.lang.annotation.Target; 10 | 11 | @Target({ElementType.PARAMETER, ElementType.TYPE}) 12 | @Retention(RetentionPolicy.RUNTIME) 13 | @Documented 14 | @AuthenticationPrincipal 15 | public @interface CurrentUser { 16 | } 17 | -------------------------------------------------------------------------------- /web-security-oauth-server/src/main/resources/db/user.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "userId": 1, 4 | "name": "system", 5 | "id": "system", 6 | "password": "$2a$12$ry/T4SyQyiNpaWbadf9sne3Cko..q92Oh2klkCMv4XB1qG6cy8iaG", 7 | "role": "SYSTEM" 8 | }, 9 | { 10 | "userId": 2, 11 | "name": "admin", 12 | "id": "admin", 13 | "password": "$2a$12$ry/T4SyQyiNpaWbadf9sne3Cko..q92Oh2klkCMv4XB1qG6cy8iaG", 14 | "role": "ADMIN" 15 | }, 16 | { 17 | "userId": 3, 18 | "name": "user", 19 | "id": "user", 20 | "password": "$2a$12$ry/T4SyQyiNpaWbadf9sne3Cko..q92Oh2klkCMv4XB1qG6cy8iaG", 21 | "role": "USER" 22 | } 23 | ] -------------------------------------------------------------------------------- /web-security/src/main/java/kr/kdev/demo/config/WebConfig.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.config; 2 | 3 | import kr.kdev.demo.interceptor.UserStateInjectionInterceptor; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.web.servlet.config.annotation.InterceptorRegistry; 6 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 7 | 8 | @Configuration(proxyBeanMethods = false) 9 | public class WebConfig { 10 | 11 | @Configuration 12 | public static class WebMvcConfig implements WebMvcConfigurer { 13 | 14 | @Override 15 | public void addInterceptors(InterceptorRegistry registry) { 16 | registry.addInterceptor(new UserStateInjectionInterceptor()); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /web-security-oauth-server/src/main/java/kr/kdev/demo/config/WebConfig.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.config; 2 | 3 | import kr.kdev.demo.interceptor.PrincipalInjectionInterceptor; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.web.servlet.config.annotation.InterceptorRegistry; 6 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 7 | 8 | @Configuration(proxyBeanMethods = false) 9 | public class WebConfig { 10 | 11 | @Configuration 12 | public static class WebMvcConfig implements WebMvcConfigurer { 13 | 14 | @Override 15 | public void addInterceptors(InterceptorRegistry registry) { 16 | registry.addInterceptor(new PrincipalInjectionInterceptor()); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /web-security/src/main/vue/README.md: -------------------------------------------------------------------------------- 1 | ```sh 2 | npm init -y 3 | 4 | # npm install for webpack. 5 | npm i -D webpack webpack-cli webpack-dev-server webpack-merge mini-css-extract-plugin friendly-errors-webpack-plugin compression-webpack-plugin 6 | npm i -D file-loader css-loader style-loader style-resources-loader sass-loader node-sass 7 | 8 | # npm install for babel. 9 | npm i -D babel-loader babel-polyfill @babel/core @babel/polyfill @babel/preset-env babel-plugin-syntax-dynamic-import 10 | 11 | # npm install for vuejs. 12 | npm i -D vue vue-loader vue-router vuex vue-style-loader vue-template-compiler 13 | npm i -D bootstrap bootstrap-vue axios 14 | 15 | # npm install for postcss. 16 | npm i -D postcss-loader postcss-cssnext precss cssnano 17 | 18 | # npm install for eslint. 19 | npm i -D eslint eslint-loader eslint-plugin-vue 20 | 21 | ``` -------------------------------------------------------------------------------- /web-security-oauth-server/src/main/resources/db/client.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "client_1", 4 | "clientId": "e0113ca5-486f-43e9-9615-674b2232caed", 5 | "clientSecret": "$2a$12$ry/T4SyQyiNpaWbadf9sne3Cko..q92Oh2klkCMv4XB1qG6cy8iaG", 6 | "authorizedGrantTypes": "authorization_code, client_credentials, refresh_token", 7 | "registeredRedirectUri": "http://localhost:8080", 8 | "accessTokenValidity": 86400, 9 | "refreshTokenValidity": 604800 10 | }, 11 | { 12 | "name": "client_2", 13 | "clientId": "429c4db4-821c-414e-bccd-2f4826ea08c4", 14 | "clientSecret": "$2a$12$ry/T4SyQyiNpaWbadf9sne3Cko..q92Oh2klkCMv4XB1qG6cy8iaG", 15 | "authorizedGrantTypes": "authorization_code, implicit, client_credentials", 16 | "registeredRedirectUri": "http://localhost:8080", 17 | "accessTokenValidity": 86400 18 | } 19 | ] -------------------------------------------------------------------------------- /web-security-oauth-kakao-login/src/main/java/kr/kdev/demo/controller/IndexController.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.controller; 2 | 3 | import org.springframework.security.core.Authentication; 4 | import org.springframework.security.core.context.SecurityContext; 5 | import org.springframework.security.core.context.SecurityContextHolder; 6 | import org.springframework.stereotype.Controller; 7 | import org.springframework.ui.Model; 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | 10 | 11 | @Controller 12 | public class IndexController { 13 | 14 | @GetMapping("/") 15 | public String index(Model model) { 16 | SecurityContext context = SecurityContextHolder.getContext(); 17 | Authentication authentication = context.getAuthentication(); 18 | model.addAttribute("authentication", authentication); 19 | return "index"; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /web-security/src/test/java/kr/kdev/demo/config/ServiceTestConfigurer.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.config; 2 | 3 | import org.junit.Before; 4 | import org.junit.runner.RunWith; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.security.core.context.SecurityContext; 8 | import org.springframework.security.core.context.SecurityContextHolder; 9 | import org.springframework.test.context.ActiveProfiles; 10 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 11 | 12 | @ActiveProfiles({"test"}) 13 | @RunWith(SpringJUnit4ClassRunner.class) 14 | @ServiceTest 15 | public class ServiceTestConfigurer { 16 | 17 | protected Logger LOG = LoggerFactory.getLogger(getClass()); 18 | protected SecurityContext securityContext; 19 | 20 | @Before 21 | public void setUp() { 22 | this.securityContext = SecurityContextHolder.getContext(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /web-security-oauth-kakao-login/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | freemarker: 3 | suffix: .html 4 | 5 | resources: 6 | static-locations: 'classpath:/META-INF/resources/, classpath:/resources/, classpath:/static/, classpath:/public/, classpath:/META-INF/resources/webjars' 7 | security: 8 | oauth2: 9 | client: 10 | registration: 11 | kakao: 12 | client-id: 13 | client-secert: 14 | redirect-uri: '{baseUrl}/login/oauth2/code/{registrationId}' 15 | authorization-grant-type: authorization_code 16 | provider: 17 | kakao: 18 | authorization-uri: https://kauth.kakao.com/oauth/authorize 19 | token-uri: https://kauth.kakao.com/oauth/token 20 | user-info-uri: https://kapi.kakao.com/v2/user/me 21 | user-info-authentication-method: POST 22 | 23 | logging: 24 | level: 25 | kr: 26 | kdev: 27 | demo: DEBUG -------------------------------------------------------------------------------- /web-security/src/main/vue/route/route.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter from 'vue-router' 3 | 4 | // const Blank = () => import(/* webpackChunkName: "common-view" */ '~/vue/common/blank') 5 | // const Index = () => import(/* webpackChunkName: "index-view" */ '~/vue/page/index') 6 | // const Main = () => import(/* webpackChunkName: "main-view" */ '~/vue/page/main') 7 | 8 | import Blank from '~/vue/common/blank' 9 | import Index from '~/vue/page/index' 10 | import Main from '~/vue/page/main' 11 | import Login from '~/vue/page/login' 12 | 13 | Vue.use(VueRouter) 14 | 15 | const routes = [ 16 | { 17 | path: '', 18 | component: Index, 19 | meta: { 20 | title: 'index' 21 | } 22 | }, 23 | { 24 | path: '/login', 25 | component: Login, 26 | meta: { 27 | title: 'login' 28 | }, 29 | } 30 | ] 31 | 32 | export default new VueRouter({ 33 | mode: 'history', 34 | routes 35 | }) -------------------------------------------------------------------------------- /web-security/src/main/resources/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Spring Demo Security 5 | 6 | 7 | 8 | 9 | 24 | 25 | 26 |
27 | 28 | 29 | -------------------------------------------------------------------------------- /web-security/src/main/java/kr/kdev/demo/api/UserApi.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.api; 2 | 3 | import org.springframework.http.ResponseEntity; 4 | import org.springframework.security.core.Authentication; 5 | import org.springframework.security.core.annotation.AuthenticationPrincipal; 6 | import org.springframework.security.core.context.SecurityContextHolder; 7 | import org.springframework.security.core.userdetails.UserDetails; 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | @RestController 12 | public class UserApi extends BaseApi { 13 | 14 | @GetMapping("/users/me") 15 | public ResponseEntity currentUser(@AuthenticationPrincipal UserDetails principal) { 16 | Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 17 | if(authentication != null) { 18 | principal = (UserDetails) authentication.getPrincipal(); 19 | } 20 | 21 | return ResponseEntity.ok(principal); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /web-security-oauth-server/src/main/java/kr/kdev/demo/endpoint/JwkSetEndpoint.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.endpoint; 2 | 3 | import com.nimbusds.jose.jwk.JWKSet; 4 | import com.nimbusds.jose.jwk.RSAKey; 5 | import org.springframework.security.oauth2.provider.endpoint.FrameworkEndpoint; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.ResponseBody; 8 | 9 | import java.security.KeyPair; 10 | import java.security.Principal; 11 | import java.security.interfaces.RSAPublicKey; 12 | import java.util.Map; 13 | 14 | //@FrameworkEndpoint 15 | public class JwkSetEndpoint { 16 | private final KeyPair keyPair; 17 | 18 | public JwkSetEndpoint(KeyPair keyPair) { 19 | this.keyPair = keyPair; 20 | } 21 | 22 | @GetMapping("/.well-known/jwks.json") 23 | @ResponseBody 24 | public Map getKey(Principal principal) { 25 | RSAPublicKey publicKey = (RSAPublicKey) this.keyPair.getPublic(); 26 | RSAKey key = new RSAKey.Builder(publicKey).build(); 27 | return new JWKSet(key).toJSONObject(); 28 | } 29 | } -------------------------------------------------------------------------------- /web-security-session/src/main/java/kr/kdev/demo/api/UserApi.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.api; 2 | 3 | import org.springframework.http.ResponseEntity; 4 | import org.springframework.security.core.Authentication; 5 | import org.springframework.security.core.annotation.AuthenticationPrincipal; 6 | import org.springframework.security.core.context.SecurityContextHolder; 7 | import org.springframework.security.core.userdetails.UserDetails; 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | @RestController 12 | public class UserApi extends BaseApi { 13 | 14 | @GetMapping("/users/me") 15 | public ResponseEntity currentUser(@AuthenticationPrincipal UserDetails principal) { 16 | Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 17 | if(authentication != null) { 18 | principal = (UserDetails) authentication.getPrincipal(); 19 | } 20 | 21 | return ResponseEntity.ok(principal); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /web-security/src/main/java/kr/kdev/demo/expression/RepositoryChecker.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.expression; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.security.access.prepost.PreAuthorize; 6 | import org.springframework.security.core.Authentication; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | public class RepositoryChecker { 11 | private static final Logger LOG = LoggerFactory.getLogger(RepositoryChecker.class); 12 | 13 | @PreAuthorize("isAuthenticated()") 14 | public boolean owner(Authentication authentication, String id) { 15 | // TODO : 인증된 사용자가 ownerId와 같은지를 검증합니다. 16 | if(id == null) { 17 | LOG.error("id is null"); 18 | return false; 19 | } 20 | return true; 21 | } 22 | 23 | @PreAuthorize("isAuthenticated()") 24 | public boolean ownerWithName(Authentication authentication, String id, String name) { 25 | // TODO : 인증된 사용자가 ownerId와 같은지 리파지토리 이름이 같은지 검증합니다. 26 | if(id == null || name == null) { 27 | LOG.error("id or name is null"); 28 | return false; 29 | } 30 | return true; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /web-security-oauth-server/src/main/java/kr/kdev/demo/interceptor/PrincipalInjectionInterceptor.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.interceptor; 2 | 3 | import org.springframework.security.core.Authentication; 4 | import org.springframework.security.core.context.SecurityContextHolder; 5 | import org.springframework.web.context.request.RequestContextHolder; 6 | import org.springframework.web.servlet.HandlerInterceptor; 7 | import org.springframework.web.servlet.ModelAndView; 8 | 9 | import javax.servlet.http.HttpServletRequest; 10 | import javax.servlet.http.HttpServletResponse; 11 | 12 | public class PrincipalInjectionInterceptor implements HandlerInterceptor { 13 | @Override 14 | public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { 15 | if(modelAndView != null) { 16 | String sessionId = RequestContextHolder.getRequestAttributes().getSessionId(); 17 | Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 18 | 19 | modelAndView.addObject("authentication", authentication); 20 | modelAndView.addObject("sessionId", sessionId); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /web-security/src/main/resources/db/schema.sql: -------------------------------------------------------------------------------- 1 | -- User 2 | CREATE TABLE IF NOT EXISTS users 3 | ( 4 | user_id BIGSERIAL NOT NULL, 5 | id VARCHAR NOT NULL, 6 | password VARCHAR NOT NULL, 7 | name VARCHAR NOT NULL, 8 | role VARCHAR NOT NULL, 9 | CONSTRAINT users_pk PRIMARY KEY (user_id), 10 | CONSTRAINT users_uq UNIQUE (id) 11 | ); 12 | 13 | -- authority of User 14 | CREATE TABLE IF NOT EXISTS users_authority 15 | ( 16 | user_id BIGINT NOT NULL, 17 | authority VARCHAR NOT NULL, 18 | CONSTRAINT users_authority_pk PRIMARY KEY (user_id, authority), 19 | CONSTRAINT users_authority_user_id_fk FOREIGN KEY (user_id) REFERENCES users (user_id) 20 | ); 21 | 22 | -- Repository 23 | CREATE TABLE IF NOT EXISTS repository 24 | ( 25 | id BIGSERIAL NOT NULL, 26 | name VARCHAR NOT NULL, 27 | description VARCHAR, 28 | is_public BOOLEAN NOT NULL DEFAULT FALSE, 29 | created TIMESTAMP NOT NULL DEFAULT (now() at time zone 'utc'), 30 | modified TIMESTAMP NOT NULL DEFAULT (now() at time zone 'utc'), 31 | owner_id BIGINT NOT NULL, 32 | CONSTRAINT repository_pk PRIMARY KEY (id), 33 | CONSTRAINT repository_owner_id_fk FOREIGN KEY (owner_id) REFERENCES users (user_id) 34 | ); 35 | 36 | -------------------------------------------------------------------------------- /web-security/src/test/java/kr/kdev/demo/config/ServiceTest.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.config; 2 | 3 | import org.springframework.boot.autoconfigure.ImportAutoConfiguration; 4 | import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; 5 | import org.springframework.boot.autoconfigure.web.ResourceProperties; 6 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 7 | import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest; 8 | import org.springframework.boot.test.autoconfigure.jdbc.TestDatabaseAutoConfiguration; 9 | import org.springframework.context.annotation.ComponentScan; 10 | import org.springframework.test.context.ContextConfiguration; 11 | 12 | import java.lang.annotation.*; 13 | 14 | /** 15 | * Apply test environment for Service Layer based on {@link JdbcTest} 16 | */ 17 | @Target(ElementType.TYPE) 18 | @Retention(RetentionPolicy.RUNTIME) 19 | @Documented 20 | @Inherited 21 | @JdbcTest(excludeAutoConfiguration = TestDatabaseAutoConfiguration.class) 22 | @ImportAutoConfiguration(classes = { 23 | SecurityAutoConfiguration.class 24 | }) 25 | @EnableConfigurationProperties({ ResourceProperties.class }) 26 | @ComponentScan(basePackages = { 27 | "kr.kdev.demo.service", 28 | "kr.kdev.demo.repository" 29 | }) 30 | @ContextConfiguration(classes = { 31 | SecurityConfig.class 32 | }) 33 | public @interface ServiceTest { 34 | } 35 | -------------------------------------------------------------------------------- /web-security/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.2.4.RELEASE' 3 | id 'io.spring.dependency-management' version '1.0.9.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'kr.kdev' 8 | version = '0.0.1-SNAPSHOT' 9 | sourceCompatibility = JavaVersion.current() 10 | 11 | configurations { 12 | compileOnly { 13 | extendsFrom annotationProcessor 14 | } 15 | } 16 | 17 | repositories { 18 | mavenCentral() 19 | } 20 | 21 | dependencies { 22 | implementation 'org.springframework.boot:spring-boot-starter-freemarker' 23 | implementation 'org.springframework.boot:spring-boot-starter-security' 24 | implementation 'org.springframework.boot:spring-boot-starter-web' 25 | implementation 'org.springframework.boot:spring-boot-starter-jdbc' 26 | implementation 'com.h2database:h2' 27 | implementation 'org.postgresql:postgresql' 28 | 29 | compileOnly 'org.projectlombok:lombok' 30 | annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' 31 | annotationProcessor 'org.projectlombok:lombok' 32 | testImplementation('org.springframework.boot:spring-boot-starter-test') { 33 | exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' 34 | } 35 | testImplementation 'org.springframework.security:spring-security-test' 36 | testImplementation 'junit:junit' 37 | } 38 | 39 | test { 40 | useJUnitPlatform() 41 | } 42 | -------------------------------------------------------------------------------- /web-security-oauth-kakao-login/src/main/java/kr/kdev/demo/bean/KakaoOAuth2User.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.bean; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import lombok.Data; 5 | import org.springframework.security.core.GrantedAuthority; 6 | import org.springframework.security.core.authority.AuthorityUtils; 7 | import org.springframework.security.oauth2.core.user.OAuth2User; 8 | 9 | import java.util.Collection; 10 | import java.util.HashMap; 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | /** 15 | * 카카오 OAuth2 사용자 16 | * 17 | * @author kdevkr 18 | */ 19 | @Data 20 | public class KakaoOAuth2User implements OAuth2User { 21 | public static final String PROVIDER = "kakao"; 22 | 23 | private List authorities = AuthorityUtils.createAuthorityList("ROLE_USER"); 24 | private String id; 25 | @JsonProperty("kakao_account") 26 | private Map kakaoAccount; 27 | private Map properties; 28 | 29 | @Override 30 | public Map getAttributes() { 31 | Map attributes = new HashMap<>(); 32 | attributes.putAll(kakaoAccount); 33 | attributes.putAll(properties); 34 | return attributes; 35 | } 36 | 37 | @Override 38 | public Collection getAuthorities() { 39 | return authorities; 40 | } 41 | 42 | @Override 43 | public String getName() { 44 | return this.id; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /web-security-session/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.2.4.RELEASE' 3 | id 'io.spring.dependency-management' version '1.0.9.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'kr.kdev' 8 | version = '0.0.1-SNAPSHOT' 9 | sourceCompatibility = JavaVersion.current() 10 | 11 | configurations { 12 | compileOnly { 13 | extendsFrom annotationProcessor 14 | } 15 | } 16 | 17 | repositories { 18 | mavenCentral() 19 | } 20 | 21 | dependencies { 22 | implementation 'org.springframework.boot:spring-boot-starter-freemarker' 23 | implementation 'org.springframework.boot:spring-boot-starter-security' 24 | implementation 'org.springframework.boot:spring-boot-starter-web' 25 | implementation 'org.springframework.boot:spring-boot-starter-jdbc' 26 | implementation 'org.springframework.session:spring-session-data-redis' 27 | implementation 'io.lettuce:lettuce-core' 28 | implementation 'it.ozimov:embedded-redis:0.7.2' 29 | implementation 'com.h2database:h2:1.4.200' 30 | 31 | compileOnly 'org.projectlombok:lombok' 32 | annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' 33 | annotationProcessor 'org.projectlombok:lombok' 34 | testImplementation('org.springframework.boot:spring-boot-starter-test') { 35 | exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' 36 | } 37 | testImplementation 'org.springframework.security:spring-security-test' 38 | } 39 | 40 | test { 41 | useJUnitPlatform() 42 | } 43 | -------------------------------------------------------------------------------- /web-security/src/main/java/kr/kdev/demo/service/RepositoryService.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.service; 2 | 3 | import kr.kdev.demo.bean.Repository; 4 | import org.springframework.security.access.prepost.PreAuthorize; 5 | import org.springframework.security.core.parameters.P; 6 | import org.springframework.stereotype.Service; 7 | 8 | import java.util.UUID; 9 | 10 | @Service 11 | public class RepositoryService extends AbstractService { 12 | 13 | /** 14 | * 신규 리파지토리 생성 15 | * 인증된 사용자가 REPOSITORY_CREATE 권한을 가져야만 접근할 수 있도록 제한합니다. 16 | */ 17 | @PreAuthorize("hasAuthority('REPOSITORY_CREATE')") 18 | public String create(Repository repository) { 19 | // TODO : Implementation 20 | LOG.info("Call create method successfully!"); 21 | return UUID.randomUUID().toString(); 22 | } 23 | 24 | /** 25 | * 리파지토리 수정 26 | * {@link Repository}의 소유자와 인증 {@link java.security.Principal}에 포함된 사용자 아이디를 비교하여 27 | * 리파지토리 정보를 수정할 수 있는 소유자인지 확인하여 접근 제어를 수행합니다. 28 | */ 29 | @PreAuthorize("@repositoryChecker.owner(authentication, #repository.id)") 30 | public boolean update(@P("repository") Repository repository) { 31 | // TODO : Implementation 32 | LOG.info("Call update method successfully!"); 33 | return true; 34 | } 35 | 36 | @PreAuthorize("@repositoryChecker.ownerWithName(authentication, #id, #name)") 37 | public boolean delete(String id, String name) { 38 | LOG.info("Call delete method successfully!"); 39 | return true; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /web-security-oauth-kakao-login/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.2.4.RELEASE' 3 | id 'io.spring.dependency-management' version '1.0.9.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'kr.kdev' 8 | version = '0.0.1-SNAPSHOT' 9 | sourceCompatibility = JavaVersion.current() 10 | 11 | configurations { 12 | compileOnly { 13 | extendsFrom annotationProcessor 14 | } 15 | } 16 | 17 | repositories { 18 | mavenCentral() 19 | } 20 | 21 | dependencies { 22 | implementation 'org.springframework.boot:spring-boot-starter-freemarker' 23 | implementation 'org.springframework.boot:spring-boot-starter-security' 24 | implementation 'org.springframework.boot:spring-boot-starter-web' 25 | implementation 'org.springframework.boot:spring-boot-starter-jdbc' 26 | implementation 'org.springframework.security:spring-security-oauth2-client' 27 | // implementation 'org.springframework.session:spring-session-core' 28 | implementation 'com.h2database:h2:1.4.200' 29 | implementation 'com.vladsch.flexmark:flexmark-all:0.60.2' 30 | 31 | compileOnly 'org.projectlombok:lombok' 32 | annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' 33 | annotationProcessor 'org.projectlombok:lombok' 34 | testImplementation('org.springframework.boot:spring-boot-starter-test') { 35 | exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' 36 | } 37 | testImplementation 'org.springframework.security:spring-security-test' 38 | } 39 | 40 | test { 41 | useJUnitPlatform() 42 | } 43 | -------------------------------------------------------------------------------- /web-security-oauth-server/src/main/resources/messages/messages_ko_KR.yml: -------------------------------------------------------------------------------- 1 | AbstractAccessDecisionManager: 2 | accessDenied: 접근이 거부되었습니다. 3 | 4 | AbstractSecurityInterceptor: 5 | authenticationNotFound: SecurityContext에서 Authentication 객체를 찾을 수 없습니다. 6 | 7 | AbstractUserDetailsAuthenticationProvider: 8 | badCredentials: 자격 증명에 실패하였습니다. 9 | credentialsExpired: 자격 증명 유효 기간이 만료되었습니다. 10 | disabled: 유효하지 않은 사용자입니다. 11 | expired: 사용자 계정의 유효 기간이 만료 되었습니다. 12 | locked: 사용자 계정이 잠겨 있습니다. 13 | onlySupports: UsernamePasswordAuthenticationToken만 지원합니다. 14 | 15 | AccountStatusUserDetailsChecker: 16 | credentialsExpired: 자격 증명 유효 기간이 만료되었습니다. 17 | disabled: 유효하지 않은 사용자입니다. 18 | expired: 사용자 계정의 유효 기간이 만료 되었습니다. 19 | locked: 사용자 계정이 잠겨 있습니다. 20 | 21 | AnonymousAuthenticationProvider: 22 | incorrectKey: 제공된 AnonymousAuthenticationToken에는 필요로하는 key가 없습니다. 23 | 24 | BindAuthenticator: 25 | badCredentials: 자격 증명에 실패하였습니다. 26 | emptyPassword: 비밀번호 항목이 비어 있습니다. 27 | 28 | ConcurrentSessionControlAuthenticationStrategy: 29 | exceededAllowed: 최대 세션 허용 수 {0}개를 초과하였습니다. 30 | 31 | PasswordComparisonAuthenticator: 32 | badCredentials: 자격 증명에 실패하였습니다. 33 | 34 | PersistentTokenBasedRememberMeServices: 35 | cookieStolen: 로그인 상태 유지를 위한 토큰이 일치하지 않습니다. 이전에 사용한 토큰이 타인으로부터 탈취 당했을 수 있습니다. 36 | 37 | ProviderManager: 38 | providerNotFound: {0}을 위한 AuthenticationProvider를 찾을 수 없습니다. 39 | 40 | RememberMeAuthenticationProvider: 41 | incorrectKey: 제공된 RememberMeAuthenticationToken에는 필요로 하는 key가 없습니다. 42 | 43 | SubjectDnX509PrincipalExtractor: 44 | noMatching: 'subjectDN\: {0} 내에 매칭되는 패턴이 없습니다.' -------------------------------------------------------------------------------- /web-security/src/main/java/kr/kdev/demo/controller/IndexController.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.controller; 2 | 3 | import kr.kdev.demo.bean.CurrentUser; 4 | import kr.kdev.demo.bean.User; 5 | import kr.kdev.demo.bean.UserState; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.security.core.annotation.AuthenticationPrincipal; 9 | import org.springframework.security.core.userdetails.UserDetails; 10 | import org.springframework.stereotype.Controller; 11 | import org.springframework.ui.Model; 12 | import org.springframework.web.bind.annotation.GetMapping; 13 | import org.springframework.web.context.request.RequestContextHolder; 14 | 15 | import javax.servlet.http.HttpServletRequest; 16 | import java.security.Principal; 17 | import java.util.UUID; 18 | 19 | 20 | @Controller 21 | public class IndexController { 22 | 23 | private static Logger LOG = LoggerFactory.getLogger(IndexController.class); 24 | 25 | public IndexController() {} 26 | 27 | @GetMapping("/") 28 | public String index(HttpServletRequest request, Model model) { 29 | model.addAttribute("entryJS", "/dist/index.js"); 30 | return "index"; 31 | } 32 | 33 | @GetMapping("/login") 34 | public String login(HttpServletRequest request, Model model) { 35 | model.addAttribute("entryJS", "/dist/index.js"); 36 | return "index"; 37 | } 38 | 39 | @GetMapping("/console") 40 | public String console(HttpServletRequest request, Model model) { 41 | model.addAttribute("entryJS", "/dist/console.js"); 42 | return "index"; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /web-security-oauth-server/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.2.4.RELEASE' 3 | id 'io.spring.dependency-management' version '1.0.9.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'kr.kdev' 8 | version = '0.0.1-SNAPSHOT' 9 | sourceCompatibility = JavaVersion.current() 10 | 11 | configurations { 12 | compileOnly { 13 | extendsFrom annotationProcessor 14 | } 15 | } 16 | 17 | repositories { 18 | mavenCentral() 19 | } 20 | 21 | dependencies { 22 | implementation 'org.springframework.boot:spring-boot-starter-freemarker' 23 | implementation 'org.springframework.boot:spring-boot-starter-security' 24 | implementation 'org.springframework.boot:spring-boot-starter-web' 25 | implementation 'org.springframework.boot:spring-boot-starter-jdbc' 26 | implementation 'org.springframework.security:spring-security-oauth2-jose' 27 | implementation 'org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:2.2.4.RELEASE' 28 | implementation 'com.h2database:h2:1.4.200' 29 | implementation 'com.google.code.gson:gson:2.8.6' 30 | 31 | compileOnly 'org.projectlombok:lombok' 32 | annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' 33 | annotationProcessor 'org.projectlombok:lombok' 34 | testImplementation('org.springframework.boot:spring-boot-starter-test') { 35 | exclude group: 'org.junit.vintage', module: 'junit-vintage-engine' 36 | } 37 | testImplementation 'org.springframework.security:spring-security-test' 38 | } 39 | 40 | test { 41 | useJUnitPlatform() 42 | } 43 | -------------------------------------------------------------------------------- /web-security/src/main/java/kr/kdev/demo/api/LoginApi.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.api; 2 | 3 | import kr.kdev.demo.bean.User; 4 | import kr.kdev.demo.service.AuthenticationService; 5 | import org.springframework.http.ResponseEntity; 6 | import org.springframework.security.core.Authentication; 7 | import org.springframework.security.core.context.SecurityContext; 8 | import org.springframework.security.core.context.SecurityContextHolder; 9 | import org.springframework.web.bind.annotation.*; 10 | 11 | import javax.servlet.ServletException; 12 | import javax.servlet.http.HttpServletRequest; 13 | 14 | @RestController 15 | public class LoginApi extends BaseApi { 16 | 17 | private final AuthenticationService authenticationService; 18 | 19 | public LoginApi(AuthenticationService authenticationService) { 20 | this.authenticationService = authenticationService; 21 | } 22 | 23 | @PostMapping("login") 24 | public ResponseEntity login(@ModelAttribute User user) { 25 | user.setId(user.getUsername()); 26 | User principal = (User) authenticationService.authentication(user); 27 | return ResponseEntity.ok(principal); 28 | } 29 | 30 | @GetMapping("logout") 31 | public ResponseEntity logout(HttpServletRequest request) throws ServletException { 32 | SecurityContext securityContext = SecurityContextHolder.getContext(); 33 | Authentication authentication = securityContext.getAuthentication(); 34 | if(authentication != null) { 35 | request.logout(); 36 | } 37 | 38 | return ResponseEntity.ok(true); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /web-security/src/test/java/kr/kdev/demo/config/ApiTestConfigurer.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.config; 2 | 3 | import org.junit.Before; 4 | import org.junit.runner.RunWith; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; 9 | import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; 10 | import org.springframework.boot.test.context.SpringBootTest; 11 | import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers; 12 | import org.springframework.test.context.ContextConfiguration; 13 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 14 | import org.springframework.test.context.web.WebAppConfiguration; 15 | import org.springframework.test.web.servlet.MockMvc; 16 | import org.springframework.test.web.servlet.setup.MockMvcBuilders; 17 | import org.springframework.web.context.WebApplicationContext; 18 | 19 | @WebAppConfiguration 20 | @AutoConfigureMockMvc 21 | @SpringBootTest 22 | @RunWith(SpringJUnit4ClassRunner.class) 23 | public class ApiTestConfigurer { 24 | 25 | protected Logger LOG = LoggerFactory.getLogger(getClass()); 26 | 27 | @Autowired 28 | protected WebApplicationContext context; 29 | protected MockMvc mvc; 30 | 31 | @Before 32 | public void setup() { 33 | this.mvc = MockMvcBuilders 34 | .webAppContextSetup(context) 35 | .apply(SecurityMockMvcConfigurers.springSecurity()) 36 | .build(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /web-security-oauth-server/src/main/java/kr/kdev/demo/service/AuthenticationService.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.service; 2 | 3 | import kr.kdev.demo.bean.User; 4 | import org.springframework.security.authentication.AuthenticationProvider; 5 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 6 | import org.springframework.security.core.Authentication; 7 | import org.springframework.security.core.context.SecurityContext; 8 | import org.springframework.security.core.context.SecurityContextHolder; 9 | import org.springframework.security.core.userdetails.UserDetails; 10 | import org.springframework.stereotype.Service; 11 | 12 | @Service 13 | public class AuthenticationService extends AbstractService { 14 | private final AuthenticationProvider authenticationProvider; 15 | 16 | public AuthenticationService(AuthenticationProvider authenticationProvider) { 17 | this.authenticationProvider = authenticationProvider; 18 | } 19 | 20 | public UserDetails authentication(User user) { 21 | return authentication(user.getId(), user.getPassword()); 22 | } 23 | 24 | public UserDetails authentication(String id, String password) { 25 | SecurityContext securityContext = SecurityContextHolder.getContext(); 26 | UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(id, password); 27 | Authentication authentication = authenticationProvider.authenticate(authenticationToken); 28 | securityContext.setAuthentication(authentication); 29 | return (UserDetails) authentication.getPrincipal(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /web-security-oauth-server/src/main/java/kr/kdev/demo/service/UserService.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.service; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.reflect.TypeToken; 5 | import kr.kdev.demo.bean.User; 6 | import org.springframework.core.io.ClassPathResource; 7 | import org.springframework.security.core.userdetails.UserDetails; 8 | import org.springframework.security.core.userdetails.UserDetailsService; 9 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 10 | import org.springframework.stereotype.Service; 11 | import org.springframework.util.StreamUtils; 12 | 13 | import java.io.IOException; 14 | import java.nio.charset.StandardCharsets; 15 | import java.util.List; 16 | 17 | @Service 18 | public class UserService implements UserDetailsService { 19 | 20 | private List users; 21 | 22 | public UserService() throws IOException { 23 | loadUsers(); 24 | } 25 | 26 | private void loadUsers() throws IOException { 27 | Gson gson = new Gson(); 28 | ClassPathResource classPathResource = new ClassPathResource("db/user.json"); 29 | String clientsJson = StreamUtils.copyToString(classPathResource.getInputStream(), StandardCharsets.UTF_8); 30 | this.users = gson.fromJson(clientsJson, new TypeToken>(){}.getType()); 31 | } 32 | 33 | @Override 34 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 35 | 36 | if(users != null) { 37 | for(User user : users) { 38 | if(user.getUsername().equals(username)) { 39 | return user; 40 | } 41 | } 42 | } 43 | 44 | 45 | return null; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /web-security/src/main/java/kr/kdev/demo/service/UserService.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.service; 2 | 3 | import kr.kdev.demo.bean.User; 4 | import org.springframework.jdbc.core.BeanPropertyRowMapper; 5 | import org.springframework.jdbc.core.JdbcTemplate; 6 | import org.springframework.security.core.userdetails.UserDetails; 7 | import org.springframework.security.core.userdetails.UserDetailsService; 8 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 9 | import org.springframework.stereotype.Service; 10 | 11 | /** 12 | * Custom UserDetailsService Implementation. 13 | * @author kdevkr 14 | */ 15 | @Service 16 | public class UserService implements UserDetailsService { 17 | 18 | private final JdbcTemplate jdbcTemplate; 19 | public UserService(JdbcTemplate jdbcTemplate) { 20 | this.jdbcTemplate = jdbcTemplate; 21 | } 22 | 23 | @Override 24 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 25 | if("system".equals(username)) { 26 | throw new UsernameNotFoundException("error.notFound"); 27 | } 28 | 29 | User user = this.getUser(username); 30 | if(user == null) { 31 | throw new UsernameNotFoundException("error.notFound"); 32 | } 33 | 34 | return user; 35 | } 36 | 37 | public User getUser(String id) { 38 | User user = jdbcTemplate.queryForObject("SELECT u.*, array_remove(array_agg(ua.authority), NULL) AS authorities FROM users u" + 39 | " LEFT JOIN users_authority ua ON u.user_id = ua.user_id" + 40 | " WHERE u.id = ?" + 41 | " GROUP BY u.user_id", new Object[]{id}, new BeanPropertyRowMapper<>(User.class)); 42 | return user; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /web-security-oauth-server/src/main/java/kr/kdev/demo/service/ClientService.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.service; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.reflect.TypeToken; 5 | import kr.kdev.demo.bean.Client; 6 | import org.springframework.core.io.ClassPathResource; 7 | import org.springframework.security.oauth2.provider.ClientDetails; 8 | import org.springframework.security.oauth2.provider.ClientDetailsService; 9 | import org.springframework.security.oauth2.provider.ClientRegistrationException; 10 | import org.springframework.stereotype.Service; 11 | import org.springframework.util.StreamUtils; 12 | 13 | import java.io.IOException; 14 | import java.nio.charset.StandardCharsets; 15 | import java.util.List; 16 | 17 | @Service 18 | public class ClientService implements ClientDetailsService { 19 | 20 | private List clients; 21 | 22 | public ClientService() throws IOException { 23 | loadClients(); 24 | } 25 | 26 | private void loadClients() throws IOException { 27 | Gson gson = new Gson(); 28 | ClassPathResource classPathResource = new ClassPathResource("db/client.json"); 29 | String clientsJson = StreamUtils.copyToString(classPathResource.getInputStream(), StandardCharsets.UTF_8); 30 | this.clients = gson.fromJson(clientsJson, new TypeToken>(){}.getType()); 31 | } 32 | 33 | @Override 34 | public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException { 35 | 36 | if(clients != null) { 37 | for (Client client : clients) { 38 | if (client.getClientId().equals(clientId)) { 39 | return client; 40 | } 41 | } 42 | } 43 | 44 | return null; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /web-security-session/src/main/java/kr/kdev/demo/api/BaseApi.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.api; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.context.MessageSource; 5 | import org.springframework.http.HttpStatus; 6 | import org.springframework.http.ResponseEntity; 7 | import org.springframework.web.bind.annotation.*; 8 | import org.springframework.web.client.HttpClientErrorException; 9 | 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | 13 | @RestControllerAdvice 14 | @RequestMapping("/api") 15 | public abstract class BaseApi { 16 | 17 | protected static String ERROR = "error"; 18 | protected static String TIMESTAMP = "timestamp"; 19 | protected static String MESSAGE = "message"; 20 | 21 | @Autowired protected MessageSource messageSource; 22 | 23 | @ExceptionHandler(value = Exception.class) 24 | @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) 25 | public ResponseEntity> handleException(Exception e) { 26 | e.printStackTrace(); 27 | Map data = new HashMap<>(); 28 | data.put(TIMESTAMP, System.currentTimeMillis()); 29 | data.put(ERROR, e.getMessage()); 30 | return new ResponseEntity<>(data, HttpStatus.INTERNAL_SERVER_ERROR); 31 | } 32 | 33 | @ExceptionHandler(value = {HttpClientErrorException.Forbidden.class}) 34 | @ResponseStatus(HttpStatus.FORBIDDEN) 35 | public ResponseEntity> unForbiddenException(Exception e) { 36 | Map data = new HashMap<>(); 37 | data.put(TIMESTAMP, System.currentTimeMillis()); 38 | data.put(ERROR, HttpStatus.FORBIDDEN.getReasonPhrase()); 39 | return new ResponseEntity<>(data, HttpStatus.FORBIDDEN); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /web-security/src/main/java/kr/kdev/demo/api/BaseApi.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.api; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.context.MessageSource; 5 | import org.springframework.http.HttpStatus; 6 | import org.springframework.http.ResponseEntity; 7 | import org.springframework.web.bind.annotation.*; 8 | import org.springframework.web.client.HttpClientErrorException; 9 | 10 | import java.util.HashMap; 11 | import java.util.Locale; 12 | import java.util.Map; 13 | 14 | @RestControllerAdvice 15 | @RequestMapping("/api") 16 | public abstract class BaseApi { 17 | 18 | protected static String ERROR = "error"; 19 | protected static String TIMESTAMP = "timestamp"; 20 | protected static String MESSAGE = "message"; 21 | 22 | @Autowired protected MessageSource messageSource; 23 | 24 | @ExceptionHandler(value = Exception.class) 25 | @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) 26 | public ResponseEntity> handleException(Exception e) { 27 | e.printStackTrace(); 28 | Map data = new HashMap<>(); 29 | data.put(TIMESTAMP, System.currentTimeMillis()); 30 | data.put(ERROR, e.getMessage()); 31 | return new ResponseEntity<>(data, HttpStatus.INTERNAL_SERVER_ERROR); 32 | } 33 | 34 | @ExceptionHandler(value = {HttpClientErrorException.Forbidden.class}) 35 | @ResponseStatus(HttpStatus.FORBIDDEN) 36 | public ResponseEntity> unForbiddenException(Exception e) { 37 | Map data = new HashMap<>(); 38 | data.put(TIMESTAMP, System.currentTimeMillis()); 39 | data.put(ERROR, HttpStatus.FORBIDDEN.getReasonPhrase()); 40 | return new ResponseEntity<>(data, HttpStatus.FORBIDDEN); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /web-security-oauth-kakao-login/src/main/java/kr/kdev/demo/api/BaseApi.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.api; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.context.MessageSource; 5 | import org.springframework.http.HttpStatus; 6 | import org.springframework.http.ResponseEntity; 7 | import org.springframework.web.bind.annotation.*; 8 | import org.springframework.web.client.HttpClientErrorException; 9 | 10 | import java.util.HashMap; 11 | import java.util.Locale; 12 | import java.util.Map; 13 | 14 | @RestControllerAdvice 15 | @RequestMapping("/api") 16 | public abstract class BaseApi { 17 | 18 | protected static String ERROR = "error"; 19 | protected static String TIMESTAMP = "timestamp"; 20 | protected static String MESSAGE = "message"; 21 | 22 | @Autowired protected MessageSource messageSource; 23 | 24 | @ExceptionHandler(value = Exception.class) 25 | @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) 26 | public ResponseEntity> handleException(Exception e) { 27 | e.printStackTrace(); 28 | Map data = new HashMap<>(); 29 | data.put(TIMESTAMP, System.currentTimeMillis()); 30 | data.put(ERROR, e.getMessage()); 31 | return new ResponseEntity<>(data, HttpStatus.INTERNAL_SERVER_ERROR); 32 | } 33 | 34 | @ExceptionHandler(value = {HttpClientErrorException.Forbidden.class}) 35 | @ResponseStatus(HttpStatus.FORBIDDEN) 36 | public ResponseEntity> unForbiddenException(Exception e) { 37 | Map data = new HashMap<>(); 38 | data.put(TIMESTAMP, System.currentTimeMillis()); 39 | data.put(ERROR, HttpStatus.FORBIDDEN.getReasonPhrase()); 40 | return new ResponseEntity<>(data, HttpStatus.FORBIDDEN); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /web-security/src/main/java/kr/kdev/demo/api/RepositoryApi.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.api; 2 | 3 | import kr.kdev.demo.bean.CurrentUser; 4 | import kr.kdev.demo.bean.Repository; 5 | import kr.kdev.demo.bean.User; 6 | import kr.kdev.demo.service.RepositoryService; 7 | import org.springframework.http.ResponseEntity; 8 | import org.springframework.security.crypto.encrypt.Encryptors; 9 | import org.springframework.security.crypto.encrypt.TextEncryptor; 10 | import org.springframework.web.bind.annotation.*; 11 | 12 | @RestController 13 | public class RepositoryApi extends BaseApi { 14 | 15 | private final RepositoryService repositoryService; 16 | 17 | public RepositoryApi(RepositoryService repositoryService) { 18 | this.repositoryService = repositoryService; 19 | } 20 | 21 | @PostMapping("/repository") 22 | public ResponseEntity create(@ModelAttribute Repository repository, @CurrentUser User user) { 23 | TextEncryptor textEncryptor = Encryptors.noOpText(); 24 | String userId = user.getUserId(); 25 | 26 | repository.setId(textEncryptor.encrypt(repository.getName() + "/" + userId)); 27 | repository.setOwnerId(userId); 28 | 29 | String newRepositoryId = repositoryService.create(repository); 30 | return ResponseEntity.ok(newRepositoryId); 31 | } 32 | 33 | @PutMapping("/repository/{repositoryId}") 34 | public ResponseEntity update(@PathVariable String repositoryId, 35 | @ModelAttribute Repository repository) { 36 | repository.setId(repositoryId); 37 | return ResponseEntity.ok(repositoryService.update(repository)); 38 | } 39 | 40 | @DeleteMapping("/repository/{repositoryId}") 41 | public ResponseEntity delete(@PathVariable String repositoryId) { 42 | return ResponseEntity.ok(null); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /web-security-oauth-server/src/main/java/kr/kdev/demo/api/BaseApi.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.api; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.context.MessageSource; 7 | import org.springframework.http.HttpStatus; 8 | import org.springframework.http.ResponseEntity; 9 | import org.springframework.web.bind.annotation.*; 10 | import org.springframework.web.client.HttpClientErrorException; 11 | 12 | import java.util.HashMap; 13 | import java.util.Locale; 14 | import java.util.Map; 15 | 16 | @RestControllerAdvice 17 | @RequestMapping("/api") 18 | public abstract class BaseApi { 19 | 20 | protected final Logger LOG = LoggerFactory.getLogger(getClass()); 21 | 22 | protected static String ERROR = "error"; 23 | protected static String TIMESTAMP = "timestamp"; 24 | protected static String MESSAGE = "message"; 25 | 26 | @Autowired protected MessageSource messageSource; 27 | 28 | @ExceptionHandler(value = Exception.class) 29 | @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) 30 | public ResponseEntity> handleException(Exception e) { 31 | e.printStackTrace(); 32 | Map data = new HashMap<>(); 33 | data.put(TIMESTAMP, System.currentTimeMillis()); 34 | data.put(ERROR, e.getMessage()); 35 | return new ResponseEntity<>(data, HttpStatus.INTERNAL_SERVER_ERROR); 36 | } 37 | 38 | @ExceptionHandler(value = {HttpClientErrorException.Forbidden.class}) 39 | @ResponseStatus(HttpStatus.FORBIDDEN) 40 | public ResponseEntity> unForbiddenException(Exception e) { 41 | Map data = new HashMap<>(); 42 | data.put(TIMESTAMP, System.currentTimeMillis()); 43 | data.put(ERROR, HttpStatus.FORBIDDEN.getReasonPhrase()); 44 | return new ResponseEntity<>(data, HttpStatus.FORBIDDEN); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /web-security/src/main/java/kr/kdev/demo/service/AuthenticationService.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.service; 2 | 3 | import kr.kdev.demo.bean.User; 4 | import org.springframework.security.authentication.AuthenticationProvider; 5 | import org.springframework.security.authentication.ProviderManager; 6 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 7 | import org.springframework.security.core.Authentication; 8 | import org.springframework.security.core.context.SecurityContext; 9 | import org.springframework.security.core.context.SecurityContextHolder; 10 | import org.springframework.security.core.userdetails.UserDetails; 11 | import org.springframework.stereotype.Service; 12 | 13 | /** 14 | * invoke authentication 15 | * 16 | * @author kdevkr 17 | */ 18 | @Service 19 | public class AuthenticationService extends AbstractService { 20 | private final AuthenticationProvider authenticationProvider; 21 | 22 | public AuthenticationService(AuthenticationProvider authenticationProvider) { 23 | this.authenticationProvider = authenticationProvider; 24 | } 25 | 26 | public UserDetails authentication(User user) { 27 | return authentication(user.getId(), user.getPassword()); 28 | } 29 | 30 | /** 31 | * 사용자의 아이디와 패스워드를 토큰 정보로 구성하여 {@link ProviderManager}로 인증 처리를 요청한 뒤 32 | * {@link SecurityContextHolder}로 부터 받아온 {@link SecurityContext}에 인증 정보를 적재합니다. 33 | */ 34 | public UserDetails authentication(String id, String password) { 35 | LOG.debug("Call authentication({}, {})", id, password); 36 | SecurityContext securityContext = SecurityContextHolder.getContext(); 37 | UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(id, password); 38 | Authentication authentication = authenticationProvider.authenticate(authenticationToken); 39 | securityContext.setAuthentication(authentication); 40 | return (UserDetails) authentication.getPrincipal(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /web-security/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spring-demo-security", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "webpack": "webpack --config webpack.config.js", 8 | "dev": "webpack -d --config webpack.config.js --watch", 9 | "dev:server": "webpack-dev-server", 10 | "build": "npm run webpack --prettier" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/kdevkr/spring-demo-security.git" 15 | }, 16 | "author": "", 17 | "license": "ISC", 18 | "bugs": { 19 | "url": "https://github.com/kdevkr/spring-demo-security/issues" 20 | }, 21 | "homepage": "https://github.com/kdevkr/spring-demo-security#readme", 22 | "keywords": [], 23 | "devDependencies": { 24 | "@babel/core": "^7.8.7", 25 | "@babel/polyfill": "^7.8.7", 26 | "@babel/preset-env": "^7.8.7", 27 | "axios": "^0.19.2", 28 | "babel-loader": "^8.0.6", 29 | "babel-plugin-syntax-dynamic-import": "^6.18.0", 30 | "babel-polyfill": "^6.26.0", 31 | "bootstrap": "^4.4.1", 32 | "bootstrap-vue": "^2.6.1", 33 | "compression-webpack-plugin": "^3.1.0", 34 | "css-loader": "^3.4.2", 35 | "cssnano": "^4.1.10", 36 | "eslint": "^6.8.0", 37 | "eslint-loader": "^3.0.3", 38 | "eslint-plugin-vue": "^6.2.2", 39 | "file-loader": "^5.1.0", 40 | "friendly-errors-webpack-plugin": "^1.7.0", 41 | "mini-css-extract-plugin": "^0.9.0", 42 | "node-sass": "^4.13.1", 43 | "postcss-cssnext": "^3.1.0", 44 | "postcss-loader": "^3.0.0", 45 | "precss": "^4.0.0", 46 | "sass-loader": "^8.0.2", 47 | "style-loader": "^1.1.3", 48 | "style-resources-loader": "^1.3.3", 49 | "vue": "^2.6.11", 50 | "vue-loader": "^15.9.0", 51 | "vue-router": "^3.1.6", 52 | "vue-style-loader": "^4.1.2", 53 | "vue-template-compiler": "^2.6.11", 54 | "vuelidate": "^0.7.5", 55 | "vuex": "^3.1.3", 56 | "webpack": "^4.42.0", 57 | "webpack-dev-server": "^3.10.3", 58 | "webpack-merge": "^4.2.2" 59 | }, 60 | "dependencies": { 61 | "webpack-cli": "^3.3.11" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /web-security/src/test/java/kr/kdev/demo/api/LoginApiTests.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.api; 2 | 3 | import kr.kdev.demo.config.ApiTestConfigurer; 4 | import org.junit.Assert; 5 | import org.junit.FixMethodOrder; 6 | import org.junit.Test; 7 | import org.junit.runners.MethodSorters; 8 | import org.springframework.mock.web.MockHttpServletResponse; 9 | import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders; 10 | import org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers; 11 | import org.springframework.test.web.servlet.MvcResult; 12 | import org.springframework.test.web.servlet.result.MockMvcResultHandlers; 13 | 14 | @FixMethodOrder(value = MethodSorters.NAME_ASCENDING) 15 | public class LoginApiTests extends ApiTestConfigurer { 16 | 17 | @Test 18 | public void TEST_000_formLogin() throws Exception { 19 | MvcResult mvcResult = mvc.perform(SecurityMockMvcRequestBuilders.formLogin().user("admin").password("password")) 20 | .andExpect(SecurityMockMvcResultMatchers.authenticated()) 21 | .andDo(MockMvcResultHandlers.print()) 22 | .andReturn(); 23 | 24 | MockHttpServletResponse response = mvcResult.getResponse(); 25 | int status = response.getStatus(); 26 | Assert.assertEquals(302, status); 27 | } 28 | 29 | @Test 30 | public void TEST_000_formLoginInvalidPassword() throws Exception { 31 | mvc.perform(SecurityMockMvcRequestBuilders.formLogin().user("admin").password("admin")) 32 | .andExpect(SecurityMockMvcResultMatchers.unauthenticated()); 33 | } 34 | 35 | @Test 36 | public void TEST_001_formLoginWithRole() throws Exception { 37 | mvc.perform(SecurityMockMvcRequestBuilders.formLogin().user("admin").password("password")) 38 | .andExpect(SecurityMockMvcResultMatchers.authenticated().withRoles("ADMIN")); 39 | } 40 | 41 | @Test 42 | public void TEST_002_logout() throws Exception { 43 | mvc.perform(SecurityMockMvcRequestBuilders.logout()) 44 | .andDo(MockMvcResultHandlers.print()); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /web-security/src/test/java/kr/kdev/demo/util/RequestMatcherTests.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.util; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.http.HttpMethod; 8 | import org.springframework.mock.web.MockHttpServletRequest; 9 | import org.springframework.security.core.Authentication; 10 | import org.springframework.security.core.context.SecurityContext; 11 | import org.springframework.security.core.context.SecurityContextHolder; 12 | import org.springframework.security.web.util.matcher.AntPathRequestMatcher; 13 | import org.springframework.web.util.UrlPathHelper; 14 | 15 | public class RequestMatcherTests { 16 | 17 | private static final Logger LOG = LoggerFactory.getLogger(RequestMatcherTests.class); 18 | 19 | static { 20 | } 21 | 22 | @Test 23 | public void TEST_000_validRequestByAntPathMatcher() { 24 | AntPathRequestMatcher requestMatcher = new AntPathRequestMatcher("/api/**", null, true, new UrlPathHelper()); 25 | MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(HttpMethod.GET.name(), "/api/users"); 26 | 27 | boolean matches = requestMatcher.matches(httpServletRequest); 28 | LOG.debug("{}", matches); 29 | Assert.assertTrue(matches); 30 | } 31 | 32 | @Test 33 | public void TEST_001_invalidRequestMethodByAntPathMatcher() { 34 | AntPathRequestMatcher requestMatcher = new AntPathRequestMatcher("/api/**", HttpMethod.GET.name(), true, new UrlPathHelper()); 35 | MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(HttpMethod.POST.name(), "/api/users"); 36 | 37 | boolean matches = requestMatcher.matches(httpServletRequest); 38 | LOG.debug("{}", matches); 39 | Assert.assertTrue(matches); 40 | } 41 | 42 | @Test 43 | public void doSomething() { 44 | SecurityContext securityContext = SecurityContextHolder.getContext(); 45 | Authentication authentication = securityContext.getAuthentication(); 46 | Object principal = authentication.getPrincipal(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /web-security-oauth-server/src/main/java/kr/kdev/demo/bean/User.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.bean; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import lombok.Data; 5 | import org.springframework.security.core.GrantedAuthority; 6 | import org.springframework.security.core.SpringSecurityCoreVersion; 7 | import org.springframework.security.core.userdetails.UserDetails; 8 | 9 | import java.util.ArrayList; 10 | import java.util.Collection; 11 | 12 | import static kr.kdev.demo.config.SecurityConfig.PRINCIPAL_LOCK_BASELINE; 13 | 14 | @Data 15 | public class User implements UserDetails { 16 | 17 | private String userId; 18 | private String id; 19 | @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) 20 | private String password; 21 | private String name; 22 | private String role; 23 | 24 | private int loginFailCount; 25 | private Long expiredAt; 26 | private Long passwordExpiredAt; 27 | private boolean deleted; 28 | 29 | private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; 30 | 31 | @Override 32 | public Collection getAuthorities() { 33 | ArrayList authorities = new ArrayList<>(); 34 | if(this.role != null) { 35 | authorities.add((GrantedAuthority) () -> "ROLE_" + this.role); 36 | } 37 | return authorities; 38 | } 39 | 40 | @Override 41 | public String getPassword() { 42 | return this.password; 43 | } 44 | 45 | @Override 46 | public String getUsername() { 47 | return this.id; 48 | } 49 | 50 | @Override 51 | public boolean isAccountNonExpired() { 52 | return expiredAt == null || (expiredAt < System.currentTimeMillis()); 53 | } 54 | 55 | @Override 56 | public boolean isAccountNonLocked() { 57 | return loginFailCount < PRINCIPAL_LOCK_BASELINE; 58 | } 59 | 60 | @Override 61 | public boolean isCredentialsNonExpired() { 62 | return passwordExpiredAt == null || (passwordExpiredAt < System.currentTimeMillis()); 63 | } 64 | 65 | @Override 66 | public boolean isEnabled() { 67 | return !deleted; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /web-security/src/test/java/kr/kdev/demo/api/UserApiTests.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.api; 2 | 3 | import kr.kdev.demo.config.ApiTestConfigurer; 4 | import org.junit.Assert; 5 | import org.junit.FixMethodOrder; 6 | import org.junit.Test; 7 | import org.junit.runners.MethodSorters; 8 | import org.springframework.mock.web.MockHttpServletResponse; 9 | import org.springframework.security.test.context.support.WithMockUser; 10 | import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders; 11 | import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors; 12 | import org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers; 13 | import org.springframework.test.web.servlet.MvcResult; 14 | import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; 15 | import org.springframework.test.web.servlet.result.MockMvcResultHandlers; 16 | 17 | @FixMethodOrder(value = MethodSorters.NAME_ASCENDING) 18 | public class UserApiTests extends ApiTestConfigurer { 19 | 20 | @WithMockUser(roles = "ADMIN") 21 | @Test 22 | public void TEST_000_currentUser() throws Exception { 23 | MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.get("/api/users/me")) 24 | .andDo(MockMvcResultHandlers.print()) 25 | .andReturn(); 26 | 27 | MockHttpServletResponse response = mvcResult.getResponse(); 28 | Assert.assertEquals(200, response.getStatus()); 29 | 30 | String content = response.getContentAsString(); 31 | LOG.debug("content : {}", content); 32 | } 33 | 34 | @Test 35 | public void TEST_001_currentUserWithUser() throws Exception { 36 | MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.get("/api/users/me") 37 | .with(SecurityMockMvcRequestPostProcessors.user("admin").password("password"))) 38 | .andDo(MockMvcResultHandlers.print()) 39 | .andReturn(); 40 | 41 | MockHttpServletResponse response = mvcResult.getResponse(); 42 | Assert.assertEquals(200, response.getStatus()); 43 | 44 | String content = response.getContentAsString(); 45 | LOG.debug("content : {}", content); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /web-security-oauth-server/src/main/resources/templates/index.ftl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Spring Demo Security 8 | 9 | 10 | 11 |
12 |
13 |

Hello, world!

14 |

This project is example using spring-security-oauth2 modules.

15 |
16 |

You can check how to set configuration for oauth2 authentication system.

17 | <#if !(authentication?exists) || (authentication.principal == "anonymousUser")> 18 | Login 19 | <#else> 20 | Logout 21 | 22 |
23 |
24 | 25 | <#if authentication?exists && !(authentication.principal == "anonymousUser")> 26 |
27 |

${authentication.principal}

28 |

You can authorize to client /oauth/authorize?client_id=e0113ca5-486f-43e9-9615-674b2232caed&response_type=code

29 |
30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /web-security/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 | -------------------------------------------------------------------------------- /web-security-session/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 | -------------------------------------------------------------------------------- /web-security-oauth-server/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 | -------------------------------------------------------------------------------- /web-security-oauth-kakao-login/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 | -------------------------------------------------------------------------------- /web-security/src/main/vue/page/login.vue: -------------------------------------------------------------------------------- 1 | 26 | -------------------------------------------------------------------------------- /web-security-oauth-kakao-login/src/main/java/kr/kdev/demo/api/UserApi.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.api; 2 | 3 | import kr.kdev.demo.bean.KakaoOAuth2User; 4 | import org.springframework.http.ResponseEntity; 5 | import org.springframework.security.core.Authentication; 6 | import org.springframework.security.core.annotation.AuthenticationPrincipal; 7 | import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; 8 | import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; 9 | import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; 10 | import org.springframework.security.oauth2.core.OAuth2AccessToken; 11 | import org.springframework.security.oauth2.core.OAuth2RefreshToken; 12 | import org.springframework.security.oauth2.core.user.OAuth2User; 13 | import org.springframework.web.bind.annotation.GetMapping; 14 | import org.springframework.web.bind.annotation.RestController; 15 | 16 | import java.security.Principal; 17 | import java.util.HashMap; 18 | import java.util.Map; 19 | 20 | @RestController 21 | public class UserApi extends BaseApi { 22 | private final OAuth2AuthorizedClientService authorizedClientService; 23 | 24 | public UserApi(OAuth2AuthorizedClientService authorizedClientService) { 25 | this.authorizedClientService = authorizedClientService; 26 | } 27 | 28 | @GetMapping("/users/me") 29 | public ResponseEntity currentUser(Authentication authentication, @AuthenticationPrincipal Principal principal) { 30 | return ResponseEntity.ok(principal); 31 | } 32 | 33 | @GetMapping("/users/me/token") 34 | public ResponseEntity currentUserToken(@AuthenticationPrincipal Principal principal) { 35 | if(principal instanceof OAuth2AuthenticationToken) { 36 | Map attributes = new HashMap<>(); 37 | OAuth2AuthenticationToken oAuth2AuthenticationToken = (OAuth2AuthenticationToken) principal; 38 | 39 | OAuth2AuthorizedClient oAuth2AuthorizedClient = authorizedClientService.loadAuthorizedClient(oAuth2AuthenticationToken.getAuthorizedClientRegistrationId(), oAuth2AuthenticationToken.getName()); 40 | OAuth2AccessToken accessToken = oAuth2AuthorizedClient.getAccessToken(); 41 | OAuth2RefreshToken refreshToken = oAuth2AuthorizedClient.getRefreshToken(); 42 | 43 | attributes.put("name", oAuth2AuthenticationToken.getName()); 44 | attributes.put("accessToken", accessToken); 45 | attributes.put("refreshToken", refreshToken); 46 | return ResponseEntity.ok(attributes); 47 | } 48 | 49 | return ResponseEntity.ok(principal); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /web-security/src/test/java/kr/kdev/demo/service/RepositoryServiceTests.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.service; 2 | 3 | import kr.kdev.demo.bean.Repository; 4 | import kr.kdev.demo.config.ServiceTestConfigurer; 5 | import kr.kdev.demo.expression.RepositoryChecker; 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.context.annotation.ComponentScan; 10 | import org.springframework.security.access.AccessDeniedException; 11 | import org.springframework.security.test.context.support.WithMockUser; 12 | import org.springframework.security.test.context.support.WithUserDetails; 13 | 14 | import java.util.UUID; 15 | 16 | @ComponentScan(basePackageClasses = {RepositoryChecker.class}) 17 | public class RepositoryServiceTests extends ServiceTestConfigurer { 18 | 19 | @Autowired 20 | private RepositoryService repositoryService; 21 | 22 | @WithMockUser(authorities = {"REPOSITORY_CREATE"}) 23 | @Test 24 | public void TEST_000_createRepository() { 25 | Repository repository = new Repository(); 26 | String repositoryId = repositoryService.create(repository); 27 | Assert.assertNotNull(repositoryId); 28 | LOG.info("repositoryId : {}", repositoryId); 29 | } 30 | 31 | @WithMockUser(authorities = {}) 32 | @Test(expected = AccessDeniedException.class) 33 | public void TEST_000_createRepositoryNoneAuthority() { 34 | Repository repository = new Repository(); 35 | String repositoryId = repositoryService.create(repository); 36 | Assert.assertNotNull(repositoryId); 37 | } 38 | 39 | @WithUserDetails(userDetailsServiceBeanName = "userService") 40 | @Test 41 | public void TEST_001_updateRepository() { 42 | Repository repository = new Repository(); 43 | repository.setId(UUID.randomUUID().toString()); 44 | boolean updated = repositoryService.update(repository); 45 | LOG.info("updated : {}", updated); 46 | } 47 | 48 | @WithUserDetails(userDetailsServiceBeanName = "userService") 49 | @Test(expected = AccessDeniedException.class) 50 | public void TEST_001_updateRepositoryWithIdIsNull() { 51 | Repository repository = new Repository(); 52 | boolean updated = repositoryService.update(repository); 53 | LOG.info("updated : {}", updated); 54 | } 55 | 56 | @WithUserDetails(userDetailsServiceBeanName = "userService") 57 | @Test(expected = AccessDeniedException.class) 58 | public void TEST_002_deleteRepositoryWithNameIsNull() { 59 | boolean deleted = repositoryService.delete("dummy", null); 60 | LOG.info("deleted : {}", deleted); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /web-security/src/main/java/kr/kdev/demo/interceptor/UserStateInjectionInterceptor.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.interceptor; 2 | 3 | import kr.kdev.demo.bean.User; 4 | import kr.kdev.demo.bean.UserState; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 8 | import org.springframework.security.core.Authentication; 9 | import org.springframework.security.core.context.SecurityContextHolder; 10 | import org.springframework.security.core.userdetails.UserDetails; 11 | import org.springframework.web.context.request.RequestContextHolder; 12 | import org.springframework.web.servlet.HandlerInterceptor; 13 | import org.springframework.web.servlet.ModelAndView; 14 | 15 | import javax.servlet.http.HttpServletRequest; 16 | import javax.servlet.http.HttpServletResponse; 17 | import java.util.UUID; 18 | 19 | public class UserStateInjectionInterceptor implements HandlerInterceptor { 20 | 21 | private final Logger LOG = LoggerFactory.getLogger(UserStateInjectionInterceptor.class); 22 | 23 | @Override 24 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 25 | return true; 26 | } 27 | 28 | @Override 29 | public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { 30 | 31 | if(modelAndView != null) { 32 | String sessionId = RequestContextHolder.getRequestAttributes().getSessionId(); 33 | Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 34 | LOG.info("Injection UserState from authentication to modelAndView"); 35 | 36 | UserState userState = new UserState(); 37 | if(authentication != null) { 38 | 39 | if(authentication instanceof UsernamePasswordAuthenticationToken) { 40 | Object principal = authentication.getPrincipal(); 41 | if(principal instanceof User) { 42 | User user = (User) principal; 43 | userState.setUserId(user.getUserId()); 44 | userState.setName(user.getName()); 45 | } else { 46 | UserDetails userDetails = (UserDetails) principal; 47 | userState.setUserId(UUID.randomUUID().toString()); 48 | userState.setName(userDetails.getUsername()); 49 | } 50 | } 51 | 52 | } 53 | 54 | userState.setSessionId(sessionId); 55 | modelAndView.addObject("state", userState); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /web-security-session/README.md: -------------------------------------------------------------------------------- 1 | ## Web Security Session 2 | 본 프로젝트에서는 스프링 시큐리티 모듈과 스프링 세션 모듈의 통합을 설명합니다. 3 | 4 | - Spring Security 5.2.2.RELEASE 5 | - Spring Boot 2.2.4.RELEASE 6 | - Spring Data Redis 2.2.4.RELEASE 7 | - Spring Session Core 2.2.0.RELEASE 8 | 9 | ### SessionConfig 10 | 세션 관련 설정을 통합하여 관리합니다. 11 | 12 | #### HttpSessionConfig 13 | @EnableRedisHttpSession를 선언하여 RedisHttpSessionConfiguration을 빈으로 등록합니다. 14 | 15 | > 만약, RedisHttpSessionConfiguration를 확장하려면 @EnableRedisHttpSession를 제거하시기 바랍니다. 16 | 17 | ```java 18 | @EnableRedisHttpSession 19 | @Configuration(proxyBeanMethods = false) 20 | public static class HttpSessionConfig {} 21 | ``` 22 | 23 | ### SecurityConfig 24 | 스프링 시큐리티 설정을 통해 몇가지 세션 관련 기능을 통합합니다. 25 | 26 | #### Remember-me 27 | 스프링 세션은 스프링 시큐리티의 리멤버-미 인증과의 통합을 제공합니다. 28 | 29 | ```java 30 | // SessionConfig 31 | @Bean 32 | public SpringSessionRememberMeServices rememberMeServices() { 33 | SpringSessionRememberMeServices rememberMeServices = new SpringSessionRememberMeServices(); 34 | rememberMeServices.setAlwaysRemember(true); 35 | return rememberMeServices; 36 | } 37 | 38 | // SecurityConfig.WebSecurityConfig 39 | @Override 40 | protected void configure(HttpSecurity http) throws Exception { 41 | http.rememberMe() 42 | .rememberMeServices(rememberMeServices); 43 | } 44 | ``` 45 | 46 | #### Concurrent Session Control 47 | 스프링 세션은 스프링 시큐리티와 함께 동시 세션 제어를 지원합니다. 48 | 49 | ```java 50 | // SessionConfig 51 | @Bean 52 | public SpringSessionBackedSessionRegistry sessionRegistry() { 53 | return new SpringSessionBackedSessionRegistry<>(sessionRepository); 54 | } 55 | 56 | // SecurityConfig.WebSecurityConfig 57 | @Override 58 | protected void configure(HttpSecurity http) throws Exception { 59 | http.sessionManagement() 60 | .maximumSessions(1) 61 | .sessionRegistry(sessionRegistry); 62 | } 63 | ``` 64 | 65 | 만약, 두개 이상의 브라우저에서 접속을 시도하면 이전 세션 정보는 만료됩니다. 66 | 67 | ```sh 68 | This session has been expired (possibly due to multiple concurrent logins being attempted as the same user). 69 | ``` 70 | 71 | 72 | ### 사용자 정의 기능 구현 73 | 74 | #### 동시 세션 인증 제한하기 75 | 최대 세션 수를 넘어가는 경우 이전에 인증된 세션을 만료합니다. 만약, 인증된 세션을 만료시키지 않고 인증 자체를 허용하지 않도록 maxSessionsPreventsLogin 여부로 설정할 수 있습니다. 76 | 77 | ```java 78 | http.sessionManagement() 79 | .maximumSessions(1) 80 | .maxSessionsPreventsLogin(true) 81 | .sessionRegistry(sessionRegistry); 82 | ``` 83 | 84 | 위 설정에 따르면 최대 세션 수가 1이므로 이미 인증된 세션이 존재하면 인증 요청이 허용되지 않습니다. 85 | 86 | ![](src/main/resources/static/images/max-sessions-prevents-login.PNG) 87 | 88 | ## 참고 89 | - [Spring Session](https://docs.spring.io/spring-session/docs/2.2.0.RELEASE/reference/html5/) 90 | - [Spring Data Redis](https://docs.spring.io/spring-data/data-redis/docs/2.2.4.RELEASE/reference/html/#) -------------------------------------------------------------------------------- /web-security/src/main/java/kr/kdev/demo/bean/User.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.bean; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import lombok.Data; 5 | import org.postgresql.jdbc.PgArray; 6 | import org.springframework.security.core.GrantedAuthority; 7 | import org.springframework.security.core.SpringSecurityCoreVersion; 8 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 9 | import org.springframework.security.core.userdetails.UserDetails; 10 | 11 | import java.sql.SQLException; 12 | import java.util.*; 13 | import java.util.stream.Collectors; 14 | 15 | import static kr.kdev.demo.config.SecurityConfig.PRINCIPAL_LOCK_BASELINE; 16 | 17 | @Data 18 | public class User implements UserDetails { 19 | 20 | private String userId = UUID.randomUUID().toString(); 21 | private String id; 22 | @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) 23 | private String password; 24 | private String name; 25 | private String role; 26 | private List authorities; 27 | 28 | private int loginFailCount; 29 | private Long expiredAt; 30 | private Long passwordExpiredAt; 31 | private boolean deleted; 32 | 33 | private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; 34 | 35 | @Override 36 | public Collection getAuthorities() { 37 | ArrayList authorities = new ArrayList<>(); 38 | if(this.authorities != null) { 39 | List grantedAuthorities = this.authorities.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList()); 40 | authorities.addAll(grantedAuthorities); 41 | } 42 | 43 | if(this.role != null) { 44 | authorities.add((GrantedAuthority) () -> "ROLE_" + this.role); 45 | } 46 | return authorities; 47 | } 48 | 49 | @Override 50 | public String getPassword() { 51 | return this.password; 52 | } 53 | 54 | @Override 55 | public String getUsername() { 56 | return this.id; 57 | } 58 | 59 | @Override 60 | public boolean isAccountNonExpired() { 61 | return expiredAt == null || (expiredAt < System.currentTimeMillis()); 62 | } 63 | 64 | @Override 65 | public boolean isAccountNonLocked() { 66 | return loginFailCount < PRINCIPAL_LOCK_BASELINE; 67 | } 68 | 69 | @Override 70 | public boolean isCredentialsNonExpired() { 71 | return passwordExpiredAt == null || (passwordExpiredAt < System.currentTimeMillis()); 72 | } 73 | 74 | @Override 75 | public boolean isEnabled() { 76 | return !deleted; 77 | } 78 | 79 | public void setAuthorities(PgArray pgArray) throws SQLException { 80 | Object array = pgArray.getArray(); 81 | String[] values = (String[]) array; 82 | List strings = Arrays.asList(values); 83 | this.authorities = strings; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /web-security-oauth-server/src/main/java/kr/kdev/demo/bean/Client.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.bean; 2 | 3 | import lombok.Data; 4 | import org.springframework.security.core.GrantedAuthority; 5 | import org.springframework.security.oauth2.provider.ClientDetails; 6 | 7 | import java.util.*; 8 | import java.util.stream.Collectors; 9 | 10 | @Data 11 | public class Client implements ClientDetails { 12 | 13 | private String name; 14 | 15 | private String clientId; 16 | private String clientSecret; 17 | private String authorities; 18 | private String scope; 19 | private String authorizedGrantTypes; 20 | private String resourceIds; 21 | private Integer accessTokenValidity; 22 | private Integer refreshTokenValidity; 23 | private String autoApprove; 24 | private String additionalInformation; 25 | private String registeredRedirectUri; 26 | 27 | @Override 28 | public String getClientId() { 29 | return this.clientId; 30 | } 31 | 32 | @Override 33 | public Set getResourceIds() { 34 | if(this.resourceIds == null) { 35 | return Collections.emptySet(); 36 | } 37 | return new HashSet<>(Arrays.asList(resourceIds.split(","))); 38 | } 39 | 40 | @Override 41 | public boolean isSecretRequired() { 42 | return this.clientSecret != null; 43 | } 44 | 45 | @Override 46 | public String getClientSecret() { 47 | return this.clientSecret; 48 | } 49 | 50 | @Override 51 | public boolean isScoped() { 52 | return this.scope != null; 53 | } 54 | 55 | @Override 56 | public Set getScope() { 57 | Set scopes = new HashSet<>(); 58 | if(this.scope == null) { 59 | // prevent null 60 | scopes.add("profile"); 61 | } else { 62 | scopes = Arrays.stream(scope.split(",")).map(String::trim).collect(Collectors.toSet()); 63 | } 64 | return scopes; 65 | } 66 | 67 | @Override 68 | public Set getAuthorizedGrantTypes() { 69 | if(this.authorizedGrantTypes == null) { 70 | return Collections.emptySet(); 71 | } 72 | return Arrays.stream(authorizedGrantTypes.split(",")).map(String::trim).collect(Collectors.toSet()); 73 | } 74 | 75 | @Override 76 | public Set getRegisteredRedirectUri() { 77 | if(registeredRedirectUri == null) { 78 | return Collections.emptySet(); 79 | } 80 | return Arrays.stream(registeredRedirectUri.split(",")).map(String::trim).collect(Collectors.toSet()); 81 | } 82 | 83 | @Override 84 | public Collection getAuthorities() { 85 | if(this.authorities == null) { 86 | return Collections.emptyList(); 87 | } 88 | return Arrays.stream(this.authorities.split(",")).map(authority -> (GrantedAuthority) authority::trim).collect(Collectors.toSet()); 89 | } 90 | 91 | @Override 92 | public Integer getAccessTokenValiditySeconds() { 93 | return this.accessTokenValidity; 94 | } 95 | 96 | @Override 97 | public Integer getRefreshTokenValiditySeconds() { 98 | return this.refreshTokenValidity; 99 | } 100 | 101 | @Override 102 | public boolean isAutoApprove(String scope) { 103 | return false; 104 | } 105 | 106 | @Override 107 | public Map getAdditionalInformation() { 108 | return null; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /web-security/src/main/vue/page/index.vue: -------------------------------------------------------------------------------- 1 | 58 | -------------------------------------------------------------------------------- /web-security-oauth-server/src/main/java/kr/kdev/demo/api/UserApi.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.api; 2 | 3 | import org.springframework.http.ResponseEntity; 4 | import org.springframework.security.core.Authentication; 5 | import org.springframework.security.core.annotation.AuthenticationPrincipal; 6 | import org.springframework.security.core.annotation.CurrentSecurityContext; 7 | import org.springframework.security.core.session.SessionInformation; 8 | import org.springframework.security.core.session.SessionRegistry; 9 | import org.springframework.security.core.userdetails.UserDetails; 10 | import org.springframework.security.oauth2.provider.OAuth2Authentication; 11 | import org.springframework.security.oauth2.provider.OAuth2Request; 12 | import org.springframework.security.web.authentication.WebAuthenticationDetails; 13 | import org.springframework.web.bind.annotation.GetMapping; 14 | import org.springframework.web.bind.annotation.RequestParam; 15 | import org.springframework.web.bind.annotation.RestController; 16 | import org.springframework.web.context.request.RequestContextHolder; 17 | 18 | import java.util.List; 19 | 20 | @RestController 21 | public class UserApi extends BaseApi { 22 | 23 | private final SessionRegistry sessionRegistry; 24 | 25 | public UserApi(SessionRegistry sessionRegistry) { 26 | this.sessionRegistry = sessionRegistry; 27 | } 28 | 29 | @GetMapping("/users/me") 30 | public ResponseEntity currentUser(@AuthenticationPrincipal UserDetails principal, 31 | // same As SecurityContextHolder.getContext().getAuthentication() 32 | @CurrentSecurityContext(expression = "authentication") Authentication authentication) { 33 | // Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 34 | if(authentication != null) { 35 | if(authentication instanceof OAuth2Authentication) { 36 | OAuth2Request oAuth2Request = ((OAuth2Authentication) authentication).getOAuth2Request(); 37 | } else { 38 | principal = (UserDetails) authentication.getPrincipal(); 39 | } 40 | } 41 | return ResponseEntity.ok(principal); 42 | } 43 | 44 | @GetMapping("/users") 45 | public ResponseEntity allUsers() { 46 | List allPrincipals = sessionRegistry.getAllPrincipals(); 47 | return ResponseEntity.ok(allPrincipals); 48 | } 49 | 50 | @GetMapping("/users/me/sessions") 51 | public ResponseEntity currentUserSessions(@CurrentSecurityContext(expression = "authentication") Authentication authentication, 52 | @RequestParam(required = false) boolean includeExpiredSessions) { 53 | List allSessions = sessionRegistry.getAllSessions(authentication.getPrincipal(), includeExpiredSessions); 54 | return ResponseEntity.ok(allSessions); 55 | } 56 | 57 | @GetMapping("/users/me/sessionInformation") 58 | public ResponseEntity currentUserSessionInformation(@CurrentSecurityContext(expression = "authentication") Authentication authentication) { 59 | String sessionId = RequestContextHolder.currentRequestAttributes().getSessionId(); 60 | SessionInformation sessionInformation = sessionRegistry.getSessionInformation(sessionId); 61 | return ResponseEntity.ok(sessionInformation); 62 | } 63 | 64 | @GetMapping("/users/me/sessionInformation2") 65 | public ResponseEntity currentUserSessionInformation2(@CurrentSecurityContext(expression = "authentication.details") WebAuthenticationDetails details) { 66 | String sessionId = details.getSessionId(); 67 | SessionInformation sessionInformation = sessionRegistry.getSessionInformation(sessionId); 68 | return ResponseEntity.ok(sessionInformation); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /web-security-session/src/main/java/kr/kdev/demo/config/SessionConfig.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.config; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import org.springframework.beans.factory.BeanClassLoaderAware; 5 | import org.springframework.beans.factory.DisposableBean; 6 | import org.springframework.beans.factory.InitializingBean; 7 | import org.springframework.boot.autoconfigure.data.redis.RedisProperties; 8 | import org.springframework.boot.web.servlet.ServletListenerRegistrationBean; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | import org.springframework.context.annotation.Profile; 12 | import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; 13 | import org.springframework.data.redis.serializer.RedisSerializer; 14 | import org.springframework.security.jackson2.SecurityJackson2Modules; 15 | import org.springframework.security.web.session.HttpSessionEventPublisher; 16 | import org.springframework.session.data.redis.RedisIndexedSessionRepository; 17 | import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession; 18 | import org.springframework.session.security.SpringSessionBackedSessionRegistry; 19 | import org.springframework.session.security.web.authentication.SpringSessionRememberMeServices; 20 | import redis.embedded.RedisServer; 21 | 22 | /** 23 | * Session Configuration Integration 24 | * 25 | * @author kdevkr 26 | */ 27 | @Configuration(proxyBeanMethods = false) 28 | public class SessionConfig { 29 | 30 | private final RedisIndexedSessionRepository sessionRepository; 31 | 32 | public SessionConfig(RedisIndexedSessionRepository sessionRepository) { 33 | this.sessionRepository = sessionRepository; 34 | } 35 | 36 | @Bean 37 | public SpringSessionBackedSessionRegistry sessionRegistry() { 38 | return new SpringSessionBackedSessionRegistry<>(sessionRepository); 39 | } 40 | 41 | /** 42 | * Activate Remember-me service with Spring Security. 43 | * {@link SecurityConfig.WebSecurityConfig} 44 | */ 45 | @Bean 46 | public SpringSessionRememberMeServices rememberMeServices() { 47 | SpringSessionRememberMeServices rememberMeServices = new SpringSessionRememberMeServices(); 48 | rememberMeServices.setAlwaysRemember(true); 49 | return rememberMeServices; 50 | } 51 | 52 | @EnableRedisHttpSession 53 | @Configuration(proxyBeanMethods = false) 54 | public static class HttpSessionConfig implements BeanClassLoaderAware { 55 | 56 | private ClassLoader classLoader; 57 | 58 | @Bean 59 | public ServletListenerRegistrationBean httpSessionEventPublisher() { 60 | return new ServletListenerRegistrationBean<>(new HttpSessionEventPublisher()); 61 | } 62 | 63 | @Bean 64 | public RedisSerializer redisSerializer(ObjectMapper objectMapper) { 65 | return new GenericJackson2JsonRedisSerializer(objectMapper.copy().registerModules(SecurityJackson2Modules.getModules(classLoader))); 66 | } 67 | 68 | @Override 69 | public void setBeanClassLoader(ClassLoader classLoader) { 70 | this.classLoader = classLoader; 71 | } 72 | } 73 | 74 | /** 75 | * Can't use embedded redis server in windows. 76 | */ 77 | @Profile("embedded-redis") 78 | @Configuration(proxyBeanMethods = false) 79 | public static class EmbeddedRedisConfig { 80 | 81 | public EmbeddedRedisConfig() {} 82 | 83 | @Bean 84 | public RedisServerBean redisServer(RedisProperties redisProperties) { 85 | return new RedisServerBean(redisProperties.getPort()); 86 | } 87 | 88 | class RedisServerBean implements InitializingBean, DisposableBean { 89 | private RedisServer redisServer; 90 | private int port; 91 | 92 | public RedisServerBean(int port) { 93 | this.port = port; 94 | } 95 | 96 | public void afterPropertiesSet() { 97 | redisServer = new RedisServer(port); 98 | redisServer.start(); 99 | } 100 | 101 | public void destroy() { 102 | if (redisServer != null) { 103 | redisServer.stop(); 104 | } 105 | } 106 | } 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /web-security/src/test/java/kr/kdev/demo/service/UserServiceTests.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.service; 2 | 3 | import kr.kdev.demo.config.ServiceTestConfigurer; 4 | import kr.kdev.demo.context.WithMockAdmin; 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | import org.springframework.security.authentication.AnonymousAuthenticationToken; 8 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 9 | import org.springframework.security.core.Authentication; 10 | import org.springframework.security.core.GrantedAuthority; 11 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 12 | import org.springframework.security.core.context.SecurityContext; 13 | import org.springframework.security.core.context.SecurityContextHolder; 14 | import org.springframework.security.test.context.support.WithAnonymousUser; 15 | import org.springframework.security.test.context.support.WithMockUser; 16 | import org.springframework.security.test.context.support.WithUserDetails; 17 | 18 | import java.util.Collection; 19 | 20 | public class UserServiceTests extends ServiceTestConfigurer { 21 | 22 | /** 23 | * 익명 사용자 테스트 24 | */ 25 | @WithAnonymousUser 26 | @Test 27 | public void TEST_000_withAnonymousUser() { 28 | // Expect current thread in security context... 29 | SecurityContext context = SecurityContextHolder.getContext(); 30 | Assert.assertNotNull(context); 31 | 32 | // Is exist authentication in security context? 33 | Authentication authentication = context.getAuthentication(); 34 | Assert.assertNotNull(authentication); 35 | 36 | // Is AnonymousAuthenticationToken? 37 | Assert.assertTrue(authentication instanceof AnonymousAuthenticationToken); 38 | } 39 | 40 | @WithMockUser(username = "admin", roles = {"ADMIN"}) 41 | @Test 42 | public void TEST_001_withMockUser() { 43 | SecurityContext context = SecurityContextHolder.getContext(); 44 | Assert.assertNotNull(context); 45 | 46 | Authentication authentication = context.getAuthentication(); 47 | Assert.assertNotNull(authentication); 48 | 49 | Assert.assertTrue(authentication instanceof UsernamePasswordAuthenticationToken); 50 | 51 | // Is contains "ADMIN" role 52 | Collection authorities = authentication.getAuthorities(); 53 | Assert.assertTrue(authorities.contains(new SimpleGrantedAuthority("ROLE_ADMIN"))); 54 | 55 | // Is equals org.springframework.security.core.userdetails.User? 56 | Object principal = authentication.getPrincipal(); 57 | Assert.assertEquals(principal.getClass(), org.springframework.security.core.userdetails.User.class); 58 | 59 | org.springframework.security.core.userdetails.User user = (org.springframework.security.core.userdetails.User) principal; 60 | LOG.info("user : {}", user); 61 | } 62 | 63 | @WithUserDetails(userDetailsServiceBeanName = "userService") 64 | @Test 65 | public void TEST_003_withUserDetails() { 66 | SecurityContext context = SecurityContextHolder.getContext(); 67 | Assert.assertNotNull(context); 68 | 69 | Authentication authentication = context.getAuthentication(); 70 | Assert.assertNotNull(authentication); 71 | 72 | Assert.assertTrue(authentication instanceof UsernamePasswordAuthenticationToken); 73 | 74 | // Is equals kr.kdev.User? 75 | Object principal = authentication.getPrincipal(); 76 | Assert.assertEquals(principal.getClass(), kr.kdev.demo.bean.User.class); 77 | 78 | kr.kdev.demo.bean.User user = (kr.kdev.demo.bean.User) principal; 79 | LOG.info("user : {}", user); 80 | LOG.info("credentials : {}", authentication.getCredentials()); 81 | } 82 | 83 | /** 84 | * Use test meta annotation. 85 | */ 86 | @WithMockAdmin 87 | @Test 88 | public void TEST_004_withCustomMockUser() { 89 | SecurityContext context = SecurityContextHolder.getContext(); 90 | Assert.assertNotNull(context); 91 | 92 | Authentication authentication = context.getAuthentication(); 93 | Assert.assertNotNull(authentication); 94 | 95 | Assert.assertTrue(authentication instanceof UsernamePasswordAuthenticationToken); 96 | LOG.info("principal : {}", authentication.getPrincipal()); 97 | LOG.info("authorities : {}", authentication.getAuthorities()); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /web-security/webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack') 2 | const path = require('path') 3 | const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin') 4 | const TerserWebpackPlugin = require('terser-webpack-plugin') 5 | const MiniCssExtractPlugin = require("mini-css-extract-plugin") 6 | const CompressionWebpackPlugin = require('compression-webpack-plugin') 7 | const VueLoaderPlugin = require('vue-loader/lib/plugin') 8 | 9 | const development = process.env.NODE_ENV !== 'production' 10 | 11 | module.exports = { 12 | mode: development ? 'development' : 'production', 13 | entry: { 14 | index: ['babel-polyfill', path.resolve(__dirname, 'src/main/vue/index.js')] 15 | }, 16 | output: { 17 | filename: '[name].js', 18 | path: path.resolve(__dirname, 'src/main/resources/dist'), 19 | publicPath: '/', 20 | }, 21 | resolve: { 22 | extensions: ['.js', '.vue', '.css', '.scss'], 23 | alias: { 24 | vue$: 'vue/dist/vue.esm.js', 25 | images: path.resolve(__dirname, './src/main/resources/static/images'), 26 | '~': path.resolve(__dirname, './src/main'), 27 | }, 28 | modules: [ 29 | path.resolve(__dirname, './src/main/resources/static/css'), 30 | "node_modules", 31 | ] 32 | }, 33 | module: { 34 | rules: [ 35 | { 36 | test: /\.(sa|sc|c)ss$/, 37 | use: [ 38 | { 39 | loader: development ? 'vue-style-loader' : MiniCssExtractPlugin.loader 40 | }, 41 | { 42 | loader: 'css-loader', 43 | options: { 44 | importLoaders: 1, 45 | sourceMap: true 46 | } 47 | }, 48 | { 49 | loader: 'postcss-loader' 50 | }, 51 | { 52 | loader: 'sass-loader' 53 | }, 54 | { 55 | loader: 'style-resources-loader', 56 | options: { 57 | patterns: [ 58 | // './path/from/context/to/scss/variables/*.scss', 59 | // './path/from/context/to/scss/mixins/*.scss', 60 | ] 61 | } 62 | } 63 | ] 64 | }, 65 | { 66 | test: /\.vue$/, 67 | loader: 'vue-loader' 68 | }, 69 | { 70 | test: /\.js$/, 71 | loader: 'babel-loader', 72 | exclude: /node_modules/, 73 | include: [path.resolve(__dirname, 'src')], 74 | }, 75 | { 76 | enforce: 'pre', 77 | test: /\.js$/, 78 | loader: 'eslint-loader', 79 | exclude: /node_modules/ 80 | } 81 | ] 82 | }, 83 | plugins: [ 84 | new MiniCssExtractPlugin({ 85 | // Options similar to the same options in webpackOptions.output 86 | // both options are optional 87 | filename: "[name].css" 88 | }), 89 | new FriendlyErrorsWebpackPlugin(), 90 | new CompressionWebpackPlugin(), 91 | new TerserWebpackPlugin({ 92 | // Enable/disable multi-process parallel running. 93 | parallel: true 94 | }), 95 | new VueLoaderPlugin() 96 | ], 97 | optimization: { 98 | splitChunks: { 99 | cacheGroups: { 100 | vendors: { 101 | priority: -10, 102 | test: /[\\/]node_modules[\\/]/ 103 | } 104 | }, 105 | 106 | chunks: 'async', 107 | minChunks: 1, 108 | minSize: 30000, 109 | name: true 110 | }, 111 | minimizer: [] 112 | }, 113 | devServer: { 114 | inline: true, 115 | hot: true, 116 | contentBase: path.resolve(__dirname, 'src/main/resources/dist'), 117 | publicPath: '/dist/', 118 | filename: '[name].js', 119 | host: 'localhost', 120 | port: 8081, 121 | proxy: { 122 | '**': 'http://localhost:8080/' 123 | }, 124 | } 125 | } -------------------------------------------------------------------------------- /web-security-oauth-kakao-login/src/main/java/kr/kdev/demo/config/SecurityConfig.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.config; 2 | 3 | import kr.kdev.demo.bean.KakaoOAuth2User; 4 | import org.springframework.boot.autoconfigure.web.ResourceProperties; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.core.env.Environment; 8 | import org.springframework.core.env.Profiles; 9 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 10 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 11 | import org.springframework.security.config.annotation.web.builders.WebSecurity; 12 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 13 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 14 | import org.springframework.security.core.userdetails.User; 15 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 16 | import org.springframework.security.crypto.password.DelegatingPasswordEncoder; 17 | import org.springframework.security.crypto.password.PasswordEncoder; 18 | import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder; 19 | import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder; 20 | 21 | import javax.sql.DataSource; 22 | import java.util.HashMap; 23 | import java.util.Map; 24 | 25 | /** 26 | * Security Configuration Integration 27 | * 28 | * @author kdevkr 29 | */ 30 | @Configuration(proxyBeanMethods = false) 31 | public class SecurityConfig { 32 | 33 | @Bean 34 | public PasswordEncoder passwordEncoder() { 35 | String idForEncode = "bcrypt"; 36 | 37 | Map encoders = new HashMap<>(); 38 | encoders.put(idForEncode, new BCryptPasswordEncoder(12)); 39 | encoders.put("pbkdf2", new Pbkdf2PasswordEncoder()); 40 | encoders.put("scrypt", new SCryptPasswordEncoder()); 41 | 42 | return new DelegatingPasswordEncoder(idForEncode, encoders); 43 | } 44 | 45 | @EnableWebSecurity 46 | @Configuration(proxyBeanMethods = false) 47 | public static class WebSecurityConfig extends WebSecurityConfigurerAdapter { 48 | 49 | private final String[] staticLocations; 50 | private final Environment environment; 51 | private final DataSource dataSource; 52 | private final PasswordEncoder passwordEncoder; 53 | 54 | public WebSecurityConfig(Environment environment, 55 | DataSource dataSource, 56 | PasswordEncoder passwordEncoder, 57 | ResourceProperties resourceProperties) { 58 | this.environment = environment; 59 | this.dataSource = dataSource; 60 | this.passwordEncoder = passwordEncoder; 61 | this.staticLocations = resourceProperties.getStaticLocations(); 62 | } 63 | 64 | @Override 65 | protected void configure(AuthenticationManagerBuilder auth) throws Exception { 66 | User.UserBuilder userBuilder = User.builder(); 67 | String password = "password"; 68 | String encodedPassword = passwordEncoder.encode(password); 69 | 70 | auth.inMemoryAuthentication() 71 | .passwordEncoder(passwordEncoder) 72 | .withUser(userBuilder.username("system").password(encodedPassword).roles("SYSTEM")); 73 | 74 | auth.jdbcAuthentication() 75 | .dataSource(dataSource) 76 | .passwordEncoder(passwordEncoder) 77 | .withDefaultSchema().withUser(userBuilder.username("admin").password(encodedPassword).roles("ADMIN")); 78 | } 79 | 80 | @Override 81 | public void configure(WebSecurity web) { 82 | web.ignoring().antMatchers(staticLocations) 83 | .and() 84 | .debug(environment.acceptsProfiles(Profiles.of("debug"))); 85 | } 86 | 87 | @Override 88 | protected void configure(HttpSecurity http) throws Exception { 89 | http.formLogin() 90 | .and() 91 | .httpBasic(); 92 | 93 | http.csrf().disable(); 94 | 95 | http.authorizeRequests() 96 | .antMatchers("/api/login", "/api/logout").permitAll() 97 | .antMatchers("/console/**").hasAnyRole("SYSTEM") 98 | .antMatchers("/", "/api/**").authenticated() 99 | .anyRequest().permitAll(); 100 | 101 | http.oauth2Login(oauth2Login -> oauth2Login 102 | .userInfoEndpoint(userInfoEndpoint -> userInfoEndpoint 103 | .customUserType(KakaoOAuth2User.class, KakaoOAuth2User.PROVIDER))); 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /web-security-session/src/main/java/kr/kdev/demo/config/SecurityConfig.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.config; 2 | 3 | import org.springframework.boot.autoconfigure.web.ResourceProperties; 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.core.env.Environment; 7 | import org.springframework.core.env.Profiles; 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.builders.WebSecurity; 11 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 12 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 13 | import org.springframework.security.core.session.SessionRegistry; 14 | import org.springframework.security.core.userdetails.User; 15 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 16 | import org.springframework.security.crypto.password.DelegatingPasswordEncoder; 17 | import org.springframework.security.crypto.password.PasswordEncoder; 18 | import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder; 19 | import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder; 20 | import org.springframework.security.web.authentication.RememberMeServices; 21 | 22 | import javax.sql.DataSource; 23 | import java.util.HashMap; 24 | import java.util.Map; 25 | 26 | /** 27 | * Security Configuration Integration 28 | * 29 | * @author kdevkr 30 | */ 31 | @Configuration(proxyBeanMethods = false) 32 | public class SecurityConfig { 33 | 34 | @Bean 35 | public PasswordEncoder passwordEncoder() { 36 | String idForEncode = "bcrypt"; 37 | 38 | Map encoders = new HashMap<>(); 39 | encoders.put(idForEncode, new BCryptPasswordEncoder(12)); 40 | encoders.put("pbkdf2", new Pbkdf2PasswordEncoder()); 41 | encoders.put("scrypt", new SCryptPasswordEncoder()); 42 | 43 | return new DelegatingPasswordEncoder(idForEncode, encoders); 44 | } 45 | 46 | @EnableWebSecurity 47 | @Configuration(proxyBeanMethods = false) 48 | public static class WebSecurityConfig extends WebSecurityConfigurerAdapter { 49 | 50 | private final String[] staticLocations; 51 | private final Environment environment; 52 | private final DataSource dataSource; 53 | private final PasswordEncoder passwordEncoder; 54 | private final SessionRegistry sessionRegistry; 55 | private final RememberMeServices rememberMeServices; 56 | 57 | public WebSecurityConfig(Environment environment, 58 | DataSource dataSource, 59 | PasswordEncoder passwordEncoder, 60 | ResourceProperties resourceProperties, 61 | SessionRegistry sessionRegistry, 62 | RememberMeServices rememberMeServices) { 63 | this.environment = environment; 64 | this.dataSource = dataSource; 65 | this.passwordEncoder = passwordEncoder; 66 | this.staticLocations = resourceProperties.getStaticLocations(); 67 | this.sessionRegistry = sessionRegistry; 68 | this.rememberMeServices = rememberMeServices; 69 | } 70 | 71 | /** 72 | * ProviderManager 커스터마이징 73 | */ 74 | @Override 75 | protected void configure(AuthenticationManagerBuilder auth) throws Exception { 76 | User.UserBuilder userBuilder = User.builder(); 77 | String password = "password"; 78 | String encodedPassword = passwordEncoder.encode(password); 79 | 80 | auth.inMemoryAuthentication() 81 | .passwordEncoder(passwordEncoder) 82 | .withUser(userBuilder.username("system").password(encodedPassword).roles("SYSTEM")); 83 | 84 | auth.jdbcAuthentication() 85 | .dataSource(dataSource) 86 | .passwordEncoder(passwordEncoder) 87 | .withDefaultSchema() 88 | .withUser(userBuilder.username("admin").password(encodedPassword).roles("ADMIN")); 89 | } 90 | 91 | @Override 92 | public void configure(WebSecurity web) { 93 | web.ignoring().antMatchers(staticLocations) 94 | .and() 95 | .debug(environment.acceptsProfiles(Profiles.of("debug"))); 96 | } 97 | 98 | @Override 99 | protected void configure(HttpSecurity http) throws Exception { 100 | http.formLogin() 101 | .and() 102 | .httpBasic(); 103 | 104 | http.csrf().disable(); 105 | 106 | http.authorizeRequests() 107 | .antMatchers("/api/**").authenticated() 108 | .anyRequest().permitAll(); 109 | 110 | http.sessionManagement() 111 | .maximumSessions(1) 112 | .maxSessionsPreventsLogin(true) 113 | .sessionRegistry(sessionRegistry); 114 | 115 | http.rememberMe() 116 | .rememberMeServices(rememberMeServices); 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /web-security/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 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 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /web-security-session/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 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 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /web-security-oauth-server/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 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 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /web-security-oauth-kakao-login/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 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 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /web-security-oauth-kakao-login/README.md: -------------------------------------------------------------------------------- 1 | ## Web Security OAuth 2 | 스프링 시큐리티 5가 릴리즈되면서 [기존의 스프링 소셜 프로젝트는 중단](https://spring.io/blog/2018/07/03/spring-social-end-of-life-announcement)되었습니다. 스프링 소셜 프로젝트에서 제공하던 OAuth 기반 로그인을 구현할 수 있는 많은 부분이 스프링 시큐리티 모듈에 포함됩니다. 3 | 4 | > Spring Security OAuth 프로젝트도 중단되었습니다. 5 | > https://projects.spring.io/spring-security-oauth/docs/Home.html 6 | 7 | 본 예제에서는 스프링 시큐리티에 포함된 OAuth2 부분을 이용하여 카카오로 로그인하기를 구현하는 것을 설명합니다. 8 | 9 | 다른 OAuth 2.0 공급자에 대한 예제는 [스프링 시큐리티 공식 레퍼런스에서 제공하는 샘플 예제](https://github.com/spring-projects/spring-security/tree/5.2.2.RELEASE/samples/boot/oauth2login)를 참고하십시오. 10 | 11 | ### OAuth2 모듈 12 | 스프링 시큐리티 5.2 부터 다음과 같은 OAuth2 모듈이 포함됩니다. 13 | 14 | - spring-security-oauth2-core.jar 15 | - spring-security-oauth2-client.jar 16 | - spring-security-oauth2-jose.jar 17 | - spring-security-oauth2-resource-server.jar 18 | 19 | 이 중에서 Core와 Client 모듈을 활용할 예정입니다. 20 | 21 | > 나머지 모듈은 OAuth 2.0 인증 서버 예제에서 활용합니다. 22 | 23 | #### Dependenceis 24 | 스프링 부트는 OAuth 2.0 로그인을 위한 자동 구성을 제공하므로 기존 Web Security 예제에서 OAuth2 의존성만 추가하면 됩니다. 25 | 26 | **build.gradle** 27 | ```groovy 28 | dependenceis { 29 | implementation 'org.springframework.security:spring-security-oauth2-client' 30 | } 31 | ``` 32 | 33 | ### OAuth 2.0 로그인 34 | > 본 예제에서는 OAuth에 대한 개념을 다루지 않습니다! 35 | > OAuth에 대한 개념이 없다면 먼저 검색해서 숙지하시기 바랍니다. 36 | 37 | #### OAuth 로그인 활성화 38 | OAuth 로그인 기능을 활성화하기 위해서는 HttpSecurity.oauth2Login()를 호출하면 됩니다. 39 | 40 | DefaultLoginPageGeneratingFilter에 의해 만들어지는 기본 로그인 페이지에 OAuth 공급자에 대한 로그인 버튼이 만들어집니다. 41 | 42 | ```java 43 | @Override 44 | protected void configure(HttpSecurity http) throws Exception { 45 | http.oauth2Login(); 46 | } 47 | ``` 48 | 49 | #### 카카오 프로바이더 추가 50 | > [Configuring Custom Provider Properties](https://docs.spring.io/spring-security/site/docs/current/reference/html5/#oauth2login-custom-provider-properties) 51 | 52 | 카카오같은 국내 OAuth 프로바이더의 경우 스프링 시큐리티가 제공하는 기본 프로바이더 목록에 없으므로 사용자가 개별적으로 추가해야합니다. 53 | 54 | application.yml 55 | ```yaml 56 | spring: 57 | security: 58 | oauth2: 59 | client: 60 | registration: 61 | kakao: 62 | client-id: 63 | client-secert: 64 | redirect-uri: '{baseUrl}/login/oauth2/code/{registrationId}' 65 | authorization-grant-type: authorization_code 66 | provider: 67 | kakao: 68 | authorization-uri: https://kauth.kakao.com/oauth/authorize 69 | token-uri: https://kauth.kakao.com/oauth/token 70 | user-info-uri: https://kapi.kakao.com/v2/user/me 71 | user-info-authentication-method: POST 72 | ``` 73 | 74 | > 스프링 부트가 spring.security.oauth2.client 프로퍼티 정보를 읽어 프로바이더를 자동으로 추가합니다. 75 | > 또는 직접 [Register a ClientRegistrationRepository @Bean](https://docs.spring.io/spring-security/site/docs/current/reference/html5/#oauth2login-register-clientregistrationrepository-bean)을 참고하여 빈으로 등록할 수 있습니다. 76 | 77 | #### 카카오 사용자 프로필 클래스 추가 78 | > [Configuring a Custom OAuth2User](https://docs.spring.io/spring-security/site/docs/current/reference/html5/#oauth2login-advanced-custom-user) 79 | 80 | 카카오 프로바이더에서 제공하는 사용자 프로필 정보를 위한 OAuth2User 구현체를 만들어야 UserInfoEndpoint에서 인증 정보를 매핑할 수 있습니다. 81 | 82 | ```java 83 | @Data 84 | public class KakaoOAuth2User implements OAuth2User { 85 | private List authorities = AuthorityUtils.createAuthorityList("ROLE_USER"); 86 | private String id; 87 | @JsonProperty("kakao_account") 88 | private Map kakaoAccount; 89 | private Map properties; 90 | 91 | @Override 92 | public Map getAttributes() { 93 | Map attributes = new HashMap<>(); 94 | attributes.putAll(kakaoAccount); 95 | attributes.putAll(properties); 96 | return attributes; 97 | } 98 | 99 | @Override 100 | public Collection getAuthorities() { 101 | return authorities; 102 | } 103 | 104 | @Override 105 | public String getName() { 106 | return this.id; 107 | } 108 | } 109 | ``` 110 | 111 | 그리고 UserInfoEndpoint에서 kakao 프로바이더가 제공하는 사용자 유형 클래스가 KakaoOAuth2User라고 제공합니다. 112 | 113 | ```java 114 | http.oauth2Login( 115 | oauth2Login -> oauth2Login.userInfoEndpoint( 116 | userInfoEndpoint -> userInfoEndpoint.customUserType(KakaoOAuth2User.class, "kakao"))); 117 | ``` 118 | 119 | #### 카카오 계정으로 로그인하기 120 | 카카오 계정으로 로그인하기 버튼을 만들어 사용자에게 제공해야하지만 지금은 예제이므로 스프링 시큐리티가 자동으로 만들어주는 로그인 페이지를 통해 카카오 계정으로 로그인해보겠습니다. 121 | 122 | ![](src/main/resources/static/images/oauth-login-form.PNG) 123 | 124 | 혹시 로그인이 안되시나요? 여러분의 카카오 애플리케이션 정보를 확인해야합니다. 정확한 클라이언트 아이디를 입력했는지, 인증 시 리다이렉트되는 URI가 일치하는지 말입니다. 125 | 126 | ![](src/main/resources/static/images/kakao-app-information.PNG) 127 | 128 | > 해당 키들은 갱신하였기에 사용할 수 없습니다. :) 129 | 130 | ![](src/main/resources/static/images/kakao-app-redirect-uri.PNG) 131 | 132 | 성공적으로 인증된 경우에는 SecurityContextHolder를 통해 인증된 사용자에 대한 정보에서 확인할 수 있습니다. 133 | 134 | ![](src/main/resources/static/images/oauth-login-authentication-debug.PNG) 135 | 136 | #### 카카오 계정으로부터 토큰 발급 137 | OAuth로 인증된 사용자는 OAuth2AuthenticationToken 유형의 Principal이며 OAuth2AuthorizedClientService를 이용하여 인증된 사용자의 정보를 활용해서 액세스 토큰과 리프래쉬 토큰을 가져올 수 있습니다. 138 | 139 | ```java 140 | @RestController 141 | public class UserApi extends BaseApi { 142 | private final OAuth2AuthorizedClientService authorizedClientService; 143 | 144 | public UserApi(OAuth2AuthorizedClientService authorizedClientService) { 145 | this.authorizedClientService = authorizedClientService; 146 | } 147 | 148 | @GetMapping("/users/me/token") 149 | public ResponseEntity currentUserToken(@AuthenticationPrincipal Principal principal) { 150 | if(principal instanceof OAuth2AuthenticationToken) { 151 | Map attributes = new HashMap<>(); 152 | OAuth2AuthenticationToken oAuth2AuthenticationToken = (OAuth2AuthenticationToken) principal; 153 | 154 | OAuth2AuthorizedClient oAuth2AuthorizedClient = authorizedClientService.loadAuthorizedClient(oAuth2AuthenticationToken.getAuthorizedClientRegistrationId(), oAuth2AuthenticationToken.getName()); 155 | OAuth2AccessToken accessToken = oAuth2AuthorizedClient.getAccessToken(); 156 | OAuth2RefreshToken refreshToken = oAuth2AuthorizedClient.getRefreshToken(); 157 | 158 | attributes.put("name", oAuth2AuthenticationToken.getName()); 159 | attributes.put("accessToken", accessToken); 160 | attributes.put("refreshToken", refreshToken); 161 | return ResponseEntity.ok(attributes); 162 | } 163 | 164 | return ResponseEntity.ok(principal); 165 | } 166 | } 167 | ``` 168 | 169 | > 어떠신가요? 스프링 시큐리티가 다해주니까 참 쉽죠? 170 | 171 | ## 참고 172 | - [OAuth2 - Spring Security](https://docs.spring.io/spring-security/site/docs/5.2.2.RELEASE/reference/html5/#oauth2) 173 | - [카카오 REST API 개발가이드](https://developers.kakao.com/docs/restapi) -------------------------------------------------------------------------------- /web-security/README.md: -------------------------------------------------------------------------------- 1 | ## Web Security 2 | 본 프로젝트에서는 스프링 시큐리티 모듈에 대한 적용을 설명합니다. 3 | 4 | - Spring Security 5.2.2.RELEASE 5 | - Spring Boot 2.2.4.RELEASE 6 | 7 | ### SecurityAutoConfiguration 8 | 본 예제는 스프링 부트를 기반으로 되어있기 때문에 기본적인 스프링 시큐리티를 자동으로 적용하고 있습니다. SecurityAutoConfiguration 클래스를 살펴보면 스프링 부트가 어떻게 스프링 시큐리티를 사용할 수 있게 해주는지 알 수 있습니다. 9 | 10 | ```java 11 | @Configuration(proxyBeanMethods = false) 12 | @Import({ SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class, 13 | SecurityDataConfiguration.class }) 14 | public class SecurityAutoConfiguration {} 15 | ``` 16 | 17 | - SpringBootWebSecurityConfiguration : WebSecurityConfigurerAdapter 유형의 빈이 없을 경우 DefaultConfigurerAdapter를 등록합니다. 18 | - WebSecurityEnablerConfiguration : WebSecurityConfigurerAdapter 유형의 빈이 있는데 springSecurityFilterChain 빈이 없을 경우 @EnableWebSecurity를 적용합니다. 19 | - SecurityDataConfiguration : 스프링 데이터 모듈과의 통합을 제공합니다. 20 | 21 | ### 커스터마이징 22 | 일반적으로 프로젝트 또는 애플리케이션마다 보안에 대한 요구사항이 상이하므로 스프링 시큐리티에 대한 설정을 커스터마이징해야합니다. 스프링 부트 기본 시큐리티 설정이 아닌 우리만의 시큐리티 설정으로 변경해봅시다. 23 | 24 | #### PasswordEncoder 25 | DelegatingPasswordEncoder 구현체를 사용하면 여러가지 패스워드 인코더를 연결하여 사용할 수 있습니다. 26 | 27 | ```java 28 | @Bean 29 | public PasswordEncoder passwordEncoder() { 30 | String idForEncode = "bcrypt"; 31 | 32 | Map encoders = new HashMap<>(); 33 | encoders.put(idForEncode, new BCryptPasswordEncoder(12)); 34 | encoders.put("pbkdf2", new Pbkdf2PasswordEncoder()); 35 | encoders.put("scrypt", new SCryptPasswordEncoder()); 36 | 37 | return new DelegatingPasswordEncoder(idForEncode, encoders); 38 | } 39 | ``` 40 | 41 | > 스프링 부트는 기본 패스워드 인코더로 BCryptPasswordEncoder를 사용합니다. 42 | 43 | #### SecurityConfig.WebSecurityConfig 44 | WebSecurityConfigurerAdapter 유형의 빈을 등록함으로써 DefaultConfigurerAdapter이 비활성화됩니다. @EnableWebSecurity는 WebSecurityEnablerConfiguration에 등록되므로 명시하지않아도 되지만 저는 WebSecurityConfig가 웹 보안 설정을 활성화한다는 의미로 선언했습니다. 45 | 46 | ```java 47 | /** 48 | * 웹 보안 설정 49 | */ 50 | @EnableWebSecurity 51 | @Configuration(proxyBeanMethods = false) 52 | public static class WebSecurityConfig extends WebSecurityConfigurerAdapter {} 53 | ``` 54 | 55 | ##### AuthenticationManagerBuilder 56 | ProviderManager는 다른 AuthenticationManager들에게 인증 요청에 대한 처리를 위임한다고 하였습니다. 57 | 58 | > 당연히 기억나시겠죠?... 59 | 60 | AuthenticationManagerBuilder는 ProviderManagerBuilder 구현체이기 때문에 ProviderManager라는 AuthenticationManager를 설정할 수 있습니다. 61 | 62 | ```java 63 | @Override 64 | protected void configure(AuthenticationManagerBuilder auth) throws Exception { 65 | User.UserBuilder userBuilder = User.builder(); 66 | String password = "{bcrypt}$2a$12$ry/T4SyQyiNpaWbadf9sne3Cko..q92Oh2klkCMv4XB1qG6cy8iaG"; 67 | 68 | auth.inMemoryAuthentication().passwordEncoder(passwordEncoder).withUser(userBuilder.username("system").password(password).roles("SYSTEM")); 69 | auth.jdbcAuthentication().dataSource(dataSource).passwordEncoder(passwordEncoder).withDefaultSchema().withUser(userBuilder.username("admin").password(password).roles("ADMIN")); 70 | auth.userDetailsService(userService).passwordEncoder(passwordEncoder); 71 | auth.authenticationProvider(daoAuthenticationProvider); 72 | } 73 | ``` 74 | 75 | ##### WebSecurity 76 | WebSecurity 설정 함수를 오버라이딩하면 웹 보안과 관련한 설정을 할 수 있습니다. 77 | 78 | 다음은 예제에서 사용한 설정입니다. 79 | ```java 80 | @Override 81 | public void configure(WebSecurity web) { 82 | web.ignoring().antMatchers(staticLocations) 83 | .and() 84 | .debug(environment.acceptsProfiles(Profiles.of("debug"))); 85 | } 86 | ``` 87 | 88 | 각 함수는 체이닝 패턴으로 연결할 수 있으며 정적 리소스에 대해서는 웹 보안 설정을 무시하도록 하였고 애플리케이션 구동시 debug 프로필을 포함하는 경우 웹 보안에 대한 디버그 모드를 활성화하도록 설정하였습니다. 89 | 90 | > @EnableWebSecurity에서 debug 속성으로 활성화 할 수 있으나 소스코드를 수정해야하는 번거러움이 있습니다. 91 | 92 | ##### HttpSecurity 93 | HttpSecurity는 WebSecurity와 달리 HTTP 통신과 관련된 보안 사항을 설정할 수 있도록 지원합니다. 94 | 95 | ```java 96 | @Override 97 | protected void configure(HttpSecurity http) throws Exception { 98 | http.formLogin() 99 | .and() 100 | .httpBasic(); 101 | 102 | http.authorizeRequests() 103 | .antMatchers("/api/login", "/api/logout").permitAll() 104 | .antMatchers("/console/**").hasAnyRole("SYSTEM") 105 | .antMatchers("/api/**").hasAnyRole("USER", "ADMIN") 106 | .anyRequest().permitAll(); 107 | 108 | http.csrf().disable(); 109 | } 110 | ``` 111 | 112 | HTTP를 통한 사용자 인증 시 Basic 인증과 폼 양식 기반 인증을 활성화하였습니다. 추가적으로 API 자원에 대해서는 인증된 사용자만 접근 가능하도록하였고 나머지에 대한 요청은 허용한다는 의미입니다. 113 | 114 | 이처럼 115 | 116 | #### SecurityConfig.GlobalMethodSecurityConfig 117 | GlobalMethodSecurityConfiguration 클래스를 확장하여 설정을 커스터마이징 할 수 있으며 @EnableGlobalMethodSecurity으로 전역 메소드 보안을 활성화 할 수 있습니다. 118 | 119 | ```java 120 | /** 121 | * 메소드 기반 보안 설정 122 | */ 123 | @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = false) 124 | @Configuration(proxyBeanMethods = false) 125 | public static class GlobalMethodSecurityConfig extends GlobalMethodSecurityConfiguration {} 126 | ``` 127 | 128 | > 사실 GlobalMethodSecurityConfiguration를 확장하는 것은 옵션일 뿐이며, @EnableGlobalMethodSecurity를 WebSecurityConfig에 선언해도 상관없습니다. 129 | 130 | ### 사용자 정의 기능 구현 131 | 132 | #### 개별 사용자 인증 처리하기 133 | 다음과 같이 AuthenticationProvider와 SecurityContextHolder를 활용하면 사용자에게 받은 정보로 직접 사용자 인증을 처리할 수 있습니다. 134 | 135 | ```java 136 | @Service 137 | public class AuthenticationService extends AbstractService { 138 | private final AuthenticationProvider authenticationProvider; 139 | 140 | public AuthenticationService(AuthenticationProvider authenticationProvider) { 141 | this.authenticationProvider = authenticationProvider; 142 | } 143 | 144 | public UserDetails authentication(User user) { 145 | return authentication(user.getId(), user.getPassword()); 146 | } 147 | 148 | public UserDetails authentication(String id, String password) { 149 | LOG.debug("Call authentication({}, {})", id, password); 150 | SecurityContext securityContext = SecurityContextHolder.getContext(); 151 | UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(id, password); 152 | Authentication authentication = authenticationProvider.authenticate(authenticationToken); 153 | securityContext.setAuthentication(authentication); 154 | return (UserDetails) authentication.getPrincipal(); 155 | } 156 | } 157 | ``` 158 | 159 | #### 인증된 사용자 정보 가져오기 160 | 인증된 사용자를 가져오는 방법은 여러가지 입니다. 161 | 162 | - @AuthenticationPrincipal : AuthenticationPrincipalArgumentResolver가 선언된 매개변수에 인증된 사용자 정보를 넣어줍니다. 163 | - SecurityContextHolder.getContext().getAuthentication().getPrincipal() 164 | 165 | > SecurityContextHolder으로부터 인증된 사용자를 가져온다면 NullPointerException 방지 코드를 작성하시기 바랍니다. 166 | 167 | ```java 168 | @GetMapping("/users/me") 169 | public ResponseEntity currentUser(@AuthenticationPrincipal UserDetails principal) { 170 | Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 171 | if(authentication != null) { 172 | principal = (UserDetails) authentication.getPrincipal(); 173 | } 174 | 175 | return ResponseEntity.ok(principal); 176 | } 177 | ``` 178 | 179 | #### 함수 결과 오브젝트와 인증된 사용자 정보를 비교하기 180 | 메소드 기반 접근 제어를 활성화 한 경우 함수로 제공된 매개변수 또는 리턴 오브젝트와 인증된 사용자 정보를 비교할 수 있습니다. 181 | 182 | > 자세한 내용은 공식 레퍼런스의 [Method Security Expressions](https://docs.spring.io/spring-security/site/docs/5.2.2.RELEASE/reference/html5/#method-security-expressions)를 참고하세요 183 | 184 | 만약, 책 소유자만 update 함수 접근을 허용한다고 하면 SpEL 표현식으로 다음과 같이 인증 정보에 포함된 사용자 정보와 비교할 수 있습니다. 185 | 186 | ```java 187 | @PreAuthorize("#book.authorId == authentication.principal.id") 188 | public boolean update(@P("book") Book book) { 189 | // 수정 로직 190 | return true; 191 | } 192 | ``` 193 | 194 | ## 끝마치며 195 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > 기존 초보 개발자가 이해하는 스프링 시큐리티는 [4.x 브랜치](https://github.com/kdevkr/spring-demo-security/tree/4.x)에서 확인하세요 2 | 3 | ## 들어가며 4 | 본 프로젝트는 스프링 시큐리티에 입문하는 분들에게 도움이 되고자 작성하는 문서입니다. 초보 개발자가 이해하는 스프링 시큐리티는 공식 레퍼런스를 참고하여 작성하였습니다. 5 | 6 | - [Spring Security Reference - 5.2.2.RELEASE](https://docs.spring.io/spring-security/site/docs/5.2.2.RELEASE/reference/html5/) 7 | - [OAuth2 Boot - 2.2.4.RELEASE](https://docs.spring.io/spring-security-oauth2-boot/docs/2.2.4.RELEASE/reference/html5/) 8 | 9 | ## 초보 개발자가 이해하는 스프링 시큐리티 10 | 스프링 시큐리티에 입문하기 위해서는 보안에 대한 개념과 스프링 AOP를 이해하고 있어야 합니다. 11 | 12 | ### 아키텍처 13 | 스프링 시큐리티 모듈을 적용하기 위해서 가장 먼저 스프링 시큐리티의 보안 아키텍처를 살펴보고 이해해여야 합니다. 보안 아키텍처를 알면 스프링 시큐리티가 동작하는 과정을 알 수 있게 됩니다. 14 | 15 | 스프링 시큐리티의 보안 아키텍처는 보안을 구성하는 두가지 영역으로 인증과 권한을 가집니다. 16 | 17 | #### 인증(Authentication) 18 | 스프링 시큐리티에서 인증이란 사용자가 자신을 입증할 수 있는 정보를 시스템에 제공함으로써 시스템이 사용자에 대한 정보를 검증하고 로그인된 사용자를 위한 보안 전략을 수립하는 것을 말합니다. 19 | 20 | > [스프링 시큐리티에서 인증이란 무엇인가요?](https://docs.spring.io/spring-security/site/docs/current/reference/html5/#what-is-authentication-in-spring-security) 21 | 22 | 예를 들어, 가장 일반적인 인증 시나리오를 생각해보겠습니다. 사용자는 이름과 비밀번호를 통해 로그인을 시도합니다. 이에 시스템은 사용자가 입력한 이름과 비밀번호가 일치하는지를 확인하고 사용자의 정보를 얻어 시스템을 사용할 수 있도록 허가하게 됩니다. 23 | 24 | #### 권한(Authorization) 25 | 스프링 시큐리티에서 권한이란 접근 제어(Access-Control)입니다. 26 | 27 | 접근 제어란 보호된 자원에 대하여 접근을 허가하거나 거부하는 기능을 말하며 애플리케이션에서 보호된 자원은 메소드 호출이나 웹 요청이 될 수 있습니다. 28 | 29 | > 스프링 시큐리티는 AOP 및 웹 요청을 위한 필터를 통해 접근 제어를 수행합니다. 30 | 31 | ![](https://docs.spring.io/spring-security/site/docs/current/reference/html5/images/security-interception.png) 32 | 33 | 위 그림은 스프링 시큐리티에서 보호된 자원에 대하여 권한을 확인하는데 사용되는 구현체를 설명합니다. 여러가지 구현체를 통해 다양한 방식으로 접근 제어가 가능하다는 것으로 이해하시기 바랍니다. 34 | 35 | 이렇게 스프링 시큐리티의 아키텍처를 종합해보면 인증과 권한을 통해 권한 승인이 필요한 부분으로 접근하기 위해서는 인증 과정을 통해 사용자(인증 주체)가 증명되어야만 한다는 것을 알 수 있습니다. 36 | 37 | > 스프링 시큐리티의 인증과 권한에 대해서 좀 더 찾아보시기 바랍니다. 38 | 39 | ### 스프링 시큐리티 시작하기 40 | 스프링 시큐리티는 `spring-security-web`과 `spring-security-config` 모듈로 이루어져있습니다. 41 | 42 | > `spring-boot-starter-security`는 두 모듈에 대한 의존성을 포함합니다. 43 | 44 | **build.gradle** 45 | 46 | ```java 47 | plugins { 48 | id 'org.springframework.boot' version '2.2.4.RELEASE' 49 | id 'io.spring.dependency-management' version '1.0.9.RELEASE' 50 | id 'java' 51 | } 52 | 53 | dependencies { 54 | implementation 'org.springframework.boot:spring-boot-starter-security' 55 | implementation 'org.springframework.boot:spring-boot-starter-web' 56 | } 57 | ``` 58 | 59 | #### 시큐리티 자동 구성 60 | 스프링 부트는 spring-boot-starter-security에 따라 스프링 시큐리티를 자동으로 구성하게 됩니다. 61 | 62 | 시큐리티 자동 구성 클래스는 SecurityAutoConfiguration입니다. 63 | 64 | 스프링 부트의 @EnableAutoConfiguration에 의해 SecurityAutoConfiguration가 빈으로 등록되며 이 과정에서 SpringBootWebSecurityConfiguration, WebSecurityEnablerConfiguration, SecurityDataConfiguration, SecurityProperties, AuthenticationEventPublisher가 빈으로 함께 등록됩니다. 65 | 66 | _**SpringBootWebSecurityConfiguration**_ 67 | SpringBootWebSecurityConfiguration는 조건식에 의해 WebSecurityConfigurerAdapter를 확장한 빈이 없으면 빈으로 등록됩니다. 68 | 69 | _**WebSecurityEnablerConfiguration**_ 70 | WebSecurityEnablerConfiguration는 WebSecurityConfigurerAdapter이 빈으로 등록되어있으면서 springSecurityFilterChain라는 이름의 빈이 존재하지않을 경우 @EnableWebSecurity를 통해 WebSecurityConfiguration를 등록합니다. 71 | 72 | ```java 73 | /** 74 | * Creates the Spring Security Filter Chain 75 | * @return the {@link Filter} that represents the security filter chain 76 | * @throws Exception 77 | */ 78 | @Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME) 79 | public Filter springSecurityFilterChain() throws Exception { 80 | boolean hasConfigurers = webSecurityConfigurers != null 81 | && !webSecurityConfigurers.isEmpty(); 82 | if (!hasConfigurers) { 83 | WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor 84 | .postProcess(new WebSecurityConfigurerAdapter() { 85 | }); 86 | webSecurity.apply(adapter); 87 | } 88 | return webSecurity.build(); 89 | } 90 | ``` 91 | 92 | > 만약, 스프링 부트 기반이 아니라면 springSecurityFilterChain이라는 이름의 빈을 필수로 등록해야합니다. 93 | 94 | _**SecurityDataConfiguration**_ 95 | SecurityDataConfiguration는 스프링 시큐리티와 스프링 데이터 모듈의 통합을 지원합니다. 96 | 97 | _**SecurityFilterAutoConfiguration**_ 98 | SecurityAutoConfiguration이 자동으로 등록된 이후에 SecurityFilterAutoConfiguration가 등록되어 앞서 등록된 springSecurityFilterChain이라는 이름의 빈을 DelegatingFilterProxy에 등록합니다. 99 | 100 | 이외에도 Saml2RelyingPartyAutoConfiguration, OAuth2ResourceServerAutoConfiguration, OAuth2ClientAutoConfiguration가 조건하에 등록됩니다. 101 | 102 | 여기까지가 스프링 부트가 스프링 시큐리티에 대한 기본 구성을 적용하는 것들입니다. 103 | 104 | ## 스프링 시큐리티 구성하기 105 | 스프링 시큐리티의 인증 매커니즘에 대해 알아보고 앞서 알아본 인증과 권한을 어떻게 적용하는지 알아보겠습니다. 106 | 107 | ### 인증 매커니즘 108 | 스프링 시큐리티는 사용자 클라이언트로부터 인증 정보를 모으는 것을 인증 매커니즘이라 합니다. 109 | 110 | 사용자가 브라우저를 통해 자신을 입증할 수 있는 이름이나 비밀번호등을 제공하면 스프링 시큐리티는 인증 매커니즘을 통해 Authentication 요청 오브젝트를 만들고 AuthenticationManager에 제공합니다. 111 | 112 | 이후 인증 매커니즘은 완전히 채워진 Authentication 오브젝트를 다시 받아 (요청이 유효한 것으로 간주함) Authentication를 SecurityContextHolder에 넣어 원래 요청을 다시 시도합니다. 113 | 114 | 만약, AuthenticationManager에 의해 요청이 거부된 경우 인증 매커니즘은 브라우저로 다시 요청하라는 응답을 합니다. 115 | 116 | ### 구현체 117 | 인증 매커니즘에 대한 동작을 처리할 때 사용되는 여러가지 인터페이스 및 구현체를 제공합니다. 118 | 119 | #### AuthenticationManager, AuthenticationProvier 120 | AuthenticationManager의 기본 구현체인 ProviderManager는 인증 요청을 자체적으로 처리하지 않고 설정된 AuthenticationProvier들에게 이를 위임합니다. 그리고 AuthenticationProvier는 차례대로 인증을 수행할 수 있는지 체크합니다. 121 | 122 | - AnonymousAuthenticationProvider 123 | - AbstractUserDetailsAuthenticationProvider 124 | - DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider 125 | 126 | #### UserDetails, GrantedAuthority 127 | UserDetails 구현체는 사용자의 이름, 비밀번호, 부여된 권한 그리고 사용자가 활성화되었는지 아닌지에 대한 정보를 제공합니다. 128 | 129 | > UserDetails를 구현하여 기존 사용자 정보를 인증하는데 사용할 수 있습니다. 130 | 131 | #### UserDetailsService 132 | UserDetailsService는 사용자가 입력한 정보(이름과 비밀번호)와 비교하여 인증을 확인하는 가장 일반적인 방법을 제공합니다. 133 | 134 | ```java UserDetailsService 135 | UserDetails loadUserByUsername(String username) throws UsernameNotFoundException; 136 | ``` 137 | 138 | - JdbcDaoImpl 139 | 140 | > 기본 JdbcDaoImpl는 다음의 [Security Database Schema](https://docs.spring.io/spring-security/site/docs/5.2.2.RELEASE/reference/html5/#appendix-schema)를 사용합니다. 141 | 142 | #### PasswordEncoder 143 | 스프링 시큐리티는 비밀번호 인코딩 처리를 위한 여러가지 PasswordEncoder 구현체를 제공합니다. 144 | 145 | - BCryptPasswordEncoder 146 | - SCryptPasswordEncoder 147 | - Pbkdf2PasswordEncoder 148 | - ~~NoOpPasswordEncoder~~ 149 | - ~~StandardPasswordEncoder~~ 150 | - ~~MessageDigestPasswordEncoder~~ 151 | - ~~Md4PasswordEncoder~~ 152 | - ~~LdapShaPasswordEncoder~~ 153 | 154 | > 스프링 시큐리티 5+에서는 안전하지 않은 비밀번호 인코더를 제외했습니다. 155 | 156 | - DelegatingPasswordEncoder 157 | 158 | > 이 비밀번호 인코더는 여러가지 비밀번호 인코더를 연결하여 사용할 수 있는 특별한 인코더입니다. 159 | 160 | #### Filter 161 | 스프링 시큐리티는 다음과 같은 Filter 구현체를 제공합니다. 162 | 163 | - FilterChainProxy 164 | - SecurityContextPersistenceFilter 165 | - CsrfFilter 166 | - LogoutFilter 167 | - UsernamePasswordAuthenticationFilter 168 | - DefaultLoginPageGeneratingFilter 169 | - DefaultLogoutPageGeneratingFilter 170 | - BasicAuthenticationFilter 171 | - RequestCacheAwareFilter 172 | - SecurityContextHolderAwareRequestFilter 173 | - AnonymousAuthenticationFilter 174 | - SessionManagementFilter 175 | - ExceptionTranslationFilter 176 | - FilterSecurityInterceptor 177 | - ChannelProcessingFilter 178 | - AbstractAuthenticationProcessingFilter 179 | - AbstractPreAuthenticatedProcessingFilter 180 | - RememberMeAuthenticationFilter 181 | - SwitchUserFilter 182 | - DigestAuthenticationFilter 183 | - ConcurrentSessionFilter 184 | - SessionManagementFilter 185 | 186 | > org.springframework.security.web.FilterChainProxy의 로그 레벨을 DEBUG로 설정하면 요청에 대한 필터 처리 순서를 파악할 수 있습니다. 187 | 188 | 이외에도 여러가지 기능을 위한 구현체가 존재하지만 넘어가도록 하겠습니다. 189 | 190 | #### SecurityContextPersistenceFilter 191 | 일반적인 웹 애플리케이션의 경우 요청 사용자 정보를 세션으로 관리합니다. 스프링 시큐리티는 SecurityContextPersistenceFilter를 통해 HTTP 요청 간에서 HttpSession에 포함된 속성들을 SecurityContext에 저장합니다. 192 | 193 | > 스프링 시큐리티는 세션에 대한 정보를 관리하므로 보안을 목적으로 HttpSession과 직접적으로 상호작용하면 안됩니다. 194 | 195 | #### SecurityContextHolder 196 | SecurityContextHolder라는 특별한 클래스는 SecurityContext를 제공하는 아주 중요한 역할을 합니다. 197 | 198 | 기본적으로 SecurityContextHolder는 ThreadLocal를 사용하여 정보를 저장하기 때문에 동일한 스레드에서 수행되는 메소드에서 SecurityContext를 항상 사용할 수 있습니다. 199 | 200 | > SecurityContextHolder가 ThreadLocal로 동작하는 것이 올바르지 않은 경우 이를 변경할 수 있습니다. 201 | > - SecurityContextHolder.MODE_GLOBAL 202 | > - SecurityContextHolder.MODE_INHERITABLETHREADLOCAL 203 | > - SecurityContextHolder.MODE_THREADLOCAL 204 | 205 | 종합적으로 요약하자면 스프링 시큐리티의 인증 매커니즘을 통해 SecurityContext에 사용자 인증 정보가 저장되어있고 SecurityContextHolder를 통해 현재 실행하고 있는 스레드에서 인증된 사용자의 정보를 가져올 수 있습니다. 206 | 207 | ## 스프링 시큐리티 예제 208 | 초보 개발자가 이해하는 스프링 시큐리티 예제는 스프링 부트를 기반으로 작성하였습니다. 스프링 부트는 스프링을 사용할 때 필요한 여러가지 기본 설정들을 자동으로 제공해주어 빠른 예제 작성을 위해 사용하였습니다. 209 | 210 | ### Web 211 | 212 | #### [Web](web-security) 213 | 스프링 시큐리티 설정부터 인증 프로세스, 접근 제어를 다루는 기초 예제 214 | 215 | #### [Web Session](web-security-session) 216 | `spring-session` 모듈과 연계하여 인증된 사용자 정보를 Redis와 같은 분산 저장소에 저장하고 세션에 대한 동시성 제어를 적용할 수 있음을 확인하는 예제 217 | 218 | ### Web with OAuth 219 | 220 | #### [OAuth Kakao Login](web-security-oauth-kakao-login) 221 | `spring-security-oauth-client` 모듈을 활용하여 카카오 프로바이더에 대한 로그인을 적용할 수 있음을 확인하는 예제 222 | 223 | #### [OAuth Server](web-security-oauth-server) 224 | 스프링 시큐리티를 통해 OAuth 2.0 시스템을 구성할 수 있음을 확인하는 예제 225 | 226 | ## 참고 227 | - [Spring Security Reference - 5.2.2.RELEASE](https://docs.spring.io/spring-security/site/docs/5.2.2.RELEASE/reference/html5/) 228 | - [Spring Security Architecture](https://spring.io/guides/topicals/spring-security-architecture) -------------------------------------------------------------------------------- /web-security/src/main/java/kr/kdev/demo/config/SecurityConfig.java: -------------------------------------------------------------------------------- 1 | package kr.kdev.demo.config; 2 | 3 | import kr.kdev.demo.service.UserService; 4 | import org.springframework.boot.autoconfigure.web.ResourceProperties; 5 | import org.springframework.context.EnvironmentAware; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.core.env.Environment; 9 | import org.springframework.core.env.Profiles; 10 | import org.springframework.http.HttpMethod; 11 | import org.springframework.security.access.annotation.Secured; 12 | import org.springframework.security.access.prepost.PostAuthorize; 13 | import org.springframework.security.access.prepost.PreAuthorize; 14 | import org.springframework.security.authentication.AuthenticationProvider; 15 | import org.springframework.security.authentication.ProviderManager; 16 | import org.springframework.security.authentication.dao.DaoAuthenticationProvider; 17 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 18 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; 19 | import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration; 20 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 21 | import org.springframework.security.config.annotation.web.builders.WebSecurity; 22 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 23 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 24 | import org.springframework.security.core.userdetails.User; 25 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 26 | import org.springframework.security.crypto.password.DelegatingPasswordEncoder; 27 | import org.springframework.security.crypto.password.NoOpPasswordEncoder; 28 | import org.springframework.security.crypto.password.PasswordEncoder; 29 | import org.springframework.security.web.authentication.AuthenticationSuccessHandler; 30 | import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; 31 | import org.springframework.web.cors.CorsConfiguration; 32 | import org.springframework.web.cors.CorsConfigurationSource; 33 | import org.springframework.web.cors.UrlBasedCorsConfigurationSource; 34 | 35 | import javax.sql.DataSource; 36 | import java.time.Duration; 37 | import java.util.HashMap; 38 | import java.util.Map; 39 | 40 | /** 41 | * Security Configuration Integration 42 | * 43 | * @author kdevkr 44 | */ 45 | @Configuration(proxyBeanMethods = false) 46 | public class SecurityConfig { 47 | 48 | public static final int PRINCIPAL_LOCK_BASELINE = 5; 49 | 50 | /** 51 | * Spring Boot 2.0는 기본적으로 BCryptPasswordEncoder를 사용합니다. 52 | * 53 | * 다음은 {@link DelegatingPasswordEncoder}를 사용하여 PasswordEncoder 체인을 구성합니다. 54 | */ 55 | @Bean 56 | public PasswordEncoder passwordEncoder() { 57 | String idForEncode = "bcrypt"; 58 | 59 | Map encoders = new HashMap<>(); 60 | encoders.put(idForEncode, new BCryptPasswordEncoder(12)); 61 | // encoders.put("pbkdf2", new Pbkdf2PasswordEncoder()); 62 | // encoders.put("scrypt", new SCryptPasswordEncoder()); 63 | encoders.put("noop", NoOpPasswordEncoder.getInstance()); 64 | 65 | return new DelegatingPasswordEncoder(idForEncode, encoders); 66 | } 67 | 68 | @Bean 69 | public DaoAuthenticationProvider daoAuthenticationProvider(PasswordEncoder passwordEncoder, 70 | UserService userService) { 71 | DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider(); 72 | authenticationProvider.setPasswordEncoder(passwordEncoder); 73 | authenticationProvider.setUserDetailsService(userService); 74 | return authenticationProvider; 75 | } 76 | 77 | @EnableWebSecurity 78 | @Configuration(proxyBeanMethods = false) 79 | public static class WebSecurityConfig extends WebSecurityConfigurerAdapter implements EnvironmentAware { 80 | 81 | private final String[] staticLocations; 82 | private final DataSource dataSource; 83 | private final PasswordEncoder passwordEncoder; 84 | private final UserService userService; 85 | private final DaoAuthenticationProvider daoAuthenticationProvider; 86 | 87 | private Environment environment; 88 | 89 | public WebSecurityConfig(DataSource dataSource, 90 | PasswordEncoder passwordEncoder, 91 | ResourceProperties resourceProperties, 92 | UserService userService, 93 | DaoAuthenticationProvider daoAuthenticationProvider) { 94 | this.dataSource = dataSource; 95 | this.passwordEncoder = passwordEncoder; 96 | this.staticLocations = resourceProperties.getStaticLocations(); 97 | this.userService = userService; 98 | this.daoAuthenticationProvider = daoAuthenticationProvider; 99 | } 100 | 101 | /** 102 | * {@link AuthenticationManagerBuilder}를 통해 {@link AuthenticationProvider}를 추가하십시오. 103 | * AuthenticationManagerBuilder는 {@link ProviderManager}를 구성합니다. 104 | */ 105 | @Override 106 | protected void configure(AuthenticationManagerBuilder auth) throws Exception { 107 | User.UserBuilder userBuilder = User.builder(); 108 | String password = "password"; 109 | String encodedPassword = passwordEncoder.encode(password); 110 | 111 | // InMemoryUserDetailsManager는 사용자 정보를 메모리에 저장하여 인증할 수 있도록 제공합니다. 112 | auth.inMemoryAuthentication() 113 | .passwordEncoder(passwordEncoder) 114 | .withUser(userBuilder.username("system").password(encodedPassword).roles("SYSTEM")); 115 | 116 | if(environment.acceptsProfiles(Profiles.of("postgres"))) { 117 | // DaoAuthenticationProvider는 UserDetailsService에서 제공하는 사용자 정보를 통해 인증할 수 있도록 제공합니다. 118 | auth.authenticationProvider(daoAuthenticationProvider) 119 | .userDetailsService(userService).passwordEncoder(passwordEncoder); 120 | } else { 121 | // JdbcUserDetailsManager는 기본 JDBC 스키마를 사용해서 사용자 정보를 구성하고 인증할 수 있도록 제공합니다. 122 | auth.jdbcAuthentication() 123 | .dataSource(dataSource).passwordEncoder(passwordEncoder).withDefaultSchema() 124 | .withUser(userBuilder.username("admin").password(encodedPassword).roles("ADMIN") 125 | .authorities("REPOSITORY_CREATE")); 126 | } 127 | } 128 | 129 | /** 130 | * 웹에 대한 보안 설정 131 | */ 132 | @Override 133 | public void configure(WebSecurity web) { 134 | web.ignoring().antMatchers(staticLocations); 135 | 136 | // StrictHttpFirewall httpFirewall = new StrictHttpFirewall(); 137 | // httpFirewall.setAllowedHostnames((hostname)-> hostname.equals("github.com")); 138 | // httpFirewall.setAllowedHostnames((hostname)-> hostname.equals("localhost")); 139 | // web.httpFirewall(httpFirewall); 140 | 141 | // 웹 보안에 대한 디버그 메시지를 활성화 할 수 있습니다. 142 | web.debug(environment.acceptsProfiles(Profiles.of("debug"))); 143 | } 144 | 145 | /** 146 | * HTTP에 대한 보안 설정 147 | */ 148 | @Override 149 | protected void configure(HttpSecurity http) throws Exception { 150 | http.formLogin() 151 | .loginPage("/login"); 152 | http.httpBasic().disable(); 153 | http.csrf().disable(); 154 | 155 | // http.cors(cors -> cors.configurationSource(configurationSource())); 156 | 157 | // Permit login and logout api. 158 | // but, other apis only can access authenticated user. 159 | http.authorizeRequests() 160 | .antMatchers("/api/login", "/api/logout").permitAll() 161 | .antMatchers("/api/**").authenticated() 162 | .antMatchers("/console/**", "/api/console/**").hasRole("SYSTEM") 163 | .antMatchers("/").permitAll(); 164 | } 165 | 166 | @Bean 167 | public AuthenticationSuccessHandler authenticationSuccessHandler() { 168 | SavedRequestAwareAuthenticationSuccessHandler authenticationSuccessHandler = new SavedRequestAwareAuthenticationSuccessHandler(); 169 | authenticationSuccessHandler.setTargetUrlParameter("redirect-uri"); 170 | return authenticationSuccessHandler; 171 | } 172 | 173 | /** 174 | * Cors 구성 예시 175 | * @return Cors 설정 정보 176 | */ 177 | private CorsConfigurationSource configurationSource() { 178 | CorsConfiguration corsConfiguration = new CorsConfiguration(); 179 | corsConfiguration.setMaxAge(Duration.ofDays(1)); 180 | corsConfiguration.setAllowCredentials(true); 181 | corsConfiguration.addAllowedOrigin("*"); 182 | corsConfiguration.addAllowedHeader("*"); 183 | corsConfiguration.addAllowedMethod(HttpMethod.GET); 184 | 185 | UrlBasedCorsConfigurationSource configurationSource = new UrlBasedCorsConfigurationSource(); 186 | configurationSource.setRemoveSemicolonContent(true); 187 | configurationSource.registerCorsConfiguration("/**", corsConfiguration); 188 | return configurationSource; 189 | } 190 | 191 | @Override 192 | public void setEnvironment(Environment environment) { 193 | this.environment = environment; 194 | } 195 | } 196 | 197 | /** 198 | * 메소드 보안 설정 199 | * 200 | * prePostEnabled : {@link PreAuthorize}와 {@link PostAuthorize}를 사용하여 함수에 대한 접근을 제어합니다. 201 | * securedEnabled : {@link Secured} 어노테이션을 사용하여 함수에 대한 접근을 제어합니다. 202 | * jsr250Enabled : JSR-250 기반의 어노테이션을 사용하여 함수에 대한 접근을 제어합니다. 203 | */ 204 | @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = false, jsr250Enabled = false) 205 | @Configuration(proxyBeanMethods = false) 206 | public static class GlobalMethodSecurityConfig extends GlobalMethodSecurityConfiguration { 207 | 208 | } 209 | } 210 | --------------------------------------------------------------------------------