Browse Source

update

main
wangshaoping 7 months ago
parent
commit
8836262863
  1. 4
      app.platform/build.gradle
  2. 2
      app.platform/src/main/resources/app/platform/i18n/messages_zh_CN.properties
  3. 7
      default-authorizationserver/src/main/java/sample/DefaultAuthorizationServerApplication.java
  4. 1
      io.sc.platform.core.frontend/src/platform/index.ts
  5. 2
      io.sc.platform.core.frontend/src/platform/layout/WBasicLayout.vue
  6. 2
      io.sc.platform.core.frontend/src/platform/layout/sub-layout/SiderCollapser.vue
  7. 11
      io.sc.platform.core.frontend/src/platform/layout/sub-layout/Topper.vue
  8. 21
      io.sc.platform.core.frontend/src/platform/plugin/axios.ts
  9. 5
      io.sc.platform.core.frontend/src/platform/plugin/environment/default-configure.json
  10. 1
      io.sc.platform.core.frontend/src/platform/plugin/index.ts
  11. 9
      io.sc.platform.core.frontend/src/platform/plugin/manager/ApplicationInitializer.ts
  12. 127
      io.sc.platform.core.frontend/src/platform/plugin/manager/Oauth2Manager.ts
  13. 1
      io.sc.platform.core.frontend/src/platform/plugin/manager/index.ts
  14. 7
      io.sc.platform.core.frontend/src/platform/plugin/router.ts
  15. 14
      io.sc.platform.core.frontend/src/platform/utils/Tools.ts
  16. 7
      io.sc.platform.core.frontend/src/platform/views/Oauth2.vue
  17. 2
      io.sc.platform.core.frontend/src/views/testcase/maxgraph/maxgraph.vue
  18. 43
      io.sc.platform.developer.doc/asciidoc/9999-appendix/oauth2/oauth2.adoc
  19. 1
      io.sc.platform.developer.doc/asciidoc/index.adoc
  20. BIN
      io.sc.platform.developer.doc/asciidoc/resources/images/9999-appendix/oauth2/010.png
  21. BIN
      io.sc.platform.developer.doc/asciidoc/resources/images/9999-appendix/oauth2/011.png
  22. BIN
      io.sc.platform.developer.doc/asciidoc/resources/images/system-design/application-entry/001.png
  23. BIN
      io.sc.platform.developer.doc/asciidoc/resources/images/system-design/application-entry/002.png
  24. BIN
      io.sc.platform.developer.doc/asciidoc/resources/images/system-design/application-entry/003.png
  25. 36
      io.sc.platform.developer.doc/asciidoc/system-design/application-entry/application-entry.adoc
  26. 3
      io.sc.platform.developer.doc/asciidoc/system-design/system-design.adoc
  27. 20
      io.sc.platform.jdbc.liquibase/src/main/java/io/sc/platform/jdbc/liquibase/service/impl/LiquibaseServiceImpl.java
  28. 9
      io.sc.platform.lcdp/src/main/java/io/sc/platform/lcdp/configure/api/Configure.java
  29. 31
      io.sc.platform.lcdp/src/main/java/io/sc/platform/lcdp/configure/api/Oauth2.java
  30. 3
      io.sc.platform.lcdp/src/main/java/io/sc/platform/lcdp/configure/controller/ConfigureJsController.java
  31. 18
      io.sc.platform.lcdp/src/main/java/io/sc/platform/lcdp/configure/service/impl/ConfigureServiceImpl.java
  32. 7
      io.sc.platform.lcdp/src/main/resources/templates/configure.js
  33. 79
      io.sc.platform.security.loginform/src/main/java/io/sc/platform/security/loginform/autoconfigure/WebSecurityAutoConfiguration.java
  34. 2
      io.sc.platform.security.oauth2.server.authorization/src/main/java/io/sc/platform/security/oauth2/server/authorization/bean/PlatformRegisteredClientRepository.java
  35. 140
      io.sc.platform.security.oauth2.server.authorization/src/main/java/io/sc/platform/security/oauth2/server/authorization/configure/PlatformOauth2AuthorizationServerAutoConfiguration.java
  36. 70
      io.sc.platform.security.oauth2.server.authorization/src/main/java/io/sc/platform/security/oauth2/server/authorization/configure/PlatformWebSecurityAutoConfiguration.java
  37. 102
      io.sc.platform.security.oauth2.server.authorization/src/main/java/io/sc/platform/security/oauth2/server/authorization/configure/SecurityAutoConfiguration.java
  38. 149
      io.sc.platform.security.oauth2.server.authorization/src/main/java/io/sc/platform/security/oauth2/server/authorization/configure/SecurityAutoConfiguration2.java
  39. 22
      io.sc.platform.security.oauth2.server.authorization/src/main/java/io/sc/platform/security/oauth2/server/authorization/configure/support/PlatformOauth2TokenCustomizer.java
  40. 2
      io.sc.platform.security.oauth2.server.authorization/src/main/java/io/sc/platform/security/oauth2/server/authorization/configure/support/PlatformRedirectUriValidator.java
  41. 2
      io.sc.platform.security.oauth2.server.authorization/src/main/java/io/sc/platform/security/oauth2/server/authorization/controller/LoginWebController.java
  42. 41
      io.sc.platform.security.oauth2.server.authorization/src/main/java/io/sc/platform/security/oauth2/server/authorization/controller/ShowOauth2TokenWebController.java
  43. 37
      io.sc.platform.security.oauth2.server.authorization/src/main/java/io/sc/platform/security/oauth2/server/authorization/initializer/Oauth2RegisteredClientInitializer.java
  44. 58
      io.sc.platform.security.oauth2.server.authorization/src/main/java/io/sc/platform/security/oauth2/server/authorization/password/PasswordGrantAuthenticationConverter.java
  45. 139
      io.sc.platform.security.oauth2.server.authorization/src/main/java/io/sc/platform/security/oauth2/server/authorization/password/PasswordGrantAuthenticationProvider.java
  46. 13
      io.sc.platform.security.oauth2.server.authorization/src/main/java/io/sc/platform/security/oauth2/server/authorization/password/PasswordGrantAuthenticationToken.java
  47. 10
      io.sc.platform.security.oauth2.server.authorization/src/main/resources/META-INF/platform/plugins/application-properties.json
  48. 5
      io.sc.platform.security.oauth2.server.authorization/src/main/resources/META-INF/platform/plugins/messages.json
  49. 6
      io.sc.platform.security.oauth2.server.authorization/src/main/resources/META-INF/spring.factories
  50. 2
      io.sc.platform.security.oauth2.server.authorization/src/main/resources/io/sc/platform/security/oauth2/server/authorization/i18n/initializer.properties
  51. 2
      io.sc.platform.security.oauth2.server.authorization/src/main/resources/io/sc/platform/security/oauth2/server/authorization/i18n/initializer_tw_CN.properties
  52. 2
      io.sc.platform.security.oauth2.server.authorization/src/main/resources/io/sc/platform/security/oauth2/server/authorization/i18n/initializer_zh_CN.properties
  53. 65
      io.sc.platform.security.oauth2.server.resource/src/main/java/io/sc/platform/security/oauth2/server/resource/configure/PlatformOauth2ResourceServerAutoConfiguration.java
  54. 2
      io.sc.platform.security.oauth2.server.resource/src/main/java/io/sc/platform/security/oauth2/server/resource/configure/support/PlatformJwtAuthenticationConverter.java
  55. 9
      io.sc.platform.security/src/main/java/io/sc/platform/security/AuthenticationMode.java
  56. 50
      io.sc.platform.security/src/main/java/io/sc/platform/security/oauth2/Token.java
  57. 33
      io.sc.platform.security/src/main/java/io/sc/platform/security/support/SecurityRoleDeserializer.java
  58. 13
      io.sc.platform.security/src/main/java/io/sc/platform/security/support/SecurityRoleMix.java
  59. 26
      io.sc.platform.security/src/main/java/io/sc/platform/security/support/SecurityUser.java
  60. 100
      io.sc.platform.security/src/main/java/io/sc/platform/security/support/SecurityUserDeserializer.java
  61. 13
      io.sc.platform.security/src/main/java/io/sc/platform/security/support/SecurityUserMix.java
  62. 3
      io.sc.platform.security/src/main/resources/META-INF/platform/plugins/application-properties.json

4
app.platform/build.gradle

@ -14,8 +14,8 @@ dependencies {
project(":io.sc.platform.app"),
project(":io.sc.platform.developer"),
project(":io.sc.platform.security.oauth2.server.authorization"),
//project(":io.sc.platform.security.oauth2.server.resource"),
//project(":io.sc.platform.security.loginform"),
project(":io.sc.platform.security.oauth2.server.resource"),
project(":io.sc.platform.security.loginform"),
/*
project(":io.sc.platform.scheduler.manager"),

2
app.platform/src/main/resources/app/platform/i18n/messages_zh_CN.properties

@ -1,3 +1,3 @@
application.title=\u5B87\u4FE1\u79D1\u6280-\u98CE\u9669\u7BA1\u7406\u5E73\u53F0
application.title=\u98CE\u9669\u7BA1\u7406\u5E73\u53F0
application.version=$version
application.copyright=Copyright \u00A9 2019\u20132022

7
default-authorizationserver/src/main/java/sample/DefaultAuthorizationServerApplication.java

@ -18,6 +18,8 @@ package sample;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.util.Base64;
/**
* @author Joe Grandja
* @since 0.0.1
@ -26,7 +28,10 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
public class DefaultAuthorizationServerApplication {
public static void main(String[] args) {
SpringApplication.run(DefaultAuthorizationServerApplication.class, args);
String base64 =Base64.getEncoder().encodeToString("platform-oidc:secret".getBytes());
System.out.println(base64);
//http://localhost:8080/oauth2/authorize?client_secret=secret&client_id=platform-oidc&response_type=code&redirect_uri=http://localhost:8080/oauth2/authorized-oidc
//SpringApplication.run(DefaultAuthorizationServerApplication.class, args);
}
}

1
io.sc.platform.core.frontend/src/platform/index.ts

@ -86,6 +86,7 @@ export { I18nMessageManager } from './plugin';
export { MenuManager } from './plugin';
export { MockManager } from './plugin';
export { NotifyManager } from './plugin';
export { Oauth2Manager } from './plugin';
export { RouterManager } from './plugin';
export { SessionManager } from './plugin';
export { TagViewManager } from './plugin';

2
io.sc.platform.core.frontend/src/platform/layout/WBasicLayout.vue

@ -3,8 +3,8 @@
</template>
<script setup lang="ts">
import type { ILayoutProvider } from '@/platform';
import { computed } from 'vue';
import type { ILayoutProvider } from '@/platform';
import { PConst, Environment } from '@/platform';
import LeftMenuLayout from './LeftMenuLayout.vue';

2
io.sc.platform.core.frontend/src/platform/layout/sub-layout/SiderCollapser.vue

@ -24,7 +24,7 @@ const props = defineProps({
});
const getCollapserIcon = computed(() => {
if (Environment.getGlobalReactive().isSiderExpaned) {
if (Environment.getConfigure().theme.sider.isSiderExpaned) {
if (props.reverse) {
return 'bi-text-indent-left';
} else {

11
io.sc.platform.core.frontend/src/platform/layout/sub-layout/Topper.vue

@ -151,7 +151,8 @@
</q-btn>
</q-toolbar>
<q-form ref="logoutForm" :action="Environment.apiContextPath('/logout')" method="post"> </q-form>
<q-form ref="logoutForm" :action="Environment.apiContextPath('/logout')" method="post" target="platformLogoutIframe"></q-form>
<iframe name="platformLogoutIframe" style="display: none"></iframe>
<AboutDialog ref="aboutDialog"></AboutDialog>
<ChangePasswordDialog ref="changePasswordDialog"></ChangePasswordDialog>
@ -163,7 +164,7 @@ import { ref } from 'vue';
import { useQuasar } from 'quasar';
import { useI18n } from 'vue-i18n';
import { axios, Environment, SessionManager, I18nMessageManager, router } from '@/platform';
import { axios, Environment, SessionManager, I18nMessageManager, Oauth2Manager } from '@/platform';
import AboutDialog from './AboutDialog.vue';
import ChangePasswordDialog from './ChangePasswordDialog.vue';
@ -218,9 +219,9 @@ const logout = () => {
};
const doLogout = () => {
axios.post(Environment.apiContextPath('/logout')).then(() => {
window.location.href = Environment.apiContextPath('/');
//logoutForm.value.$el.submit();
logoutForm.value.$el.submit();
Oauth2Manager.revokeTokens(() => {
window.location.href = Environment.getWebContextPath();
});
};

21
io.sc.platform.core.frontend/src/platform/plugin/axios.ts

@ -3,7 +3,7 @@ import Axios from 'axios';
import { i18n } from './i18n';
import { PConst } from '@/platform/PConst';
import { Environment } from '@/platform/plugin/environment';
import { NotifyManager } from './manager';
import { NotifyManager, Oauth2Manager } from './manager';
import { QuasarTools } from '@/platform/utils';
const ignoredUrls: string[] = [PConst.API_I18N_MESSAGES_URL, PConst.API_APP_CONFIGURE_URL];
@ -18,11 +18,15 @@ const requestInterceptor = (config: any) => {
return config;
}
}
const result = config;
result.headers.Authorization = 'Bearer ' + Oauth2Manager.getLocalAccessToken();
/*
// 对于需要认证的请求 URL 添加 basic 认证
const result = config;
if (gc.axios?.basicAuth?.enable) {
result.headers.Authorization = 'Basic ' + window.btoa(gc.axios.basicAuth.username + ':' + gc.axios.basicAuth.password);
}
*/
// 如果请求时传入 { loading: true } 属性, 则自动显示 "正在处理..., 请等待" 模态对话框
if (config?.loading) {
QuasarTools.getQuasar()?.loading?.show({
@ -67,6 +71,21 @@ const responseErrorInterceptor = (error: any) => {
const response = error?.response;
const status = response?.status;
const data = response?.data;
if (status === 401) {
if (Oauth2Manager.isAuthorizationCodeRedirectUri()) {
return Promise.resolve({});
} else {
if (gc.oauth2) {
let url = '/oauth2/authorize?';
url += 'client_id=' + gc.oauth2.clientId;
url += '&client_secret=' + gc.oauth2.clientSecret;
url += '&response_type=code';
url += '&redirect_uri=' + encodeURIComponent(gc.oauth2.redirectUri);
window.location.href = Environment.apiContextPath(url);
}
}
return;
}
//下载错误
if (error.request.responseType === 'blob') {
NotifyManager.error(i18n.global.t(error?.code));

5
io.sc.platform.core.frontend/src/platform/plugin/environment/default-configure.json

@ -1,4 +1,9 @@
{
"oauth2" : {
"clientId" : "platform-oidc",
"clientSecret" : "secret",
"redirectUri" : "http://localhost:3000"
},
"setting" : {
"routerHistoryMode" : "hash",
"homePage" : "/home",

1
io.sc.platform.core.frontend/src/platform/plugin/index.ts

@ -30,6 +30,7 @@ export { I18nMessageManager } from './manager';
export { MenuManager } from './manager';
export { MockManager } from './manager';
export { NotifyManager } from './manager';
export { Oauth2Manager } from './manager';
export { RouterManager } from './manager';
export { SessionManager } from './manager';
export { TagViewManager } from './manager';

9
io.sc.platform.core.frontend/src/platform/plugin/manager/ApplicationInitializer.ts

@ -9,6 +9,7 @@ import {
SessionManager,
I18nMessageManager,
NotifyManager,
Oauth2Manager,
MenuManager,
RouterManager,
} from '@/platform/plugin/manager';
@ -38,6 +39,14 @@ class ApplicationInitializer {
I18nMessageManager.setLocalI18nMessages(parameter.localI18nMessages);
await I18nMessageManager.changeLanguage(gc.setting.i18n.locale as I18nMessageLocaleType, false);
// Oauth2 鉴权
if (Oauth2Manager.isAuthorizationCodeRedirectUri()) {
Oauth2Manager.loadTokens(() => {
window.location.href = Environment.getWebContextPath();
});
return;
}
// 获取用户会话
const userSession = await SessionManager.loadUserSession();
if (userSession) {

127
io.sc.platform.core.frontend/src/platform/plugin/manager/Oauth2Manager.ts

@ -0,0 +1,127 @@
import { Tools } from '@/platform/utils';
import Axios from 'axios';
import { Environment } from '@/platform/plugin/environment';
class Oauth2Manager {
/**
*
* @returns
*/
public static isAuthorizationCodeRedirectUri() {
return window.location.href.includes('?code=');
}
/**
* 访
*/
public static loadTokens(callback) {
const code = Tools.getQueryStringValue('code');
if (code) {
const gc = Environment.getConfigure();
const axios = Axios.create({});
axios.interceptors.request.use((config: any) => {
config.headers.Authorization = 'Basic ' + window.btoa(gc.oauth2.clientId + ':' + gc.oauth2.clientSecret);
return config;
});
axios
.post(
Environment.apiContextPath('/oauth2/token'),
{
code: code,
grant_type: 'authorization_code',
redirect_uri: gc.oauth2.redirectUri,
},
{
headers: {
'Content-Type': 'multipart/form-data',
},
},
)
.then((response) => {
Oauth2Manager.setLocalAccessToken(response.data.access_token);
Oauth2Manager.setLocalRefreshToken(response.data.refresh_token);
if (callback) {
callback();
}
});
}
}
public static revokeTokens(callback) {
const accessToken = Oauth2Manager.getLocalAccessToken();
const refreshToken = Oauth2Manager.getLocalRefreshToken();
Oauth2Manager.removeLocalAccessToken();
Oauth2Manager.removeLocalRefreshToken();
const gc = Environment.getConfigure();
const axios = Axios.create({});
axios.interceptors.request.use((config: any) => {
config.headers.Authorization = 'Basic ' + window.btoa(gc.oauth2.clientId + ':' + gc.oauth2.clientSecret);
return config;
});
axios
.post(
Environment.apiContextPath('/oauth2/revoke'),
{
token: accessToken,
},
{
headers: {
'Content-Type': 'multipart/form-data',
},
},
)
.then((response) => {
if (callback) {
callback();
}
});
}
/**
* 访
*/
public static getLocalAccessToken() {
return localStorage.getItem('access_token');
}
/**
* 访
* @param accessToken 访
*/
public static setLocalAccessToken(accessToken) {
localStorage.setItem('access_token', accessToken);
}
/**
*
*/
public static getLocalRefreshToken() {
return localStorage.getItem('refresh_token');
}
/**
*
* @param refreshToken
*/
public static setLocalRefreshToken(refreshToken) {
localStorage.setItem('refresh_token', refreshToken);
}
/**
* 访
*/
public static removeLocalAccessToken() {
localStorage.removeItem('access_token');
}
/**
*
*/
public static removeLocalRefreshToken() {
localStorage.removeItem('refresh_token');
}
}
export { Oauth2Manager };

1
io.sc.platform.core.frontend/src/platform/plugin/manager/index.ts

@ -6,6 +6,7 @@ export { I18nMessageManager } from './I18nMessageManager';
export { MenuManager } from './MenuManager';
export { MockManager } from './MockManager';
export { NotifyManager } from './NotifyManager';
export { Oauth2Manager } from './Oauth2Manager';
export { RouterManager } from './RouterManager';
export { SessionManager } from './SessionManager';
export { TagViewManager } from './TagViewManager';

7
io.sc.platform.core.frontend/src/platform/plugin/router.ts

@ -6,6 +6,7 @@ import { TagViewManager } from './manager';
import WBasicLayout from '@/platform/layout/WBasicLayout.vue';
import Login from '@/platform/views/Login.vue';
import Home from '@/platform/views/Home.vue';
import Oauth2 from '@/platform/views/Oauth2.vue';
import P404 from '@/platform/views/404.vue';
const gc = Environment.getConfigure();
@ -38,6 +39,12 @@ const PLATFORM_ROUTES = [
path: '/home',
component: Home,
},
{
parent: '/',
name: 'oauth2',
path: '/oauth2',
component: Oauth2,
},
{
parent: '/',
name: '404',

14
io.sc.platform.core.frontend/src/platform/utils/Tools.ts

@ -543,6 +543,20 @@ class Tools {
}
}
/**
*
* @param key key
* @returns
*/
public static getQueryStringValue(key: string) {
const reg = new RegExp('(^|&)' + key + '=([^&]*)(&|$)', 'i');
const r = window.location.search.substring(1).match(reg);
if (r != null) {
return decodeURIComponent(r[2]);
}
return null;
}
/**
* URL URL
* @param url1 URL

7
io.sc.platform.core.frontend/src/platform/views/Oauth2.vue

@ -0,0 +1,7 @@
<template>
<div class="flex justify-center items-center px-2 py-2" style="height: 100%">Oauth2</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
</script>

2
io.sc.platform.core.frontend/src/views/testcase/maxgraph/maxgraph.vue

@ -5,6 +5,7 @@
import { onMounted, vue } from 'vue';
import { type CellStyle, Graph, InternalEvent } from '@maxgraph/core';
/*
onMounted(() => {
const container = <HTMLElement>document.getElementById('graph-container');
// Disables the built-in context menu
@ -48,4 +49,5 @@ onMounted(() => {
});
});
});
*/
</script>

43
io.sc.platform.developer.doc/asciidoc/9999-appendix/oauth2/oauth2.adoc

@ -220,35 +220,18 @@ private_key_jwt 和 client_secret_jwt 唯一的区别就是生成 JWT 的方式
| token_type_hint | 请求 | |
|===
== 示例
=== 获取授权码
http://localhost:8080/oauth2/authorize?client_secret=secret&client_id=platform-oidc&response_type=code&redirect_uri=http://localhost:8080/oauth2/authorized-oidc
|===
| 参数名 | 参数值 | 说明
| client_id | | 注册客户端 ID
| response_type | code | 响应类型
|
|===
在浏览器地址栏中输入:
http://localhost:8080/oauth2/authorize?client_id=platform-oidc&client_secret=secret&response_type=code&redirect_uri=http://localhost:8080/oauth2/authorized-oidc
image::9999-appendix/oauth2/010.png[,100%]
如果还未登录过,系统重定向到登录页面
image::9999-appendix/oauth2/011.png[,80%]
输入登录用户名和密码,成功登录后,返回授权码。
org.springframework.security.web.session.DisableEncodeUrlFilter,
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter,
org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.AuthorizationServerContextFilter,
org.springframework.security.web.context.SecurityContextPersistenceFilter,
org.springframework.security.web.header.HeaderWriterFilter,
org.springframework.security.web.csrf.CsrfFilter,
org.springframework.security.web.authentication.logout.LogoutFilter,
org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationServerMetadataEndpointFilter,
org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationEndpointFilter,
org.springframework.security.oauth2.server.authorization.oidc.web.OidcProviderConfigurationEndpointFilter,
org.springframework.security.oauth2.server.authorization.web.NimbusJwkSetEndpointFilter,
org.springframework.security.oauth2.server.authorization.web.OAuth2ClientAuthenticationFilter,
org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter,
org.springframework.security.web.savedrequest.RequestCacheAwareFilter,
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter,
org.springframework.security.web.authentication.AnonymousAuthenticationFilter,
org.springframework.security.web.session.SessionManagementFilter,
org.springframework.security.web.access.ExceptionTranslationFilter,
org.springframework.security.web.access.intercept.FilterSecurityInterceptor,
org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter,
org.springframework.security.oauth2.server.authorization.web.OAuth2TokenIntrospectionEndpointFilter,
org.springframework.security.oauth2.server.authorization.web.OAuth2TokenRevocationEndpointFilter,
org.springframework.security.oauth2.server.authorization.oidc.web.OidcUserInfoEndpointFilter
=== 通过授权码获取令牌

1
io.sc.platform.developer.doc/asciidoc/index.adoc

@ -33,6 +33,7 @@
include::getting-started/getting-started.adoc[leveloffset=+1]
include::platform-extension/platform-extension.adoc[leveloffset=+1]
include::system-design/system-design.adoc[leveloffset=+1]
include::frontend/frontend.adoc[leveloffset=+1]
include::bank-business/bank-business.adoc[leveloffset=+1]

BIN
io.sc.platform.developer.doc/asciidoc/resources/images/9999-appendix/oauth2/010.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 965 KiB

BIN
io.sc.platform.developer.doc/asciidoc/resources/images/9999-appendix/oauth2/011.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 MiB

BIN
io.sc.platform.developer.doc/asciidoc/resources/images/system-design/application-entry/001.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 KiB

BIN
io.sc.platform.developer.doc/asciidoc/resources/images/system-design/application-entry/002.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

BIN
io.sc.platform.developer.doc/asciidoc/resources/images/system-design/application-entry/003.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

36
io.sc.platform.developer.doc/asciidoc/system-design/application-entry/application-entry.adoc

@ -0,0 +1,36 @@
= 应用入口 (War 包场景)
== 核心原理
image::system-design/application-entry/001.png[,100%]
<1> 应用入口 URL
<2> 用户登录页面
<3> 后端处理, 返回视图。 通过查看 io.sc.platform.mvc 模块的源码,在 resources/template 目录中没有 io.sc.platform.mvc.frontend.html 模版文件,该模版文件通过以下步骤生成
<4> io.sc.platform.mvc.frontend 是前端模块, public/index.html 文件就是首页模版文件
<5> 构建前端,构建后的前端文件位于 dist/public/io.sc.platform.mvc.frontend 目录中
<6> 将前端模块打包成 jar时, 将前端模块中的 dist/public/io.sc.platform.mvc.frontend/index.html 文件复制成 resources/template/io.sc.platform.mvc.frontend.html 文件
== index.html 解析
image::system-design/application-entry/002.png[,100%]
该 index.html 文件需要加载 configure.js 文件,该文件包含前端运行时的最核心配置信息,主要包括:
. webContextPath: 应用上下文路径
. apiContextPaths: 默认后端 API 请求的服务地址前缀
[source,javascript]
----
<script src="/configure.js" th:src="@{/configure.js}"></script>
----
[TIP]
<script> 的写法, 既包含 src 属性也包含 th:src 属性。
在纯前端发布模式下: src 属性生效, th:src 属性被忽略;
在 war 发布模式下: th:src 生效且覆盖 src 属性
在 war 包情景下, 加载 /configure.js 文件,该文件如何获得?
image::system-design/application-entry/003.png[,100%]

3
io.sc.platform.developer.doc/asciidoc/system-design/system-design.adoc

@ -0,0 +1,3 @@
= 系统设计
include::application-entry/application-entry.adoc[leveloffset=+1]

20
io.sc.platform.jdbc.liquibase/src/main/java/io/sc/platform/jdbc/liquibase/service/impl/LiquibaseServiceImpl.java

@ -70,20 +70,20 @@ public class LiquibaseServiceImpl implements LiquibaseService {
}
@Override
public boolean isUpdated() throws Exception {
public synchronized boolean isUpdated() throws Exception {
log.info("check database schema ......");
if(isUpdated!=null){
if (isUpdated != null) {
return isUpdated;
}
List<LiquibaseChangeLogWrapper> changeSets =getChangeSets();
if(changeSets!=null && !changeSets.isEmpty()){
isUpdated =false;
}else{
isUpdated =true;
List<LiquibaseChangeLogWrapper> changeSets = getChangeSets();
if (changeSets != null && !changeSets.isEmpty()) {
isUpdated = false;
} else {
isUpdated = true;
}
if(isUpdated) {
if (isUpdated) {
log.info("database schema is update to date");
}else{
} else {
log.info("database schema is NOT update to date");
}
return isUpdated;
@ -91,7 +91,7 @@ public class LiquibaseServiceImpl implements LiquibaseService {
@Override
@Async
public void update() throws Exception {
public synchronized void update() throws Exception {
log.info("update database schema ......");
progressInfo =new ProgressInfo(100,1);
progressInfo.setCurrentWeight(10);

9
io.sc.platform.lcdp/src/main/java/io/sc/platform/lcdp/configure/api/Configure.java

@ -3,9 +3,18 @@ package io.sc.platform.lcdp.configure.api;
import io.sc.platform.orm.api.vo.CorporationAuditorVo;
public class Configure extends CorporationAuditorVo {
private Oauth2 oauth2 =new Oauth2();
private Setting setting =new Setting();
private Theme theme =new Theme();
public Oauth2 getOauth2() {
return oauth2;
}
public void setOauth2(Oauth2 oauth2) {
this.oauth2 = oauth2;
}
public Setting getSetting() {
return setting;
}

31
io.sc.platform.lcdp/src/main/java/io/sc/platform/lcdp/configure/api/Oauth2.java

@ -0,0 +1,31 @@
package io.sc.platform.lcdp.configure.api;
public class Oauth2 {
private String clientId; // 注册客户端 ID
private String clientSecret; // 注册客户端密码
private String redirectUri; // 注册客户端回调 URI
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public String getClientSecret() {
return clientSecret;
}
public void setClientSecret(String clientSecret) {
this.clientSecret = clientSecret;
}
public String getRedirectUri() {
return redirectUri;
}
public void setRedirectUri(String redirectUri) {
this.redirectUri = redirectUri;
}
}

3
io.sc.platform.lcdp/src/main/java/io/sc/platform/lcdp/configure/controller/ConfigureJsController.java

@ -1,13 +1,10 @@
package io.sc.platform.lcdp.configure.controller;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class ConfigureJsController {
private boolean isExists =new DefaultResourceLoader().getResource("classpath:/public/component.js").exists();
@RequestMapping("/configure.js")
public String configureJs(){
return "configure.js";

18
io.sc.platform.lcdp/src/main/java/io/sc/platform/lcdp/configure/service/impl/ConfigureServiceImpl.java

@ -1,6 +1,6 @@
package io.sc.platform.lcdp.configure.service.impl;
import io.sc.platform.core.Environment;
import io.sc.platform.lcdp.configure.api.Oauth2;
import io.sc.platform.lcdp.configure.api.Setting;
import io.sc.platform.lcdp.configure.api.Theme;
import io.sc.platform.lcdp.configure.jpa.entity.ConfigureEntity;
@ -10,6 +10,7 @@ import io.sc.platform.lcdp.configure.api.Configure;
import io.sc.platform.mvc.service.SystemParameterService;
import io.sc.platform.orm.service.impl.DaoServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
@ -18,6 +19,7 @@ import javax.transaction.Transactional;
@Service
public class ConfigureServiceImpl extends DaoServiceImpl<ConfigureEntity, String, ConfigureRepository> implements ConfigureService {
@Autowired private Environment environment;
@Autowired private JdbcTemplate jdbcTemplate;
@Autowired private SystemParameterService systemParameterService;
@ -25,8 +27,18 @@ public class ConfigureServiceImpl extends DaoServiceImpl<ConfigureEntity, String
public Configure getActiveConfigure() {
Configure configure = new Configure();
String clientId =environment.getProperty("spring.security.oauth2.authorizationserver.registeredClient.id",String.class);
String clientSecret =environment.getProperty("spring.security.oauth2.authorizationserver.registeredClient.secret",String.class);
String redirectUri =environment.getProperty("spring.security.oauth2.authorizationserver.registeredClient.redirectUri",String.class);
Oauth2 oauth2 =new Oauth2();
oauth2.setClientId(clientId);
oauth2.setClientSecret(clientSecret);
oauth2.setRedirectUri(redirectUri);
ConfigureEntity entity =repository.findActiveConfigure();
if(entity!=null) {
configure.setOauth2(oauth2);
configure.setSetting(entity.getSetting());
configure.setTheme(entity.getTheme());
}
@ -34,7 +46,7 @@ public class ConfigureServiceImpl extends DaoServiceImpl<ConfigureEntity, String
if(StringUtils.hasText(homePage)) {
configure.getSetting().setHomePage(homePage);
}
configure.getSetting().setIsMultiCorporationMode(Environment.getInstance().isMultiCorportationMode());
configure.getSetting().setIsMultiCorporationMode(io.sc.platform.core.Environment.getInstance().isMultiCorportationMode());
return configure;
}
@ -54,7 +66,7 @@ public class ConfigureServiceImpl extends DaoServiceImpl<ConfigureEntity, String
if(entity==null){ throw exceptionProvider.getCreateNullObjectException(); }
entity.setTheme(new Theme());
entity.setSetting(new Setting());
entity.getSetting().setIsMultiCorporationMode(Environment.getInstance().isMultiCorportationMode());
entity.getSetting().setIsMultiCorporationMode(io.sc.platform.core.Environment.getInstance().isMultiCorportationMode());
return super.add(entity);
}
}

7
io.sc.platform.lcdp/src/main/resources/templates/configure.js

@ -1,11 +1,12 @@
// 在浏览器 window 对象中新建名为 APP 的容器变量, 用于存放平台的全局变量
window.APP = {};
// 全局配置
window.APP.configure ={
window.APP.configure = {
// 应用上下文路径
webContextPath: '[(@{/})]'.startsWith('[')? '/' : '[(@{/})]',
webContextPath: '[(@{/})]',
// 默认后端 API 请求的服务地址前缀
apiContextPaths: {
DEFAULT: '[(@{/})]'.startsWith('[') ? 'http://localhost:8080/' : '[(@{/})]'
DEFAULT: '[(@{/})]'
}
};

79
io.sc.platform.security.loginform/src/main/java/io/sc/platform/security/loginform/autoconfigure/WebSecurityAutoConfiguration.java

@ -101,54 +101,39 @@ public class WebSecurityAutoConfiguration {
}).build();
}
return http
// 在匹配了上述 SecurityFilterChain 规则的基础上进行权限控制
.authorizeRequests(authorizeRequests -> {
// 放行所有 plugins/security.json 中定义的 url
authorizeRequests.antMatchers(securityConfigureService.getIgnoredUrls()).permitAll();
// 放行登录页 url
authorizeRequests.antMatchers(securityProperties.getFormLogin().getLoginPage()).permitAll();
// 放行登录处理 url
authorizeRequests.antMatchers(securityProperties.getFormLogin().getLoginProcessingUrl()).permitAll();
// 放行登录失败 url
authorizeRequests.antMatchers(securityProperties.getFormLogin().getFailureUrl()).permitAll();
// 其他全部需要认证
authorizeRequests.anyRequest().authenticated();
})
.csrf(csrfConfigurer -> {
csrfConfigurer.disable();
})
.cors(corsConfigurer->{
corsConfigurer.configurationSource(securityConfigureService.getCorsConfigurationSource());
})
.authenticationProvider(authenticationProvider)
.formLogin(formLoginConfigurer -> {
formLoginConfigurer.loginPage(securityProperties.getFormLogin().getLoginPage());
formLoginConfigurer.loginProcessingUrl(securityProperties.getFormLogin().getLoginProcessingUrl());
formLoginConfigurer.failureUrl(securityProperties.getFormLogin().getFailureUrl());
formLoginConfigurer.successHandler(authenticationSuccessHandler());
formLoginConfigurer.failureHandler(authenticationFailureHandler());
})
.httpBasic(httpBasicConfigurer ->{
})
.logout(logoutConfigurer -> {
logoutConfigurer.logoutUrl(securityProperties.getLogout().getLogoutUrl());
logoutConfigurer.logoutSuccessUrl(securityProperties.getLogout().getLogoutSuccessUrl());
logoutConfigurer.logoutSuccessHandler(logoutSuccessHandler());
})
.headers(headersConfigurer -> {
//接收从 iframe 发送的请求
headersConfigurer.frameOptions().disable();
})
.exceptionHandling()
http.authorizeRequests(authorizeRequests -> {
// 放行所有 plugins/security.json 中定义的 url
authorizeRequests.antMatchers(securityConfigureService.getIgnoredUrls()).permitAll();
// 放行登录页 url
authorizeRequests.antMatchers(securityProperties.getFormLogin().getLoginPage()).permitAll();
// 放行登录处理 url
authorizeRequests.antMatchers(securityProperties.getFormLogin().getLoginProcessingUrl()).permitAll();
// 放行登录失败 url
authorizeRequests.antMatchers(securityProperties.getFormLogin().getFailureUrl()).permitAll();
// 其他全部需要认证
authorizeRequests.anyRequest().authenticated();
});
http.csrf(csrf -> csrf.disable());
http.cors(cors-> cors.configurationSource(securityConfigureService.getCorsConfigurationSource()));
http.authenticationProvider(authenticationProvider);
http.formLogin(formLogin -> {
formLogin.loginPage(securityProperties.getFormLogin().getLoginPage());
formLogin.loginProcessingUrl(securityProperties.getFormLogin().getLoginProcessingUrl());
formLogin.failureUrl(securityProperties.getFormLogin().getFailureUrl());
formLogin.successHandler(authenticationSuccessHandler());
formLogin.failureHandler(authenticationFailureHandler());
});
http.httpBasic(httpBasic -> {});
http.logout(logout -> {
logout.logoutUrl(securityProperties.getLogout().getLogoutUrl());
logout.logoutSuccessUrl(securityProperties.getLogout().getLogoutSuccessUrl());
logout.logoutSuccessHandler(logoutSuccessHandler());
});
http.headers(headers -> headers.frameOptions().disable());//接收从 iframe 发送的请求
http.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint())
.accessDeniedHandler(accessDeniedHandler()).and()
.accessDeniedHandler(accessDeniedHandler());
.build();
return http.build();
}
}

2
io.sc.platform.security.oauth2.server.authorization/src/main/java/io/sc/platform/security/oauth2/server/authorization/bean/PlatformRegisteredClientRepository.java

@ -3,6 +3,7 @@ package io.sc.platform.security.oauth2.server.authorization.bean;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.sc.platform.security.oauth2.server.authorization.configure.support.PlatformRegisteredClient;
import io.sc.platform.security.oauth2.server.authorization.jpa.entity.Client;
import io.sc.platform.security.oauth2.server.authorization.jpa.repository.ClientRepository;
import org.springframework.security.jackson2.SecurityJackson2Modules;
@ -84,7 +85,6 @@ public class PlatformRegisteredClientRepository implements RegisteredClientRepos
Map<String, Object> tokenSettingsMap = parseMap(client.getTokenSettings());
builder.tokenSettings(TokenSettings.withSettings(tokenSettingsMap).build());
return builder.build();
}

140
io.sc.platform.security.oauth2.server.authorization/src/main/java/io/sc/platform/security/oauth2/server/authorization/configure/PlatformOauth2AuthorizationServerAutoConfiguration.java

@ -1,5 +1,7 @@
package io.sc.platform.security.oauth2.server.authorization.configure;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
@ -7,30 +9,44 @@ import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import io.sc.platform.core.service.RuntimeService;
import io.sc.platform.core.util.RSAUtil;
import io.sc.platform.security.SecurityProperties;
import io.sc.platform.security.oauth2.server.authorization.password.PasswordGrantAuthenticationConverter;
import io.sc.platform.security.oauth2.server.authorization.password.PasswordGrantAuthenticationProvider;
import io.sc.platform.security.oauth2.server.authorization.bean.PlatformRegisteredClientRepository;
import io.sc.platform.security.oauth2.server.authorization.configure.support.PlatformOauth2TokenCustomizer;
import io.sc.platform.security.oauth2.server.authorization.configure.support.PlatformRedirectUriValidator;
import io.sc.platform.security.oauth2.server.authorization.jpa.repository.ClientRepository;
import io.sc.platform.security.service.SecurityConfigureService;
import io.sc.platform.security.SecurityProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.sc.platform.security.support.SecurityRole;
import io.sc.platform.security.support.SecurityRoleMix;
import io.sc.platform.security.support.SecurityUser;
import io.sc.platform.security.support.SecurityUserMix;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.lob.DefaultLobHandler;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
import org.springframework.security.jackson2.SecurityJackson2Modules;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtEncoder;
import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationServerJackson2Module;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.token.*;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.util.matcher.RequestMatcher;
import java.io.IOException;
import java.security.KeyPair;
@ -38,12 +54,13 @@ import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.util.List;
@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 100)
@EnableConfigurationProperties(SecurityProperties.class)
@ConditionalOnProperty(name = "application.authentication.mode", havingValue = "oauth2")
public class PlatformOauth2AuthorizationServerAutoConfiguration {
private static final Logger log = LoggerFactory.getLogger(PlatformOauth2AuthorizationServerAutoConfiguration.class);
private RuntimeService runtimeService;
private SecurityConfigureService securityConfigureService;
private SecurityProperties securityProperties;
@ -62,48 +79,62 @@ public class PlatformOauth2AuthorizationServerAutoConfiguration {
}
@Bean
public SecurityFilterChain auth2AuthorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
@Order(Ordered.HIGHEST_PRECEDENCE + 100)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
if(!runtimeService.isReady()) {
return http.csrf(csrfConfigurer -> {
csrfConfigurer.disable();
}).build();
}
OAuth2AuthorizationServerConfigurer configurer =new OAuth2AuthorizationServerConfigurer();
configurer.authorizationEndpoint(endpoint -> {
//endpoint.consentPage("/oauth2/consent");
endpoint.authenticationProviders(PlatformRedirectUriValidator.getAuthenticationValidator());
}).oidc(Customizer.withDefaults());
RequestMatcher endpointsMatcher = configurer.getEndpointsMatcher();
http.requestMatcher(endpointsMatcher)
.authorizeRequests(authorizeRequests -> authorizeRequests.anyRequest().authenticated())
.csrf(csrf -> csrf.ignoringRequestMatchers(endpointsMatcher))
.apply(configurer);
http.apply(configurer);
http.requestMatcher(configurer.getEndpointsMatcher());
http.authorizeRequests(authorizeRequests -> {
authorizeRequests.anyRequest().authenticated();
});
http.csrf(csrf -> csrf.disable());
http.cors(cors -> cors.configurationSource(securityConfigureService.getCorsConfigurationSource()));
http.headers(headers -> headers.frameOptions().disable());
http.exceptionHandling((exceptions) -> {
exceptions.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"));
});
http.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
// 授权服务器配置支持 OpenID Connect 1.0
configurer.oidc(Customizer.withDefaults());
configurer.authorizationEndpoint(endpoint -> endpoint.authenticationProviders(PlatformRedirectUriValidator.getAuthenticationValidator()));
// 增加通过用户名和密码获取 token 的授权方式
// configurer.tokenEndpoint(tokenEndpoint -> {
// tokenEndpoint.accessTokenRequestConverters(authenticationConverters ->{
// authenticationConverters.add(new PasswordGrantAuthenticationConverter());
// });
// tokenEndpoint.authenticationProviders(authenticationProviders->{
// authenticationProviders.add(new PasswordGrantAuthenticationProvider(authorizationService, tokenGenerator));
// });
// });
return http.build();
/*
OAuth2AuthorizationServerConfigurer configurer =new OAuth2AuthorizationServerConfigurer();
RequestMatcher endpointsMatcher = configurer.getEndpointsMatcher();
configurer
.authorizationEndpoint(endpoint -> endpoint.authenticationProviders(PlatformRedirectUriValidator.getAuthenticationValidator()))
.oidc(Customizer.withDefaults());
http
.requestMatcher(endpointsMatcher).authorizeRequests(authorizeRequests -> {
//authorizeRequests.antMatchers(new String[]{"/oauth2/x"}).permitAll();
authorizeRequests.anyRequest().authenticated();
})
.csrf(csrf -> csrf.ignoringRequestMatchers(endpointsMatcher))
.exceptionHandling((exceptions) -> exceptions
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"))
)
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
.apply(configurer);
return http.build();
*/
}
/**
* 授权服务器设置
* @return 授权服务器设置
*/
@Bean
@ConditionalOnMissingBean
public AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder()
.issuer(securityProperties.getOauth2().getAuthorizationserver().getIssuerUri())
.build();
}
/**
* 注册客户端存储库
* @return 注册客户端存储库
*/
@Bean
@ConditionalOnMissingBean
public RegisteredClientRepository registeredClientRepository() {
@ -131,11 +162,44 @@ public class PlatformOauth2AuthorizationServerAutoConfiguration {
return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
}
/**
* JWT 令牌定制化器用于自定义 JWT 令牌的内容
* @return JWT 令牌定制化器
*/
@Bean
@ConditionalOnMissingBean
public AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder()
.issuer(securityProperties.getOauth2().getAuthorizationserver().getIssuerUri())
.build();
public OAuth2TokenCustomizer<JwtEncodingContext> platformOAuth2TokenCustomizer(){
return new PlatformOauth2TokenCustomizer();
}
@Bean
@ConditionalOnMissingBean
public OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {
JdbcOAuth2AuthorizationService service =new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository);
JdbcOAuth2AuthorizationService.OAuth2AuthorizationRowMapper authorizationRowMapper = new JdbcOAuth2AuthorizationService.OAuth2AuthorizationRowMapper(registeredClientRepository);
authorizationRowMapper.setLobHandler(new DefaultLobHandler());
ClassLoader classLoader = JdbcOAuth2AuthorizationService.class.getClassLoader();
List<Module> securityModules = SecurityJackson2Modules.getModules(classLoader);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModules(securityModules);
objectMapper.registerModule(new OAuth2AuthorizationServerJackson2Module());
objectMapper.addMixIn(SecurityUser.class, SecurityUserMix.class);
objectMapper.addMixIn(SecurityRole.class, SecurityRoleMix.class);
authorizationRowMapper.setObjectMapper(objectMapper);
service.setAuthorizationRowMapper(authorizationRowMapper);
return service;
}
//
// @Bean
// @ConditionalOnMissingBean
// public OAuth2TokenGenerator<?> tokenGenerator(JWKSource<SecurityContext> jwkSource) {
// JwtGenerator jwtGenerator = new JwtGenerator(new NimbusJwtEncoder(jwkSource));
// OAuth2AccessTokenGenerator accessTokenGenerator = new OAuth2AccessTokenGenerator();
// OAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator();
// return new DelegatingOAuth2TokenGenerator(jwtGenerator, accessTokenGenerator, refreshTokenGenerator);
// }
}

70
io.sc.platform.security.oauth2.server.authorization/src/main/java/io/sc/platform/security/oauth2/server/authorization/configure/PlatformWebSecurityAutoConfiguration.java

@ -1,70 +0,0 @@
package io.sc.platform.security.oauth2.server.authorization.configure;
import io.sc.platform.core.service.RuntimeService;
import io.sc.platform.security.SecurityProperties;
import io.sc.platform.security.service.SecurityConfigureService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
/**
* 框架安全自动配置类
*/
@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 200)
@EnableConfigurationProperties(SecurityProperties.class)
public class PlatformWebSecurityAutoConfiguration {
private static final Logger log = LoggerFactory.getLogger(PlatformWebSecurityAutoConfiguration.class);
private RuntimeService runtimeService;
private SecurityConfigureService securityConfigureService;
private SecurityProperties securityProperties;
public PlatformWebSecurityAutoConfiguration(RuntimeService runtimeService, SecurityConfigureService securityConfigureService, SecurityProperties securityProperties){
this.runtimeService =runtimeService;
this.securityConfigureService =securityConfigureService;
this.securityProperties =securityProperties;
}
@Bean
public SecurityFilterChain auth2WebSecurityFilterChain(HttpSecurity http) throws Exception {
if(!runtimeService.isReady()) {
return http.csrf(csrfConfigurer -> {
csrfConfigurer.disable();
}).build();
}
return http
.authorizeRequests(authorizeRequests -> {
authorizeRequests.antMatchers(securityConfigureService.getIgnoredUrls()).permitAll();
authorizeRequests.anyRequest().authenticated();
})
.csrf(csrfConfigurer -> {
csrfConfigurer.disable();
})
.cors(corsConfigurer->{
corsConfigurer.configurationSource(securityConfigureService.getCorsConfigurationSource());
})
.formLogin(formLoginConfigurer -> {
SecurityProperties.FormLogin formLogin =securityProperties.getFormLogin();
formLoginConfigurer.loginPage(formLogin.getLoginPage()).permitAll();
formLoginConfigurer.loginProcessingUrl(formLogin.getLoginProcessingUrl()).permitAll();
formLoginConfigurer.failureUrl(securityProperties.getFormLogin().getFailureUrl()).permitAll();
})
.logout(logoutConfigurer -> {
logoutConfigurer.logoutUrl(securityProperties.getLogout().getLogoutUrl());
logoutConfigurer.logoutSuccessUrl(securityProperties.getLogout().getLogoutSuccessUrl());
})
.headers(headersConfigurer -> {
headersConfigurer.frameOptions().disable();
})
.build();
}
}

102
io.sc.platform.security.oauth2.server.authorization/src/main/java/io/sc/platform/security/oauth2/server/authorization/configure/SecurityAutoConfiguration.java

@ -1,102 +0,0 @@
package io.sc.platform.security.oauth2.server.authorization.configure;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import io.sc.platform.core.util.RSAUtil;
import io.sc.platform.security.oauth2.server.authorization.bean.PlatformRegisteredClientRepository;
import io.sc.platform.security.oauth2.server.authorization.jpa.repository.ClientRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.UUID;
@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 500)
public class SecurityAutoConfiguration {
@Autowired private ClientRepository clientRepository;
@Bean
@Order(1)
public SecurityFilterChain auth2AuthorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
// Enable OpenID Connect 1.0
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class).oidc(Customizer.withDefaults());
// Redirect to the login page when not authenticated from the
// authorization endpoint
http.exceptionHandling((exceptions) -> exceptions
.authenticationEntryPoint(
new LoginUrlAuthenticationEntryPoint("/login"))
);
// Accept access tokens for User Info and/or Client Registration
http.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
return http.build();
}
@Bean
@Order(2)
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http)
throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated()
)
// Form login handles the redirect to the login page from the
// authorization server filter chain
.formLogin(Customizer.withDefaults());
return http.build();
}
@Bean
public RegisteredClientRepository registeredClientRepository() {
return new PlatformRegisteredClientRepository(clientRepository);
}
@Bean
@ConditionalOnMissingBean
public JWKSource<SecurityContext> jwkSource() throws NoSuchAlgorithmException {
KeyPair keyPair = RSAUtil.generateKeyPair();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
RSAKey rsaKey = new RSAKey.Builder(publicKey)
.privateKey(privateKey)
.keyID(UUID.randomUUID().toString())
.build();
JWKSet jwkSet = new JWKSet(rsaKey);
return new ImmutableJWKSet<>(jwkSet);
}
@Bean
@ConditionalOnMissingBean
public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
}
@Bean
@ConditionalOnMissingBean
public AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder().build();
}
}

149
io.sc.platform.security.oauth2.server.authorization/src/main/java/io/sc/platform/security/oauth2/server/authorization/configure/SecurityAutoConfiguration2.java

@ -1,149 +0,0 @@
package io.sc.platform.security.oauth2.server.authorization.configure;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import io.sc.platform.core.service.RuntimeService;
import io.sc.platform.core.util.RSAUtil;
import io.sc.platform.security.oauth2.server.authorization.bean.PlatformOAuth2AuthorizationConsentService;
import io.sc.platform.security.oauth2.server.authorization.bean.PlatformOAuth2AuthorizationService;
import io.sc.platform.security.oauth2.server.authorization.jpa.repository.AuthorizationConsentRepository;
import io.sc.platform.security.oauth2.server.authorization.jpa.repository.AuthorizationRepository;
import io.sc.platform.security.oauth2.server.authorization.jpa.repository.ClientRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.UUID;
@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 500)
public class SecurityAutoConfiguration2 {
@Autowired private RuntimeService runtimeService;
@Autowired private ClientRepository clientRepository;
@Autowired private AuthorizationRepository authorizationRepository;
@Autowired private AuthorizationConsentRepository authorizationConsentRepository;
@Bean
@Order(1)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
.oidc(Customizer.withDefaults()); // Enable OpenID Connect 1.0
http
// Redirect to the login page when not authenticated from the
// authorization endpoint
.exceptionHandling((exceptions) -> exceptions
.authenticationEntryPoint(
new LoginUrlAuthenticationEntryPoint("/login"))
)
// Accept access tokens for User Info and/or Client Registration
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
return http.build();
}
@Bean
@Order(2)
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated()
)
// Form login handles the redirect to the login page from the
// authorization server filter chain
.formLogin(Customizer.withDefaults());
return http.build();
}
@Bean
@ConditionalOnMissingBean
public RegisteredClientRepository registeredClientRepository() {
//RegisteredClientRepository result =new PlatformRegisteredClientRepository(clientRepository);
//return result;
RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("client")
.clientSecret("{noop}secret")
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.redirectUri("http://127.0.0.1:8080/login/oauth2/code/messaging-client-oidc")
.redirectUri("http://127.0.0.1:8080/authorized")
.scope(OidcScopes.OPENID)
.scope(OidcScopes.PROFILE)
.scope("message.read")
.scope("message.write")
.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
.build();
return new InMemoryRegisteredClientRepository(registeredClient);
}
@Bean
@ConditionalOnMissingBean
public OAuth2AuthorizationConsentService oAuth2AuthorizationConsentService(RegisteredClientRepository registeredClientRepository){
return new PlatformOAuth2AuthorizationConsentService(authorizationConsentRepository,registeredClientRepository);
}
@Bean
@ConditionalOnMissingBean
public OAuth2AuthorizationService oAuth2AuthorizationService(RegisteredClientRepository registeredClientRepository){
return new PlatformOAuth2AuthorizationService(authorizationRepository,registeredClientRepository);
}
@Bean
@ConditionalOnMissingBean
public JWKSource<SecurityContext> jwkSource() throws NoSuchAlgorithmException {
KeyPair keyPair = RSAUtil.generateKeyPair();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
RSAKey rsaKey = new RSAKey.Builder(publicKey)
.privateKey(privateKey)
.keyID(UUID.randomUUID().toString())
.build();
JWKSet jwkSet = new JWKSet(rsaKey);
return new ImmutableJWKSet<>(jwkSet);
}
@Bean
@ConditionalOnMissingBean
public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
}
@Bean
@ConditionalOnMissingBean
public AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder().build();
}
}

22
io.sc.platform.security.oauth2.server.authorization/src/main/java/io/sc/platform/security/oauth2/server/authorization/configure/support/PlatformOauth2TokenCustomizer.java

@ -0,0 +1,22 @@
package io.sc.platform.security.oauth2.server.authorization.configure.support;
import io.sc.platform.security.support.SecurityUser;
import io.sc.platform.security.util.SecurityUtil;
import org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer;
/**
* 自定义 JWT 令牌生成器
*/
public class PlatformOauth2TokenCustomizer implements OAuth2TokenCustomizer<JwtEncodingContext> {
@Override
public void customize(JwtEncodingContext context) {
Object principal = context.getPrincipal().getPrincipal();
if(principal instanceof SecurityUser){
SecurityUser user =(SecurityUser)principal;
context.getClaims().claims(map ->{
map.putAll(SecurityUtil.securityUser2map(user));
});
}
}
}

2
io.sc.platform.security.oauth2.server.authorization/src/main/java/io/sc/platform/security/oauth2/server/authorization/configure/support/PlatformRedirectUriValidator.java

@ -19,7 +19,7 @@ public class PlatformRedirectUriValidator implements Consumer<OAuth2Authorizatio
providers.forEach(provider -> {
if (provider instanceof OAuth2AuthorizationCodeRequestAuthenticationProvider) {
OAuth2AuthorizationCodeRequestAuthenticationProvider oauth2Provider =(OAuth2AuthorizationCodeRequestAuthenticationProvider) provider;
Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> validator =new PlatformRedirectUriValidator().andThen(OAuth2AuthorizationCodeRequestAuthenticationValidator.DEFAULT_REDIRECT_URI_VALIDATOR);
Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> validator =new PlatformRedirectUriValidator();//.andThen(OAuth2AuthorizationCodeRequestAuthenticationValidator.DEFAULT_REDIRECT_URI_VALIDATOR);
oauth2Provider.setAuthenticationValidator(validator);
}
});

2
io.sc.platform.security.oauth2.server.authorization/src/main/java/io/sc/platform/security/oauth2/server/authorization/controller/LoginWebController.java

@ -12,7 +12,7 @@ import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import java.security.Principal;
@Controller
//@Controller
public class LoginWebController {
private static final String SPRING_THYMELEAF_PREFIX ="spring.thymeleaf.prefix";
private static final String DEFAULT_LOGIN_TEMPLATE ="io.sc.platform.security.frontend.html";

41
io.sc.platform.security.oauth2.server.authorization/src/main/java/io/sc/platform/security/oauth2/server/authorization/controller/ShowOauth2TokenWebController.java

@ -1,13 +1,48 @@
package io.sc.platform.security.oauth2.server.authorization.controller;
import com.fasterxml.jackson.core.type.TypeReference;
import io.sc.platform.core.annotation.IgnoreResponseBodyAdvice;
import io.sc.platform.core.response.ResponseWrapper;
import io.sc.platform.core.response.SuccessResponseWrapper;
import io.sc.platform.core.util.ObjectMapper4Json;
import io.sc.platform.security.oauth2.Token;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.support.BasicAuthenticationInterceptor;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.io.IOException;
import java.util.Map;
@RestController
public class ShowOauth2TokenWebController {
@RequestMapping("/show-oauth2-token")
public Object showToken(@RequestParam("code")String code) {
return code;
@RequestMapping("/oauth2/authorized-oidc")
@IgnoreResponseBodyAdvice
public Object showToken(@RequestParam("code")String code) throws Exception {
MultiValueMap<String,String> body =new LinkedMultiValueMap<>();
body.set("code",code);
body.set("grant_type","authorization_code");
body.set("redirect_uri","http://localhost:8080/oauth2/authorized-oidc");
HttpHeaders headers =new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<MultiValueMap<String,String>> request =new HttpEntity(body,headers);
RestTemplate restTemplate = new RestTemplateBuilder().basicAuthentication("platform-oidc","secret").build();
Token token =restTemplate.postForObject("http://localhost:8080/oauth2/token",request, Token.class);
return token;
}
}

37
io.sc.platform.security.oauth2.server.authorization/src/main/java/io/sc/platform/security/oauth2/server/authorization/initializer/Oauth2RegisteredClientInitializer.java

@ -5,6 +5,7 @@ import io.sc.platform.core.initializer.ApplicationInitializerExecuteException;
import io.sc.platform.security.oauth2.server.authorization.bean.PlatformRegisteredClientRepository;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
@ -22,7 +23,9 @@ import java.util.UUID;
public class Oauth2RegisteredClientInitializer implements ApplicationInitializer {
private Environment environment;
private PlatformRegisteredClientRepository platformRegisteredClientRepository;
private PasswordEncoder passwordEncoder;
private Boolean isInitialized =null;
private boolean isOauth2 =false;
@Override
public int getOrder() {
@ -31,8 +34,14 @@ public class Oauth2RegisteredClientInitializer implements ApplicationInitializer
@Override
public void init(ApplicationContext applicationContext) {
platformRegisteredClientRepository =applicationContext.getBean(PlatformRegisteredClientRepository.class);
environment =applicationContext.getEnvironment();
passwordEncoder =applicationContext.getBean(PasswordEncoder.class);
String authenticationMode =environment.getProperty("application.authentication.mode",String.class,"loginform");
if("oauth2".equalsIgnoreCase(authenticationMode)) {
isOauth2 = true;
platformRegisteredClientRepository = applicationContext.getBean(PlatformRegisteredClientRepository.class);
}
}
@Override
@ -41,6 +50,11 @@ public class Oauth2RegisteredClientInitializer implements ApplicationInitializer
return isInitialized;
}
if(!isOauth2){
isInitialized =true;
return isInitialized;
}
RegisteredClient client =platformRegisteredClientRepository.findByClientId("platform-oidc");
if(client==null) {
isInitialized =false;
@ -57,10 +71,18 @@ public class Oauth2RegisteredClientInitializer implements ApplicationInitializer
}
private RegisteredClient getDefaultOidcRegisteredClient() {
String clientId =environment.getProperty("spring.security.oauth2.authorizationserver.registeredClient.id",String.class);
String clientSecret =environment.getProperty("spring.security.oauth2.authorizationserver.registeredClient.secret",String.class);
String clientName =environment.getProperty("spring.security.oauth2.authorizationserver.registeredClient.name",String.class);
String redirectUri =environment.getProperty("spring.security.oauth2.authorizationserver.registeredClient.redirectUri",String.class);
boolean consent =environment.getProperty("spring.security.oauth2.authorizationserver.registeredClient.consent",Boolean.class);
int accessTokenExpired =environment.getProperty("spring.security.oauth2.authorizationserver.registeredClient.accessTokenExpired",Integer.class);
int refreshTokenExpired =environment.getProperty("spring.security.oauth2.authorizationserver.registeredClient.refreshTokenExpired",Integer.class);
return RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("platform-oidc")
//.clientSecret("secret")
.clientName("平台默认客户端(OpenID模式)")
.clientId(clientId)
.clientSecret(passwordEncoder.encode(clientSecret))
.clientName(clientName)
.clientIdIssuedAt(Instant.now())
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
@ -74,20 +96,21 @@ public class Oauth2RegisteredClientInitializer implements ApplicationInitializer
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
.redirectUris(redirectUrisConsumer->{
redirectUrisConsumer.addAll(getUrls("oauth2/authorized-oidc"));
redirectUrisConsumer.add(redirectUri);
})
.scope(OidcScopes.OPENID)
.scope(OidcScopes.PROFILE)
.scope("all")
.clientSettings(
ClientSettings.builder()
.requireAuthorizationConsent(false)
.requireAuthorizationConsent(consent) //是否需要用户同意
.build()
)
.tokenSettings(
TokenSettings.builder()
.accessTokenTimeToLive(Duration.ofMinutes(30))
.accessTokenTimeToLive(Duration.ofMinutes(accessTokenExpired))
.reuseRefreshTokens(false)
.refreshTokenTimeToLive(Duration.ofMinutes(30))
.refreshTokenTimeToLive(Duration.ofMinutes(refreshTokenExpired))
.build()
)
.build();

58
io.sc.platform.security.oauth2.server.authorization/src/main/java/io/sc/platform/security/oauth2/server/authorization/password/PasswordGrantAuthenticationConverter.java

@ -0,0 +1,58 @@
package io.sc.platform.security.oauth2.server.authorization.password;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
public class PasswordGrantAuthenticationConverter implements AuthenticationConverter {
@Override
public Authentication convert(HttpServletRequest request) {
String grantType = request.getParameter(OAuth2ParameterNames.GRANT_TYPE);
if (!AuthorizationGrantType.PASSWORD.getValue().equals(grantType)) {
return null;
}
Authentication clientPrincipal = SecurityContextHolder.getContext().getAuthentication();
MultiValueMap<String, String> parameters = getParameters(request);
String username = parameters.getFirst(OAuth2ParameterNames.USERNAME);
if (!StringUtils.hasText(username) || parameters.get(OAuth2ParameterNames.USERNAME).size() != 1) {
throw new OAuth2AuthenticationException("username must not be empty!");
}
String password = parameters.getFirst(OAuth2ParameterNames.PASSWORD);
if (!StringUtils.hasText(password) ||
parameters.get(OAuth2ParameterNames.PASSWORD).size() != 1) {
throw new OAuth2AuthenticationException("password must not be empty!");
}
Map<String, Object> additionalParameters = new HashMap<>();
parameters.forEach((key, value) -> {
if (!key.equals(OAuth2ParameterNames.GRANT_TYPE) &&
!key.equals(OAuth2ParameterNames.CLIENT_ID) &&
!key.equals(OAuth2ParameterNames.CODE)) {
additionalParameters.put(key, value.get(0));
}
});
return new PasswordGrantAuthenticationToken(clientPrincipal, additionalParameters);
}
private static MultiValueMap<String, String> getParameters(HttpServletRequest request) {
Map<String, String[]> parameterMap = request.getParameterMap();
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>(parameterMap.size());
parameterMap.forEach((key, values) -> {
if (values.length > 0) {
for (String value : values) {
parameters.add(key, value);
}
}
});
return parameters;
}
}

139
io.sc.platform.security.oauth2.server.authorization/src/main/java/io/sc/platform/security/oauth2/server/authorization/password/PasswordGrantAuthenticationProvider.java

@ -0,0 +1,139 @@
package io.sc.platform.security.oauth2.server.authorization.password;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.*;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.context.AuthorizationServerContextHolder;
import org.springframework.security.oauth2.server.authorization.token.DefaultOAuth2TokenContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
import org.springframework.util.Assert;
import javax.annotation.Resource;
import java.security.Principal;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class PasswordGrantAuthenticationProvider implements AuthenticationProvider {
private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2";
@Resource private UserDetailsService userDetailsService;
@Resource private PasswordEncoder passwordEncoder;
private final OAuth2AuthorizationService authorizationService;
private final OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator;
public PasswordGrantAuthenticationProvider(OAuth2AuthorizationService authorizationService, OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator) {
Assert.notNull(authorizationService, "authorizationService cannot be null");
Assert.notNull(tokenGenerator, "tokenGenerator cannot be null");
this.authorizationService = authorizationService;
this.tokenGenerator = tokenGenerator;
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
PasswordGrantAuthenticationToken passwordGrantAuthenticationToken =(PasswordGrantAuthenticationToken) authentication;
Map<String, Object> additionalParameters = passwordGrantAuthenticationToken.getAdditionalParameters();
// 授权类型
AuthorizationGrantType authorizationGrantType = passwordGrantAuthenticationToken.getGrantType();
// 用户名
String username = (String)additionalParameters.get(OAuth2ParameterNames.USERNAME);
// 密码
String password = (String)additionalParameters.get(OAuth2ParameterNames.PASSWORD);
// Scope
String requestScopesStr = (String)additionalParameters.get(OAuth2ParameterNames.SCOPE);
Set<String> requestScopeSet = Stream.of(requestScopesStr.split(" ")).collect(Collectors.toSet());
OAuth2ClientAuthenticationToken clientPrincipal =getAuthenticatedClientElseThrowInvalidClient(passwordGrantAuthenticationToken);
RegisteredClient registeredClient = clientPrincipal.getRegisteredClient();
if (!registeredClient.getAuthorizationGrantTypes().contains(authorizationGrantType)) {
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT);
}
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if(!passwordEncoder.matches(password,userDetails.getPassword())){
throw new OAuth2AuthenticationException("password error!");
}
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = UsernamePasswordAuthenticationToken.authenticated(userDetails,clientPrincipal,userDetails.getAuthorities());
DefaultOAuth2TokenContext.Builder tokenContextBuilder = DefaultOAuth2TokenContext.builder()
.registeredClient(registeredClient)
.principal(usernamePasswordAuthenticationToken)
.authorizationServerContext(AuthorizationServerContextHolder.getContext())
.authorizationGrantType(authorizationGrantType)
.authorizedScopes(requestScopeSet)
.authorizationGrant(passwordGrantAuthenticationToken);
OAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization.withRegisteredClient(registeredClient)
.principalName(clientPrincipal.getName())
.authorizedScopes(requestScopeSet)
.attribute(Principal.class.getName(), usernamePasswordAuthenticationToken)
.authorizationGrantType(authorizationGrantType);
// 访问令牌
OAuth2TokenContext tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.ACCESS_TOKEN).build();
OAuth2Token generatedAccessToken = this.tokenGenerator.generate(tokenContext);
if (generatedAccessToken == null) {
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,
"The token generator failed to generate the access token.", ERROR_URI);
throw new OAuth2AuthenticationException(error);
}
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,
generatedAccessToken.getTokenValue(), generatedAccessToken.getIssuedAt(),
generatedAccessToken.getExpiresAt(), tokenContext.getAuthorizedScopes());
if (generatedAccessToken instanceof ClaimAccessor) {
authorizationBuilder.token(accessToken, (metadata) ->
metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, ((ClaimAccessor) generatedAccessToken).getClaims()));
} else {
authorizationBuilder.accessToken(accessToken);
}
// 刷新令牌
OAuth2RefreshToken refreshToken = null;
if (registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.REFRESH_TOKEN) && !clientPrincipal.getClientAuthenticationMethod().equals(ClientAuthenticationMethod.NONE)) {
tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.REFRESH_TOKEN).build();
OAuth2Token generatedRefreshToken = this.tokenGenerator.generate(tokenContext);
if (!(generatedRefreshToken instanceof OAuth2RefreshToken)) {
OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR, "The token generator failed to generate the refresh token.", ERROR_URI);
throw new OAuth2AuthenticationException(error);
}
authorizationBuilder.refreshToken((OAuth2RefreshToken) generatedRefreshToken);
}
OAuth2Authorization authorization = authorizationBuilder.build();
this.authorizationService.save(authorization);
return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken, refreshToken, additionalParameters);
}
@Override
public boolean supports(Class<?> authentication) {
return PasswordGrantAuthenticationToken.class.isAssignableFrom(authentication);
}
private static OAuth2ClientAuthenticationToken getAuthenticatedClientElseThrowInvalidClient(Authentication authentication) {
OAuth2ClientAuthenticationToken clientPrincipal = null;
if (OAuth2ClientAuthenticationToken.class.isAssignableFrom(authentication.getPrincipal().getClass())) {
clientPrincipal = (OAuth2ClientAuthenticationToken) authentication.getPrincipal();
}
if (clientPrincipal != null && clientPrincipal.isAuthenticated()) {
return clientPrincipal;
}
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_CLIENT);
}
}

13
io.sc.platform.security.oauth2.server.authorization/src/main/java/io/sc/platform/security/oauth2/server/authorization/password/PasswordGrantAuthenticationToken.java

@ -0,0 +1,13 @@
package io.sc.platform.security.oauth2.server.authorization.password;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationGrantAuthenticationToken;
import java.util.Map;
public class PasswordGrantAuthenticationToken extends OAuth2AuthorizationGrantAuthenticationToken {
public PasswordGrantAuthenticationToken(Authentication clientPrincipal, Map<String, Object> additionalParameters) {
super(AuthorizationGrantType.PASSWORD,clientPrincipal, additionalParameters);
}
}

10
io.sc.platform.security.oauth2.server.authorization/src/main/resources/META-INF/platform/plugins/application-properties.json

@ -12,7 +12,15 @@
"",
"spring.security.oauth2.authorizationserver.rsa.jwk-key-id = io.sc.platform.security.oauth2.authorizationserver.rsa.jwk-key-id",
"spring.security.oauth2.authorizationserver.rsa.public-key-location = file://${dir.config.oauth2}/public-key.txt",
"spring.security.oauth2.authorizationserver.rsa.private-key-location = file://${dir.config.oauth2}/private-key.txt"
"spring.security.oauth2.authorizationserver.rsa.private-key-location = file://${dir.config.oauth2}/private-key.txt",
"",
"spring.security.oauth2.authorizationserver.registeredClient.id = platform-oidc",
"spring.security.oauth2.authorizationserver.registeredClient.secret = secret",
"spring.security.oauth2.authorizationserver.registeredClient.name = 平台默认客户端(OpenID模式)",
"spring.security.oauth2.authorizationserver.registeredClient.redirectUri = http://localhost:3000",
"spring.security.oauth2.authorizationserver.registeredClient.consent = false",
"spring.security.oauth2.authorizationserver.registeredClient.accessTokenExpired = 30",
"spring.security.oauth2.authorizationserver.registeredClient.refreshTokenExpired = 30"
]
}
]

5
io.sc.platform.security.oauth2.server.authorization/src/main/resources/META-INF/platform/plugins/messages.json

@ -0,0 +1,5 @@
{
"includes":[
"io/sc/platform/security/oauth2/server/authorization/i18n/initializer"
]
}

6
io.sc.platform.security.oauth2.server.authorization/src/main/resources/META-INF/spring.factories

@ -1,7 +1,3 @@
# Auto Configuration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
io.sc.platform.security.oauth2.server.authorization.configure.PlatformOauth2AuthorizationServerAutoConfiguration,\
io.sc.platform.security.oauth2.server.authorization.configure.PlatformWebSecurityAutoConfiguration
#org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
#io.sc.platform.security.oauth2.server.authorization.configure.SecurityAutoConfiguration
io.sc.platform.security.oauth2.server.authorization.configure.PlatformOauth2AuthorizationServerAutoConfiguration

2
io.sc.platform.security.oauth2.server.authorization/src/main/resources/io/sc/platform/security/oauth2/server/authorization/i18n/initializer.properties

@ -0,0 +1,2 @@
io.sc.platform.security.oauth2.server.authorization.initializer.Oauth2RegisteredClientInitializer.name=Oauth2 Registered Client Initializer
io.sc.platform.security.oauth2.server.authorization.initializer.Oauth2RegisteredClientInitializer.description=Oauth2 Registered Client Initializer

2
io.sc.platform.security.oauth2.server.authorization/src/main/resources/io/sc/platform/security/oauth2/server/authorization/i18n/initializer_tw_CN.properties

@ -0,0 +1,2 @@
io.sc.platform.security.oauth2.server.authorization.initializer.Oauth2RegisteredClientInitializer.name=Oauth2 \u8A3B\u518A\u5BA2\u6236\u7AEF\u521D\u59CB\u5316\u5668
io.sc.platform.security.oauth2.server.authorization.initializer.Oauth2RegisteredClientInitializer.description=Oauth2 \u8A3B\u518A\u5BA2\u6236\u7AEF\u521D\u59CB\u5316\u5668

2
io.sc.platform.security.oauth2.server.authorization/src/main/resources/io/sc/platform/security/oauth2/server/authorization/i18n/initializer_zh_CN.properties

@ -0,0 +1,2 @@
io.sc.platform.security.oauth2.server.authorization.initializer.Oauth2RegisteredClientInitializer.name=Oauth2 \u6CE8\u518C\u5BA2\u6237\u7AEF\u521D\u59CB\u5316\u5668
io.sc.platform.security.oauth2.server.authorization.initializer.Oauth2RegisteredClientInitializer.description=Oauth2 \u6CE8\u518C\u5BA2\u6237\u7AEF\u521D\u59CB\u5316\u5668

65
io.sc.platform.security.oauth2.server.resource/src/main/java/io/sc/platform/security/oauth2/server/resource/configure/PlatformOauth2ResourceServerAutoConfiguration.java

@ -6,6 +6,8 @@ import io.sc.platform.security.oauth2.server.resource.configure.support.Platform
import io.sc.platform.security.service.SecurityConfigureService;
import io.sc.platform.security.SecurityProperties;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -22,6 +24,7 @@ import org.springframework.security.web.SecurityFilterChain;
@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 300)
@EnableConfigurationProperties(SecurityProperties.class)
@ConditionalOnProperty(name = "application.authentication.mode", havingValue = "oauth2")
public class PlatformOauth2ResourceServerAutoConfiguration {
private RuntimeService runtimeService;
private SecurityProperties properties;
@ -33,59 +36,29 @@ public class PlatformOauth2ResourceServerAutoConfiguration {
this.platformWebSecurityService =platformWebSecurityService;
}
/**
* SecurityFilterChain's Filters:
* org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter,
* org.springframework.security.web.context.SecurityContextPersistenceFilter,
* org.springframework.security.web.header.HeaderWriterFilter,
* org.springframework.web.filter.CorsFilter,
* org.springframework.security.web.csrf.CsrfFilter,
* org.springframework.security.web.authentication.logout.LogoutFilter,
* org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter,
* org.springframework.security.web.savedrequest.RequestCacheAwareFilter,
* org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter,
* org.springframework.security.web.authentication.AnonymousAuthenticationFilter,
* org.springframework.security.web.session.SessionManagementFilter,
* org.springframework.security.web.access.ExceptionTranslationFilter,
* org.springframework.security.web.access.intercept.FilterSecurityInterceptor
* @param http
* @return
* @throws Exception
*/
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE + 100)
SecurityFilterChain oauth2ResourceServerSecurityFilterChain(HttpSecurity http) throws Exception {
public SecurityFilterChain oauth2ResourceServerSecurityFilterChain(HttpSecurity http) throws Exception {
if(!runtimeService.isReady()) {
return http.csrf(csrfConfigurer -> {
csrfConfigurer.disable();
}).build();
}
return http
// 设置进入此 SecurityFilterChain 的匹配规则
.antMatcher("/api/**")
// 在匹配了上述 SecurityFilterChain 规则的基础上进行权限控制
.authorizeRequests(expressionUrlAuthorizationConfigurer ->{
expressionUrlAuthorizationConfigurer.antMatchers(platformWebSecurityService.getIgnoredUrls()).permitAll();
expressionUrlAuthorizationConfigurer.antMatchers("/api/**").authenticated();
})
.csrf(csrfConfigurer -> {
csrfConfigurer.disable();
})
.cors(corsConfigurer->{
corsConfigurer.configurationSource(platformWebSecurityService.getCorsConfigurationSource());
})
.oauth2ResourceServer(oAuth2ResourceServerConfigurer->{//配置资源服务器
oAuth2ResourceServerConfigurer.jwt(jwtCustomizer-> {//配置资源服务器的JWT
jwtCustomizer.jwtAuthenticationConverter(new PlatformJwtAuthenticationConverter());
});
})
.httpBasic(httpBasicConfigurer ->{
})
.headers(headersConfigurer -> {
//接收从 iframe 发送的请求
headersConfigurer.frameOptions().disable();
})
.build();
http.mvcMatcher("/api/**");
http.authorizeRequests(authorizeRequests -> {
authorizeRequests.antMatchers(platformWebSecurityService.getIgnoredUrls()).permitAll();
authorizeRequests.mvcMatchers("/api/**").authenticated();
});
http.csrf(csrf -> csrf.disable());
http.cors(cors-> cors.configurationSource(platformWebSecurityService.getCorsConfigurationSource()));
http.oauth2ResourceServer(oAuth2ResourceServerConfigurer->{//配置资源服务器
oAuth2ResourceServerConfigurer.jwt(jwtCustomizer-> {//配置资源服务器的JWT
jwtCustomizer.jwtAuthenticationConverter(new PlatformJwtAuthenticationConverter());
});
});
http.httpBasic(httpBasic ->{ });
http.headers(headers -> headers.frameOptions().disable());
return http.build();
}
}

2
io.sc.platform.security.oauth2.server.resource/src/main/java/io/sc/platform/security/oauth2/server/resource/configure/support/PlatformJwtAuthenticationConverter.java

@ -15,7 +15,7 @@ import java.util.*;
/**
* 框架认证转换类
* 覆盖默认的认证转换类本类用于将 jwt token 的认证转换为类似于用户名密码登录的认证类这样可以统一普通的登录认证和基于jwt token 的认证方式
* 使得应用的开发均可使用 org.wsp.framework.security.util.SecurityUtil 中的方法获取用户信息
* 使得应用的开发均可使用 io.sc.platform.security.util.SecurityUtil 中的方法获取用户信息
*/
public class PlatformJwtAuthenticationConverter implements Converter<Jwt, PlatformJwtAuthenticationToken> {
@Override

9
io.sc.platform.security/src/main/java/io/sc/platform/security/AuthenticationMode.java

@ -0,0 +1,9 @@
package io.sc.platform.security;
/**
* 认证模式
*/
public enum AuthenticationMode {
LOGIN_FORM, // 登录表单
OAUTH2; // Oauth2
}

50
io.sc.platform.security/src/main/java/io/sc/platform/security/oauth2/Token.java

@ -0,0 +1,50 @@
package io.sc.platform.security.oauth2;
import com.fasterxml.jackson.annotation.JsonProperty;
public class Token {
@JsonProperty("access_token")
private String accessToken;
@JsonProperty("refresh_token")
private String refreshToken;
@JsonProperty("token_type")
private String tokenType;
@JsonProperty("expires_in")
private String expiresIn;
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public String getRefreshToken() {
return refreshToken;
}
public void setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
}
public String getTokenType() {
return tokenType;
}
public void setTokenType(String tokenType) {
this.tokenType = tokenType;
}
public String getExpiresIn() {
return expiresIn;
}
public void setExpiresIn(String expiresIn) {
this.expiresIn = expiresIn;
}
}

33
io.sc.platform.security/src/main/java/io/sc/platform/security/support/SecurityRoleDeserializer.java

@ -0,0 +1,33 @@
package io.sc.platform.security.support;
import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.MissingNode;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import java.io.IOException;
import java.util.List;
public class SecurityRoleDeserializer extends JsonDeserializer<SecurityRole> {
@Override
public SecurityRole deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
ObjectMapper mapper = (ObjectMapper) p.getCodec();
JsonNode tree = mapper.readTree(p);
String id =readJsonNode(tree,"id").asText();
String code =readJsonNode(tree,"code").asText();
String name = readJsonNode(tree, "name").asText();
return new SecurityRole(id,code,name);
}
private JsonNode readJsonNode(JsonNode jsonNode, String field){
return jsonNode.has(field) ? jsonNode.get(field) : MissingNode.getInstance();
}
}

13
io.sc.platform.security/src/main/java/io/sc/platform/security/support/SecurityRoleMix.java

@ -0,0 +1,13 @@
package io.sc.platform.security.support;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
@JsonDeserialize(using = SecurityRoleDeserializer.class)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE)
@JsonIgnoreProperties(ignoreUnknown = true)
public interface SecurityRoleMix {
}

26
io.sc.platform.security/src/main/java/io/sc/platform/security/support/SecurityUser.java

@ -178,30 +178,4 @@ public class SecurityUser extends User{
public void setCorporationName(String corporationName) {
this.corporationName = corporationName;
}
@Override
public String toString() {
return "SecurityUser{" +
"userId='" + userId + '\'' +
", loginName='" + loginName + '\'' +
", userName='" + userName + '\'' +
", defaultAppId='" + this.defaultAppId + '\'' +
", defaultAppCode='" + this.defaultAppCode + '\'' +
", defaultAppName='" + this.defaultAppName + '\'' +
", defaultRoleId='" + defaultRoleId + '\'' +
", defaultRoleCode='" + defaultRoleCode + '\'' +
", defaultRoleName='" + defaultRoleName + '\'' +
", defaultOrgId='" + defaultOrgId + '\'' +
", defaultOrgCode='" + defaultOrgCode + '\'' +
", defaultOrgName='" + defaultOrgName + '\'' +
", parentOrgId='" + parentOrgId + '\'' +
", parentOrgCode='" + parentOrgCode + '\'' +
", parentOrgName='" + parentOrgName + '\'' +
", rootOrgId='" + rootOrgId + '\'' +
", rootOrgCode='" + rootOrgCode + '\'' +
", rootOrgName='" + rootOrgName + '\'' +
", corporationCode='" + corporationCode + '\'' +
", corporationName='" + corporationName + '\'' +
'}';
}
}

100
io.sc.platform.security/src/main/java/io/sc/platform/security/support/SecurityUserDeserializer.java

@ -0,0 +1,100 @@
package io.sc.platform.security.support;
import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.MissingNode;
import io.sc.platform.core.util.ObjectMapper4Json;
import org.springframework.security.core.GrantedAuthority;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class SecurityUserDeserializer extends JsonDeserializer<SecurityUser> {
private static final TypeReference<List<SecurityRole>> SECURITY_ROLE_SET = new TypeReference<List<SecurityRole>>(){};
@Override
public SecurityUser deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
ObjectMapper mapper = (ObjectMapper) p.getCodec();
JsonNode tree = mapper.readTree(p);
String username =readString(tree,"username");
String password =readString(tree,"password");
password =password==null?"":password;
boolean accountNonExpired = readBoolean(tree, "accountNonExpired");
boolean credentialsNonExpired = readBoolean(tree, "credentialsNonExpired");
boolean accountNonLocked = readBoolean(tree, "accountNonLocked");
boolean enabled =readBoolean(tree,"enabled");
String userId =readString(tree,"userId");
String loginName =readString(tree,"loginName");
String userName =readString(tree,"userName");
String defaultAppId =readString(tree,"defaultAppId");
String defaultAppCode =readString(tree,"defaultAppCode");
String defaultAppName =readString(tree,"defaultAppName");
String defaultRoleId =readString(tree,"defaultRoleId");
String defaultRoleCode =readString(tree,"defaultRoleCode");
String defaultRoleName =readString(tree,"defaultRoleName");
String defaultOrgId =readString(tree,"defaultOrgId");
String defaultOrgCode =readString(tree,"defaultOrgCode");
String defaultOrgName =readString(tree,"defaultOrgName");
String parentOrgId =readString(tree,"parentOrgId");
String parentOrgCode =readString(tree,"parentOrgCode");
String parentOrgName =readString(tree,"parentOrgName");
String rootOrgId =readString(tree,"rootOrgId");
String rootOrgCode =readString(tree,"rootOrgCode");
String rootOrgName =readString(tree,"rootOrgName");
String corporationCode =readString(tree,"corporationCode");
String corporationName =readString(tree,"corporationName");
List<? extends GrantedAuthority> authorities = new ArrayList<>();
JsonNode authoritiesNode =tree.get("authorities");
if(authoritiesNode!=null && authoritiesNode.isArray() && authoritiesNode.size()>1){
String json =authoritiesNode.get(1).toString();
authorities = ObjectMapper4Json.getMapper().readValue(json,SECURITY_ROLE_SET);
}
SecurityUser securityUser =new SecurityUser(username,password,enabled,accountNonExpired,credentialsNonExpired,accountNonLocked,authorities);
securityUser.setUserId(userId);
securityUser.setLoginName(loginName);
securityUser.setUserName(userName);
securityUser.setDefaultAppId(defaultAppId);
securityUser.setDefaultAppCode(defaultAppCode);
securityUser.setDefaultAppName(defaultAppName);
securityUser.setDefaultRoleId(defaultRoleId);
securityUser.setDefaultRoleCode(defaultRoleCode);
securityUser.setDefaultRoleName(defaultRoleName);
securityUser.setDefaultOrgId(defaultOrgId);
securityUser.setDefaultOrgCode(defaultOrgCode);
securityUser.setDefaultOrgName(defaultOrgName);
securityUser.setParentOrgId(parentOrgId);
securityUser.setParentOrgCode(parentOrgCode);
securityUser.setParentOrgName(parentOrgName);
securityUser.setRootOrgId(rootOrgId);
securityUser.setRootOrgCode(rootOrgCode);
securityUser.setRootOrgName(rootOrgName);
securityUser.setCorporationCode(corporationCode);
securityUser.setCorporationName(corporationName);
return securityUser;
}
private String readString(JsonNode jsonNode, String field){
JsonNode node =jsonNode.has(field) ? jsonNode.get(field) : MissingNode.getInstance();
if(node.isNull()){
return null;
}
return node.asText();
}
private boolean readBoolean(JsonNode jsonNode, String field){
JsonNode node =jsonNode.has(field) ? jsonNode.get(field) : MissingNode.getInstance();
return node.asBoolean();
}
}

13
io.sc.platform.security/src/main/java/io/sc/platform/security/support/SecurityUserMix.java

@ -0,0 +1,13 @@
package io.sc.platform.security.support;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
@JsonDeserialize(using = SecurityUserDeserializer.class)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE)
@JsonIgnoreProperties(ignoreUnknown = true)
public interface SecurityUserMix {
}

3
io.sc.platform.security/src/main/resources/META-INF/platform/plugins/application-properties.json

@ -5,6 +5,9 @@
"description" : "",
"properties": [
"# - io.sc.platform.security",
"# the mode of authentication",
"#application.authentication.mode = loginform",
"application.authentication.mode = oauth2",
"# the default password, apply to reset password",
"application.password.default = password",

Loading…
Cancel
Save