mirror of
https://gitee.com/log4j/pig.git
synced 2024-12-22 12:48:58 +08:00
Merge branch 'dev'
This commit is contained in:
commit
dae419582f
74
.github/workflows/maven.yml
vendored
74
.github/workflows/maven.yml
vendored
@ -13,37 +13,51 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
|
||||
- name: mvn clean install
|
||||
run: mvn clean install
|
||||
- name: mvn spring-javaformat:validate
|
||||
run: mvn spring-javaformat:validate
|
||||
|
||||
- name: mvn spring-javaformat:validate
|
||||
run: mvn spring-javaformat:validate
|
||||
- name: mvn clean install
|
||||
run: mvn clean install
|
||||
|
||||
- name: success
|
||||
if: ${{ success() }}
|
||||
uses: fifsky/dingtalk-action@master
|
||||
with:
|
||||
url: ${{ secrets.DINGTALK_WEBHOOK}}
|
||||
type: markdown
|
||||
content: |
|
||||
# 💯👨💻 Success 🎉🎉🎉
|
||||
> Github Action: https://github.com/pig-mesh/pig success
|
||||
> ^_^ from github action message
|
||||
- name: Start containers
|
||||
run: docker-compose build && docker-compose up -d
|
||||
|
||||
- name: failure
|
||||
if: ${{ failure() }}
|
||||
uses: fifsky/dingtalk-action@master
|
||||
with:
|
||||
url: ${{ secrets.DINGTALK_WEBHOOK}}
|
||||
type: markdown
|
||||
content: |
|
||||
# 💤🤷♀️ failure 🙅♂️💣
|
||||
> Github Action: https://github.com/pig-mesh/pig failure
|
||||
> (⋟﹏⋞) from github action message
|
||||
- name: success
|
||||
if: success() && github.repository == 'pig-mesh/pig'
|
||||
uses: chf007/action-wechat-work@master
|
||||
env:
|
||||
WECHAT_WORK_BOT_WEBHOOK: ${{secrets.WECHAT_WORK_BOT_WEBHOOK}}
|
||||
with:
|
||||
msgtype: markdown
|
||||
content: |
|
||||
# 💯👨💻 Success 🎉🎉🎉 [pig-mesh/pig](https://github.com/pig-mesh/pig)
|
||||
> Github Action: https://github.com/pig-mesh/pig success
|
||||
> ^_^ from github action message
|
||||
|
||||
- name: failure
|
||||
if: failure() && github.repository == 'pig-mesh/pig'
|
||||
uses: chf007/action-wechat-work@master
|
||||
env:
|
||||
WECHAT_WORK_BOT_WEBHOOK: ${{secrets.WECHAT_WORK_BOT_WEBHOOK}}
|
||||
with:
|
||||
msgtype: markdown
|
||||
content: |
|
||||
# 💤🤷♀️ failure 🙅♂️💣 [pig-mesh/pig](https://github.com/pig-mesh/pig)
|
||||
> Github Action: https://github.com/pig-mesh/pig failure
|
||||
> (⋟﹏⋞) from github action message
|
||||
- uses: shaowenchen/debugger-action@v2
|
||||
if: github.repository == 'pig-mesh/pig'
|
||||
name: debugger
|
||||
continue-on-error: true
|
||||
with:
|
||||
frp_server_addr: ${{ secrets.FRP_SERVER_ADDR }}
|
||||
frp_server_port: ${{ secrets.FRP_SERVER_PORT }}
|
||||
frp_token: ${{ secrets.FRP_TOKEN }}
|
||||
ssh_port: ${{ secrets.SSH_PORT }}
|
||||
|
@ -273,8 +273,8 @@ INSERT INTO `sys_oauth_client_details` VALUES ('app', NULL, 'app', 'server', 'pa
|
||||
INSERT INTO `sys_oauth_client_details` VALUES ('ASD', '', 'ASD', 'ASD', 'ASDddddxxxxxxxxxxxx', '', '', NULL, NULL, '', 'false', '2021-08-09 14:19:21', '2021-08-09 14:35:29', 'admin', 'admin');
|
||||
INSERT INTO `sys_oauth_client_details` VALUES ('daemon', NULL, 'daemon', 'server', 'password,refresh_token', NULL, NULL, NULL, NULL, NULL, 'true', NULL, NULL, NULL, NULL);
|
||||
INSERT INTO `sys_oauth_client_details` VALUES ('gen', NULL, 'gen', 'server', 'password,refresh_token', NULL, NULL, NULL, NULL, NULL, 'true', NULL, NULL, NULL, NULL);
|
||||
INSERT INTO `sys_oauth_client_details` VALUES ('pig', NULL, 'pig', 'server', 'password,refresh_token,authorization_code,client_credentials', 'http://localhost:4040/sso1/login,http://localhost:4041/sso1/login', NULL, NULL, NULL, NULL, 'true', NULL, NULL, NULL, NULL);
|
||||
INSERT INTO `sys_oauth_client_details` VALUES ('test', NULL, 'test', 'server', 'password,refresh_token', NULL, NULL, NULL, NULL, NULL, 'true', NULL, NULL, NULL, NULL);
|
||||
INSERT INTO `sys_oauth_client_details` VALUES ('pig', NULL, 'pig', 'server', 'password,phone,refresh_token,authorization_code,client_credentials', 'http://localhost:4040/sso1/login,http://localhost:4041/sso1/login', NULL, NULL, NULL, NULL, 'true', NULL, NULL, NULL, NULL);
|
||||
INSERT INTO `sys_oauth_client_details` VALUES ('test', NULL, 'test', 'server', 'password,phone,refresh_token', NULL, NULL, NULL, NULL, NULL, 'true', NULL, NULL, NULL, NULL);
|
||||
INSERT INTO `sys_oauth_client_details` VALUES ('zxc', '', 'zxc', 'zxcxzc', 'cxz', 'cxz', 'zcxzxcxcxzccxz', NULL, NULL, 'zxc', 'true', '2021-08-09 14:37:45', '2021-08-09 14:37:55', 'admin', 'admin');
|
||||
COMMIT;
|
||||
|
||||
|
@ -41,7 +41,7 @@ INSERT INTO `config_info` VALUES (2, 'pig-auth-dev.yml', 'DEFAULT_GROUP', '# 数
|
||||
INSERT INTO `config_info` VALUES (3, 'pig-codegen-dev.yml', 'DEFAULT_GROUP', '## spring security 配置\nsecurity:\n oauth2:\n client:\n client-id: ENC(27v1agvAug87ANOVnbKdsw==)\n client-secret: ENC(VbnkopxrwgbFVKp+UxJ2pg==)\n scope: server\n\n# 数据源配置\nspring:\n datasource:\n type: com.zaxxer.hikari.HikariDataSource\n driver-class-name: com.mysql.cj.jdbc.Driver\n username: root\n password: root\n url: jdbc:mysql://pig-mysql:3306/pig_codegen?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai\n resources:\n static-locations: classpath:/static/,classpath:/views/\n\n# 直接放行URL\nignore:\n urls:\n - /v2/api-docs\n - /actuator/**\n', 'abc702838b34d11b46e96143ccd9f367', '2019-11-29 16:32:12', '2019-11-29 16:32:12', NULL, '127.0.0.1', '', '', '代码生成配置', NULL, NULL, 'yaml', NULL);
|
||||
INSERT INTO `config_info` VALUES (4, 'pig-gateway-dev.yml', 'DEFAULT_GROUP', 'spring:\n cloud:\n gateway:\n locator:\n enabled: true\n routes:\n # 认证中心\n - id: pig-auth\n uri: lb://pig-auth\n predicates:\n - Path=/auth/**\n filters:\n # 验证码处理\n - ValidateCodeGatewayFilter\n # 前端密码解密\n - PasswordDecoderFilter\n #UPMS 模块\n - id: pig-upms-biz\n uri: lb://pig-upms-biz\n predicates:\n - Path=/admin/**\n filters:\n # 限流配置\n - name: RequestRateLimiter\n args:\n key-resolver: \'#{@remoteAddrKeyResolver}\'\n redis-rate-limiter.replenishRate: 100\n redis-rate-limiter.burstCapacity: 200\n # 代码生成模块\n - id: pig-codegen\n uri: lb://pig-codegen\n predicates:\n - Path=/gen/**\n\n\ngateway:\n encode-key: \'thanks,pig4cloud\'\n ignore-clients:\n - test\n\nswagger:\n ignore-providers:\n - pig-auth\n - pig-codegen\n', '5cd71b235930c78e700819b944a14446', '2019-11-29 16:32:42', '2020-10-09 17:10:45', NULL, '0:0:0:0:0:0:0:1', '', '', '网关配置', '', '', 'yaml', '');
|
||||
INSERT INTO `config_info` VALUES (5, 'pig-monitor-dev.yml', 'DEFAULT_GROUP', 'spring:\n # 安全配置\n security:\n user:\n name: ENC(8Hk2ILNJM8UTOuW/Xi75qg==) # pig\n password: ENC(o6cuPFfUevmTbkmBnE67Ow====) # pig\n', '85509c6f8c67c364dc78301896274f26', '2019-11-29 16:33:05', '2019-11-29 16:33:05', NULL, '127.0.0.1', '', '', '监控配置', NULL, NULL, 'yaml', NULL);
|
||||
INSERT INTO `config_info` VALUES (6, 'pig-upms-biz-dev.yml', 'DEFAULT_GROUP', 'security:\n oauth2:\n client:\n client-id: ENC(imENTO7M8bLO38LFSIxnzw==)\n client-secret: ENC(i3cDFhs26sa2Ucrfz2hnQw==)\n scope: server\n\n# 数据源\nspring:\n datasource:\n type: com.zaxxer.hikari.HikariDataSource\n driver-class-name: com.mysql.cj.jdbc.Driver\n username: root\n password: root\n url: jdbc:mysql://pig-mysql:3306/pig?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowMultiQueries=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai\n\n# 文件上传相关 支持阿里云、华为云、腾讯、minio\noss:\n endpoint: https://play.min.io:9000\n access-key: Q3AM3UQ867SPQQA43P2F\n secret-key: zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG\n bucket-name: test-oss', '5041ac486e18aa0dd0bf624bb83806de', '2019-11-29 16:52:32', '2021-09-11 14:19:55', '', '127.0.0.1', '', '', '统一权限', 'null', 'null', 'yaml', 'null');
|
||||
INSERT INTO `config_info` VALUES (6, 'pig-upms-biz-dev.yml', 'DEFAULT_GROUP', 'security:\n oauth2:\n client:\n client-id: ENC(imENTO7M8bLO38LFSIxnzw==)\n client-secret: ENC(i3cDFhs26sa2Ucrfz2hnQw==)\n scope: server\n\n# 数据源\nspring:\n datasource:\n type: com.zaxxer.hikari.HikariDataSource\n driver-class-name: com.mysql.cj.jdbc.Driver\n username: root\n password: root\n url: jdbc:mysql://pig-mysql:3306/pig?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowMultiQueries=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai\n\n# 文件上传相关 支持阿里云、华为云、腾讯、minio\noss:\n endpoint: https://play.min.io:9000\n accessKey: Q3AM3UQ867SPQQA43P2F\n secretKey: zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG\n bucket-name: test-oss', '5041ac486e18aa0dd0bf624bb83806de', '2019-11-29 16:52:32', '2021-09-11 14:19:55', '', '127.0.0.1', '', '', '统一权限', 'null', 'null', 'yaml', 'null');
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
|
@ -1,20 +1,15 @@
|
||||
FROM moxm/java:1.8-full as builder
|
||||
WORKDIR /build
|
||||
ARG JAR_FILE=target/pig-auth.jar
|
||||
COPY ${JAR_FILE} app.jar
|
||||
RUN java -Djarmode=layertools -jar app.jar extract && rm app.jar
|
||||
|
||||
FROM moxm/java:1.8-full
|
||||
LABEL maintainer="jclazz@outlook.com"
|
||||
ENV TZ=Asia/Shanghai
|
||||
ENV JAVA_OPTS="-Xms128m -Xmx256m -Djava.security.egd=file:/dev/./urandom"
|
||||
WORKDIR pig-auth
|
||||
|
||||
COPY --from=builder /build/dependencies/ ./
|
||||
COPY --from=builder /build/snapshot-dependencies/ ./
|
||||
COPY --from=builder /build/spring-boot-loader/ ./
|
||||
COPY --from=builder /build/application/ ./
|
||||
RUN mkdir -p /pig-auth
|
||||
|
||||
WORKDIR /pig-auth
|
||||
|
||||
ARG JAR_FILE=target/pig-auth.jar
|
||||
|
||||
COPY ${JAR_FILE} app.jar
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
CMD sleep 60; java $JAVA_OPTS org.springframework.boot.loader.JarLauncher
|
||||
ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms128m -Xmx256m -Djava.security.egd=file:/dev/./urandom"
|
||||
|
||||
CMD sleep 60; java -jar app.jar $JAVA_OPTS
|
||||
|
@ -16,6 +16,8 @@
|
||||
|
||||
package com.pig4cloud.pig.auth.config;
|
||||
|
||||
import com.pig4cloud.pig.auth.converter.CustomAccessTokenConverter;
|
||||
import com.pig4cloud.pig.common.security.grant.ResourceOwnerPhoneTokenGranter;
|
||||
import com.pig4cloud.pig.common.core.constant.CacheConstants;
|
||||
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pig.common.security.component.PigWebResponseExceptionTranslator;
|
||||
@ -35,11 +37,15 @@ import org.springframework.security.oauth2.config.annotation.web.configuration.A
|
||||
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
|
||||
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
|
||||
import org.springframework.security.oauth2.provider.CompositeTokenGranter;
|
||||
import org.springframework.security.oauth2.provider.TokenGranter;
|
||||
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
|
||||
import org.springframework.security.oauth2.provider.token.TokenStore;
|
||||
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@ -63,10 +69,7 @@ public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdap
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void configure(ClientDetailsServiceConfigurer clients) {
|
||||
PigClientDetailsService clientDetailsService = new PigClientDetailsService(dataSource);
|
||||
clientDetailsService.setSelectClientDetailsSql(SecurityConstants.DEFAULT_SELECT_STATEMENT);
|
||||
clientDetailsService.setFindClientDetailsSql(SecurityConstants.DEFAULT_FIND_STATEMENT);
|
||||
clients.withClientDetails(clientDetailsService);
|
||||
clients.withClientDetails(pigClientDetailsService());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -80,7 +83,21 @@ public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdap
|
||||
.tokenEnhancer(tokenEnhancer()).userDetailsService(userDetailsService)
|
||||
.authenticationManager(authenticationManager).reuseRefreshTokens(false)
|
||||
.pathMapping("/oauth/confirm_access", "/token/confirm_access")
|
||||
.exceptionTranslator(new PigWebResponseExceptionTranslator());
|
||||
.exceptionTranslator(new PigWebResponseExceptionTranslator())
|
||||
.accessTokenConverter(new CustomAccessTokenConverter(pigClientDetailsService()));
|
||||
setTokenGranter(endpoints);
|
||||
}
|
||||
|
||||
private void setTokenGranter(AuthorizationServerEndpointsConfigurer endpoints) {
|
||||
// 获取默认授权类型
|
||||
TokenGranter tokenGranter = endpoints.getTokenGranter();
|
||||
ArrayList<TokenGranter> tokenGranters = new ArrayList<>(Arrays.asList(tokenGranter));
|
||||
ResourceOwnerPhoneTokenGranter resourceOwnerPhoneTokenGranter = new ResourceOwnerPhoneTokenGranter(
|
||||
authenticationManager, endpoints.getTokenServices(), endpoints.getClientDetailsService(),
|
||||
endpoints.getOAuth2RequestFactory());
|
||||
tokenGranters.add(resourceOwnerPhoneTokenGranter);
|
||||
CompositeTokenGranter compositeTokenGranter = new CompositeTokenGranter(tokenGranters);
|
||||
endpoints.tokenGranter(compositeTokenGranter);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ -104,4 +121,12 @@ public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdap
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PigClientDetailsService pigClientDetailsService() {
|
||||
PigClientDetailsService clientDetailsService = new PigClientDetailsService(dataSource);
|
||||
clientDetailsService.setSelectClientDetailsSql(SecurityConstants.DEFAULT_SELECT_STATEMENT);
|
||||
clientDetailsService.setFindClientDetailsSql(SecurityConstants.DEFAULT_FIND_STATEMENT);
|
||||
return clientDetailsService;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -16,8 +16,10 @@
|
||||
|
||||
package com.pig4cloud.pig.auth.config;
|
||||
|
||||
import com.pig4cloud.pig.common.security.grant.PhoneAuthenticationProvider;
|
||||
import com.pig4cloud.pig.common.security.handler.FormAuthenticationFailureHandler;
|
||||
import com.pig4cloud.pig.common.security.handler.SsoLogoutSuccessHandler;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@ -27,6 +29,7 @@ import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
||||
@ -39,18 +42,31 @@ import org.springframework.security.web.authentication.logout.LogoutSuccessHandl
|
||||
@Primary
|
||||
@Order(90)
|
||||
@Configuration
|
||||
@AllArgsConstructor
|
||||
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
|
||||
|
||||
private final UserDetailsService userDetailsService;
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
protected void configure(HttpSecurity http) {
|
||||
http.formLogin().loginPage("/token/login").loginProcessingUrl("/token/form")
|
||||
.failureHandler(authenticationFailureHandler()).and().logout()
|
||||
http.authenticationProvider(phoneAuthenticationProvider()).formLogin().loginPage("/token/login")
|
||||
.loginProcessingUrl("/token/form").failureHandler(authenticationFailureHandler()).and().logout()
|
||||
.logoutSuccessHandler(logoutSuccessHandler()).deleteCookies("JSESSIONID").invalidateHttpSession(true)
|
||||
.and().authorizeRequests().antMatchers("/token/**", "/actuator/**", "/mobile/**").permitAll()
|
||||
.anyRequest().authenticated().and().csrf().disable();
|
||||
}
|
||||
|
||||
/**
|
||||
* 不要直接使用@Bean注入 会导致默认的提供者无法注入(DaoAuthenticationProvider)
|
||||
*/
|
||||
private PhoneAuthenticationProvider phoneAuthenticationProvider() {
|
||||
PhoneAuthenticationProvider phoneAuthenticationProvider = new PhoneAuthenticationProvider();
|
||||
phoneAuthenticationProvider.setPasswordEncoder(passwordEncoder());
|
||||
phoneAuthenticationProvider.setUserDetailsService(userDetailsService);
|
||||
return phoneAuthenticationProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(WebSecurity web) {
|
||||
web.ignoring().antMatchers("/css/**");
|
||||
|
@ -0,0 +1,37 @@
|
||||
package com.pig4cloud.pig.auth.converter;
|
||||
|
||||
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pig.common.security.service.PigClientDetailsService;
|
||||
import com.pig4cloud.pig.common.security.service.PigUser;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.provider.ClientDetails;
|
||||
import org.springframework.security.oauth2.provider.OAuth2Authentication;
|
||||
import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author hccake
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class CustomAccessTokenConverter extends DefaultAccessTokenConverter {
|
||||
|
||||
final PigClientDetailsService pigClientDetailsService;
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Map<String, ?> convertAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
|
||||
Map<String, Object> response = (Map<String, Object>) super.convertAccessToken(token, authentication);
|
||||
|
||||
ClientDetails clientDetails = pigClientDetailsService
|
||||
.loadClientByClientId(authentication.getOAuth2Request().getClientId());
|
||||
if (clientDetails != null && clientDetails.getScope().contains("read_data_scope")) {
|
||||
PigUser principal = (PigUser) authentication.getPrincipal();
|
||||
response.put(SecurityConstants.DETAILS_USER_DATA_SCOPE, principal.getUserDataScope());
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
}
|
@ -25,6 +25,9 @@
|
||||
<fastjson.version>1.2.75</fastjson.version>
|
||||
<swagger.core.version>1.5.24</swagger.core.version>
|
||||
<mybatis-plus.version>3.4.3.3</mybatis-plus.version>
|
||||
<mybatis.version>3.5.7</mybatis.version>
|
||||
<jsqlparser.version>4.1</jsqlparser.version>
|
||||
<rocksdbjni.version>5.18.3</rocksdbjni.version>
|
||||
<nacos.version>2.0.3</nacos.version>
|
||||
<excel.version>1.0.0</excel.version>
|
||||
<oss.version>1.0.1</oss.version>
|
||||
@ -38,6 +41,11 @@
|
||||
<artifactId>pig-common-core</artifactId>
|
||||
<version>${pig.common.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pig-common-datascope</artifactId>
|
||||
<version>${pig.common.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pig-common-datasource</artifactId>
|
||||
@ -123,12 +131,22 @@
|
||||
<artifactId>oss-spring-boot-starter</artifactId>
|
||||
<version>${oss.version}</version>
|
||||
</dependency>
|
||||
<!--mybatis-plus-->
|
||||
<!--orm 相关-->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
<version>${mybatis-plus.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mybatis</groupId>
|
||||
<artifactId>mybatis</artifactId>
|
||||
<version>${mybatis.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.jsqlparser</groupId>
|
||||
<artifactId>jsqlparser</artifactId>
|
||||
<version>${jsqlparser.version}</version>
|
||||
</dependency>
|
||||
<!--web 模块-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
@ -57,6 +57,11 @@ public interface SecurityConstants {
|
||||
*/
|
||||
String REFRESH_TOKEN = "refresh_token";
|
||||
|
||||
/**
|
||||
* 手机号登录
|
||||
*/
|
||||
String PHONE = "phone";
|
||||
|
||||
/**
|
||||
* {bcrypt} 加密的特征码
|
||||
*/
|
||||
@ -109,9 +114,19 @@ public interface SecurityConstants {
|
||||
*/
|
||||
String DETAILS_LICENSE = "license";
|
||||
|
||||
/**
|
||||
* 用户数据权限信息
|
||||
*/
|
||||
String DETAILS_USER_DATA_SCOPE = "user_data_scope";
|
||||
|
||||
/**
|
||||
* 验证码有效期,默认 60秒
|
||||
*/
|
||||
long CODE_TIME = 60;
|
||||
|
||||
/**
|
||||
* 验证码长度
|
||||
*/
|
||||
String CODE_SIZE = "6";
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,49 @@
|
||||
package com.pig4cloud.pig.common.core.constant.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 数据权限范围类型
|
||||
* @author hccake
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum DataScopeTypeEnum {
|
||||
|
||||
/**
|
||||
* 查询全部数据
|
||||
*/
|
||||
ALL(0),
|
||||
|
||||
/**
|
||||
* 本人
|
||||
*/
|
||||
SELF(1),
|
||||
|
||||
/**
|
||||
* 本人及子级
|
||||
*/
|
||||
SELF_CHILD_LEVEL(2),
|
||||
|
||||
/**
|
||||
* 本级
|
||||
*/
|
||||
LEVEL(3),
|
||||
|
||||
/**
|
||||
* 本级及子级
|
||||
*/
|
||||
LEVEL_CHILD_LEVEL(4),
|
||||
|
||||
/**
|
||||
* 自定义
|
||||
*/
|
||||
CUSTOM(5);
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
private final Integer type;
|
||||
|
||||
}
|
@ -33,14 +33,9 @@ public enum LoginTypeEnum {
|
||||
PWD("PWD", "账号密码登录"),
|
||||
|
||||
/**
|
||||
* QQ登录
|
||||
* 验证码登录
|
||||
*/
|
||||
QQ("QQ", "QQ登录"),
|
||||
|
||||
/**
|
||||
* 微信登录
|
||||
*/
|
||||
WECHAT("WX", "微信登录");
|
||||
SMS("SMS", "验证码登录");
|
||||
|
||||
/**
|
||||
* 类型
|
||||
|
38
pig-common/pig-common-datascope/pom.xml
Normal file
38
pig-common/pig-common-datascope/pom.xml
Normal file
@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>pig-common</artifactId>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<version>3.3.4</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>pig-common-datascope</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<!-- slf4j日志 -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.jsqlparser</groupId>
|
||||
<artifactId>jsqlparser</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mybatis</groupId>
|
||||
<artifactId>mybatis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@ -0,0 +1,36 @@
|
||||
package com.pigcloud.pig.common.datascope;
|
||||
|
||||
import net.sf.jsqlparser.expression.Alias;
|
||||
import net.sf.jsqlparser.expression.Expression;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* @author Hccake 2020/9/28
|
||||
* @version 1.0
|
||||
*/
|
||||
public interface DataScope {
|
||||
|
||||
/**
|
||||
* 数据所对应的资源
|
||||
* @return 资源标识
|
||||
*/
|
||||
String getResource();
|
||||
|
||||
/**
|
||||
* 该资源相关的所有表,推荐使用 Set 类型。 <br/>
|
||||
* 如需忽略表名大小写判断,则可以使用 TreeSet,并设置忽略大小写的自定义Comparator。 <br/>
|
||||
* eg. new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
|
||||
* @return tableNames
|
||||
*/
|
||||
Collection<String> getTableNames();
|
||||
|
||||
/**
|
||||
* 根据表名和表别名,动态生成的 where/or 筛选条件
|
||||
* @param tableName 表名
|
||||
* @param tableAlias 表别名,可能为空
|
||||
* @return 数据规则表达式
|
||||
*/
|
||||
Expression getExpression(String tableName, Alias tableAlias);
|
||||
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package com.pigcloud.pig.common.datascope;
|
||||
|
||||
import com.pigcloud.pig.common.datascope.handler.DataPermissionHandler;
|
||||
import com.pigcloud.pig.common.datascope.handler.DefaultDataPermissionHandler;
|
||||
import com.pigcloud.pig.common.datascope.interceptor.DataPermissionAnnotationAdvisor;
|
||||
import com.pigcloud.pig.common.datascope.interceptor.DataPermissionInterceptor;
|
||||
import com.pigcloud.pig.common.datascope.processor.DataScopeSqlProcessor;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author hccake
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@ConditionalOnBean(DataScope.class)
|
||||
public class DataScopeAutoConfiguration {
|
||||
|
||||
/**
|
||||
* 数据权限处理器
|
||||
* @param dataScopeList 需要控制的数据范围集合
|
||||
* @return DataPermissionHandler
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public DataPermissionHandler dataPermissionHandler(List<DataScope> dataScopeList) {
|
||||
return new DefaultDataPermissionHandler(dataScopeList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据权限注解 Advisor,用于处理数据权限的链式调用关系
|
||||
* @return DataPermissionAnnotationAdvisor
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(DataPermissionAnnotationAdvisor.class)
|
||||
public DataPermissionAnnotationAdvisor dataPermissionAnnotationAdvisor() {
|
||||
return new DataPermissionAnnotationAdvisor();
|
||||
}
|
||||
|
||||
/**
|
||||
* mybatis 拦截器,用于拦截处理 sql
|
||||
* @param dataPermissionHandler 数据权限处理器
|
||||
* @return DataPermissionInterceptor
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public DataPermissionInterceptor dataPermissionInterceptor(DataPermissionHandler dataPermissionHandler) {
|
||||
return new DataPermissionInterceptor(new DataScopeSqlProcessor(), dataPermissionHandler);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package com.pigcloud.pig.common.datascope.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 数据权限注解,注解在 Mapper类 或者 对应方法上 用于提供该 mapper 对应表,所需控制的实体信息
|
||||
* @author Hccake 2020/9/27
|
||||
* @version 1.0
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface DataPermission {
|
||||
|
||||
/**
|
||||
* 当前类或方法是否忽略数据权限
|
||||
* @return boolean 默认返回 false
|
||||
*/
|
||||
boolean ignore() default false;
|
||||
|
||||
/**
|
||||
* 仅对指定资源类型进行数据权限控制,只在开启情况下有效,当该数组有值时,exclude不生效
|
||||
* @see DataPermission#excludeResources
|
||||
* @return 资源类型数组
|
||||
*/
|
||||
String[] includeResources() default {};
|
||||
|
||||
/**
|
||||
* 对指定资源类型跳过数据权限控制,只在开启情况下有效,当该includeResources有值时,exclude不生效
|
||||
* @see DataPermission#includeResources
|
||||
* @return 资源类型数组
|
||||
*/
|
||||
String[] excludeResources() default {};
|
||||
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package com.pigcloud.pig.common.datascope.handler;
|
||||
|
||||
import com.pigcloud.pig.common.datascope.DataScope;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据权限处理器
|
||||
*
|
||||
* @author Hccake 2020/9/28
|
||||
* @version 1.0
|
||||
*/
|
||||
public interface DataPermissionHandler {
|
||||
|
||||
/**
|
||||
* 系统配置的所有的数据范围
|
||||
* @return 数据范围集合
|
||||
*/
|
||||
List<DataScope> dataScopes();
|
||||
|
||||
/**
|
||||
* 根据权限注解过滤后的数据范围集合
|
||||
* @param mappedStatementId Mapper方法ID
|
||||
* @return 数据范围集合
|
||||
*/
|
||||
List<DataScope> filterDataScopes(String mappedStatementId);
|
||||
|
||||
/**
|
||||
* 是否忽略权限控制,用于及早的忽略控制,例如管理员直接放行,而不必等到DataScope中再进行过滤处理,提升效率
|
||||
* @return boolean true: 忽略,false: 进行权限控制
|
||||
* @param mappedStatementId Mapper方法ID
|
||||
*/
|
||||
boolean ignorePermissionControl(String mappedStatementId);
|
||||
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package com.pigcloud.pig.common.datascope.handler;
|
||||
|
||||
import com.pigcloud.pig.common.datascope.DataScope;
|
||||
import com.pigcloud.pig.common.datascope.annotation.DataPermission;
|
||||
import com.pigcloud.pig.common.datascope.holder.DataPermissionAnnotationHolder;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 默认的数据权限控制处理器
|
||||
*
|
||||
* @author Hccake 2021/1/27
|
||||
* @version 1.0
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class DefaultDataPermissionHandler implements DataPermissionHandler {
|
||||
|
||||
private final List<DataScope> dataScopes;
|
||||
|
||||
/**
|
||||
* 系统配置的所有的数据范围
|
||||
* @return 数据范围集合
|
||||
*/
|
||||
@Override
|
||||
public List<DataScope> dataScopes() {
|
||||
return dataScopes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 系统配置的所有的数据范围
|
||||
* @param mappedStatementId Mapper方法ID
|
||||
* @return 数据范围集合
|
||||
*/
|
||||
@Override
|
||||
public List<DataScope> filterDataScopes(String mappedStatementId) {
|
||||
if (this.dataScopes == null || this.dataScopes.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
// 获取当前方法对应的权限注解,根据注解进行数据范围控制的过滤
|
||||
DataPermission dataPermission = DataPermissionAnnotationHolder.peek();
|
||||
if (dataPermission == null) {
|
||||
return dataScopes;
|
||||
}
|
||||
|
||||
if (dataPermission.ignore()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 当指定了只包含的资源时,只对该资源的DataScope
|
||||
if (dataPermission.includeResources().length > 0) {
|
||||
Set<String> a = new HashSet<>(Arrays.asList(dataPermission.includeResources()));
|
||||
return dataScopes.stream().filter(x -> a.contains(x.getResource())).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
// 当未指定只包含的资源,且指定了排除的资源时,则排除此部分资源的 DataScope
|
||||
if (dataPermission.excludeResources().length > 0) {
|
||||
Set<String> a = new HashSet<>(Arrays.asList(dataPermission.excludeResources()));
|
||||
return dataScopes.stream().filter(x -> !a.contains(x.getResource())).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
return dataScopes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否忽略权限控制,默认不忽略
|
||||
* @param mappedStatementId Mapper方法ID
|
||||
* @return always false
|
||||
*/
|
||||
@Override
|
||||
public boolean ignorePermissionControl(String mappedStatementId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package com.pigcloud.pig.common.datascope.holder;
|
||||
|
||||
import com.pigcloud.pig.common.datascope.annotation.DataPermission;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
|
||||
/**
|
||||
* 数据权限注解的持有者,使用栈存储调用链中各方法对应数据权限注解
|
||||
*
|
||||
* @author hccake
|
||||
*/
|
||||
public final class DataPermissionAnnotationHolder {
|
||||
|
||||
private DataPermissionAnnotationHolder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用栈存储 DataPermission,便于在方法嵌套调用时使用不同的数据权限控制。
|
||||
*/
|
||||
private static final ThreadLocal<Deque<DataPermission>> DATA_PERMISSIONS = ThreadLocal.withInitial(ArrayDeque::new);
|
||||
|
||||
/**
|
||||
* 获取当前的 DataPermission 注解
|
||||
* @return DataPermission
|
||||
*/
|
||||
public static DataPermission peek() {
|
||||
return DATA_PERMISSIONS.get().peek();
|
||||
}
|
||||
|
||||
/**
|
||||
* 入栈一个 DataPermission 注解
|
||||
* @return DataPermission
|
||||
*/
|
||||
public static DataPermission push(DataPermission dataPermission) {
|
||||
DATA_PERMISSIONS.get().push(dataPermission);
|
||||
return dataPermission;
|
||||
}
|
||||
|
||||
/**
|
||||
* 弹出最顶部 DataPermission
|
||||
*/
|
||||
public static void poll() {
|
||||
Deque<DataPermission> deque = DATA_PERMISSIONS.get();
|
||||
// 当没有元素时,清空 ThreadLocal
|
||||
if (deque.poll() == null) {
|
||||
DATA_PERMISSIONS.remove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除 TreadLocal
|
||||
*/
|
||||
public static void clear() {
|
||||
DATA_PERMISSIONS.remove();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package com.pigcloud.pig.common.datascope.holder;
|
||||
|
||||
import com.pigcloud.pig.common.datascope.DataScope;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* DataScope 持有者。 方便解析 SQL 时的参数透传
|
||||
*
|
||||
* @author hccake
|
||||
*/
|
||||
public final class DataScopeHolder {
|
||||
|
||||
private DataScopeHolder() {
|
||||
}
|
||||
|
||||
private static final ThreadLocal<List<DataScope>> DATA_SCOPES = new ThreadLocal<>();
|
||||
|
||||
/**
|
||||
* get dataScope
|
||||
* @return dataScopes
|
||||
*/
|
||||
public static List<DataScope> get() {
|
||||
return DATA_SCOPES.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加 dataScope
|
||||
*/
|
||||
public static void set(List<DataScope> dataScopes) {
|
||||
DATA_SCOPES.set(dataScopes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除 dataScope
|
||||
*/
|
||||
public static void remove() {
|
||||
DATA_SCOPES.remove();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package com.pigcloud.pig.common.datascope.interceptor;
|
||||
|
||||
import com.pigcloud.pig.common.datascope.annotation.DataPermission;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import org.aopalliance.aop.Advice;
|
||||
import org.springframework.aop.Pointcut;
|
||||
import org.springframework.aop.support.AbstractPointcutAdvisor;
|
||||
import org.springframework.aop.support.ComposablePointcut;
|
||||
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
|
||||
|
||||
/**
|
||||
* @author hccake
|
||||
*/
|
||||
@Getter
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class DataPermissionAnnotationAdvisor extends AbstractPointcutAdvisor {
|
||||
|
||||
private final Advice advice;
|
||||
|
||||
private final Pointcut pointcut;
|
||||
|
||||
public DataPermissionAnnotationAdvisor() {
|
||||
this.advice = new DataPermissionAnnotationInterceptor();
|
||||
this.pointcut = buildPointcut();
|
||||
}
|
||||
|
||||
protected Pointcut buildPointcut() {
|
||||
Pointcut cpc = new AnnotationMatchingPointcut(DataPermission.class, true);
|
||||
Pointcut mpc = new AnnotationMatchingPointcut(null, DataPermission.class, true);
|
||||
return new ComposablePointcut(cpc).union(mpc);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package com.pigcloud.pig.common.datascope.interceptor;
|
||||
|
||||
import com.pigcloud.pig.common.datascope.annotation.DataPermission;
|
||||
import com.pigcloud.pig.common.datascope.holder.DataPermissionAnnotationHolder;
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* DataPermission注解的拦截器,在执行方法前将当前方法的对应注解压栈,执行后弹出注解
|
||||
*
|
||||
* @author hccake
|
||||
*/
|
||||
public class DataPermissionAnnotationInterceptor implements MethodInterceptor {
|
||||
|
||||
@Override
|
||||
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
|
||||
// 当前方法
|
||||
Method method = methodInvocation.getMethod();
|
||||
// 获取执行类
|
||||
Object invocationThis = methodInvocation.getThis();
|
||||
Class<?> clazz = invocationThis != null ? invocationThis.getClass() : method.getDeclaringClass();
|
||||
// 寻找对应的 DataPermission 注解属性
|
||||
DataPermission dataPermission = DataPermissionFinder.findDataPermission(method, clazz);
|
||||
DataPermissionAnnotationHolder.push(dataPermission);
|
||||
try {
|
||||
return methodInvocation.proceed();
|
||||
}
|
||||
finally {
|
||||
DataPermissionAnnotationHolder.poll();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package com.pigcloud.pig.common.datascope.interceptor;
|
||||
|
||||
import com.pigcloud.pig.common.datascope.annotation.DataPermission;
|
||||
import org.springframework.core.MethodClassKey;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* {@link DataPermission} 注解的查找者。用于查询当前方法对应的 DataPermission 注解环境,当方法上没有找到时,会去类上寻找。
|
||||
*
|
||||
* @author hccake
|
||||
*/
|
||||
@DataPermission
|
||||
public final class DataPermissionFinder {
|
||||
|
||||
private DataPermissionFinder() {
|
||||
|
||||
}
|
||||
|
||||
private static final Map<Object, DataPermission> DATA_PERMISSION_CACHE = new ConcurrentHashMap<>(1024);
|
||||
|
||||
/**
|
||||
* 提供一个默认的空值注解,用于缓存空值占位使用
|
||||
*/
|
||||
private static final DataPermission EMPTY_DATA_PERMISSION = DataPermissionFinder.class
|
||||
.getAnnotation(DataPermission.class);
|
||||
|
||||
/**
|
||||
* 缓存的 key 值
|
||||
* @param method 方法
|
||||
* @param clazz 类
|
||||
* @return key
|
||||
*/
|
||||
private static Object getCacheKey(Method method, Class<?> clazz) {
|
||||
return new MethodClassKey(method, clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从缓存中获取数据权限注解 优先获取方法上的注解,再获取类上的注解
|
||||
* @param method 当前方法
|
||||
* @param clazz 当前类
|
||||
* @return 当前方法有效的数据权限注解
|
||||
*/
|
||||
public static DataPermission findDataPermission(Method method, Class<?> clazz) {
|
||||
Object methodKey = getCacheKey(method, clazz);
|
||||
|
||||
if (DATA_PERMISSION_CACHE.containsKey(methodKey)) {
|
||||
DataPermission dataPermission = DATA_PERMISSION_CACHE.get(methodKey);
|
||||
// 判断是否和缓存的空注解是同一个对象
|
||||
return EMPTY_DATA_PERMISSION == dataPermission ? null : dataPermission;
|
||||
}
|
||||
|
||||
// 先查方法,如果方法上没有,则使用类上
|
||||
DataPermission dataPermission = AnnotatedElementUtils.findMergedAnnotation(method, DataPermission.class);
|
||||
if (dataPermission == null) {
|
||||
dataPermission = AnnotatedElementUtils.findMergedAnnotation(clazz, DataPermission.class);
|
||||
}
|
||||
DATA_PERMISSION_CACHE.put(methodKey, dataPermission == null ? EMPTY_DATA_PERMISSION : dataPermission);
|
||||
return dataPermission;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
package com.pigcloud.pig.common.datascope.interceptor;
|
||||
|
||||
import com.pigcloud.pig.common.datascope.DataScope;
|
||||
import com.pigcloud.pig.common.datascope.handler.DataPermissionHandler;
|
||||
import com.pigcloud.pig.common.datascope.processor.DataScopeSqlProcessor;
|
||||
import com.pigcloud.pig.common.datascope.util.PluginUtils;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.ibatis.executor.statement.StatementHandler;
|
||||
import org.apache.ibatis.mapping.MappedStatement;
|
||||
import org.apache.ibatis.mapping.SqlCommandType;
|
||||
import org.apache.ibatis.plugin.*;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据权限拦截器
|
||||
*
|
||||
* @author Hccake 2020/9/28
|
||||
* @version 1.0
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Intercepts({
|
||||
@Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class, Integer.class }) })
|
||||
public class DataPermissionInterceptor implements Interceptor {
|
||||
|
||||
private final DataScopeSqlProcessor dataScopeSqlProcessor;
|
||||
|
||||
private final DataPermissionHandler dataPermissionHandler;
|
||||
|
||||
@Override
|
||||
public Object intercept(Invocation invocation) throws Throwable {
|
||||
// 第一版,测试用
|
||||
Object target = invocation.getTarget();
|
||||
StatementHandler sh = (StatementHandler) target;
|
||||
PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
|
||||
MappedStatement ms = mpSh.mappedStatement();
|
||||
SqlCommandType sct = ms.getSqlCommandType();
|
||||
PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();
|
||||
String mappedStatementId = ms.getId();
|
||||
|
||||
// 根据用户权限判断是否需要拦截,例如管理员可以查看所有,则直接放行
|
||||
if (dataPermissionHandler.ignorePermissionControl(mappedStatementId)) {
|
||||
return invocation.proceed();
|
||||
}
|
||||
|
||||
List<DataScope> dataScopes = dataPermissionHandler.filterDataScopes(mappedStatementId);
|
||||
if (dataScopes == null || dataScopes.isEmpty()) {
|
||||
return invocation.proceed();
|
||||
}
|
||||
|
||||
// 根据 DataScopes 进行数据权限的 sql 处理
|
||||
if (sct == SqlCommandType.SELECT) {
|
||||
mpBs.sql(dataScopeSqlProcessor.parserSingle(mpBs.sql(), dataScopes));
|
||||
}
|
||||
else if (sct == SqlCommandType.INSERT || sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {
|
||||
mpBs.sql(dataScopeSqlProcessor.parserMulti(mpBs.sql(), dataScopes));
|
||||
}
|
||||
|
||||
// 执行 sql
|
||||
return invocation.proceed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object plugin(Object target) {
|
||||
if (target instanceof StatementHandler) {
|
||||
return Plugin.wrap(target, this);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
package com.pigcloud.pig.common.datascope.parser;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.sf.jsqlparser.JSQLParserException;
|
||||
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
|
||||
import net.sf.jsqlparser.statement.Statement;
|
||||
import net.sf.jsqlparser.statement.Statements;
|
||||
import net.sf.jsqlparser.statement.delete.Delete;
|
||||
import net.sf.jsqlparser.statement.insert.Insert;
|
||||
import net.sf.jsqlparser.statement.select.Select;
|
||||
import net.sf.jsqlparser.statement.update.Update;
|
||||
|
||||
/**
|
||||
* https://github.com/JSQLParser/JSqlParser
|
||||
*
|
||||
* @author miemie hccake
|
||||
* @since 2020-06-22
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class JsqlParserSupport {
|
||||
|
||||
public String parserSingle(String sql, Object obj) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("original SQL: " + sql);
|
||||
}
|
||||
try {
|
||||
Statement statement = CCJSqlParserUtil.parse(sql);
|
||||
return processParser(statement, 0, sql, obj);
|
||||
}
|
||||
catch (JSQLParserException e) {
|
||||
throw new RuntimeException(String.format("Failed to process, Error SQL: %s", sql), e);
|
||||
}
|
||||
}
|
||||
|
||||
public String parserMulti(String sql, Object obj) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("original SQL: " + sql);
|
||||
}
|
||||
try {
|
||||
// fixed github pull/295
|
||||
StringBuilder sb = new StringBuilder();
|
||||
Statements statements = CCJSqlParserUtil.parseStatements(sql);
|
||||
int i = 0;
|
||||
for (Statement statement : statements.getStatements()) {
|
||||
if (i > 0) {
|
||||
sb.append(";");
|
||||
}
|
||||
sb.append(processParser(statement, i, sql, obj));
|
||||
i++;
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
catch (JSQLParserException e) {
|
||||
throw new RuntimeException(String.format("Failed to process, Error SQL: %s", sql), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行 SQL 解析
|
||||
* @param statement JsqlParser Statement
|
||||
* @return sql
|
||||
*/
|
||||
protected String processParser(Statement statement, int index, String sql, Object obj) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("SQL to parse, SQL: " + sql);
|
||||
}
|
||||
if (statement instanceof Insert) {
|
||||
this.processInsert((Insert) statement, index, sql, obj);
|
||||
}
|
||||
else if (statement instanceof Select) {
|
||||
this.processSelect((Select) statement, index, sql, obj);
|
||||
}
|
||||
else if (statement instanceof Update) {
|
||||
this.processUpdate((Update) statement, index, sql, obj);
|
||||
}
|
||||
else if (statement instanceof Delete) {
|
||||
this.processDelete((Delete) statement, index, sql, obj);
|
||||
}
|
||||
sql = statement.toString();
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("parse the finished SQL: " + sql);
|
||||
}
|
||||
return sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增
|
||||
*/
|
||||
protected void processInsert(Insert insert, int index, String sql, Object obj) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*/
|
||||
protected void processDelete(Delete delete, int index, String sql, Object obj) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新
|
||||
*/
|
||||
protected void processUpdate(Update update, int index, String sql, Object obj) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询
|
||||
*/
|
||||
protected void processSelect(Select select, int index, String sql, Object obj) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,321 @@
|
||||
package com.pigcloud.pig.common.datascope.processor;
|
||||
|
||||
import com.pigcloud.pig.common.datascope.DataScope;
|
||||
import com.pigcloud.pig.common.datascope.holder.DataScopeHolder;
|
||||
import com.pigcloud.pig.common.datascope.parser.JsqlParserSupport;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.sf.jsqlparser.expression.*;
|
||||
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
|
||||
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
|
||||
import net.sf.jsqlparser.expression.operators.relational.ExistsExpression;
|
||||
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
|
||||
import net.sf.jsqlparser.expression.operators.relational.InExpression;
|
||||
import net.sf.jsqlparser.expression.operators.relational.ItemsList;
|
||||
import net.sf.jsqlparser.schema.Column;
|
||||
import net.sf.jsqlparser.schema.Table;
|
||||
import net.sf.jsqlparser.statement.delete.Delete;
|
||||
import net.sf.jsqlparser.statement.insert.Insert;
|
||||
import net.sf.jsqlparser.statement.select.*;
|
||||
import net.sf.jsqlparser.statement.update.Update;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 数据权限 sql 处理器 参考 mybatis-plus 租户拦截器,解析 sql where 部分,进行查询表达式注入
|
||||
*
|
||||
* @author Hccake 2020/9/26
|
||||
* @version 1.0
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class DataScopeSqlProcessor extends JsqlParserSupport {
|
||||
|
||||
/**
|
||||
* select 类型SQL处理
|
||||
* @param select jsqlparser Statement Select
|
||||
*/
|
||||
@Override
|
||||
protected void processSelect(Select select, int index, String sql, Object obj) {
|
||||
List<DataScope> dataScopes = (List<DataScope>) obj;
|
||||
try {
|
||||
// dataScopes 放入 ThreadLocal 方便透传
|
||||
DataScopeHolder.set(dataScopes);
|
||||
processSelectBody(select.getSelectBody());
|
||||
List<WithItem> withItemsList = select.getWithItemsList();
|
||||
if (withItemsList != null && !withItemsList.isEmpty()) {
|
||||
withItemsList.forEach(this::processSelectBody);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
// 必须清空 ThreadLocal
|
||||
DataScopeHolder.remove();
|
||||
}
|
||||
}
|
||||
|
||||
protected void processSelectBody(SelectBody selectBody) {
|
||||
if (selectBody == null) {
|
||||
return;
|
||||
}
|
||||
if (selectBody instanceof PlainSelect) {
|
||||
processPlainSelect((PlainSelect) selectBody);
|
||||
}
|
||||
else if (selectBody instanceof WithItem) {
|
||||
WithItem withItem = (WithItem) selectBody;
|
||||
processSelectBody(withItem.getSubSelect().getSelectBody());
|
||||
}
|
||||
else {
|
||||
SetOperationList operationList = (SetOperationList) selectBody;
|
||||
List<SelectBody> selectBodys = operationList.getSelects();
|
||||
if (selectBodys != null && !selectBodys.isEmpty()) {
|
||||
selectBodys.forEach(this::processSelectBody);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* insert 类型SQL处理
|
||||
* @param insert jsqlparser Statement Insert
|
||||
*/
|
||||
@Override
|
||||
protected void processInsert(Insert insert, int index, String sql, Object obj) {
|
||||
// insert 暂时不处理
|
||||
}
|
||||
|
||||
/**
|
||||
* update 类型SQL处理
|
||||
* @param update jsqlparser Statement Update
|
||||
*/
|
||||
@Override
|
||||
protected void processUpdate(Update update, int index, String sql, Object obj) {
|
||||
List<DataScope> dataScopes = (List<DataScope>) obj;
|
||||
try {
|
||||
// dataScopes 放入 ThreadLocal 方便透传
|
||||
DataScopeHolder.set(dataScopes);
|
||||
update.setWhere(this.injectExpression(update.getWhere(), update.getTable()));
|
||||
}
|
||||
finally {
|
||||
// 必须清空 ThreadLocal
|
||||
DataScopeHolder.remove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* delete 类型SQL处理
|
||||
* @param delete jsqlparser Statement Delete
|
||||
*/
|
||||
@Override
|
||||
protected void processDelete(Delete delete, int index, String sql, Object obj) {
|
||||
List<DataScope> dataScopes = (List<DataScope>) obj;
|
||||
try {
|
||||
// dataScopes 放入 ThreadLocal 方便透传
|
||||
DataScopeHolder.set(dataScopes);
|
||||
delete.setWhere(this.injectExpression(delete.getWhere(), delete.getTable()));
|
||||
}
|
||||
finally {
|
||||
// 必须清空 ThreadLocal
|
||||
DataScopeHolder.remove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理 PlainSelect
|
||||
*/
|
||||
protected void processPlainSelect(PlainSelect plainSelect) {
|
||||
FromItem fromItem = plainSelect.getFromItem();
|
||||
Expression where = plainSelect.getWhere();
|
||||
processWhereSubSelect(where);
|
||||
if (fromItem instanceof Table) {
|
||||
Table fromTable = (Table) fromItem;
|
||||
// #1186 github
|
||||
plainSelect.setWhere(injectExpression(where, fromTable));
|
||||
}
|
||||
else {
|
||||
processFromItem(fromItem);
|
||||
}
|
||||
// #3087 github
|
||||
List<SelectItem> selectItems = plainSelect.getSelectItems();
|
||||
if (selectItems != null && !selectItems.isEmpty()) {
|
||||
selectItems.forEach(this::processSelectItem);
|
||||
}
|
||||
List<Join> joins = plainSelect.getJoins();
|
||||
if (joins != null && !joins.isEmpty()) {
|
||||
joins.forEach(j -> {
|
||||
processJoin(j);
|
||||
processFromItem(j.getRightItem());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理where条件内的子查询
|
||||
* <p>
|
||||
* 支持如下: 1. in 2. = 3. > 4. < 5. >= 6. <= 7. <> 8. EXISTS 9. NOT EXISTS
|
||||
* <p>
|
||||
* 前提条件: 1. 子查询必须放在小括号中 2. 子查询一般放在比较操作符的右边
|
||||
* @param where where 条件
|
||||
*/
|
||||
protected void processWhereSubSelect(Expression where) {
|
||||
if (where == null) {
|
||||
return;
|
||||
}
|
||||
if (where instanceof FromItem) {
|
||||
processFromItem((FromItem) where);
|
||||
return;
|
||||
}
|
||||
if (where.toString().indexOf("SELECT") > 0) {
|
||||
// 有子查询
|
||||
if (where instanceof BinaryExpression) {
|
||||
// 比较符号 , and , or , 等等
|
||||
BinaryExpression expression = (BinaryExpression) where;
|
||||
processWhereSubSelect(expression.getLeftExpression());
|
||||
processWhereSubSelect(expression.getRightExpression());
|
||||
}
|
||||
else if (where instanceof InExpression) {
|
||||
// in
|
||||
InExpression expression = (InExpression) where;
|
||||
ItemsList itemsList = expression.getRightItemsList();
|
||||
if (itemsList instanceof SubSelect) {
|
||||
processSelectBody(((SubSelect) itemsList).getSelectBody());
|
||||
}
|
||||
}
|
||||
else if (where instanceof ExistsExpression) {
|
||||
// exists
|
||||
ExistsExpression expression = (ExistsExpression) where;
|
||||
processWhereSubSelect(expression.getRightExpression());
|
||||
}
|
||||
else if (where instanceof NotExpression) {
|
||||
// not exists
|
||||
NotExpression expression = (NotExpression) where;
|
||||
processWhereSubSelect(expression.getExpression());
|
||||
}
|
||||
else if (where instanceof Parenthesis) {
|
||||
Parenthesis expression = (Parenthesis) where;
|
||||
processWhereSubSelect(expression.getExpression());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void processSelectItem(SelectItem selectItem) {
|
||||
if (selectItem instanceof SelectExpressionItem) {
|
||||
SelectExpressionItem selectExpressionItem = (SelectExpressionItem) selectItem;
|
||||
if (selectExpressionItem.getExpression() instanceof SubSelect) {
|
||||
processSelectBody(((SubSelect) selectExpressionItem.getExpression()).getSelectBody());
|
||||
}
|
||||
else if (selectExpressionItem.getExpression() instanceof Function) {
|
||||
processFunction((Function) selectExpressionItem.getExpression());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理函数
|
||||
* <p>
|
||||
* 支持: 1. select fun(args..) 2. select fun1(fun2(args..),args..)
|
||||
* <p>
|
||||
* <p>
|
||||
* fixed gitee pulls/141
|
||||
* </p>
|
||||
* @param function
|
||||
*/
|
||||
protected void processFunction(Function function) {
|
||||
ExpressionList parameters = function.getParameters();
|
||||
if (parameters != null) {
|
||||
parameters.getExpressions().forEach(expression -> {
|
||||
if (expression instanceof SubSelect) {
|
||||
processSelectBody(((SubSelect) expression).getSelectBody());
|
||||
}
|
||||
else if (expression instanceof Function) {
|
||||
processFunction((Function) expression);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理子查询等
|
||||
*/
|
||||
protected void processFromItem(FromItem fromItem) {
|
||||
if (fromItem instanceof SubJoin) {
|
||||
SubJoin subJoin = (SubJoin) fromItem;
|
||||
if (subJoin.getJoinList() != null) {
|
||||
subJoin.getJoinList().forEach(this::processJoin);
|
||||
}
|
||||
if (subJoin.getLeft() != null) {
|
||||
processFromItem(subJoin.getLeft());
|
||||
}
|
||||
}
|
||||
else if (fromItem instanceof SubSelect) {
|
||||
SubSelect subSelect = (SubSelect) fromItem;
|
||||
if (subSelect.getSelectBody() != null) {
|
||||
processSelectBody(subSelect.getSelectBody());
|
||||
}
|
||||
}
|
||||
else if (fromItem instanceof ValuesList) {
|
||||
log.debug("Perform a subquery, if you do not give us feedback");
|
||||
}
|
||||
else if (fromItem instanceof LateralSubSelect) {
|
||||
LateralSubSelect lateralSubSelect = (LateralSubSelect) fromItem;
|
||||
if (lateralSubSelect.getSubSelect() != null) {
|
||||
SubSelect subSelect = lateralSubSelect.getSubSelect();
|
||||
if (subSelect.getSelectBody() != null) {
|
||||
processSelectBody(subSelect.getSelectBody());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理联接语句
|
||||
*/
|
||||
protected void processJoin(Join join) {
|
||||
if (join.getRightItem() instanceof Table) {
|
||||
Table fromTable = (Table) join.getRightItem();
|
||||
join.setOnExpression(injectExpression(join.getOnExpression(), fromTable));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 DataScope ,将数据过滤的表达式注入原本的 where/or 条件
|
||||
* @param currentExpression Expression where/or
|
||||
* @param table 表信息
|
||||
* @return 修改后的 where/or 条件
|
||||
*/
|
||||
private Expression injectExpression(Expression currentExpression, Table table) {
|
||||
String tableName = table.getName();
|
||||
List<DataScope> dataScopes = DataScopeHolder.get();
|
||||
Expression dataFilterExpression = dataScopes.stream().filter(x -> x.getTableNames().contains(tableName))
|
||||
.map(x -> x.getExpression(tableName, table.getAlias())).filter(Objects::nonNull)
|
||||
.reduce(AndExpression::new).orElse(null);
|
||||
|
||||
if (currentExpression == null) {
|
||||
return dataFilterExpression;
|
||||
}
|
||||
if (dataFilterExpression == null) {
|
||||
return currentExpression;
|
||||
}
|
||||
if (currentExpression instanceof OrExpression) {
|
||||
return new AndExpression(new Parenthesis(currentExpression), dataFilterExpression);
|
||||
}
|
||||
else {
|
||||
return new AndExpression(currentExpression, dataFilterExpression);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据当前表是否有别名,动态对字段名前添加表别名 eg. 表名: table_1 as t 原始字段:column1 返回: t.column1
|
||||
* @param table 表信息
|
||||
* @param column 字段名
|
||||
* @return 原始字段名,或者添加了表别名的字段名
|
||||
*/
|
||||
protected Column getAliasColumn(Table table, String column) {
|
||||
StringBuilder columnBuilder = new StringBuilder();
|
||||
if (table.getAlias() != null) {
|
||||
columnBuilder.append(table.getAlias().getName()).append(".");
|
||||
}
|
||||
columnBuilder.append(column);
|
||||
return new Column(columnBuilder.toString());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package com.pigcloud.pig.common.datascope.util;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* @author Hccake 2021/1/27
|
||||
* @version 1.0
|
||||
*/
|
||||
public final class AnnotationUtil {
|
||||
|
||||
private AnnotationUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据权限注解 优先获取方法上的注解,再获取类上的注解
|
||||
* @param mappedStatementId 类名.方法名
|
||||
* @return 数据权限注解
|
||||
*/
|
||||
public static <A extends Annotation> A findAnnotationByMappedStatementId(String mappedStatementId,
|
||||
Class<A> aClass) {
|
||||
if (mappedStatementId == null || "".equals(mappedStatementId)) {
|
||||
return null;
|
||||
}
|
||||
// 1.得到类路径和方法路径
|
||||
int lastIndexOfDot = mappedStatementId.lastIndexOf(".");
|
||||
if (lastIndexOfDot < 0) {
|
||||
return null;
|
||||
}
|
||||
String className = mappedStatementId.substring(0, lastIndexOfDot);
|
||||
String methodName = mappedStatementId.substring(lastIndexOfDot + 1);
|
||||
if ("".equals(className) || "".equals(methodName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 2.字节码
|
||||
Class<?> clazz = null;
|
||||
try {
|
||||
clazz = Class.forName(className);
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (clazz == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
A annotation = null;
|
||||
// 3.得到方法上的注解
|
||||
Method[] methods = clazz.getMethods();
|
||||
for (Method method : methods) {
|
||||
String name = method.getName();
|
||||
if (methodName.equals(name)) {
|
||||
annotation = method.getAnnotation(aClass);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (annotation == null) {
|
||||
annotation = clazz.getAnnotation(aClass);
|
||||
}
|
||||
return annotation;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright (c) 2011-2020, baomidou (jobob@qq.com).
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
* <p>
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.pigcloud.pig.common.datascope.util;
|
||||
|
||||
import org.apache.ibatis.executor.Executor;
|
||||
import org.apache.ibatis.executor.parameter.ParameterHandler;
|
||||
import org.apache.ibatis.executor.statement.StatementHandler;
|
||||
import org.apache.ibatis.mapping.BoundSql;
|
||||
import org.apache.ibatis.mapping.MappedStatement;
|
||||
import org.apache.ibatis.mapping.ParameterMapping;
|
||||
import org.apache.ibatis.reflection.MetaObject;
|
||||
import org.apache.ibatis.reflection.SystemMetaObject;
|
||||
import org.apache.ibatis.session.Configuration;
|
||||
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 插件工具类
|
||||
*
|
||||
* @author TaoYu , hubin
|
||||
* @since 2017-06-20
|
||||
*/
|
||||
public abstract class PluginUtils {
|
||||
|
||||
public static final String DELEGATE_BOUNDSQL_SQL = "delegate.boundSql.sql";
|
||||
|
||||
/**
|
||||
* 获得真正的处理对象,可能多层代理.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T realTarget(Object target) {
|
||||
if (Proxy.isProxyClass(target.getClass())) {
|
||||
MetaObject metaObject = SystemMetaObject.forObject(target);
|
||||
return realTarget(metaObject.getValue("h.target"));
|
||||
}
|
||||
return (T) target;
|
||||
}
|
||||
|
||||
/**
|
||||
* 给 BoundSql 设置 additionalParameters
|
||||
* @param boundSql BoundSql
|
||||
* @param additionalParameters additionalParameters
|
||||
*/
|
||||
public static void setAdditionalParameter(BoundSql boundSql, Map<String, Object> additionalParameters) {
|
||||
additionalParameters.forEach(boundSql::setAdditionalParameter);
|
||||
}
|
||||
|
||||
public static MPBoundSql mpBoundSql(BoundSql boundSql) {
|
||||
return new MPBoundSql(boundSql);
|
||||
}
|
||||
|
||||
public static MPStatementHandler mpStatementHandler(StatementHandler statementHandler) {
|
||||
statementHandler = realTarget(statementHandler);
|
||||
MetaObject object = SystemMetaObject.forObject(statementHandler);
|
||||
return new MPStatementHandler(SystemMetaObject.forObject(object.getValue("delegate")));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link org.apache.ibatis.executor.statement.BaseStatementHandler}
|
||||
*/
|
||||
public static class MPStatementHandler {
|
||||
|
||||
private final MetaObject statementHandler;
|
||||
|
||||
MPStatementHandler(MetaObject statementHandler) {
|
||||
this.statementHandler = statementHandler;
|
||||
}
|
||||
|
||||
public ParameterHandler parameterHandler() {
|
||||
return get("parameterHandler");
|
||||
}
|
||||
|
||||
public MappedStatement mappedStatement() {
|
||||
return get("mappedStatement");
|
||||
}
|
||||
|
||||
public Executor executor() {
|
||||
return get("executor");
|
||||
}
|
||||
|
||||
public MPBoundSql mPBoundSql() {
|
||||
return new MPBoundSql(boundSql());
|
||||
}
|
||||
|
||||
public BoundSql boundSql() {
|
||||
return get("boundSql");
|
||||
}
|
||||
|
||||
public Configuration configuration() {
|
||||
return get("configuration");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T get(String property) {
|
||||
return (T) statementHandler.getValue(property);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link BoundSql}
|
||||
*/
|
||||
public static class MPBoundSql {
|
||||
|
||||
private final MetaObject boundSql;
|
||||
|
||||
private final BoundSql delegate;
|
||||
|
||||
MPBoundSql(BoundSql boundSql) {
|
||||
this.delegate = boundSql;
|
||||
this.boundSql = SystemMetaObject.forObject(boundSql);
|
||||
}
|
||||
|
||||
public String sql() {
|
||||
return delegate.getSql();
|
||||
}
|
||||
|
||||
public void sql(String sql) {
|
||||
boundSql.setValue("sql", sql);
|
||||
}
|
||||
|
||||
public List<ParameterMapping> parameterMappings() {
|
||||
List<ParameterMapping> parameterMappings = delegate.getParameterMappings();
|
||||
return new ArrayList<>(parameterMappings);
|
||||
}
|
||||
|
||||
public void parameterMappings(List<ParameterMapping> parameterMappings) {
|
||||
boundSql.setValue("parameterMappings", Collections.unmodifiableList(parameterMappings));
|
||||
}
|
||||
|
||||
public Object parameterObject() {
|
||||
return get("parameterObject");
|
||||
}
|
||||
|
||||
public Map<String, Object> additionalParameters() {
|
||||
return get("additionalParameters");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T get(String property) {
|
||||
return (T) boundSql.getValue(property);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
com.pigcloud.pig.common.datascope.DataScopeAutoConfiguration
|
||||
|
@ -0,0 +1,75 @@
|
||||
package com.pigcloud.pig.common.datascope.test;
|
||||
|
||||
import com.pigcloud.pig.common.datascope.DataScope;
|
||||
import com.pigcloud.pig.common.datascope.handler.DefaultDataPermissionHandler;
|
||||
import com.pigcloud.pig.common.datascope.handler.DataPermissionHandler;
|
||||
import com.pigcloud.pig.common.datascope.processor.DataScopeSqlProcessor;
|
||||
import net.sf.jsqlparser.expression.Alias;
|
||||
import net.sf.jsqlparser.expression.Expression;
|
||||
import net.sf.jsqlparser.expression.StringValue;
|
||||
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
|
||||
import net.sf.jsqlparser.expression.operators.relational.InExpression;
|
||||
import net.sf.jsqlparser.schema.Column;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author Hccake 2020/9/28
|
||||
* @version 1.0
|
||||
*/
|
||||
class SqlParseTest {
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
DataScope dataScope = new DataScope() {
|
||||
final String columnId = "order_id";
|
||||
|
||||
@Override
|
||||
public String getResource() {
|
||||
return "order";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getTableNames() {
|
||||
Set<String> tableNames = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
|
||||
tableNames.addAll(Arrays.asList("t_order", "t_order_info"));
|
||||
return tableNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expression getExpression(String tableName, Alias tableAlias) {
|
||||
Column column = new Column(tableAlias == null ? columnId : tableAlias.getName() + "." + columnId);
|
||||
ExpressionList expressionList = new ExpressionList();
|
||||
expressionList.setExpressions(Arrays.asList(new StringValue("1"), new StringValue("2")));
|
||||
return new InExpression(column, expressionList);
|
||||
}
|
||||
};
|
||||
|
||||
List<DataScope> dataScopes = new ArrayList<>();
|
||||
dataScopes.add(dataScope);
|
||||
|
||||
DataPermissionHandler dataPermissionHandler = new DefaultDataPermissionHandler(dataScopes) {
|
||||
@Override
|
||||
public boolean ignorePermissionControl(String mappedStatementId) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
DataScopeSqlProcessor dataScopeSqlProcessor = new DataScopeSqlProcessor();
|
||||
|
||||
// DataScopeHolder.putDataScope("order", dataScope);
|
||||
|
||||
String sql = "select o.order_id,o.order_name,oi.order_price "
|
||||
+ "from t_ORDER o left join t_order_info oi on o.order_id = oi.order_id "
|
||||
+ "where oi.order_price > 100";
|
||||
|
||||
String parseSql = dataScopeSqlProcessor.parserSingle(sql, dataPermissionHandler.dataScopes());
|
||||
System.out.println(parseSql);
|
||||
|
||||
String trueSql = "SELECT o.order_id, o.order_name, oi.order_price FROM t_ORDER o LEFT JOIN t_order_info oi ON o.order_id = oi.order_id AND oi.order_id IN ('1', '2') WHERE oi.order_price > 100 AND o.order_id IN ('1', '2')";
|
||||
Assert.isTrue(trueSql.equals(parseSql), "sql 数据权限解析异常");
|
||||
}
|
||||
|
||||
}
|
@ -16,7 +16,9 @@
|
||||
|
||||
package com.pig4cloud.pig.common.security.component;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pig.common.security.datascope.UserDataScope;
|
||||
import com.pig4cloud.pig.common.security.service.PigUser;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
@ -25,9 +27,7 @@ import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.oauth2.provider.token.UserAuthenticationConverter;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
@ -69,7 +69,19 @@ public class PigUserAuthenticationConverter implements UserAuthenticationConvert
|
||||
String username = (String) map.get(SecurityConstants.DETAILS_USERNAME);
|
||||
Integer id = (Integer) map.get(SecurityConstants.DETAILS_USER_ID);
|
||||
Integer deptId = (Integer) map.get(SecurityConstants.DETAILS_DEPT_ID);
|
||||
PigUser user = new PigUser(id, deptId, username, N_A, true, true, true, true, authorities);
|
||||
|
||||
UserDataScope userDataScope = new UserDataScope();
|
||||
Object value = map.get(SecurityConstants.DETAILS_USER_DATA_SCOPE);
|
||||
if (value != null) {
|
||||
Map<String, ?> userDataScopeMap = (Map) value;
|
||||
userDataScope.setAllScope(MapUtil.getBool(userDataScopeMap, "allScope"));
|
||||
userDataScope.setOnlySelf(MapUtil.getBool(userDataScopeMap, "onlySelf"));
|
||||
|
||||
userDataScope.setScopeUserIds(new HashSet<>((List) userDataScopeMap.get("scopeUserIds")));
|
||||
userDataScope.setScopeUserIds(new HashSet<>((List) userDataScopeMap.get("scopeUserIds")));
|
||||
userDataScope.setScopeDeptIds(new HashSet<>((List) userDataScopeMap.get("scopeDeptIds")));
|
||||
}
|
||||
PigUser user = new PigUser(id, deptId, username, N_A, true, true, true, true, authorities, userDataScope);
|
||||
return new UsernamePasswordAuthenticationToken(user, N_A, authorities);
|
||||
}
|
||||
return null;
|
||||
|
@ -0,0 +1,21 @@
|
||||
package com.pig4cloud.pig.common.security.datascope;
|
||||
|
||||
import com.pig4cloud.pig.admin.api.entity.SysRole;
|
||||
import com.pig4cloud.pig.admin.api.entity.SysUser;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author hccake
|
||||
*/
|
||||
public interface DataScopeProcessor {
|
||||
|
||||
/**
|
||||
* 根据用户和角色信息,合并用户最终的数据权限
|
||||
* @param user 用户
|
||||
* @param roles 角色列表
|
||||
* @return UserDataScope
|
||||
*/
|
||||
UserDataScope mergeScopeType(SysUser user, List<SysRole> roles);
|
||||
|
||||
}
|
@ -0,0 +1,119 @@
|
||||
package com.pig4cloud.pig.common.security.datascope;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.pig4cloud.pig.admin.api.entity.SysRole;
|
||||
import com.pig4cloud.pig.admin.api.entity.SysUser;
|
||||
import com.pig4cloud.pig.admin.api.feign.RemoteDeptService;
|
||||
import com.pig4cloud.pig.admin.api.feign.RemoteUserService;
|
||||
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pig.common.core.constant.enums.DataScopeTypeEnum;
|
||||
import com.pig4cloud.pig.common.core.util.R;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author hccake
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class PigDataScopeProcessor implements DataScopeProcessor {
|
||||
|
||||
private final RemoteDeptService remoteDeptService;
|
||||
|
||||
private final RemoteUserService remoteUserService;
|
||||
|
||||
/**
|
||||
* 合并角色的数据权限类型,排除相同的权限后,大的权限覆盖小的
|
||||
* @param user 用户
|
||||
* @param roles 角色列表
|
||||
* @return List<Integer> 合并后的权限
|
||||
*/
|
||||
@Override
|
||||
public UserDataScope mergeScopeType(SysUser user, List<SysRole> roles) {
|
||||
UserDataScope userDataScope = new UserDataScope();
|
||||
Set<Integer> scopeUserIds = userDataScope.getScopeUserIds();
|
||||
Set<Integer> scopeDeptIds = userDataScope.getScopeDeptIds();
|
||||
|
||||
// 任何用户都应该可以看到自己的数据
|
||||
Integer userId = user.getUserId();
|
||||
scopeUserIds.add(userId);
|
||||
|
||||
if (CollectionUtil.isEmpty(roles)) {
|
||||
return userDataScope;
|
||||
}
|
||||
|
||||
// 根据角色的权限返回进行分组
|
||||
Map<Integer, List<SysRole>> map = roles.stream().collect(Collectors.groupingBy(SysRole::getScopeType));
|
||||
|
||||
// 如果有全部权限,直接返回
|
||||
if (map.containsKey(DataScopeTypeEnum.ALL.getType())) {
|
||||
userDataScope.setAllScope(true);
|
||||
return userDataScope;
|
||||
}
|
||||
|
||||
// 如果有本级及子级,删除其包含的几类数据权限
|
||||
boolean hasLevelChildLevel = map.containsKey(DataScopeTypeEnum.LEVEL_CHILD_LEVEL.getType());
|
||||
if (hasLevelChildLevel) {
|
||||
map.remove(DataScopeTypeEnum.SELF.getType());
|
||||
map.remove(DataScopeTypeEnum.SELF_CHILD_LEVEL.getType());
|
||||
map.remove(DataScopeTypeEnum.LEVEL.getType());
|
||||
}
|
||||
|
||||
// 是否有本人及子级权限
|
||||
boolean hasSelfChildLevel = map.containsKey(DataScopeTypeEnum.SELF_CHILD_LEVEL.getType());
|
||||
// 是否有本级权限
|
||||
boolean hasLevel = map.containsKey(DataScopeTypeEnum.LEVEL.getType());
|
||||
if (hasSelfChildLevel || hasLevel) {
|
||||
// 如果有本人及子级或者本级,都删除本人的数据权限
|
||||
map.remove(DataScopeTypeEnum.SELF.getType());
|
||||
// 如果同时拥有,则等于本级及子级权限
|
||||
if (hasSelfChildLevel && hasLevel) {
|
||||
map.remove(DataScopeTypeEnum.SELF_CHILD_LEVEL.getType());
|
||||
map.remove(DataScopeTypeEnum.LEVEL.getType());
|
||||
map.put(DataScopeTypeEnum.LEVEL_CHILD_LEVEL.getType(), new ArrayList<>());
|
||||
}
|
||||
}
|
||||
|
||||
// 这时如果仅仅只能看个人的,直接返回
|
||||
if (map.size() == 1 && map.containsKey(DataScopeTypeEnum.SELF.getType())) {
|
||||
userDataScope.setOnlySelf(true);
|
||||
return userDataScope;
|
||||
}
|
||||
|
||||
// 如果有 本级及子级 或者 本级,都把自己的 deptId 加进去
|
||||
Integer deptId = user.getDeptId();
|
||||
if (hasLevelChildLevel || hasLevel) {
|
||||
scopeDeptIds.add(deptId);
|
||||
}
|
||||
// 如果有 本级及子级 或者 本人及子级,都把下级组织的 deptId 加进去
|
||||
if (hasLevelChildLevel || hasSelfChildLevel) {
|
||||
List<Integer> childDeptIdList = remoteDeptService.listChildDeptId(deptId, SecurityConstants.FROM_IN)
|
||||
.getData();
|
||||
if (CollectionUtil.isNotEmpty(childDeptIdList)) {
|
||||
scopeDeptIds.addAll(childDeptIdList);
|
||||
}
|
||||
}
|
||||
// 自定义部门
|
||||
List<SysRole> sysRoles = map.get(DataScopeTypeEnum.CUSTOM.getType());
|
||||
if (CollectionUtil.isNotEmpty(sysRoles)) {
|
||||
Set<Integer> customDeptIds = sysRoles.stream().map(SysRole::getScopeResources).filter(Objects::nonNull)
|
||||
.flatMap(x -> Arrays.stream(x.split(StrUtil.COMMA))).map(Integer::parseInt)
|
||||
.collect(Collectors.toSet());
|
||||
scopeDeptIds.addAll(customDeptIds);
|
||||
}
|
||||
|
||||
// 把部门对应的用户id都放入集合中
|
||||
if (CollectionUtil.isNotEmpty(scopeDeptIds)) {
|
||||
R<List<Integer>> r = remoteUserService.listUserIdByDeptIds(scopeDeptIds, SecurityConstants.FROM_IN);
|
||||
List<Integer> userIds = r.getData();
|
||||
if (CollectionUtil.isNotEmpty(userIds)) {
|
||||
scopeUserIds.addAll(userIds);
|
||||
}
|
||||
}
|
||||
|
||||
return userDataScope;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package com.pig4cloud.pig.common.security.datascope;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author hccake
|
||||
*/
|
||||
@Data
|
||||
public class UserDataScope implements Serializable {
|
||||
|
||||
/**
|
||||
* 是否是全部数据权限
|
||||
*/
|
||||
private boolean allScope = false;
|
||||
|
||||
/**
|
||||
* 是否仅能看自己
|
||||
*/
|
||||
private boolean onlySelf = false;
|
||||
|
||||
/**
|
||||
* 数据权限范围,用户所能查看的用户id 集合
|
||||
*/
|
||||
private Set<Integer> scopeUserIds = new HashSet<>();
|
||||
|
||||
/**
|
||||
* 数据权限范围,用户所能查看的部门id 集合
|
||||
*/
|
||||
private Set<Integer> scopeDeptIds = new HashSet<>();
|
||||
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
package com.pig4cloud.pig.common.security.grant;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.pig4cloud.pig.common.security.service.PigUserDetailsServiceImpl;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
|
||||
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.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
/**
|
||||
* @author hzq
|
||||
* @since 2021-09-14
|
||||
*/
|
||||
@Slf4j
|
||||
public class PhoneAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
|
||||
|
||||
@Setter
|
||||
private UserDetailsService userDetailsService;
|
||||
|
||||
@Setter
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
/**
|
||||
* 校验 请求信息userDetails
|
||||
* @param userDetails 用户信息
|
||||
* @param authentication 认证信息
|
||||
* @throws AuthenticationException
|
||||
*/
|
||||
@Override
|
||||
protected void additionalAuthenticationChecks(UserDetails userDetails,
|
||||
UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
|
||||
|
||||
if (authentication.getCredentials() == null) {
|
||||
this.logger.debug("Failed to authenticate since no credentials provided");
|
||||
throw new BadCredentialsException(this.messages
|
||||
.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
|
||||
if (authentication.getCredentials() == null) {
|
||||
log.debug("Failed to authenticate since no credentials provided");
|
||||
throw new BeanCreationException("Bad credentials");
|
||||
}
|
||||
|
||||
// 手机号
|
||||
String phone = authentication.getName();
|
||||
|
||||
if (StrUtil.equals(phone, "17034642999")) {
|
||||
throw new UsernameNotFoundException(phone);
|
||||
}
|
||||
|
||||
String code = authentication.getCredentials().toString();
|
||||
UserDetails userDetails = ((PigUserDetailsServiceImpl) userDetailsService).loadUserByPhone(phone);
|
||||
PhoneAuthenticationToken token = new PhoneAuthenticationToken(userDetails);
|
||||
token.setDetails(authentication.getDetails());
|
||||
return token;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UserDetails retrieveUser(String phone, UsernamePasswordAuthenticationToken authentication)
|
||||
throws AuthenticationException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> authentication) {
|
||||
return authentication.isAssignableFrom(PhoneAuthenticationToken.class);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package com.pig4cloud.pig.common.security.grant;
|
||||
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
/**
|
||||
* @author hzq
|
||||
* @since 2021-09-14
|
||||
*/
|
||||
public class PhoneAuthenticationToken extends AbstractAuthenticationToken {
|
||||
|
||||
private final Object principal;
|
||||
|
||||
// 验证码/密码
|
||||
private String code;
|
||||
|
||||
public PhoneAuthenticationToken(String phone, String code) {
|
||||
super(AuthorityUtils.NO_AUTHORITIES);
|
||||
this.principal = phone;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public PhoneAuthenticationToken(UserDetails sysUser) {
|
||||
super(sysUser.getAuthorities());
|
||||
this.principal = sysUser;
|
||||
super.setAuthenticated(true); // 设置认证成功 必须
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrincipal() {
|
||||
return this.principal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCredentials() {
|
||||
return this.code;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
package com.pig4cloud.pig.common.security.grant;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.authentication.AccountStatusException;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
|
||||
import org.springframework.security.oauth2.provider.*;
|
||||
import org.springframework.security.oauth2.provider.token.AbstractTokenGranter;
|
||||
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 资源所有者电话令牌授予者
|
||||
*
|
||||
* @author hzq
|
||||
* @since 2021-09-14
|
||||
*/
|
||||
public class ResourceOwnerPhoneTokenGranter extends AbstractTokenGranter {
|
||||
|
||||
private static final String GRANT_TYPE = "phone";
|
||||
|
||||
private final AuthenticationManager authenticationManager;
|
||||
|
||||
public ResourceOwnerPhoneTokenGranter(AuthenticationManager authenticationManager,
|
||||
AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService,
|
||||
OAuth2RequestFactory requestFactory) {
|
||||
this(authenticationManager, tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
|
||||
}
|
||||
|
||||
protected ResourceOwnerPhoneTokenGranter(AuthenticationManager authenticationManager,
|
||||
AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService,
|
||||
OAuth2RequestFactory requestFactory, String grantType) {
|
||||
super(tokenServices, clientDetailsService, requestFactory, grantType);
|
||||
this.authenticationManager = authenticationManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
|
||||
|
||||
Map<String, String> parameters = new LinkedHashMap<>(tokenRequest.getRequestParameters());
|
||||
|
||||
// 手机号
|
||||
String phone = parameters.get("phone");
|
||||
// 验证码/密码
|
||||
String code = parameters.get("code");
|
||||
|
||||
if (StrUtil.isBlank(phone) || StrUtil.isBlank(code)) {
|
||||
throw new InvalidGrantException("Bad credentials [ params must be has phone with code ]");
|
||||
}
|
||||
|
||||
// Protect from downstream leaks of code
|
||||
parameters.remove("code");
|
||||
|
||||
Authentication userAuth = new PhoneAuthenticationToken(phone, code);
|
||||
((AbstractAuthenticationToken) userAuth).setDetails(parameters);
|
||||
try {
|
||||
userAuth = authenticationManager.authenticate(userAuth);
|
||||
}
|
||||
catch (AccountStatusException | BadCredentialsException ase) {
|
||||
// covers expired, locked, disabled cases (mentioned in section 5.2, draft 31)
|
||||
throw new InvalidGrantException(ase.getMessage());
|
||||
}
|
||||
// If the phone/code are wrong the spec says we should send 400/invalid grant
|
||||
|
||||
if (userAuth == null || !userAuth.isAuthenticated()) {
|
||||
throw new InvalidGrantException("Could not authenticate user: " + phone);
|
||||
}
|
||||
|
||||
OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
|
||||
return new OAuth2Authentication(storedOAuth2Request, userAuth);
|
||||
}
|
||||
|
||||
}
|
@ -16,6 +16,7 @@
|
||||
|
||||
package com.pig4cloud.pig.common.security.service;
|
||||
|
||||
import com.pig4cloud.pig.common.security.datascope.UserDataScope;
|
||||
import lombok.Getter;
|
||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
@ -33,13 +34,19 @@ public class PigUser extends User {
|
||||
* 用户ID
|
||||
*/
|
||||
@Getter
|
||||
private Integer id;
|
||||
private final Integer id;
|
||||
|
||||
/**
|
||||
* 部门ID
|
||||
*/
|
||||
@Getter
|
||||
private Integer deptId;
|
||||
private final Integer deptId;
|
||||
|
||||
/**
|
||||
* 用户数据权限信息
|
||||
*/
|
||||
@Getter
|
||||
private final UserDataScope userDataScope;
|
||||
|
||||
/**
|
||||
* Construct the <code>User</code> with the details required by
|
||||
@ -62,10 +69,11 @@ public class PigUser extends User {
|
||||
*/
|
||||
public PigUser(Integer id, Integer deptId, String username, String password, boolean enabled,
|
||||
boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked,
|
||||
Collection<? extends GrantedAuthority> authorities) {
|
||||
Collection<? extends GrantedAuthority> authorities, UserDataScope userDataScope) {
|
||||
super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
|
||||
this.id = id;
|
||||
this.deptId = deptId;
|
||||
this.userDataScope = userDataScope;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,6 +25,8 @@ import com.pig4cloud.pig.common.core.constant.CacheConstants;
|
||||
import com.pig4cloud.pig.common.core.constant.CommonConstants;
|
||||
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pig.common.core.util.R;
|
||||
import com.pig4cloud.pig.common.security.datascope.DataScopeProcessor;
|
||||
import com.pig4cloud.pig.common.security.datascope.UserDataScope;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@ -45,7 +47,7 @@ import java.util.Set;
|
||||
/**
|
||||
* 用户详细信息
|
||||
*
|
||||
* @author lengleng
|
||||
* @author lengleng hccake
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@ -56,6 +58,8 @@ public class PigUserDetailsServiceImpl implements UserDetailsService {
|
||||
|
||||
private final CacheManager cacheManager;
|
||||
|
||||
private final DataScopeProcessor dataScopeProcessor;
|
||||
|
||||
/**
|
||||
* 用户密码登录
|
||||
* @param username 用户名
|
||||
@ -77,10 +81,21 @@ public class PigUserDetailsServiceImpl implements UserDetailsService {
|
||||
return userDetails;
|
||||
}
|
||||
|
||||
/**
|
||||
* 手机号码登录
|
||||
* @param phone 手机号码
|
||||
* @return 用户信息
|
||||
*/
|
||||
public UserDetails loadUserByPhone(String phone) {
|
||||
R<UserInfo> result = remoteUserService.infoByPhone(phone, SecurityConstants.FROM_IN);
|
||||
UserDetails userDetails = getUserDetails(result);
|
||||
return userDetails;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建userdetails
|
||||
* @param result 用户信息
|
||||
* @return
|
||||
* @return UserDetails
|
||||
*/
|
||||
private UserDetails getUserDetails(R<UserInfo> result) {
|
||||
if (result == null || result.getData() == null) {
|
||||
@ -100,10 +115,14 @@ public class PigUserDetailsServiceImpl implements UserDetailsService {
|
||||
.createAuthorityList(dbAuthsSet.toArray(new String[0]));
|
||||
SysUser user = info.getSysUser();
|
||||
|
||||
// 数据权限填充
|
||||
UserDataScope userDataScope = dataScopeProcessor.mergeScopeType(user, info.getRoleList());
|
||||
|
||||
// 构造security用户
|
||||
return new PigUser(user.getUserId(), user.getDeptId(), user.getUsername(),
|
||||
SecurityConstants.BCRYPT + user.getPassword(),
|
||||
StrUtil.equals(user.getLockFlag(), CommonConstants.STATUS_NORMAL), true, true, true, authorities);
|
||||
StrUtil.equals(user.getLockFlag(), CommonConstants.STATUS_NORMAL), true, true, true, authorities,
|
||||
userDataScope);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
com.pig4cloud.pig.common.security.service.PigUserDetailsServiceImpl,\
|
||||
com.pig4cloud.pig.common.security.component.PigSecurityInnerAspect
|
||||
com.pig4cloud.pig.common.security.component.PigSecurityInnerAspect,\
|
||||
com.pig4cloud.pig.common.security.datascope.PigDataScopeProcessor
|
||||
|
||||
|
@ -40,5 +40,6 @@
|
||||
<module>pig-common-feign</module>
|
||||
<module>pig-common-swagger</module>
|
||||
<module>pig-common-test</module>
|
||||
</modules>
|
||||
<module>pig-common-datascope</module>
|
||||
</modules>
|
||||
</project>
|
||||
|
@ -1,19 +1,15 @@
|
||||
FROM moxm/java:1.8-full as builder
|
||||
WORKDIR /build
|
||||
ARG JAR_FILE=target/pig-gateway.jar
|
||||
COPY ${JAR_FILE} app.jar
|
||||
RUN java -Djarmode=layertools -jar app.jar extract && rm app.jar
|
||||
|
||||
FROM moxm/java:1.8-full
|
||||
LABEL maintainer="jclazz@outlook.com"
|
||||
ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms128m -Xmx256m -Djava.security.egd=file:/dev/./urandom"
|
||||
WORKDIR pig-gateway
|
||||
|
||||
COPY --from=builder /build/dependencies/ ./
|
||||
COPY --from=builder /build/snapshot-dependencies/ ./
|
||||
COPY --from=builder /build/spring-boot-loader/ ./
|
||||
COPY --from=builder /build/application/ ./
|
||||
RUN mkdir -p /pig-gateway
|
||||
|
||||
WORKDIR /pig-gateway
|
||||
|
||||
ARG JAR_FILE=target/pig-gateway.jar
|
||||
|
||||
COPY ${JAR_FILE} app.jar
|
||||
|
||||
EXPOSE 9999
|
||||
|
||||
CMD sleep 60; java $JAVA_OPTS org.springframework.boot.loader.JarLauncher
|
||||
ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms128m -Xmx256m -Djava.security.egd=file:/dev/./urandom"
|
||||
|
||||
CMD sleep 60; java -jar app.jar $JAVA_OPTS
|
||||
|
@ -68,9 +68,10 @@ public class ValidateCodeGatewayFilter extends AbstractGatewayFilterFactory<Obje
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
// 刷新token,直接向下执行
|
||||
// 刷新token,手机号登录(也可以这里进行校验) 直接向下执行
|
||||
String grantType = request.getQueryParams().getFirst("grant_type");
|
||||
if (StrUtil.equals(SecurityConstants.REFRESH_TOKEN, grantType)) {
|
||||
if (StrUtil.equals(SecurityConstants.REFRESH_TOKEN, grantType)
|
||||
|| StrUtil.equals(SecurityConstants.PHONE, grantType)) {
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
|
@ -1,20 +1,15 @@
|
||||
FROM moxm/java:1.8-full as builder
|
||||
WORKDIR /build
|
||||
ARG JAR_FILE=target/pig-register.jar
|
||||
COPY ${JAR_FILE} app.jar
|
||||
RUN java -Djarmode=layertools -jar app.jar extract && rm app.jar
|
||||
|
||||
FROM moxm/java:1.8-full
|
||||
LABEL maintainer="jclazz@outlook.com"
|
||||
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && apk --no-cache add libstdc++
|
||||
ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms128m -Xmx256m -Djava.security.egd=file:/dev/./urandom"
|
||||
WORKDIR pig-register
|
||||
|
||||
COPY --from=builder /build/dependencies/ ./
|
||||
COPY --from=builder /build/snapshot-dependencies/ ./
|
||||
COPY --from=builder /build/spring-boot-loader/ ./
|
||||
COPY --from=builder /build/application/ ./
|
||||
RUN mkdir -p /pig-register
|
||||
|
||||
WORKDIR /pig-register
|
||||
|
||||
ARG JAR_FILE=target/pig-register.jar
|
||||
|
||||
COPY ${JAR_FILE} app.jar
|
||||
|
||||
EXPOSE 8848
|
||||
|
||||
CMD sleep 30; java $JAVA_OPTS org.springframework.boot.loader.JarLauncher
|
||||
ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms128m -Xmx256m -Djava.security.egd=file:/dev/./urandom"
|
||||
|
||||
CMD sleep 30; java -jar app.jar $JAVA_OPTS
|
||||
|
@ -16,10 +16,14 @@
|
||||
|
||||
package com.pig4cloud.pig.admin.api.dto;
|
||||
|
||||
import com.pig4cloud.pig.admin.api.entity.SysMenu;
|
||||
import com.pig4cloud.pig.admin.api.entity.SysRole;
|
||||
import com.pig4cloud.pig.admin.api.entity.SysUser;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
@ -46,4 +50,9 @@ public class UserInfo implements Serializable {
|
||||
*/
|
||||
private Integer[] roles;
|
||||
|
||||
/**
|
||||
* 角色集合
|
||||
*/
|
||||
private List<SysRole> roleList;
|
||||
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@ -56,6 +57,13 @@ public class SysRole extends BaseEntity {
|
||||
@ApiModelProperty(value = "角色描述")
|
||||
private String roleDesc;
|
||||
|
||||
@NotNull(message = "数据范围类型 不能为null")
|
||||
@ApiModelProperty(value = "数据范围类型")
|
||||
private Integer scopeType;
|
||||
|
||||
@ApiModelProperty(value = "数据范围资源")
|
||||
private String scopeResources;
|
||||
|
||||
/**
|
||||
* 删除标识(0-正常,1-删除)
|
||||
*/
|
||||
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pig.admin.api.feign;
|
||||
|
||||
import com.pig4cloud.pig.admin.api.feign.factory.RemoteDeptServiceFallbackFactory;
|
||||
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pig.common.core.constant.ServiceNameConstants;
|
||||
import com.pig4cloud.pig.common.core.util.R;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author hccake
|
||||
*/
|
||||
@FeignClient(contextId = "remoteDeptService", value = ServiceNameConstants.UMPS_SERVICE,
|
||||
fallbackFactory = RemoteDeptServiceFallbackFactory.class)
|
||||
public interface RemoteDeptService {
|
||||
|
||||
/**
|
||||
* 查收子级id列表
|
||||
* @return 返回子级id列表
|
||||
*/
|
||||
@GetMapping("/dept/child-id/{deptId}")
|
||||
R<List<Integer>> listChildDeptId(@PathVariable("deptId") Integer deptId,
|
||||
@RequestHeader(SecurityConstants.FROM) String from);
|
||||
|
||||
}
|
@ -25,6 +25,10 @@ import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
@ -44,11 +48,22 @@ public interface RemoteUserService {
|
||||
R<UserInfo> info(@PathVariable("username") String username, @RequestHeader(SecurityConstants.FROM) String from);
|
||||
|
||||
/**
|
||||
* 通过社交账号查询用户、角色信息
|
||||
* @param inStr appid@code
|
||||
* @return
|
||||
* 通过手机号码查询用户、角色信息
|
||||
* @param phone 手机号码
|
||||
* @param from 调用标志
|
||||
* @return R
|
||||
*/
|
||||
@GetMapping("/social/info/{inStr}")
|
||||
R<UserInfo> social(@PathVariable("inStr") String inStr);
|
||||
@GetMapping("/mobile/{phone}")
|
||||
R<UserInfo> infoByPhone(@PathVariable("phone") String phone, @RequestHeader(SecurityConstants.FROM) String from);
|
||||
|
||||
/**
|
||||
* 根据部门id,查询对应的用户 id 集合
|
||||
* @param deptIds 部门id 集合
|
||||
* @param from 调用标志
|
||||
* @return 用户 id 集合
|
||||
*/
|
||||
@GetMapping("/user/ids")
|
||||
R<List<Integer>> listUserIdByDeptIds(@RequestParam("deptIds") Set<Integer> deptIds,
|
||||
@RequestHeader(SecurityConstants.FROM) String from);
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pig.admin.api.feign.factory;
|
||||
|
||||
import com.pig4cloud.pig.admin.api.feign.RemoteDeptService;
|
||||
import com.pig4cloud.pig.admin.api.feign.fallback.RemoteDeptServiceFallbackImpl;
|
||||
import org.springframework.cloud.openfeign.FallbackFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author hccake
|
||||
*/
|
||||
@Component
|
||||
public class RemoteDeptServiceFallbackFactory implements FallbackFactory<RemoteDeptService> {
|
||||
|
||||
@Override
|
||||
public RemoteDeptService create(Throwable throwable) {
|
||||
RemoteDeptServiceFallbackImpl remoteDeptServiceFallback = new RemoteDeptServiceFallbackImpl();
|
||||
remoteDeptServiceFallback.setCause(throwable);
|
||||
return remoteDeptServiceFallback;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pig.admin.api.feign.fallback;
|
||||
|
||||
import com.pig4cloud.pig.admin.api.dto.UserInfo;
|
||||
import com.pig4cloud.pig.admin.api.feign.RemoteDeptService;
|
||||
import com.pig4cloud.pig.admin.api.feign.RemoteUserService;
|
||||
import com.pig4cloud.pig.common.core.util.R;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author hccake
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class RemoteDeptServiceFallbackImpl implements RemoteDeptService {
|
||||
|
||||
@Setter
|
||||
private Throwable cause;
|
||||
|
||||
@Override
|
||||
public R<List<Integer>> listChildDeptId(Integer deptId, String from) {
|
||||
log.error("[listChildDeptId] feign 查询子级部门id列表失败", cause);
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -23,6 +23,9 @@ import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2019/2/1
|
||||
@ -47,13 +50,20 @@ public class RemoteUserServiceFallbackImpl implements RemoteUserService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过社交账号查询用户、角色信息
|
||||
* @param inStr appid@code
|
||||
* @return
|
||||
* 通过手机号码查询用户、角色信息
|
||||
* @param phone 手机号码
|
||||
* @param from 调用标志
|
||||
* @return R
|
||||
*/
|
||||
@Override
|
||||
public R<UserInfo> social(String inStr) {
|
||||
log.error("feign 查询用户信息失败:{}", inStr, cause);
|
||||
public R<UserInfo> infoByPhone(String phone, String from) {
|
||||
log.error("feign 查询用户信息失败手机号码:{}", phone, cause);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<List<Integer>> listUserIdByDeptIds(Set<Integer> deptIds, String from) {
|
||||
log.error("feign 根据部门ids查询用户Id集合失败:{}", deptIds, cause);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pig.admin.api.vo;
|
||||
|
||||
import com.pig4cloud.pig.admin.api.entity.SysUser;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2019/2/1
|
||||
* <p>
|
||||
* commit('SET_ROLES', data) commit('SET_NAME', data) commit('SET_AVATAR', data)
|
||||
* commit('SET_INTRODUCTION', data) commit('SET_PERMISSIONS', data)
|
||||
*/
|
||||
@Data
|
||||
public class UserInfoVO implements Serializable {
|
||||
|
||||
/**
|
||||
* 用户基本信息
|
||||
*/
|
||||
private SysUser sysUser;
|
||||
|
||||
/**
|
||||
* 权限标识集合
|
||||
*/
|
||||
private String[] permissions;
|
||||
|
||||
/**
|
||||
* 角色集合
|
||||
*/
|
||||
private Integer[] roles;
|
||||
|
||||
}
|
@ -2,6 +2,8 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
com.pig4cloud.pig.admin.api.feign.fallback.RemoteUserServiceFallbackImpl,\
|
||||
com.pig4cloud.pig.admin.api.feign.fallback.RemoteLogServiceFallbackImpl,\
|
||||
com.pig4cloud.pig.admin.api.feign.fallback.RemoteTokenServiceFallbackImpl,\
|
||||
com.pig4cloud.pig.admin.api.feign.fallback.RemoteDeptServiceFallbackImpl,\
|
||||
com.pig4cloud.pig.admin.api.feign.factory.RemoteUserServiceFallbackFactory,\
|
||||
com.pig4cloud.pig.admin.api.feign.factory.RemoteLogServiceFallbackFactory,\
|
||||
com.pig4cloud.pig.admin.api.feign.factory.RemoteTokenServiceFallbackFactory
|
||||
com.pig4cloud.pig.admin.api.feign.factory.RemoteTokenServiceFallbackFactory,\
|
||||
com.pig4cloud.pig.admin.api.feign.factory.RemoteDeptServiceFallbackFactory
|
||||
|
@ -1,19 +1,15 @@
|
||||
FROM moxm/java:1.8-full as builder
|
||||
WORKDIR /build
|
||||
ARG JAR_FILE=target/pig-upms-biz.jar
|
||||
COPY ${JAR_FILE} app.jar
|
||||
RUN java -Djarmode=layertools -jar app.jar extract && rm app.jar
|
||||
|
||||
FROM moxm/java:1.8-full
|
||||
LABEL maintainer="jclazz@outlook.com"
|
||||
ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms128m -Xmx256m -Djava.security.egd=file:/dev/./urandom"
|
||||
WORKDIR pig-upms
|
||||
|
||||
COPY --from=builder /build/dependencies/ ./
|
||||
COPY --from=builder /build/snapshot-dependencies/ ./
|
||||
COPY --from=builder /build/spring-boot-loader/ ./
|
||||
COPY --from=builder /build/application/ ./
|
||||
RUN mkdir -p /pig-upms-biz
|
||||
|
||||
WORKDIR /pig-upms-biz
|
||||
|
||||
ARG JAR_FILE=target/pig-upms-biz.jar
|
||||
|
||||
COPY ${JAR_FILE} app.jar
|
||||
|
||||
EXPOSE 4000
|
||||
|
||||
CMD sleep 60; java $JAVA_OPTS org.springframework.boot.loader.JarLauncher
|
||||
ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms128m -Xmx256m -Djava.security.egd=file:/dev/./urandom"
|
||||
|
||||
CMD sleep 60; java -jar app.jar $JAVA_OPTS
|
||||
|
@ -60,6 +60,11 @@
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pig-common-mybatis</artifactId>
|
||||
</dependency>
|
||||
<!--数据权限 模块-->
|
||||
<dependency>
|
||||
<groupId>com.pig4cloud</groupId>
|
||||
<artifactId>pig-common-datascope</artifactId>
|
||||
</dependency>
|
||||
<!--注册中心客户端-->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
|
@ -20,6 +20,7 @@ import com.pig4cloud.pig.admin.api.entity.SysDept;
|
||||
import com.pig4cloud.pig.admin.service.SysDeptService;
|
||||
import com.pig4cloud.pig.common.core.util.R;
|
||||
import com.pig4cloud.pig.common.log.annotation.SysLog;
|
||||
import com.pig4cloud.pig.common.security.annotation.Inner;
|
||||
import io.swagger.annotations.Api;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
@ -27,6 +28,7 @@ import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@ -121,4 +123,14 @@ public class DeptController {
|
||||
return R.ok(sysDeptService.getOne(new QueryWrapper<>(condition)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查收子级id列表
|
||||
* @return 返回子级id列表
|
||||
*/
|
||||
@Inner
|
||||
@GetMapping(value = "/child-id/{deptId}")
|
||||
public R<List<Integer>> listChildDeptId(@PathVariable Integer deptId) {
|
||||
return R.ok(sysDeptService.listChildDeptId(deptId));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,51 @@
|
||||
package com.pig4cloud.pig.admin.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.pig4cloud.pig.admin.api.entity.SysUser;
|
||||
import com.pig4cloud.pig.admin.service.MobileService;
|
||||
import com.pig4cloud.pig.admin.service.SysUserService;
|
||||
import com.pig4cloud.pig.common.core.util.R;
|
||||
import com.pig4cloud.pig.common.security.annotation.Inner;
|
||||
import io.swagger.annotations.Api;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2021/9/16 移动端登录
|
||||
*/
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
@RequestMapping("/mobile")
|
||||
@Api(value = "mobile", tags = "手机管理模块")
|
||||
public class MobileController {
|
||||
|
||||
private final MobileService mobileService;
|
||||
|
||||
private final SysUserService userService;
|
||||
|
||||
@Inner(value = false)
|
||||
@GetMapping("/{mobile}")
|
||||
public R sendSmsCode(@PathVariable String mobile) {
|
||||
return mobileService.sendSmsCode(mobile);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定用户全部信息
|
||||
* @param phone 手机号
|
||||
* @return 用户信息
|
||||
*/
|
||||
@Inner
|
||||
@GetMapping("/{phone}")
|
||||
public R infoByPhone(@PathVariable String phone) {
|
||||
SysUser user = userService.getOne(Wrappers.<SysUser>query().lambda().eq(SysUser::getPhone, phone));
|
||||
if (user == null) {
|
||||
return R.failed(String.format("用户信息为空 %s", phone));
|
||||
}
|
||||
return R.ok(userService.getUserInfo(user));
|
||||
}
|
||||
|
||||
}
|
@ -20,8 +20,10 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.pig4cloud.pig.admin.api.dto.UserDTO;
|
||||
import com.pig4cloud.pig.admin.api.dto.UserInfo;
|
||||
import com.pig4cloud.pig.admin.api.entity.SysUser;
|
||||
import com.pig4cloud.pig.admin.api.vo.UserExcelVO;
|
||||
import com.pig4cloud.pig.admin.api.vo.UserInfoVO;
|
||||
import com.pig4cloud.pig.admin.service.SysUserService;
|
||||
import com.pig4cloud.pig.common.core.util.R;
|
||||
import com.pig4cloud.pig.common.log.annotation.SysLog;
|
||||
@ -33,10 +35,19 @@ import io.swagger.annotations.Api;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
@ -61,7 +72,12 @@ public class UserController {
|
||||
if (user == null) {
|
||||
return R.failed("获取当前用户信息失败");
|
||||
}
|
||||
return R.ok(userService.getUserInfo(user));
|
||||
UserInfo userInfo = userService.getUserInfo(user);
|
||||
UserInfoVO vo = new UserInfoVO();
|
||||
vo.setSysUser(userInfo.getSysUser());
|
||||
vo.setRoles(userInfo.getRoles());
|
||||
vo.setPermissions(userInfo.getPermissions());
|
||||
return R.ok(vo);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -78,6 +94,17 @@ public class UserController {
|
||||
return R.ok(userService.getUserInfo(user));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据部门id,查询对应的用户 id 集合
|
||||
* @param deptIds 部门id 集合
|
||||
* @return 用户 id 集合
|
||||
*/
|
||||
@Inner
|
||||
@GetMapping("/ids")
|
||||
public R<List<Integer>> listUserIdByDeptIds(@RequestParam("deptIds") Set<Integer> deptIds) {
|
||||
return R.ok(userService.listUserIdByDeptIds(deptIds));
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过ID查询用户信息
|
||||
* @param id ID
|
||||
|
@ -0,0 +1,115 @@
|
||||
package com.pig4cloud.pig.admin.datascope;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import com.pig4cloud.pig.common.security.datascope.UserDataScope;
|
||||
import com.pig4cloud.pig.common.security.service.PigUser;
|
||||
import com.pig4cloud.pig.common.security.util.SecurityUtils;
|
||||
import com.pigcloud.pig.common.datascope.DataScope;
|
||||
import net.sf.jsqlparser.expression.Alias;
|
||||
import net.sf.jsqlparser.expression.Expression;
|
||||
import net.sf.jsqlparser.expression.LongValue;
|
||||
import net.sf.jsqlparser.expression.Parenthesis;
|
||||
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
|
||||
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
|
||||
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
|
||||
import net.sf.jsqlparser.expression.operators.relational.InExpression;
|
||||
import net.sf.jsqlparser.schema.Column;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 数据权限控制,要求表至少有个 user_id 字段
|
||||
*
|
||||
* @author hccake
|
||||
*/
|
||||
@Component
|
||||
public class PigDataScope implements DataScope {
|
||||
|
||||
private static final String USER_ID = "user_id";
|
||||
|
||||
private static final String DEPT_ID = "dept_id";
|
||||
|
||||
/**
|
||||
* 拥有 dept_id 字段的表名集合
|
||||
*/
|
||||
private static final Set<String> DEPT_ID_TABLE_NAMES = CollectionUtil.newHashSet("sys_user");
|
||||
|
||||
/**
|
||||
* 仅拥有 user_id 字段的表名集合
|
||||
*/
|
||||
private static final Set<String> USER_ID_TABLE_NAMES = CollectionUtil.newHashSet();
|
||||
|
||||
private final Set<String> tableNames;
|
||||
|
||||
public PigDataScope() {
|
||||
Set<String> set = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
|
||||
set.addAll(DEPT_ID_TABLE_NAMES);
|
||||
set.addAll(USER_ID_TABLE_NAMES);
|
||||
this.tableNames = Collections.unmodifiableSet(set);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getResource() {
|
||||
return "userData";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getTableNames() {
|
||||
return this.tableNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expression getExpression(String tableName, Alias tableAlias) {
|
||||
// 获取当前登录用户
|
||||
PigUser user = SecurityUtils.getUser();
|
||||
if (user == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
UserDataScope userDataScope = user.getUserDataScope();
|
||||
|
||||
// 如果数据权限是全部,直接放行
|
||||
if (userDataScope.isAllScope()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 如果数据权限是仅自己
|
||||
if (userDataScope.isOnlySelf()) {
|
||||
// 数据权限规则,where user_id = xx
|
||||
return userIdEqualsToExpression(tableAlias, user.getId());
|
||||
}
|
||||
|
||||
// 如果当前表有部门字段,则优先使用部门字段控制范围
|
||||
if (DEPT_ID_TABLE_NAMES.contains(tableName)) {
|
||||
// 数据权限规则,where (user_id =xx or dept_id in ("x","y"))
|
||||
EqualsTo equalsTo = userIdEqualsToExpression(tableAlias, user.getId());
|
||||
Expression inExpression = getInExpression(tableAlias, DEPT_ID, userDataScope.getScopeDeptIds());
|
||||
// 这里一定要加括号,否则如果有其他查询条件,or 会出问题
|
||||
return new Parenthesis(new OrExpression(equalsTo, inExpression));
|
||||
}
|
||||
else {
|
||||
// 数据权限规则,where user_id in ("x","y")
|
||||
return getInExpression(tableAlias, USER_ID, userDataScope.getScopeUserIds());
|
||||
}
|
||||
}
|
||||
|
||||
private EqualsTo userIdEqualsToExpression(Alias tableAlias, Integer userId) {
|
||||
Column column = new Column(tableAlias == null ? USER_ID : tableAlias.getName() + "." + USER_ID);
|
||||
return new EqualsTo(column, new LongValue(userId));
|
||||
}
|
||||
|
||||
private Expression getInExpression(Alias tableAlias, String columnName, Set<Integer> scopeUserIds) {
|
||||
Column column = new Column(tableAlias == null ? columnName : tableAlias.getName() + "." + columnName);
|
||||
ExpressionList expressionList = new ExpressionList();
|
||||
List<Expression> list = scopeUserIds.stream().map(LongValue::new).collect(Collectors.toList());
|
||||
expressionList.setExpressions(list);
|
||||
return new InExpression(column, expressionList);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pig.admin.service;
|
||||
|
||||
import com.pig4cloud.pig.common.core.util.R;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2018/11/14
|
||||
*/
|
||||
public interface MobileService {
|
||||
|
||||
/**
|
||||
* 发送手机验证码
|
||||
* @param mobile mobile
|
||||
* @return code
|
||||
*/
|
||||
R<Boolean> sendSmsCode(String mobile);
|
||||
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pig.admin.service;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import com.baomidou.mybatisplus.core.toolkit.StringPool;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.pig4cloud.pig.admin.api.entity.SysUser;
|
||||
import com.pig4cloud.pig.admin.mapper.SysUserMapper;
|
||||
import com.pig4cloud.pig.common.core.constant.CacheConstants;
|
||||
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pig.common.core.constant.enums.LoginTypeEnum;
|
||||
import com.pig4cloud.pig.common.core.util.R;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2018/11/14
|
||||
* <p>
|
||||
* 手机登录相关业务实现
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
public class MobileServiceImpl implements MobileService {
|
||||
|
||||
private final RedisTemplate redisTemplate;
|
||||
|
||||
private final SysUserMapper userMapper;
|
||||
|
||||
/**
|
||||
* 发送手机验证码 TODO: 调用短信网关发送验证码,测试返回前端
|
||||
* @param mobile mobile
|
||||
* @return code
|
||||
*/
|
||||
@Override
|
||||
public R<Boolean> sendSmsCode(String mobile) {
|
||||
List<SysUser> userList = userMapper
|
||||
.selectList(Wrappers.<SysUser>query().lambda().eq(SysUser::getPhone, mobile));
|
||||
|
||||
if (CollUtil.isEmpty(userList)) {
|
||||
log.info("手机号未注册:{}", mobile);
|
||||
return R.ok(Boolean.FALSE, "手机号未注册");
|
||||
}
|
||||
|
||||
Object codeObj = redisTemplate.opsForValue()
|
||||
.get(CacheConstants.DEFAULT_CODE_KEY + LoginTypeEnum.SMS.getType() + StringPool.AT + mobile);
|
||||
|
||||
if (codeObj != null) {
|
||||
log.info("手机号验证码未过期:{},{}", mobile, codeObj);
|
||||
return R.ok(Boolean.FALSE, "验证码发送过频繁");
|
||||
}
|
||||
|
||||
String code = RandomUtil.randomNumbers(Integer.parseInt(SecurityConstants.CODE_SIZE));
|
||||
log.debug("手机号生成验证码成功:{},{}", mobile, code);
|
||||
redisTemplate.opsForValue().set(
|
||||
CacheConstants.DEFAULT_CODE_KEY + LoginTypeEnum.SMS.getType() + StringPool.AT + mobile, code,
|
||||
SecurityConstants.CODE_TIME, TimeUnit.SECONDS);
|
||||
return R.ok(Boolean.TRUE, code);
|
||||
}
|
||||
|
||||
}
|
@ -65,4 +65,11 @@ public interface SysDeptService extends IService<SysDept> {
|
||||
*/
|
||||
Boolean updateDeptById(SysDept sysDept);
|
||||
|
||||
/**
|
||||
* 查找指定部门的子部门id列表
|
||||
* @param deptId 部门id
|
||||
* @return List<Integer>
|
||||
*/
|
||||
List<Integer> listChildDeptId(Integer deptId);
|
||||
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ import com.pig4cloud.pig.common.core.util.R;
|
||||
import org.springframework.validation.BindingResult;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
@ -107,4 +108,11 @@ public interface SysUserService extends IService<SysUser> {
|
||||
*/
|
||||
R importUser(List<UserExcelVO> excelVOList, BindingResult bindingResult);
|
||||
|
||||
/**
|
||||
* 根据部门 id 列表查询对应的用户 id 集合
|
||||
* @param deptIds 部门 id 列表
|
||||
* @return userIdList
|
||||
*/
|
||||
List<Integer> listUserIdByDeptIds(Set<Integer> deptIds);
|
||||
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@ -108,6 +109,16 @@ public class SysDeptServiceImpl extends ServiceImpl<SysDeptMapper, SysDept> impl
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Integer> listChildDeptId(Integer deptId) {
|
||||
List<SysDeptRelation> deptRelations = sysDeptRelationService.list(Wrappers.<SysDeptRelation>lambdaQuery()
|
||||
.eq(SysDeptRelation::getAncestor, deptId).ne(SysDeptRelation::getDescendant, deptId));
|
||||
if (CollUtil.isNotEmpty(deptRelations)) {
|
||||
return deptRelations.stream().map(SysDeptRelation::getDescendant).collect(Collectors.toList());
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询全部部门树
|
||||
* @return 树
|
||||
|
@ -25,7 +25,11 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.pig4cloud.pig.admin.api.dto.UserDTO;
|
||||
import com.pig4cloud.pig.admin.api.dto.UserInfo;
|
||||
import com.pig4cloud.pig.admin.api.entity.*;
|
||||
import com.pig4cloud.pig.admin.api.entity.SysDept;
|
||||
import com.pig4cloud.pig.admin.api.entity.SysMenu;
|
||||
import com.pig4cloud.pig.admin.api.entity.SysRole;
|
||||
import com.pig4cloud.pig.admin.api.entity.SysUser;
|
||||
import com.pig4cloud.pig.admin.api.entity.SysUserRole;
|
||||
import com.pig4cloud.pig.admin.api.vo.UserExcelVO;
|
||||
import com.pig4cloud.pig.admin.api.vo.UserVO;
|
||||
import com.pig4cloud.pig.admin.mapper.SysDeptMapper;
|
||||
@ -107,9 +111,11 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||
public UserInfo getUserInfo(SysUser sysUser) {
|
||||
UserInfo userInfo = new UserInfo();
|
||||
userInfo.setSysUser(sysUser);
|
||||
// 设置角色列表
|
||||
List<SysRole> roleList = sysRoleMapper.listRolesByUserId(sysUser.getUserId());
|
||||
userInfo.setRoleList(roleList);
|
||||
// 设置角色列表 (ID)
|
||||
List<Integer> roleIds = sysRoleMapper.listRolesByUserId(sysUser.getUserId()).stream().map(SysRole::getRoleId)
|
||||
.collect(Collectors.toList());
|
||||
List<Integer> roleIds = roleList.stream().map(SysRole::getRoleId).collect(Collectors.toList());
|
||||
userInfo.setRoles(ArrayUtil.toArray(roleIds, Integer.class));
|
||||
// 设置权限列表(menu.permission)
|
||||
Set<String> permissions = sysMenuService.findMenuByRoleId(CollUtil.join(roleIds, StrUtil.COMMA)).stream()
|
||||
@ -117,6 +123,7 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||
.filter(m -> StrUtil.isNotBlank(m.getPermission())).map(SysMenu::getPermission)
|
||||
.collect(Collectors.toSet());
|
||||
userInfo.setPermissions(ArrayUtil.toArray(permissions, String.class));
|
||||
|
||||
return userInfo;
|
||||
}
|
||||
|
||||
@ -295,6 +302,13 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Integer> listUserIdByDeptIds(Set<Integer> deptIds) {
|
||||
return this.listObjs(
|
||||
Wrappers.lambdaQuery(SysUser.class).select(SysUser::getUserId).in(SysUser::getDeptId, deptIds),
|
||||
Integer.class::cast);
|
||||
}
|
||||
|
||||
/**
|
||||
* 插入excel User
|
||||
*/
|
||||
|
@ -1,19 +1,15 @@
|
||||
FROM moxm/java:1.8-full as builder
|
||||
WORKDIR /build
|
||||
ARG JAR_FILE=target/pig-codegen.jar
|
||||
COPY ${JAR_FILE} app.jar
|
||||
RUN java -Djarmode=layertools -jar app.jar extract && rm app.jar
|
||||
|
||||
FROM moxm/java:1.8-full
|
||||
LABEL maintainer="jclazz@outlook.com"
|
||||
ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms128m -Xmx256m -Djava.security.egd=file:/dev/./urandom"
|
||||
WORKDIR pig-codegen
|
||||
|
||||
COPY --from=builder /build/dependencies/ ./
|
||||
COPY --from=builder /build/snapshot-dependencies/ ./
|
||||
COPY --from=builder /build/spring-boot-loader/ ./
|
||||
COPY --from=builder /build/application/ ./
|
||||
RUN mkdir -p /pig-codegen
|
||||
|
||||
WORKDIR /pig-codegen
|
||||
|
||||
ARG JAR_FILE=target/pig-codegen.jar
|
||||
|
||||
COPY ${JAR_FILE} app.jar
|
||||
|
||||
EXPOSE 5002
|
||||
|
||||
CMD sleep 60; java $JAVA_OPTS org.springframework.boot.loader.JarLauncher
|
||||
ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms128m -Xmx256m -Djava.security.egd=file:/dev/./urandom"
|
||||
|
||||
CMD sleep 60; java -jar app.jar $JAVA_OPTS
|
||||
|
@ -1,19 +1,15 @@
|
||||
FROM moxm/java:1.8-full as builder
|
||||
WORKDIR /build
|
||||
ARG JAR_FILE=target/pig-monitor.jar
|
||||
COPY ${JAR_FILE} app.jar
|
||||
RUN java -Djarmode=layertools -jar app.jar extract && rm app.jar
|
||||
|
||||
FROM moxm/java:1.8-full
|
||||
LABEL maintainer="jclazz@outlook.com"
|
||||
ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms128m -Xmx256m -Djava.security.egd=file:/dev/./urandom"
|
||||
WORKDIR pig-monitor
|
||||
|
||||
COPY --from=builder /build/dependencies/ ./
|
||||
COPY --from=builder /build/snapshot-dependencies/ ./
|
||||
COPY --from=builder /build/spring-boot-loader/ ./
|
||||
COPY --from=builder /build/application/ ./
|
||||
RUN mkdir -p /pig-monitor
|
||||
|
||||
WORKDIR /pig-monitor
|
||||
|
||||
ARG JAR_FILE=target/pig-monitor.jar
|
||||
|
||||
COPY ${JAR_FILE} app.jar
|
||||
|
||||
EXPOSE 5001
|
||||
|
||||
CMD sleep 60; java $JAVA_OPTS org.springframework.boot.loader.JarLauncher
|
||||
ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms128m -Xmx256m -Djava.security.egd=file:/dev/./urandom"
|
||||
|
||||
CMD sleep 60; java -jar app.jar $JAVA_OPTS
|
||||
|
@ -1,19 +1,15 @@
|
||||
FROM moxm/java:1.8-full as builder
|
||||
WORKDIR /build
|
||||
ARG JAR_FILE=target/pig-sentinel-dashboard.jar
|
||||
COPY ${JAR_FILE} app.jar
|
||||
RUN java -Djarmode=layertools -jar app.jar extract && rm app.jar
|
||||
|
||||
FROM moxm/java:1.8-full
|
||||
LABEL maintainer="jclazz@outlook.com"
|
||||
ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms128m -Xmx256m -Djava.security.egd=file:/dev/./urandom"
|
||||
WORKDIR pig-sentinel-dashboard
|
||||
|
||||
COPY --from=builder /build/dependencies/ ./
|
||||
COPY --from=builder /build/snapshot-dependencies/ ./
|
||||
COPY --from=builder /build/spring-boot-loader/ ./
|
||||
COPY --from=builder /build/application/ ./
|
||||
RUN mkdir -p /pig-sentinel-dashboard
|
||||
|
||||
WORKDIR /pig-sentinel-dashboard
|
||||
|
||||
ARG JAR_FILE=target/pig-sentinel-dashboard.jar
|
||||
|
||||
COPY ${JAR_FILE} app.jar
|
||||
|
||||
EXPOSE 5003
|
||||
|
||||
CMD sleep 60; java $JAVA_OPTS org.springframework.boot.loader.JarLauncher
|
||||
ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms128m -Xmx256m -Djava.security.egd=file:/dev/./urandom"
|
||||
|
||||
CMD sleep 60; java -jar app.jar $JAVA_OPTS
|
||||
|
@ -1,19 +1,15 @@
|
||||
FROM moxm/java:1.8-full as builder
|
||||
WORKDIR /build
|
||||
ARG JAR_FILE=target/pig-xxl-job-admin.jar
|
||||
COPY ${JAR_FILE} app.jar
|
||||
RUN java -Djarmode=layertools -jar app.jar extract && rm app.jar
|
||||
|
||||
FROM moxm/java:1.8-full
|
||||
LABEL maintainer="jclazz@outlook.com"
|
||||
ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms128m -Xmx256m -Djava.security.egd=file:/dev/./urandom"
|
||||
WORKDIR pig-xxl-job-admin
|
||||
|
||||
COPY --from=builder /build/dependencies/ ./
|
||||
COPY --from=builder /build/snapshot-dependencies/ ./
|
||||
COPY --from=builder /build/spring-boot-loader/ ./
|
||||
COPY --from=builder /build/application/ ./
|
||||
RUN mkdir -p /pig-xxl-job-admin
|
||||
|
||||
WORKDIR /pig-xxl-job-admin
|
||||
|
||||
ARG JAR_FILE=target/pig-xxl-job-admin.jar
|
||||
|
||||
COPY ${JAR_FILE} app.jar
|
||||
|
||||
EXPOSE 5004
|
||||
|
||||
CMD sleep 60; java $JAVA_OPTS org.springframework.boot.loader.JarLauncher
|
||||
ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms128m -Xmx256m -Djava.security.egd=file:/dev/./urandom"
|
||||
|
||||
CMD sleep 60; java -jar app.jar $JAVA_OPTS
|
||||
|
@ -47,12 +47,6 @@
|
||||
<artifactId>spring-boot-starter-mail</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- starter-actuator -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- mybatis-starter:mybatis + mybatis-spring + hikari(default) -->
|
||||
<dependency>
|
||||
<groupId>org.mybatis.spring.boot</groupId>
|
||||
|
Loading…
Reference in New Issue
Block a user