✨ 支持根据角色动态脱敏指定数据属性
(cherry picked from commit 51541692ba7ee9ea5cf68f054f33c52133ca1a71)
This commit is contained in:
1 parent
69aa10d54e
commit
835d582b3b
9 files changed
+516
-402
No files matched your search
@@ -541,6 +541,7 @@ INSERT INTO `sys_menu` (`menu_id`, `name`, `permission`, `path`, `component`, `p
|
||||
INSERT INTO `sys_menu` (`menu_id`, `name`, `permission`, `path`, `component`, `parent_id`, `icon`, `visible`, `sort_order`, `keep_alive`, `embedded`, `menu_type`, `create_by`, `create_time`, `update_by`, `update_time`, `del_flag`, `tenant_id`) VALUES (2103, '审计日志', NULL, '/admin/audit/index', NULL, 2001, 'iconfont icon-biaodan', '1', 1, '0', '0', '0', '', NULL, 'admin', '2023-03-02 12:28:47', '0', 1);
|
||||
INSERT INTO `sys_menu` (`menu_id`, `name`, `permission`, `path`, `component`, `parent_id`, `icon`, `visible`, `sort_order`, `keep_alive`, `embedded`, `menu_type`, `create_by`, `create_time`, `update_by`, `update_time`, `del_flag`, `tenant_id`) VALUES (2104, '审计记录表删除', 'sys_audit_del', NULL, NULL, 2103, '1', '1', 3, '0', NULL, '1', '', NULL, 'admin', '2023-02-28 20:23:43', '0', 1);
|
||||
INSERT INTO `sys_menu` (`menu_id`, `name`, `permission`, `path`, `component`, `parent_id`, `icon`, `visible`, `sort_order`, `keep_alive`, `embedded`, `menu_type`, `create_by`, `create_time`, `update_by`, `update_time`, `del_flag`, `tenant_id`) VALUES (2105, '导入导出', 'sys_audit_export', NULL, NULL, 2103, '1', '1', 3, '0', NULL, '1', '', NULL, 'admin', '2023-02-28 20:23:51', '0', 1);
|
||||
INSERT INTO `sys_menu` (`menu_id`, `name`, `permission`, `path`, `component`, `parent_id`, `icon`, `visible`, `sort_order`, `keep_alive`, `embedded`, `menu_type`, `create_by`, `create_time`, `update_by`, `update_time`, `del_flag`, `tenant_id`) VALUES (2106, '敏感数据查看', 'no_mask', NULL, NULL, 2103, '1', '1', 3, '0', NULL, '1', '', NULL, 'admin', '2023-02-28 20:23:51', '0', 1);
|
||||
INSERT INTO `sys_menu` (`menu_id`, `name`, `permission`, `path`, `component`, `parent_id`, `icon`, `visible`, `sort_order`, `keep_alive`, `embedded`, `menu_type`, `create_by`, `create_time`, `update_by`, `update_time`, `del_flag`, `tenant_id`) VALUES (2200, '字典管理', NULL, '/admin/dict/index', NULL, 2000, 'iconfont icon-zhongduancanshuchaxun', '1', 6, '0', NULL, '0', '', '2017-11-29 11:30:52', 'admin', '2023-02-16 15:24:29', '0', 1);
|
||||
INSERT INTO `sys_menu` (`menu_id`, `name`, `permission`, `path`, `component`, `parent_id`, `icon`, `visible`, `sort_order`, `keep_alive`, `embedded`, `menu_type`, `create_by`, `create_time`, `update_by`, `update_time`, `del_flag`, `tenant_id`) VALUES (2201, '字典删除', 'sys_dict_del', NULL, NULL, 2200, NULL, '1', 1, '0', NULL, '1', ' ', '2017-11-29 11:30:11', ' ', '2021-05-25 03:12:55', '0', 1);
|
||||
INSERT INTO `sys_menu` (`menu_id`, `name`, `permission`, `path`, `component`, `parent_id`, `icon`, `visible`, `sort_order`, `keep_alive`, `embedded`, `menu_type`, `create_by`, `create_time`, `update_by`, `update_time`, `del_flag`, `tenant_id`) VALUES (2202, '字典新增', 'sys_dict_add', NULL, NULL, 2200, NULL, '1', 1, '0', NULL, '1', ' ', '2018-05-11 22:34:55', ' ', '2021-05-25 03:12:55', '0', 1);
|
||||
|
||||
@@ -538,6 +538,7 @@ INSERT INTO `sys_menu` (`menu_id`, `name`, `permission`, `path`, `component`, `p
|
||||
INSERT INTO `sys_menu` (`menu_id`, `name`, `permission`, `path`, `component`, `parent_id`, `icon`, `visible`, `sort_order`, `keep_alive`, `embedded`, `menu_type`, `create_by`, `create_time`, `update_by`, `update_time`, `del_flag`, `tenant_id`) VALUES (2103, '审计日志', NULL, '/admin/audit/index', NULL, 2001, 'iconfont icon-biaodan', '1', 1, '0', '0', '0', '', NULL, 'admin', '2023-03-02 12:28:47', '0', 1);
|
||||
INSERT INTO `sys_menu` (`menu_id`, `name`, `permission`, `path`, `component`, `parent_id`, `icon`, `visible`, `sort_order`, `keep_alive`, `embedded`, `menu_type`, `create_by`, `create_time`, `update_by`, `update_time`, `del_flag`, `tenant_id`) VALUES (2104, '审计记录表删除', 'sys_audit_del', NULL, NULL, 2103, '1', '1', 3, '0', NULL, '1', '', NULL, 'admin', '2023-02-28 20:23:43', '0', 1);
|
||||
INSERT INTO `sys_menu` (`menu_id`, `name`, `permission`, `path`, `component`, `parent_id`, `icon`, `visible`, `sort_order`, `keep_alive`, `embedded`, `menu_type`, `create_by`, `create_time`, `update_by`, `update_time`, `del_flag`, `tenant_id`) VALUES (2105, '导入导出', 'sys_audit_export', NULL, NULL, 2103, '1', '1', 3, '0', NULL, '1', '', NULL, 'admin', '2023-02-28 20:23:51', '0', 1);
|
||||
INSERT INTO `sys_menu` (`menu_id`, `name`, `permission`, `path`, `component`, `parent_id`, `icon`, `visible`, `sort_order`, `keep_alive`, `embedded`, `menu_type`, `create_by`, `create_time`, `update_by`, `update_time`, `del_flag`, `tenant_id`) VALUES (2106, '敏感数据查看', 'no_mask', NULL, NULL, 2103, '1', '1', 3, '0', NULL, '1', '', NULL, 'admin', '2023-02-28 20:23:51', '0', 1);
|
||||
INSERT INTO `sys_menu` (`menu_id`, `name`, `permission`, `path`, `component`, `parent_id`, `icon`, `visible`, `sort_order`, `keep_alive`, `embedded`, `menu_type`, `create_by`, `create_time`, `update_by`, `update_time`, `del_flag`, `tenant_id`) VALUES (2200, '字典管理', NULL, '/admin/dict/index', NULL, 2000, 'iconfont icon-zhongduancanshuchaxun', '1', 6, '0', NULL, '0', '', '2017-11-29 11:30:52', 'admin', '2023-02-16 15:24:29', '0', 1);
|
||||
INSERT INTO `sys_menu` (`menu_id`, `name`, `permission`, `path`, `component`, `parent_id`, `icon`, `visible`, `sort_order`, `keep_alive`, `embedded`, `menu_type`, `create_by`, `create_time`, `update_by`, `update_time`, `del_flag`, `tenant_id`) VALUES (2201, '字典删除', 'sys_dict_del', NULL, NULL, 2200, NULL, '1', 1, '0', NULL, '1', ' ', '2017-11-29 11:30:11', ' ', '2021-05-25 03:12:55', '0', 1);
|
||||
INSERT INTO `sys_menu` (`menu_id`, `name`, `permission`, `path`, `component`, `parent_id`, `icon`, `visible`, `sort_order`, `keep_alive`, `embedded`, `menu_type`, `create_by`, `create_time`, `update_by`, `update_time`, `del_flag`, `tenant_id`) VALUES (2202, '字典新增', 'sys_dict_add', NULL, NULL, 2200, NULL, '1', 1, '0', NULL, '1', ' ', '2018-05-11 22:34:55', ' ', '2021-05-25 03:12:55', '0', 1);
|
||||
|
||||
+81
-77
@@ -30,6 +30,7 @@ import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pigx.common.security.component.PermitAllUrlProperties;
|
||||
import com.pig4cloud.pigx.common.security.component.PigxBearerTokenExtractor;
|
||||
import com.pig4cloud.pigx.common.security.component.ResourceAuthExceptionEntryPoint;
|
||||
import com.pig4cloud.pigx.common.security.handler.SensitiveSkipHandler;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -45,6 +46,7 @@ import org.springframework.security.oauth2.server.authorization.config.annotatio
|
||||
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
|
||||
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
|
||||
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
|
||||
import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter;
|
||||
import org.springframework.security.web.DefaultSecurityFilterChain;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.AuthenticationConverter;
|
||||
@@ -58,102 +60,104 @@ import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
@RequiredArgsConstructor
|
||||
public class PigxBootSecurityServerConfiguration {
|
||||
|
||||
private final ResourceAuthExceptionEntryPoint resourceAuthExceptionEntryPoint;
|
||||
private final ResourceAuthExceptionEntryPoint resourceAuthExceptionEntryPoint;
|
||||
|
||||
private final OpaqueTokenIntrospector customOpaqueTokenIntrospector;
|
||||
private final OpaqueTokenIntrospector customOpaqueTokenIntrospector;
|
||||
|
||||
private final AuthenticationConverter accessTokenRequestConverter;
|
||||
private final AuthenticationConverter accessTokenRequestConverter;
|
||||
|
||||
private final OAuth2AuthorizationService authorizationService;
|
||||
private final OAuth2AuthorizationService authorizationService;
|
||||
|
||||
private final PigxBearerTokenExtractor pigBearerTokenExtractor;
|
||||
private final PigxBearerTokenExtractor pigBearerTokenExtractor;
|
||||
|
||||
private final PasswordDecoderFilter passwordDecoderFilter;
|
||||
private final PasswordDecoderFilter passwordDecoderFilter;
|
||||
|
||||
private final OAuth2TokenGenerator oAuth2TokenGenerator;
|
||||
private final OAuth2TokenGenerator oAuth2TokenGenerator;
|
||||
|
||||
private final ValidateCodeFilter validateCodeFilter;
|
||||
private final ValidateCodeFilter validateCodeFilter;
|
||||
|
||||
private final PermitAllUrlProperties permitAllUrl;
|
||||
private final PermitAllUrlProperties permitAllUrl;
|
||||
|
||||
@Bean
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http, PigxAuthenticationSuccessEventHandler successEventHandler,
|
||||
PigxAuthenticationFailureEventHandler failureEventHandler) throws Exception {
|
||||
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = new OAuth2AuthorizationServerConfigurer();
|
||||
@Bean
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http,
|
||||
PigxAuthenticationSuccessEventHandler successEventHandler,
|
||||
PigxAuthenticationFailureEventHandler failureEventHandler) throws Exception {
|
||||
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = new OAuth2AuthorizationServerConfigurer();
|
||||
|
||||
// 增加验证码过滤器
|
||||
http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
// 增加密码解密过滤器
|
||||
http.addFilterBefore(passwordDecoderFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
// 增加验证码过滤器
|
||||
http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
// 增加密码解密过滤器
|
||||
http.addFilterBefore(passwordDecoderFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
http.addFilterAfter(new SensitiveSkipHandler(), BearerTokenAuthenticationFilter.class);
|
||||
|
||||
// 认证服务器配置
|
||||
http.with(authorizationServerConfigurer.tokenEndpoint((tokenEndpoint) -> {// 个性化认证授权端点
|
||||
tokenEndpoint.accessTokenRequestConverter(accessTokenRequestConverter) // 注入自定义的授权认证Converter
|
||||
.accessTokenResponseHandler(successEventHandler) // 登录成功处理器
|
||||
.errorResponseHandler(failureEventHandler);// 登录失败处理器
|
||||
}).clientAuthentication(oAuth2ClientAuthenticationConfigurer -> // 个性化客户端认证
|
||||
oAuth2ClientAuthenticationConfigurer.errorResponseHandler(failureEventHandler))// 处理客户端认证异常
|
||||
, Customizer.withDefaults())
|
||||
.with(authorizationServerConfigurer.authorizationService(authorizationService)// redis存储token的实现
|
||||
.authorizationServerSettings(
|
||||
AuthorizationServerSettings.builder().issuer(SecurityConstants.PIGX_LICENSE).build()),
|
||||
Customizer.withDefaults());
|
||||
// 认证服务器配置
|
||||
http.with(authorizationServerConfigurer.tokenEndpoint((tokenEndpoint) -> {// 个性化认证授权端点
|
||||
tokenEndpoint.accessTokenRequestConverter(accessTokenRequestConverter) // 注入自定义的授权认证Converter
|
||||
.accessTokenResponseHandler(successEventHandler) // 登录成功处理器
|
||||
.errorResponseHandler(failureEventHandler);// 登录失败处理器
|
||||
}).clientAuthentication(oAuth2ClientAuthenticationConfigurer -> // 个性化客户端认证
|
||||
oAuth2ClientAuthenticationConfigurer.errorResponseHandler(failureEventHandler))// 处理客户端认证异常
|
||||
, Customizer.withDefaults())
|
||||
.with(authorizationServerConfigurer.authorizationService(authorizationService)// redis存储token的实现
|
||||
.authorizationServerSettings(
|
||||
AuthorizationServerSettings.builder().issuer(SecurityConstants.PIGX_LICENSE).build()),
|
||||
Customizer.withDefaults());
|
||||
|
||||
// 资源服务器配置
|
||||
AntPathRequestMatcher[] requestMatchers = permitAllUrl.getIgnoreUrls()
|
||||
.stream()
|
||||
.map(AntPathRequestMatcher::new)
|
||||
.toList()
|
||||
.toArray(new AntPathRequestMatcher[]{});
|
||||
// 资源服务器配置
|
||||
AntPathRequestMatcher[] requestMatchers = permitAllUrl.getIgnoreUrls()
|
||||
.stream()
|
||||
.map(AntPathRequestMatcher::new)
|
||||
.toList()
|
||||
.toArray(new AntPathRequestMatcher[] {});
|
||||
|
||||
http.authorizeHttpRequests(authorizeRequests -> authorizeRequests.requestMatchers(requestMatchers)
|
||||
.permitAll()
|
||||
.anyRequest()
|
||||
.authenticated())
|
||||
.oauth2ResourceServer(
|
||||
oauth2 -> oauth2.opaqueToken(token -> token.introspector(customOpaqueTokenIntrospector))
|
||||
.authenticationEntryPoint(resourceAuthExceptionEntryPoint)
|
||||
.bearerTokenResolver(pigBearerTokenExtractor))
|
||||
.exceptionHandling(configurer -> configurer.authenticationEntryPoint(resourceAuthExceptionEntryPoint))
|
||||
.headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable))
|
||||
.csrf(AbstractHttpConfigurer::disable);
|
||||
http.authorizeHttpRequests(authorizeRequests -> authorizeRequests.requestMatchers(requestMatchers)
|
||||
.permitAll()
|
||||
.anyRequest()
|
||||
.authenticated())
|
||||
.oauth2ResourceServer(
|
||||
oauth2 -> oauth2.opaqueToken(token -> token.introspector(customOpaqueTokenIntrospector))
|
||||
.authenticationEntryPoint(resourceAuthExceptionEntryPoint)
|
||||
.bearerTokenResolver(pigBearerTokenExtractor))
|
||||
.exceptionHandling(configurer -> configurer.authenticationEntryPoint(resourceAuthExceptionEntryPoint))
|
||||
.headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable))
|
||||
.csrf(AbstractHttpConfigurer::disable);
|
||||
|
||||
http.with(authorizationServerConfigurer.authorizationService(authorizationService)// redis存储token的实现
|
||||
.authorizationServerSettings(
|
||||
AuthorizationServerSettings.builder().issuer(SecurityConstants.PIGX_LICENSE).build()),
|
||||
Customizer.withDefaults());
|
||||
http.with(authorizationServerConfigurer.authorizationService(authorizationService)// redis存储token的实现
|
||||
.authorizationServerSettings(
|
||||
AuthorizationServerSettings.builder().issuer(SecurityConstants.PIGX_LICENSE).build()),
|
||||
Customizer.withDefaults());
|
||||
|
||||
DefaultSecurityFilterChain securityFilterChain = http.build();
|
||||
DefaultSecurityFilterChain securityFilterChain = http.build();
|
||||
|
||||
// 注入自定义授权模式实现
|
||||
addCustomOAuth2GrantAuthenticationProvider(http);
|
||||
return securityFilterChain;
|
||||
}
|
||||
// 注入自定义授权模式实现
|
||||
addCustomOAuth2GrantAuthenticationProvider(http);
|
||||
return securityFilterChain;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入授权模式实现提供方
|
||||
* <p>
|
||||
* 1. 密码模式 </br>
|
||||
* 2. 短信登录 </br>
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private void addCustomOAuth2GrantAuthenticationProvider(HttpSecurity http) {
|
||||
AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
|
||||
OAuth2AuthorizationService authorizationService = http.getSharedObject(OAuth2AuthorizationService.class);
|
||||
/**
|
||||
* 注入授权模式实现提供方
|
||||
* <p>
|
||||
* 1. 密码模式 </br>
|
||||
* 2. 短信登录 </br>
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private void addCustomOAuth2GrantAuthenticationProvider(HttpSecurity http) {
|
||||
AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
|
||||
OAuth2AuthorizationService authorizationService = http.getSharedObject(OAuth2AuthorizationService.class);
|
||||
|
||||
OAuth2ResourceOwnerPasswordAuthenticationProvider resourceOwnerPasswordAuthenticationProvider = new OAuth2ResourceOwnerPasswordAuthenticationProvider(
|
||||
authenticationManager, authorizationService, oAuth2TokenGenerator);
|
||||
OAuth2ResourceOwnerPasswordAuthenticationProvider resourceOwnerPasswordAuthenticationProvider = new OAuth2ResourceOwnerPasswordAuthenticationProvider(
|
||||
authenticationManager, authorizationService, oAuth2TokenGenerator);
|
||||
|
||||
OAuth2ResourceOwnerSmsAuthenticationProvider resourceOwnerSmsAuthenticationProvider = new OAuth2ResourceOwnerSmsAuthenticationProvider(
|
||||
authenticationManager, authorizationService, oAuth2TokenGenerator);
|
||||
OAuth2ResourceOwnerSmsAuthenticationProvider resourceOwnerSmsAuthenticationProvider = new OAuth2ResourceOwnerSmsAuthenticationProvider(
|
||||
authenticationManager, authorizationService, oAuth2TokenGenerator);
|
||||
|
||||
// 处理 UsernamePasswordAuthenticationToken
|
||||
http.authenticationProvider(new PigxDaoAuthenticationProvider());
|
||||
// 处理 OAuth2ResourceOwnerPasswordAuthenticationToken
|
||||
http.authenticationProvider(resourceOwnerPasswordAuthenticationProvider);
|
||||
// 处理 OAuth2ResourceOwnerSmsAuthenticationToken
|
||||
http.authenticationProvider(resourceOwnerSmsAuthenticationProvider);
|
||||
}
|
||||
// 处理 UsernamePasswordAuthenticationToken
|
||||
http.authenticationProvider(new PigxDaoAuthenticationProvider());
|
||||
// 处理 OAuth2ResourceOwnerPasswordAuthenticationToken
|
||||
http.authenticationProvider(resourceOwnerPasswordAuthenticationProvider);
|
||||
// 处理 OAuth2ResourceOwnerSmsAuthenticationToken
|
||||
http.authenticationProvider(resourceOwnerSmsAuthenticationProvider);
|
||||
}
|
||||
|
||||
}
|
||||
+185
-180
@@ -25,228 +25,233 @@ package com.pig4cloud.pigx.common.core.constant;
|
||||
*/
|
||||
public interface SecurityConstants {
|
||||
|
||||
/**
|
||||
* 启动时是否检查Inner注解安全性
|
||||
*/
|
||||
boolean INNER_CHECK = true;
|
||||
/**
|
||||
* 启动时是否检查Inner注解安全性
|
||||
*/
|
||||
boolean INNER_CHECK = true;
|
||||
|
||||
/**
|
||||
* 刷新
|
||||
*/
|
||||
String REFRESH_TOKEN = "refresh_token";
|
||||
/**
|
||||
* 刷新
|
||||
*/
|
||||
String REFRESH_TOKEN = "refresh_token";
|
||||
|
||||
/**
|
||||
* 验证码有效期
|
||||
*/
|
||||
int CODE_TIME = 60;
|
||||
/**
|
||||
* 验证码有效期
|
||||
*/
|
||||
int CODE_TIME = 60;
|
||||
|
||||
/**
|
||||
* 验证码长度
|
||||
*/
|
||||
String CODE_SIZE = "4";
|
||||
/**
|
||||
* 验证码长度
|
||||
*/
|
||||
String CODE_SIZE = "4";
|
||||
|
||||
/**
|
||||
* 角色前缀
|
||||
*/
|
||||
String ROLE = "ROLE_";
|
||||
/**
|
||||
* 角色前缀
|
||||
*/
|
||||
String ROLE = "ROLE_";
|
||||
|
||||
/**
|
||||
* 前缀
|
||||
*/
|
||||
String PIGX_PREFIX = "pigx_";
|
||||
/**
|
||||
* 前缀
|
||||
*/
|
||||
String PIGX_PREFIX = "pigx_";
|
||||
|
||||
/**
|
||||
* token 相关前缀
|
||||
*/
|
||||
String TOKEN_PREFIX = "token:";
|
||||
/**
|
||||
* token 相关前缀
|
||||
*/
|
||||
String TOKEN_PREFIX = "token:";
|
||||
|
||||
/**
|
||||
* oauth 相关前缀
|
||||
*/
|
||||
String OAUTH_PREFIX = "oauth:";
|
||||
/**
|
||||
* oauth 相关前缀
|
||||
*/
|
||||
String OAUTH_PREFIX = "oauth:";
|
||||
|
||||
/**
|
||||
* 授权码模式code key 前缀
|
||||
*/
|
||||
String OAUTH_CODE_PREFIX = "oauth:code:";
|
||||
/**
|
||||
* 授权码模式code key 前缀
|
||||
*/
|
||||
String OAUTH_CODE_PREFIX = "oauth:code:";
|
||||
|
||||
/**
|
||||
* 项目的license
|
||||
*/
|
||||
String PIGX_LICENSE = "https://pig4cloud.com";
|
||||
/**
|
||||
* 项目的license
|
||||
*/
|
||||
String PIGX_LICENSE = "https://pig4cloud.com";
|
||||
|
||||
/**
|
||||
* 内部
|
||||
*/
|
||||
String FROM_IN = "Y";
|
||||
/**
|
||||
* 内部
|
||||
*/
|
||||
String FROM_IN = "Y";
|
||||
|
||||
/**
|
||||
* 标志
|
||||
*/
|
||||
String FROM = "from";
|
||||
/**
|
||||
* 标志
|
||||
*/
|
||||
String FROM = "from";
|
||||
|
||||
/**
|
||||
* 请求header
|
||||
*/
|
||||
String HEADER_FROM_IN = FROM + "=" + FROM_IN;
|
||||
/**
|
||||
* 请求header
|
||||
*/
|
||||
String HEADER_FROM_IN = FROM + "=" + FROM_IN;
|
||||
|
||||
/**
|
||||
* OAUTH URL
|
||||
*/
|
||||
String OAUTH_TOKEN_URL = "/oauth2/token";
|
||||
/**
|
||||
* OAUTH URL
|
||||
*/
|
||||
String OAUTH_TOKEN_URL = "/oauth2/token";
|
||||
|
||||
/**
|
||||
* 移动端授权
|
||||
*/
|
||||
String GRANT_MOBILE = "mobile";
|
||||
/**
|
||||
* 移动端授权
|
||||
*/
|
||||
String GRANT_MOBILE = "mobile";
|
||||
|
||||
/**
|
||||
* TOC 客户端
|
||||
*/
|
||||
String HEADER_TOC = "CLIENT-TOC";
|
||||
/**
|
||||
* TOC 客户端
|
||||
*/
|
||||
String HEADER_TOC = "CLIENT-TOC";
|
||||
|
||||
/**
|
||||
* TOC 客户端
|
||||
*/
|
||||
String HEADER_TOC_YES = "Y";
|
||||
/**
|
||||
* TOC 客户端
|
||||
*/
|
||||
String HEADER_TOC_YES = "Y";
|
||||
|
||||
/**
|
||||
* QQ获取token
|
||||
*/
|
||||
String QQ_AUTHORIZATION_CODE_URL = "https://graph.qq.com/oauth2.0/token?grant_type="
|
||||
+ "authorization_code&code=%S&client_id=%s&redirect_uri=" + "%s&client_secret=%s";
|
||||
/**
|
||||
* QQ获取token
|
||||
*/
|
||||
String QQ_AUTHORIZATION_CODE_URL = "https://graph.qq.com/oauth2.0/token?grant_type="
|
||||
+ "authorization_code&code=%S&client_id=%s&redirect_uri=" + "%s&client_secret=%s";
|
||||
|
||||
/**
|
||||
* 微信获取OPENID
|
||||
*/
|
||||
String WX_AUTHORIZATION_CODE_URL = "https://api.weixin.qq.com/sns/oauth2/access_token"
|
||||
+ "?appid=%s&secret=%s&code=%s&grant_type=authorization_code";
|
||||
/**
|
||||
* 微信获取OPENID
|
||||
*/
|
||||
String WX_AUTHORIZATION_CODE_URL = "https://api.weixin.qq.com/sns/oauth2/access_token"
|
||||
+ "?appid=%s&secret=%s&code=%s&grant_type=authorization_code";
|
||||
|
||||
/**
|
||||
* 微信小程序OPENID
|
||||
*/
|
||||
String MINI_APP_AUTHORIZATION_CODE_URL = "https://api.weixin.qq.com/sns/jscode2session"
|
||||
+ "?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code";
|
||||
/**
|
||||
* 微信小程序OPENID
|
||||
*/
|
||||
String MINI_APP_AUTHORIZATION_CODE_URL = "https://api.weixin.qq.com/sns/jscode2session"
|
||||
+ "?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code";
|
||||
|
||||
/**
|
||||
* 码云获取token
|
||||
*/
|
||||
String GITEE_AUTHORIZATION_CODE_URL = "https://gitee.com/oauth/token?grant_type="
|
||||
+ "authorization_code&code=%S&client_id=%s&redirect_uri=" + "%s&client_secret=%s";
|
||||
/**
|
||||
* 码云获取token
|
||||
*/
|
||||
String GITEE_AUTHORIZATION_CODE_URL = "https://gitee.com/oauth/token?grant_type="
|
||||
+ "authorization_code&code=%S&client_id=%s&redirect_uri=" + "%s&client_secret=%s";
|
||||
|
||||
/**
|
||||
* 开源中国获取token
|
||||
*/
|
||||
String OSC_AUTHORIZATION_CODE_URL = "https://www.oschina.net/action/openapi/token";
|
||||
/**
|
||||
* 开源中国获取token
|
||||
*/
|
||||
String OSC_AUTHORIZATION_CODE_URL = "https://www.oschina.net/action/openapi/token";
|
||||
|
||||
/**
|
||||
* QQ获取用户信息
|
||||
*/
|
||||
String QQ_USER_INFO_URL = "https://graph.qq.com/oauth2.0/me?access_token=%s";
|
||||
/**
|
||||
* QQ获取用户信息
|
||||
*/
|
||||
String QQ_USER_INFO_URL = "https://graph.qq.com/oauth2.0/me?access_token=%s";
|
||||
|
||||
/**
|
||||
* 码云获取用户信息
|
||||
*/
|
||||
String GITEE_USER_INFO_URL = "https://gitee.com/api/v5/user?access_token=%s";
|
||||
/**
|
||||
* 码云获取用户信息
|
||||
*/
|
||||
String GITEE_USER_INFO_URL = "https://gitee.com/api/v5/user?access_token=%s";
|
||||
|
||||
/**
|
||||
* 开源中国用户信息
|
||||
*/
|
||||
String OSC_USER_INFO_URL = "https://www.oschina.net/action/openapi/user?access_token=%s&dataType=json";
|
||||
/**
|
||||
* 开源中国用户信息
|
||||
*/
|
||||
String OSC_USER_INFO_URL = "https://www.oschina.net/action/openapi/user?access_token=%s&dataType=json";
|
||||
|
||||
/**
|
||||
* 钉钉获取 token
|
||||
*/
|
||||
String DING_OLD_GET_TOKEN = "https://oapi.dingtalk.com/gettoken";
|
||||
/**
|
||||
* 钉钉获取 token
|
||||
*/
|
||||
String DING_OLD_GET_TOKEN = "https://oapi.dingtalk.com/gettoken";
|
||||
|
||||
/**
|
||||
* 钉钉同步部门列表
|
||||
*/
|
||||
String DING_OLD_DEPT_URL = "https://oapi.dingtalk.com/topapi/v2/department/listsub";
|
||||
/**
|
||||
* 钉钉同步部门列表
|
||||
*/
|
||||
String DING_OLD_DEPT_URL = "https://oapi.dingtalk.com/topapi/v2/department/listsub";
|
||||
|
||||
/**
|
||||
* 钉钉部门用户id列表
|
||||
*/
|
||||
String DING_DEPT_USERIDS_URL = "https://oapi.dingtalk.com/topapi/user/listid";
|
||||
/**
|
||||
* 钉钉部门用户id列表
|
||||
*/
|
||||
String DING_DEPT_USERIDS_URL = "https://oapi.dingtalk.com/topapi/user/listid";
|
||||
|
||||
/**
|
||||
* 钉钉用户详情
|
||||
*/
|
||||
String DING_USER_INFO_URL = "https://oapi.dingtalk.com/topapi/v2/user/get";
|
||||
/**
|
||||
* 钉钉用户详情
|
||||
*/
|
||||
String DING_USER_INFO_URL = "https://oapi.dingtalk.com/topapi/v2/user/get";
|
||||
|
||||
/**
|
||||
* {bcrypt} 加密的特征码
|
||||
*/
|
||||
String BCRYPT = "{bcrypt}";
|
||||
/**
|
||||
* {bcrypt} 加密的特征码
|
||||
*/
|
||||
String BCRYPT = "{bcrypt}";
|
||||
|
||||
/**
|
||||
* 客户端模式
|
||||
*/
|
||||
String CLIENT_CREDENTIALS = "client_credentials";
|
||||
/**
|
||||
* 客户端模式
|
||||
*/
|
||||
String CLIENT_CREDENTIALS = "client_credentials";
|
||||
|
||||
/**
|
||||
* 客户端编号
|
||||
*/
|
||||
String CLIENT_ID = "client_id";
|
||||
/**
|
||||
* 客户端编号
|
||||
*/
|
||||
String CLIENT_ID = "client_id";
|
||||
|
||||
/**
|
||||
* 客户端唯一令牌
|
||||
*/
|
||||
String CLIENT_RECREATE = "recreate_flag";
|
||||
/**
|
||||
* 客户端唯一令牌
|
||||
*/
|
||||
String CLIENT_RECREATE = "recreate_flag";
|
||||
|
||||
/**
|
||||
* 用户ID字段
|
||||
*/
|
||||
String DETAILS_USER_ID = "user_id";
|
||||
/**
|
||||
* 用户ID字段
|
||||
*/
|
||||
String DETAILS_USER_ID = "user_id";
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
String DETAILS_USERNAME = "username";
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
String DETAILS_USERNAME = "username";
|
||||
|
||||
/**
|
||||
* 姓名
|
||||
*/
|
||||
String NAME = "name";
|
||||
/**
|
||||
* 姓名
|
||||
*/
|
||||
String NAME = "name";
|
||||
|
||||
/**
|
||||
* 协议字段
|
||||
*/
|
||||
String DETAILS_LICENSE = "license";
|
||||
/**
|
||||
* 协议字段
|
||||
*/
|
||||
String DETAILS_LICENSE = "license";
|
||||
|
||||
/**
|
||||
* 激活字段 兼容外围系统接入
|
||||
*/
|
||||
String ACTIVE = "active";
|
||||
/**
|
||||
* 激活字段 兼容外围系统接入
|
||||
*/
|
||||
String ACTIVE = "active";
|
||||
|
||||
/**
|
||||
* AES 加密
|
||||
*/
|
||||
String AES = "aes";
|
||||
/**
|
||||
* AES 加密
|
||||
*/
|
||||
String AES = "aes";
|
||||
|
||||
/**
|
||||
* 授权码模式confirm
|
||||
*/
|
||||
String CUSTOM_CONSENT_PAGE_URI = "/token/confirm_access";
|
||||
/**
|
||||
* 授权码模式confirm
|
||||
*/
|
||||
String CUSTOM_CONSENT_PAGE_URI = "/token/confirm_access";
|
||||
|
||||
/**
|
||||
* {noop} 加密的特征码
|
||||
*/
|
||||
String NOOP = "{noop}";
|
||||
/**
|
||||
* {noop} 加密的特征码
|
||||
*/
|
||||
String NOOP = "{noop}";
|
||||
|
||||
/**
|
||||
* 短信登录 参数名称
|
||||
*/
|
||||
String SMS_PARAMETER_NAME = "mobile";
|
||||
/**
|
||||
* 短信登录 参数名称
|
||||
*/
|
||||
String SMS_PARAMETER_NAME = "mobile";
|
||||
|
||||
/**
|
||||
* 手机号登录
|
||||
*/
|
||||
String APP = "mobile";
|
||||
/**
|
||||
* 手机号登录
|
||||
*/
|
||||
String APP = "mobile";
|
||||
|
||||
/**
|
||||
* 用户信息
|
||||
*/
|
||||
String DETAILS_USER = "user_info";
|
||||
/**
|
||||
* 用户信息
|
||||
*/
|
||||
String DETAILS_USER = "user_info";
|
||||
|
||||
/**
|
||||
* 不脱敏的权限
|
||||
*/
|
||||
String NO_MASK = "no_mask";
|
||||
|
||||
}
|
||||
+74
-67
@@ -41,77 +41,84 @@ import java.util.Objects;
|
||||
@AllArgsConstructor
|
||||
public class SensitiveSerialize extends JsonSerializer<String> implements ContextualSerializer {
|
||||
|
||||
private SensitiveTypeEnum type;
|
||||
private SensitiveTypeEnum type;
|
||||
|
||||
private Integer prefixNoMaskLen;
|
||||
private Integer prefixNoMaskLen;
|
||||
|
||||
private Integer suffixNoMaskLen;
|
||||
private Integer suffixNoMaskLen;
|
||||
|
||||
private String maskStr;
|
||||
private String maskStr;
|
||||
|
||||
@Override
|
||||
public void serialize(final String origin, final JsonGenerator jsonGenerator,
|
||||
final SerializerProvider serializerProvider) throws IOException {
|
||||
switch (type) {
|
||||
case CHINESE_NAME:
|
||||
jsonGenerator.writeString(DesensitizedUtils.chineseName(origin));
|
||||
break;
|
||||
case ID_CARD:
|
||||
jsonGenerator.writeString(DesensitizedUtils.idCardNum(origin));
|
||||
break;
|
||||
case FIXED_PHONE:
|
||||
jsonGenerator.writeString(DesensitizedUtils.fixedPhone(origin));
|
||||
break;
|
||||
case MOBILE_PHONE:
|
||||
jsonGenerator.writeString(DesensitizedUtils.mobilePhone(origin));
|
||||
break;
|
||||
case ADDRESS:
|
||||
jsonGenerator.writeString(DesensitizedUtils.address(origin));
|
||||
break;
|
||||
case EMAIL:
|
||||
jsonGenerator.writeString(DesensitizedUtils.email(origin));
|
||||
break;
|
||||
case BANK_CARD:
|
||||
jsonGenerator.writeString(DesensitizedUtils.bankCard(origin));
|
||||
break;
|
||||
case PASSWORD:
|
||||
jsonGenerator.writeString(DesensitizedUtils.password(origin));
|
||||
break;
|
||||
case KEY:
|
||||
jsonGenerator.writeString(DesensitizedUtils.key(origin));
|
||||
break;
|
||||
case IPV4:
|
||||
jsonGenerator.writeString(DesensitizedUtils.ipv4(origin));
|
||||
break;
|
||||
case CAR_LICENSE:
|
||||
jsonGenerator.writeString(DesensitizedUtil.carLicense(origin));
|
||||
break;
|
||||
case CUSTOMER:
|
||||
jsonGenerator
|
||||
.writeString(DesensitizedUtils.desValue(origin, prefixNoMaskLen, suffixNoMaskLen, maskStr));
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknow sensitive type enum " + type);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void serialize(final String origin, final JsonGenerator jsonGenerator,
|
||||
final SerializerProvider serializerProvider) throws IOException {
|
||||
|
||||
@Override
|
||||
public JsonSerializer<?> createContextual(final SerializerProvider serializerProvider,
|
||||
final BeanProperty beanProperty) throws JsonMappingException {
|
||||
if (beanProperty != null) {
|
||||
if (Objects.equals(beanProperty.getType().getRawClass(), String.class)) {
|
||||
Sensitive sensitive = beanProperty.getAnnotation(Sensitive.class);
|
||||
if (sensitive == null) {
|
||||
sensitive = beanProperty.getContextAnnotation(Sensitive.class);
|
||||
}
|
||||
if (sensitive != null) {
|
||||
return new SensitiveSerialize(sensitive.type(), sensitive.prefixNoMaskLen(),
|
||||
sensitive.suffixNoMaskLen(), sensitive.maskStr());
|
||||
}
|
||||
}
|
||||
return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
|
||||
}
|
||||
return serializerProvider.findNullValueSerializer(null);
|
||||
}
|
||||
// 跳过脱敏
|
||||
if (SensitiveSkipContextHolder.getSkip()) {
|
||||
jsonGenerator.writeString(origin);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case CHINESE_NAME:
|
||||
jsonGenerator.writeString(DesensitizedUtils.chineseName(origin));
|
||||
break;
|
||||
case ID_CARD:
|
||||
jsonGenerator.writeString(DesensitizedUtils.idCardNum(origin));
|
||||
break;
|
||||
case FIXED_PHONE:
|
||||
jsonGenerator.writeString(DesensitizedUtils.fixedPhone(origin));
|
||||
break;
|
||||
case MOBILE_PHONE:
|
||||
jsonGenerator.writeString(DesensitizedUtils.mobilePhone(origin));
|
||||
break;
|
||||
case ADDRESS:
|
||||
jsonGenerator.writeString(DesensitizedUtils.address(origin));
|
||||
break;
|
||||
case EMAIL:
|
||||
jsonGenerator.writeString(DesensitizedUtils.email(origin));
|
||||
break;
|
||||
case BANK_CARD:
|
||||
jsonGenerator.writeString(DesensitizedUtils.bankCard(origin));
|
||||
break;
|
||||
case PASSWORD:
|
||||
jsonGenerator.writeString(DesensitizedUtils.password(origin));
|
||||
break;
|
||||
case KEY:
|
||||
jsonGenerator.writeString(DesensitizedUtils.key(origin));
|
||||
break;
|
||||
case IPV4:
|
||||
jsonGenerator.writeString(DesensitizedUtils.ipv4(origin));
|
||||
break;
|
||||
case CAR_LICENSE:
|
||||
jsonGenerator.writeString(DesensitizedUtil.carLicense(origin));
|
||||
break;
|
||||
case CUSTOMER:
|
||||
jsonGenerator
|
||||
.writeString(DesensitizedUtils.desValue(origin, prefixNoMaskLen, suffixNoMaskLen, maskStr));
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknow sensitive type enum " + type);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonSerializer<?> createContextual(final SerializerProvider serializerProvider,
|
||||
final BeanProperty beanProperty) throws JsonMappingException {
|
||||
if (beanProperty != null) {
|
||||
if (Objects.equals(beanProperty.getType().getRawClass(), String.class)) {
|
||||
Sensitive sensitive = beanProperty.getAnnotation(Sensitive.class);
|
||||
if (sensitive == null) {
|
||||
sensitive = beanProperty.getContextAnnotation(Sensitive.class);
|
||||
}
|
||||
if (sensitive != null) {
|
||||
return new SensitiveSerialize(sensitive.type(), sensitive.prefixNoMaskLen(),
|
||||
sensitive.suffixNoMaskLen(), sensitive.maskStr());
|
||||
}
|
||||
}
|
||||
return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
|
||||
}
|
||||
return serializerProvider.findNullValueSerializer(null);
|
||||
}
|
||||
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
package com.pig4cloud.pigx.common.core.sensitive;
|
||||
|
||||
import com.alibaba.ttl.TransmittableThreadLocal;
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 脱密跳过ttl
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2024/6/26
|
||||
*/
|
||||
@UtilityClass
|
||||
public class SensitiveSkipContextHolder {
|
||||
|
||||
private final ThreadLocal<Boolean> THREAD_LOCAL_SKIP = new TransmittableThreadLocal<>();
|
||||
|
||||
/**
|
||||
* 设置是否跳过脱敏
|
||||
*
|
||||
* @param skip 是否跳过
|
||||
*/
|
||||
public void setSkip(Boolean skip) {
|
||||
THREAD_LOCAL_SKIP.set(skip);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取是否跳过脱敏
|
||||
*
|
||||
* @return 是否跳过
|
||||
*/
|
||||
public Boolean getSkip() {
|
||||
return Objects.isNull(THREAD_LOCAL_SKIP.get()) ? Boolean.FALSE : THREAD_LOCAL_SKIP.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除
|
||||
*/
|
||||
public void clear() {
|
||||
THREAD_LOCAL_SKIP.remove();
|
||||
}
|
||||
}
|
||||
+30
-25
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.pig4cloud.pigx.common.security.component;
|
||||
|
||||
import com.pig4cloud.pigx.common.security.handler.SensitiveSkipHandler;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -27,13 +28,14 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
|
||||
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
|
||||
import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2022-06-04
|
||||
*
|
||||
* <p>
|
||||
* 资源服务器认证授权配置
|
||||
*/
|
||||
@Slf4j
|
||||
@@ -42,35 +44,38 @@ import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
@RequiredArgsConstructor
|
||||
public class PigxResourceServerConfiguration {
|
||||
|
||||
protected final ResourceAuthExceptionEntryPoint resourceAuthExceptionEntryPoint;
|
||||
protected final ResourceAuthExceptionEntryPoint resourceAuthExceptionEntryPoint;
|
||||
|
||||
private final PermitAllUrlProperties permitAllUrl;
|
||||
private final PermitAllUrlProperties permitAllUrl;
|
||||
|
||||
private final PigxBearerTokenExtractor pigxBearerTokenExtractor;
|
||||
private final PigxBearerTokenExtractor pigxBearerTokenExtractor;
|
||||
|
||||
private final OpaqueTokenIntrospector customOpaqueTokenIntrospector;
|
||||
private final OpaqueTokenIntrospector customOpaqueTokenIntrospector;
|
||||
|
||||
@Bean
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
AntPathRequestMatcher[] requestMatchers = permitAllUrl.getIgnoreUrls()
|
||||
.stream()
|
||||
.map(AntPathRequestMatcher::new)
|
||||
.toList()
|
||||
.toArray(new AntPathRequestMatcher[] {});
|
||||
@Bean
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
AntPathRequestMatcher[] requestMatchers = permitAllUrl.getIgnoreUrls()
|
||||
.stream()
|
||||
.map(AntPathRequestMatcher::new)
|
||||
.toList()
|
||||
.toArray(new AntPathRequestMatcher[]{});
|
||||
|
||||
http.authorizeHttpRequests(authorizeRequests -> authorizeRequests.requestMatchers(requestMatchers)
|
||||
.permitAll()
|
||||
.anyRequest()
|
||||
.authenticated())
|
||||
.oauth2ResourceServer(
|
||||
oauth2 -> oauth2.opaqueToken(token -> token.introspector(customOpaqueTokenIntrospector))
|
||||
.authenticationEntryPoint(resourceAuthExceptionEntryPoint)
|
||||
.bearerTokenResolver(pigxBearerTokenExtractor))
|
||||
.headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable))
|
||||
.csrf(AbstractHttpConfigurer::disable);
|
||||
// 增加输出脱敏的处理
|
||||
http.addFilterAfter(new SensitiveSkipHandler(), BearerTokenAuthenticationFilter.class);
|
||||
|
||||
return http.build();
|
||||
}
|
||||
http.authorizeHttpRequests(authorizeRequests -> authorizeRequests.requestMatchers(requestMatchers)
|
||||
.permitAll()
|
||||
.anyRequest()
|
||||
.authenticated())
|
||||
.oauth2ResourceServer(
|
||||
oauth2 -> oauth2.opaqueToken(token -> token.introspector(customOpaqueTokenIntrospector))
|
||||
.authenticationEntryPoint(resourceAuthExceptionEntryPoint)
|
||||
.bearerTokenResolver(pigxBearerTokenExtractor))
|
||||
.headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable))
|
||||
.csrf(AbstractHttpConfigurer::disable);
|
||||
|
||||
return http.build();
|
||||
}
|
||||
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package com.pig4cloud.pigx.common.security.handler;
|
||||
|
||||
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pigx.common.core.sensitive.SensitiveSkipContextHolder;
|
||||
import com.pig4cloud.pigx.common.security.service.PigxUser;
|
||||
import com.pig4cloud.pigx.common.security.util.SecurityUtils;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 敏感词跳过的处理器
|
||||
*
|
||||
* @author lengleng
|
||||
* @date 2024/6/26
|
||||
*/
|
||||
public class SensitiveSkipHandler extends OncePerRequestFilter {
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
||||
PigxUser user = SecurityUtils.getUser();
|
||||
|
||||
if (user == null) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (user.getAuthorities().stream().map(GrantedAuthority::getAuthority)
|
||||
.toList().contains(SecurityConstants.NO_MASK)) {
|
||||
SensitiveSkipContextHolder.setSkip(true);
|
||||
}
|
||||
filterChain.doFilter(request, response);
|
||||
} finally {
|
||||
SensitiveSkipContextHolder.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
+59
-53
@@ -38,63 +38,69 @@ import java.util.List;
|
||||
@UtilityClass
|
||||
public class SecurityUtils {
|
||||
|
||||
/**
|
||||
* 获取Authentication
|
||||
*/
|
||||
public Authentication getAuthentication() {
|
||||
return SecurityContextHolder.getContext().getAuthentication();
|
||||
}
|
||||
/**
|
||||
* 获取Authentication
|
||||
*/
|
||||
public Authentication getAuthentication() {
|
||||
return SecurityContextHolder.getContext().getAuthentication();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Authentication Token
|
||||
* @return
|
||||
*/
|
||||
public String getToken(){
|
||||
Authentication authentication = SecurityUtils.getAuthentication();
|
||||
if (authentication instanceof BearerTokenAuthentication bearerTokenAuthentication) {
|
||||
return bearerTokenAuthentication.getToken().getTokenValue();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* 获取Authentication Token
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getToken() {
|
||||
Authentication authentication = SecurityUtils.getAuthentication();
|
||||
if (authentication instanceof BearerTokenAuthentication bearerTokenAuthentication) {
|
||||
return bearerTokenAuthentication.getToken().getTokenValue();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户
|
||||
* @param authentication
|
||||
* @return PigxUser
|
||||
* <p>
|
||||
*/
|
||||
public PigxUser getUser(Authentication authentication) {
|
||||
Object principal = authentication.getPrincipal();
|
||||
if (principal instanceof PigxUser) {
|
||||
return (PigxUser) principal;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* 获取用户
|
||||
*
|
||||
* @param authentication
|
||||
* @return PigxUser
|
||||
* <p>
|
||||
*/
|
||||
public PigxUser getUser(Authentication authentication) {
|
||||
if (authentication == null) {
|
||||
return null;
|
||||
}
|
||||
Object principal = authentication.getPrincipal();
|
||||
if (principal instanceof PigxUser) {
|
||||
return (PigxUser) principal;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户
|
||||
*/
|
||||
public PigxUser getUser() {
|
||||
Authentication authentication = getAuthentication();
|
||||
return getUser(authentication);
|
||||
}
|
||||
/**
|
||||
* 获取用户
|
||||
*/
|
||||
public PigxUser getUser() {
|
||||
Authentication authentication = getAuthentication();
|
||||
return getUser(authentication);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户角色信息
|
||||
* @return 角色集合
|
||||
*/
|
||||
public List<Long> getRoleIds() {
|
||||
Authentication authentication = getAuthentication();
|
||||
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
|
||||
/**
|
||||
* 获取用户角色信息
|
||||
*
|
||||
* @return 角色集合
|
||||
*/
|
||||
public List<Long> getRoleIds() {
|
||||
Authentication authentication = getAuthentication();
|
||||
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
|
||||
|
||||
List<Long> roleIds = new ArrayList<>();
|
||||
authorities.stream()
|
||||
.filter(granted -> StrUtil.startWith(granted.getAuthority(), SecurityConstants.ROLE))
|
||||
.forEach(granted -> {
|
||||
String id = StrUtil.removePrefix(granted.getAuthority(), SecurityConstants.ROLE);
|
||||
roleIds.add(Long.parseLong(id));
|
||||
});
|
||||
return roleIds;
|
||||
}
|
||||
List<Long> roleIds = new ArrayList<>();
|
||||
authorities.stream()
|
||||
.filter(granted -> StrUtil.startWith(granted.getAuthority(), SecurityConstants.ROLE))
|
||||
.forEach(granted -> {
|
||||
String id = StrUtil.removePrefix(granted.getAuthority(), SecurityConstants.ROLE);
|
||||
roleIds.add(Long.parseLong(id));
|
||||
});
|
||||
return roleIds;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user