commit 58f358e038b3992601f1983bab4d0959176b7fe4 Author: ytChen <1650611030@qq.com> Date: Tue Mar 5 15:42:27 2024 +0800 初始化 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ff6309 --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..35410ca --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml new file mode 100644 index 0000000..2df0afb --- /dev/null +++ b/.idea/dataSources.xml @@ -0,0 +1,17 @@ + + + + + mysql.8 + true + com.mysql.cj.jdbc.Driver + jdbc:mysql://localhost:3306 + + + + + + $ProjectFileDir$ + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..4bfbfc0 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..dc13fb0 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/mybatisx/templates.xml b/.idea/mybatisx/templates.xml new file mode 100644 index 0000000..d1678bb --- /dev/null +++ b/.idea/mybatisx/templates.xml @@ -0,0 +1,87 @@ + + + + + + \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..2b63946 --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/hoe-admin/admin-boot/pom.xml b/hoe-admin/admin-boot/pom.xml new file mode 100644 index 0000000..d28b45c --- /dev/null +++ b/hoe-admin/admin-boot/pom.xml @@ -0,0 +1,94 @@ + + + 4.0.0 + + com.recovery + hoe-admin + 1.0.0 + + + admin-boot + + + 8 + 8 + UTF-8 + + + + + org.springframework.boot + spring-boot-configuration-processor + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + org.springframework.cloud + spring-cloud-starter-bootstrap + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + com.nimbusds + nimbus-jose-jwt + + + + com.recovery + common-redis + ${hoe-version} + + + com.recovery + common-mybatis-plus + ${hoe-version} + + + com.recovery + common-web + ${hoe-version} + + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + com.spotify + docker-maven-plugin + + + + \ No newline at end of file diff --git a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/AdminApp.java b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/AdminApp.java new file mode 100644 index 0000000..d930c1d --- /dev/null +++ b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/AdminApp.java @@ -0,0 +1,22 @@ +package com.recovery.admin.boot; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication +@EnableDiscoveryClient +@RefreshScope +@EnableFeignClients +@ComponentScan(basePackages = {"com.recovery.admin.boot","com.recovery.common.base"}) +@MapperScan("com.recovery.admin.boot.mapper") +public class AdminApp { + public static void main(String[] args) { + SpringApplication.run(AdminApp.class, args); + } +} + diff --git a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/aspect/WebLogAspect.java b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/aspect/WebLogAspect.java new file mode 100644 index 0000000..7dc2273 --- /dev/null +++ b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/aspect/WebLogAspect.java @@ -0,0 +1,81 @@ +package com.recovery.admin.boot.aspect; + + +import com.recovery.common.base.util.HspHostUtil; + +import com.recovery.common.base.utils.RedisUtils; +import lombok.extern.slf4j.Slf4j; +import com.recovery.common.base.ds.*; + +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.After; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.annotation.Order; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.net.URL; + +/** + * Web层日志切面 + * + * @author RyanWang + * @version 1.0.0 + * @date 16/5/17 上午10:42. + */ +@Aspect +@Order(0) +@Slf4j +@Component +public class WebLogAspect { + @Resource + private StringRedisTemplate stringRedisTemplate; +// + @Pointcut("execution(public * com.recovery.admin.boot.controller..*.*(..)) || execution(public * com.recovery.admin.boot.rest.*.*(..))") + public void webLog(){} + + @Before("webLog()") + public void doBefore(JoinPoint joinPoint) throws Throwable { + + // 接收到请求,记录请求内容 + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + //获取请求的域名 + HttpServletRequest request = attributes.getRequest(); + // 根据域名获取网站信息 + StringBuffer urlBuf = request.getRequestURL(); + URL url = new URL(urlBuf.toString()); + String path = url.getPath(); + HttpServletResponse response = (HttpServletResponse)attributes.getResponse(); + String hospitalHost = request.getHeader("hospitalHost"); +// if(StringUtils.isEmpty(hospitalHost)){ +// return; +// } + + //切割获取访问目标模块 + String[] split = path.split("/"); + String module = split[0]; + System.out.println(RedisUtils.getDBInfoByHostAndModule(stringRedisTemplate,hospitalHost,"admin")); + //根据域名和请求的模块名查询目标数据库 + HttpSession session = request.getSession(); + /** + * 切换为动态数据源实例 + */ + HspHostUtil.switchDB(hospitalHost,"admin",stringRedisTemplate); + + } + //执行完切面后,将线程共享中的数据源名称清空 + @After("webLog()") + public void after(JoinPoint joinPoint){ + DataSourceContextHolder.clearDBType(); + } +} + diff --git a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/config/CorsConfig.java b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/config/CorsConfig.java new file mode 100644 index 0000000..d424881 --- /dev/null +++ b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/config/CorsConfig.java @@ -0,0 +1,36 @@ +package com.recovery.admin.boot.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; + +//跨越请求配置类 +@Configuration +public class CorsConfig { + + private CorsConfiguration buildConfig() { + CorsConfiguration corsConfiguration = new CorsConfiguration(); + // 你需要跨域的地址 注意这里的 127.0.0.1 != localhost + // * 表示对所有的地址都可以访问 + corsConfiguration.addAllowedOrigin("*"); // 1 + // 跨域的请求头 + corsConfiguration.addAllowedHeader("*"); // 2 + // 跨域的请求方法 + corsConfiguration.addAllowedMethod("*"); // 3 + //加上了这一句,大致意思是可以携带 cookie + //最终的结果是可以 在跨域请求的时候获取同一个 session + corsConfiguration.setAllowCredentials(true); + return corsConfiguration; + } + @Bean + public CorsFilter corsFilter() { + + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + //配置 可以访问的地址 + source.registerCorsConfiguration("/**", buildConfig()); + return new CorsFilter(source); + } + +} diff --git a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/config/InterceptConfig.java b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/config/InterceptConfig.java new file mode 100644 index 0000000..092ef34 --- /dev/null +++ b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/config/InterceptConfig.java @@ -0,0 +1,25 @@ +package com.recovery.admin.boot.config; + + +import com.recovery.admin.boot.interceptor.JwtInterceptor; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + + +@Configuration +public class InterceptConfig implements WebMvcConfigurer { + @Override + public void addInterceptors(InterceptorRegistry registry) { + //添加拦截器 + registry.addInterceptor(new JwtInterceptor()) + //拦截的路径 需要进行token验证的路径 + .addPathPatterns("/**") + //放行的路径 + .excludePathPatterns("/api/rest/users/getUserByUsername") + .excludePathPatterns("/api/user/sendMsg") + .excludePathPatterns("/api/user/loginPhone") + //放行swagger 测试验证 + .excludePathPatterns("/api/user/get"); + } +} diff --git a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/config/ds/DataSourceConfig.java b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/config/ds/DataSourceConfig.java new file mode 100644 index 0000000..2d17563 --- /dev/null +++ b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/config/ds/DataSourceConfig.java @@ -0,0 +1,96 @@ +package com.recovery.admin.boot.config.ds; + +import com.alibaba.druid.pool.DruidDataSource; + +import com.recovery.common.base.ds.DynamicDataSource; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.session.SqlSessionFactory; +import org.mybatis.spring.SqlSessionFactoryBean; +import org.mybatis.spring.SqlSessionTemplate; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; + +import javax.sql.DataSource; +import java.util.HashMap; +import java.util.Map; + +/** + * Created by YHYR on 2017-12-25 + */ +@Slf4j +@Configuration +@MapperScan(basePackages = DataSourceConfig.PACKAGE, sqlSessionFactoryRef = "commonSqlSessionFactory") +public class DataSourceConfig { + + // 精确到模块目录,以便跟其他数据源隔离 + static final String PACKAGE = "com.recovery.admin.boot.mapper"; + static final String MAPPER_LOCATION = "classpath:mapper/*.xml"; + + @Value("${master.datasource.url}") + private String masterDBUrl; + + @Value("${master.datasource.username}") + private String masterDBUser; + + @Value("${master.datasource.password}") + private String masterDBPassword; + + @Value("${master.datasource.driverClassName}") + private String masterDBDreiverName; + + + @Bean(name = "commonDataSource") + @Primary + public DynamicDataSource dynamicDataSource(){ + DynamicDataSource dynamicDataSource = DynamicDataSource.getInstance(); + + DruidDataSource masterDataSource = new DruidDataSource(); + try { + masterDataSource.setUrl(masterDBUrl); + masterDataSource.setUsername(masterDBUser); + masterDataSource.setPassword(masterDBPassword); + masterDataSource.setDriverClassName(masterDBDreiverName); + }catch (Exception e){ + log.error(e.getMessage()); + } + + Map map = new HashMap<>(); + map.put("master", masterDataSource); + dynamicDataSource.setTargetDataSources(map); + + return dynamicDataSource; + } + + @Bean(name = "commonSqlSessionFactory") + @Primary + public SqlSessionFactory sqlSessionFactory( + @Qualifier("commonDataSource") DataSource dynamicDataSource) + throws Exception { + SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); + bean.setDataSource(dynamicDataSource); + bean.setMapperLocations(new PathMatchingResourcePatternResolver() + .getResources(DataSourceConfig.MAPPER_LOCATION)); + return bean.getObject(); + + } + + @Bean(name = "commonTransactionManager") + @Primary + public DataSourceTransactionManager commonTransactionManager() { + return new DataSourceTransactionManager(dynamicDataSource()); + } + + + @Bean(name = "commonSqlSessionTemplate") + public SqlSessionTemplate sqlSessionTemplate( + @Qualifier("commonSqlSessionFactory") SqlSessionFactory sqlSessionFactory) + throws Exception { + return new SqlSessionTemplate(sqlSessionFactory); + } +} diff --git a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/config/ds/TeachingDataSourceConfig.java b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/config/ds/TeachingDataSourceConfig.java new file mode 100644 index 0000000..c948855 --- /dev/null +++ b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/config/ds/TeachingDataSourceConfig.java @@ -0,0 +1,58 @@ +package com.recovery.admin.boot.config.ds; + +import com.alibaba.druid.pool.DruidDataSource; +import org.apache.ibatis.session.SqlSessionFactory; +import org.mybatis.spring.SqlSessionFactoryBean; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; + +import javax.sql.DataSource; + +//@Configuration +//// 扫描 Mapper 接口并容器管理 +//@MapperScan(basePackages = TeachingDataSourceConfig.PACKAGE, sqlSessionFactoryRef = "teachingSqlSessionFactory") +public class TeachingDataSourceConfig { + // 精确到 course 目录,以便跟其他数据源隔离 + static final String PACKAGE = "com.recovery.admin.boot.mapper"; + static final String MAPPER_LOCATION = "classpath:mapper/*.xml"; + + @Value("${admin.datasource.url}") + private String url; + + @Value("${admin.datasource.username}") + private String user; + + @Value("${admin.datasource.password}") + private String password; + + @Value("${admin.datasource.driverClassName}") + private String driverClass; + + @Bean(name = "adminDataSource") + public DataSource cpdDataSource() { + DruidDataSource dataSource = new DruidDataSource(); + dataSource.setDriverClassName(driverClass); + dataSource.setUrl(url); + dataSource.setUsername(user); + dataSource.setPassword(password); + return dataSource; + } + + @Bean(name = "adminTransactionManager") + public DataSourceTransactionManager courseTransactionManager() { + return new DataSourceTransactionManager(cpdDataSource()); + } + + @Bean(name = "adminSqlSessionFactory") + public SqlSessionFactory cpdSqlSessionFactory(@Qualifier("adminDataSource") DataSource cpdDataSource) + throws Exception { + final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); + sessionFactory.setDataSource(cpdDataSource); + sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver() + .getResources(TeachingDataSourceConfig.MAPPER_LOCATION)); + return sessionFactory.getObject(); + } +} diff --git a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/controller/OauthClientController.java b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/controller/OauthClientController.java new file mode 100644 index 0000000..40fc4a3 --- /dev/null +++ b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/controller/OauthClientController.java @@ -0,0 +1,35 @@ +package com.recovery.admin.boot.controller; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.lang.Assert; + +import com.recovery.admin.boot.entity.SysOauthClient; +import com.recovery.admin.boot.service.ISysOauthClientService; + +import com.recovery.common.base.dto.OAuth2ClientDTO; +import com.recovery.common.base.result.ApiResult; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +/** + * @author: + */ +@RequestMapping("/api/oauth-clients") +@Slf4j +@AllArgsConstructor +@RestController +public class OauthClientController { + private ISysOauthClientService iSysOauthClientService; + + @GetMapping("/getOAuth2ClientById") + public ApiResult getOAuth2ClientById(@RequestParam String clientId) { + SysOauthClient client = iSysOauthClientService.getById(clientId); + Assert.notNull(client, "OAuth2 客户端不存在"); + OAuth2ClientDTO oAuth2ClientDTO = new OAuth2ClientDTO(); + BeanUtil.copyProperties(client, oAuth2ClientDTO); + return ApiResult.ok(oAuth2ClientDTO); + } +} diff --git a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/controller/UserController.java b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/controller/UserController.java new file mode 100644 index 0000000..1cc4c98 --- /dev/null +++ b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/controller/UserController.java @@ -0,0 +1,46 @@ +package com.recovery.admin.boot.controller; + + +import com.recovery.admin.boot.service.IMemberUserService; +import com.recovery.admin.boot.service.ISysUserService; + +import com.recovery.common.base.dto.MemberUserAuthDTO; +import com.recovery.common.base.dto.UserAuthDTO; +import com.recovery.common.base.enums.PasswordEncoderTypeEnum; +import com.recovery.common.base.result.ApiResult; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.web.bind.annotation.*; + +/** + * @author: + */ +@RestController +@RequestMapping("/api/v1/users") +@Slf4j +@RequiredArgsConstructor +public class UserController { + + private final ISysUserService iSysUserService; + + private final IMemberUserService memberUserService; + + /** + * 获取用户信息 + */ + @GetMapping("/username") + public ApiResult getUserByUsername(@RequestParam String username) { + UserAuthDTO user = iSysUserService.getByUsername(username); + return ApiResult.ok(user); + } + /** + * 获取会员用户信息 + */ + @GetMapping("/member/username/{username}") + public ApiResult getMemberUserByUsername(@PathVariable String username) { + log.info("获取member user info。。。"); + MemberUserAuthDTO user = memberUserService.getByUsername(username); + return ApiResult.ok(user); + } +} diff --git a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/controller/testController.java b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/controller/testController.java new file mode 100644 index 0000000..7967cea --- /dev/null +++ b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/controller/testController.java @@ -0,0 +1,39 @@ +package com.recovery.admin.boot.controller; + + +import com.recovery.admin.boot.service.ISysUserService; +import com.recovery.common.base.dto.UserAuthDTO; +import com.recovery.common.base.result.ApiResult; +import com.recovery.common.base.util.HspHostUtil; +import com.recovery.common.base.util.RedisUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; + +@RestController +@RequestMapping("/api/v1/test") +@Slf4j +public class testController { + + @Resource + ISysUserService iSysUserService; + @Resource + RedisUtil redisUtil; + + /** + * cs + */ + @GetMapping("/cs") + public ApiResult cs(@RequestParam String name, HttpServletRequest request) { + UserAuthDTO authDTO = iSysUserService.getByUsername(name); + log.info("测试库:"+authDTO.getStatus()); + log.info(redisUtil.get("123").toString()); + return ApiResult.ok(authDTO); + } +} diff --git a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/entity/MemberUser.java b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/entity/MemberUser.java new file mode 100644 index 0000000..b2bdb0d --- /dev/null +++ b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/entity/MemberUser.java @@ -0,0 +1,50 @@ +package com.recovery.admin.boot.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import lombok.Data; + +import java.util.List; + +/** + * @author: + */ +@Data +public class MemberUser { + + @TableId(type = IdType.AUTO) + private Long id; + + private String username; + + private String nickname; + + private String mobile; + + private Integer gender; + + private String avatar; + + private String password; + + private String email; + + private Integer status; + + @TableLogic(value = "0", delval = "1") + private Integer deleted; + + + @TableField(exist = false) + private List roleIds; + + @TableField(exist = false) + private String roleNames; + + @TableField(exist = false) + private List roles; + + +} diff --git a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/entity/SysOauthClient.java b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/entity/SysOauthClient.java new file mode 100644 index 0000000..5663be6 --- /dev/null +++ b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/entity/SysOauthClient.java @@ -0,0 +1,69 @@ +package com.recovery.admin.boot.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import lombok.Data; + +/** + * @author: + */ +@Data +public class SysOauthClient { + + @TableId(type = IdType.INPUT) + /** + * 客户端ID + */ + private String clientId; + + /** + * 客户端密钥 + */ + private String clientSecret; + + /** + * 资源id列表 + */ + private String resourceIds; + + /** + * 资源id列表 + */ + private String scope; + + /** + * 资源id列表 + */ + private String authorizedGrantTypes; + + /** + * 回调地址 + */ + private String webServerRedirectUri; + + /** + * 权限列表 + */ + private String authorities; + + /** + * 认证令牌时效 + */ + private Integer accessTokenValidity; + + /** + * 刷新认证令牌时效 + */ + private Integer refreshTokenValidity; + + /** + * 扩展信息 + */ + private String additionalInformation; + + /** + * 是否自动放行 + */ + private String autoapprove; + +} diff --git a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/entity/SysPermission.java b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/entity/SysPermission.java new file mode 100644 index 0000000..aacaf59 --- /dev/null +++ b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/entity/SysPermission.java @@ -0,0 +1,30 @@ +package com.recovery.admin.boot.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.List; +/** + * @author: + */ +@Data +@Accessors(chain = true) +public class SysPermission { + + @TableId(type = IdType.AUTO) + private Long id; + + private String name; + + private Long menuId; + + private String urlPerm; + + // 有权限的角色编号集合 + @TableField(exist = false) + private List roles; + +} diff --git a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/entity/User.java b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/entity/User.java new file mode 100644 index 0000000..0c020a2 --- /dev/null +++ b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/entity/User.java @@ -0,0 +1,50 @@ +package com.recovery.admin.boot.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import lombok.Data; + +import java.util.List; + +/** + * @author: + */ +@Data +public class User { + + @TableId(type = IdType.AUTO) + private Long id; + + private String username; + + private String nickname; + + private String mobile; + + private Integer gender; + + private String avatar; + + private String password; + + private String email; + + private Integer status; + + @TableLogic(value = "0", delval = "1") + private Integer deleted; + + + @TableField(exist = false) + private List roleIds; + + @TableField(exist = false) + private String roleNames; + + @TableField(exist = false) + private List roles; + + +} diff --git a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/exception/BusinessException.java b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/exception/BusinessException.java new file mode 100644 index 0000000..66e005b --- /dev/null +++ b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/exception/BusinessException.java @@ -0,0 +1,38 @@ +package com.recovery.admin.boot.exception; + + + +import com.recovery.common.base.result.IResultCode; +import lombok.Data; + + +/** + * 自定义异常类 + */ +@Data +public class BusinessException extends RuntimeException{ + + private String code; + + private String msg; + + + public BusinessException(IResultCode apiCode) { + super(apiCode.getMsg()); + this.code = apiCode.getCode(); + this.msg = apiCode.getMsg(); + } + + public BusinessException(String code, String message) { + super(code+":"+message); + this.code = code; + this.msg = message; + } + + public BusinessException(String message) { + super("5001"+":"+message); + this.code = "5001"; + this.msg = message; + } + +} diff --git a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/exception/GlobalExceptionHandler.java b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..7d60485 --- /dev/null +++ b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/exception/GlobalExceptionHandler.java @@ -0,0 +1,38 @@ +package com.recovery.admin.boot.exception; + + +import com.recovery.common.base.result.ApiResult; +import com.recovery.common.base.result.ResultCode; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +/** + * 全局异常处理 + */ +@Slf4j +@RestControllerAdvice +public class GlobalExceptionHandler { + + //自定义异常 + @ExceptionHandler(BusinessException.class) + public ApiResult systemExceptionHandler(BusinessException e) { + log.error("BusinessException全局异常:{}",e); + return ApiResult.failed(e.getCode(), e.getMsg()); + } + + //系统异常 + @ExceptionHandler(Exception.class) + public ApiResult exceptionHandler(Exception e) { + log.error("Exception全局异常:{}",e); + return ApiResult.failed(ResultCode.SYSTEM_EXECUTION_ERROR.getCode(), e.getMessage()); + } + + //Security +// @ExceptionHandler(value = AccessDeniedException.class) +// public void accessDeniedException(AccessDeniedException e) { +// throw e; +// } + + +} diff --git a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/filter/RequestWrapper.java b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/filter/RequestWrapper.java new file mode 100644 index 0000000..20ad03c --- /dev/null +++ b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/filter/RequestWrapper.java @@ -0,0 +1,133 @@ +package com.recovery.admin.boot.filter; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.ServletRequest; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import java.io.*; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.charset.StandardCharsets; + +public class RequestWrapper extends HttpServletRequestWrapper { + private final Logger logger = LoggerFactory.getLogger(RequestWrapper.class); + private final byte[] body; + + public RequestWrapper(HttpServletRequest request) { + super(request); + String sessionStream = getBodyString(request); + body = sessionStream.getBytes(StandardCharsets.UTF_8); + } + + public String getBodyString() { + return new String(body, StandardCharsets.UTF_8); + } + + /** + * @date: 2023/2/6 12:46 + * @author: zhouzhaodong + * @description: 获取请求Body + */ + public String getBodyString(final ServletRequest request) { + StringBuilder sb = new StringBuilder(); + InputStream inputStream = null; + BufferedReader reader = null; + try { + inputStream = cloneInputStream(request.getInputStream()); + reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + String line = ""; + while ((line = reader.readLine()) != null) { + sb.append(line); + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + // 接收到请求,记录请求内容 + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + //获取请求的域名 + HttpServletRequest req = attributes.getRequest(); + // 根据域名获取网站信息 + StringBuffer urlBuf = req.getRequestURL(); + try { + logger.info("获取请求地址:" + new URL(urlBuf.toString()).getPath()); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + logger.info("获取body请求参数:" + sb); + return sb.toString(); + } + + /** + * @date: 2023/2/6 12:46 + * @author: zhouzhaodong + * @description: 复制输入流 + */ + public InputStream cloneInputStream(ServletInputStream inputStream) { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int len; + try { + while ((len = inputStream.read(buffer)) > -1) { + byteArrayOutputStream.write(buffer, 0, len); + } + byteArrayOutputStream.flush(); + } catch (IOException e) { + e.printStackTrace(); + } + return new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); + } + + @Override + public BufferedReader getReader() { + return new BufferedReader(new InputStreamReader(getInputStream())); + } + + @Override + public ServletInputStream getInputStream() { + + final ByteArrayInputStream bais = new ByteArrayInputStream(body); + return new ServletInputStream() { + + @Override + public int read() { + return bais.read(); + } + + @Override + public boolean isFinished() { + return false; + } + + @Override + public boolean isReady() { + return false; + } + + @Override + public void setReadListener(ReadListener readListener) { + + } + }; + } +} diff --git a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/interceptor/JwtInterceptor.java b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/interceptor/JwtInterceptor.java new file mode 100644 index 0000000..1afc8e4 --- /dev/null +++ b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/interceptor/JwtInterceptor.java @@ -0,0 +1,85 @@ +package com.recovery.admin.boot.interceptor; + + +import com.alibaba.fastjson.JSONObject; +import com.auth0.jwt.interfaces.DecodedJWT; + +import com.recovery.admin.boot.exception.BusinessException; +import com.recovery.admin.boot.filter.RequestWrapper; +import com.recovery.common.base.constant.Constants; +import com.recovery.common.base.result.ResultCode; +import com.recovery.common.base.utils.JwtUtils; +import lombok.extern.slf4j.Slf4j; + +import org.springframework.util.StringUtils; +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.ModelAndView; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Date; +import java.util.HashMap; + +@Slf4j +public class JwtInterceptor implements HandlerInterceptor { + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + HashMap map=new HashMap<>(); + //获取请求参数 + RequestWrapper requestWrapper = new RequestWrapper(request); + //这里getBodyString()方法无参数 + log.info("RequestBody: {}", requestWrapper.getBodyString()); + //从http请求头获取token + + String token = requestWrapper.getHeader(Constants.LOGIN_USRE_TOKEN); + if (StringUtils.isEmpty(token)) { + throw new BusinessException(ResultCode.TOKEN_INVALID_OR_EXPIRED); + } + //把变量放在request请求域中,仅可以被这次请求,即同一个requerst使用 + request.setAttribute(Constants.LOGIN_USRE_TOKEN,token); + try { + //如果验证成功放行请求 + DecodedJWT verify = JwtUtils.verifyToken(token); + return true; + } + catch (Exception exception) + { + throw new BusinessException(ResultCode.TOKEN_INVALID_OR_EXPIRED); + } + } + /** + * @date: 2023/2/6 12:46 + * @author: zhouzhaodong + * @description: 访问控制器方法后执行 + */ + @Override + public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { + log.info(new Date() + "--postHandle:" + request.getRequestURL()); + } + + /** + * @date: 2023/2/6 12:46 + * @author: zhouzhaodong + * @description: postHandle方法执行完成后执行,一般用于释放资源 + */ + @Override + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { + log.info(new Date() + "--afterCompletion:" + request.getRequestURL()); + } + + public void renderJson(HttpServletResponse response, Object object) { + response.reset(); + response.setHeader("Access-Control-Allow-Origin", "*"); + response.setHeader("Access-Control-Allow-Methods", "*"); + response.setHeader("Access-Control-Allow-Headers", "x-requested-with,content-type"); + response.setContentType("application/json"); + response.setCharacterEncoding("utf-8"); + try { + response.getWriter().print(JSONObject.toJSONString(object)); + } catch (IOException e) { + e.printStackTrace(); + } + } +} + diff --git a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/listener/InitResourcePermissionCache.java b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/listener/InitResourcePermissionCache.java new file mode 100644 index 0000000..38acbfd --- /dev/null +++ b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/listener/InitResourcePermissionCache.java @@ -0,0 +1,25 @@ +package com.recovery.admin.boot.listener; + + +import com.recovery.admin.boot.service.ISysPermissionService; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +/** + * @author: + */ +@Component +@Slf4j +@AllArgsConstructor +public class InitResourcePermissionCache implements CommandLineRunner { + + private ISysPermissionService iSysPermissionService; + + @Override + public void run(String... args) { + log.info("刷新权限------------------------------"); +// iSysPermissionService.refreshPermRolesRules(); + } +} diff --git a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/mapper/MemberUserMapper.java b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/mapper/MemberUserMapper.java new file mode 100644 index 0000000..5cce7f2 --- /dev/null +++ b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/mapper/MemberUserMapper.java @@ -0,0 +1,18 @@ +package com.recovery.admin.boot.mapper; + + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.recovery.admin.boot.entity.MemberUser; + +import com.recovery.common.base.dto.MemberUserAuthDTO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +/** + * @author: + */ +@Mapper +public interface MemberUserMapper extends BaseMapper { + + MemberUserAuthDTO getByUsername(@Param("userName") String userName); +} diff --git a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/mapper/SysOauthClientMapper.java b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/mapper/SysOauthClientMapper.java new file mode 100644 index 0000000..27ff71a --- /dev/null +++ b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/mapper/SysOauthClientMapper.java @@ -0,0 +1,12 @@ +package com.recovery.admin.boot.mapper; + + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.recovery.admin.boot.entity.SysOauthClient; +import org.apache.ibatis.annotations.Mapper; +/** + * @author: + */ +@Mapper +public interface SysOauthClientMapper extends BaseMapper { +} diff --git a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/mapper/SysPermissionMapper.java b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/mapper/SysPermissionMapper.java new file mode 100644 index 0000000..3a28a5c --- /dev/null +++ b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/mapper/SysPermissionMapper.java @@ -0,0 +1,16 @@ +package com.recovery.admin.boot.mapper; + + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.recovery.admin.boot.entity.SysPermission; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; +/** + * @author: + */ +@Mapper +public interface SysPermissionMapper extends BaseMapper { + + List listPermRoles(); +} diff --git a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/mapper/UserMapper.java b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/mapper/UserMapper.java new file mode 100644 index 0000000..ef69bcf --- /dev/null +++ b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/mapper/UserMapper.java @@ -0,0 +1,18 @@ +package com.recovery.admin.boot.mapper; + + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.recovery.admin.boot.entity.User; + +import com.recovery.common.base.dto.UserAuthDTO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +/** + * @author: + */ +@Mapper +public interface UserMapper extends BaseMapper { + + UserAuthDTO getByUsername(@Param("userName") String userName); +} diff --git a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/rest/UserRest.java b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/rest/UserRest.java new file mode 100644 index 0000000..1b159db --- /dev/null +++ b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/rest/UserRest.java @@ -0,0 +1,50 @@ +package com.recovery.admin.boot.rest; + + +import com.recovery.admin.boot.service.IMemberUserService; +import com.recovery.admin.boot.service.ISysUserService; +import com.recovery.common.base.dto.MemberUserAuthDTO; +import com.recovery.common.base.dto.UserAuthDTO; +import com.recovery.common.base.enums.PasswordEncoderTypeEnum; +import com.recovery.common.base.result.ApiResult; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; + +/** + * @author: + */ +@RestController +@RequestMapping("/api/rest/users") +@Slf4j +public class UserRest { + + @Resource + private ISysUserService iSysUserService; + @Resource + private IMemberUserService memberUserService; + + /** + * 获取用户信息 + */ + @GetMapping("/getUserByUsername") + public ApiResult getUserByUsername(@RequestParam String username) { + UserAuthDTO user = iSysUserService.getByUsername(username); + if (user == null) { + throw new RuntimeException(); + } + return ApiResult.ok(user); + } + /**12 + * 获取会员用户信息 + */ + @GetMapping("/member/username/{username}") + public ApiResult getMemberUserByUsername(@PathVariable String username) { + log.info("获取member user info。。。"); + MemberUserAuthDTO user = memberUserService.getByUsername(username); + return ApiResult.ok(user); + } +} diff --git a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/service/IMemberUserService.java b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/service/IMemberUserService.java new file mode 100644 index 0000000..2730dcd --- /dev/null +++ b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/service/IMemberUserService.java @@ -0,0 +1,25 @@ +package com.recovery.admin.boot.service; + + + + +import com.baomidou.mybatisplus.extension.service.IService; +import com.recovery.admin.boot.entity.MemberUser; +import com.recovery.common.base.dto.MemberUserAuthDTO; + + +/** + * @author: + */ +public interface IMemberUserService extends IService { + + + /** + * 根据用户名获取认证用户信息,携带角色和密码 + * + * @param username + * @return + */ + MemberUserAuthDTO getByUsername(String username); + +} diff --git a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/service/ISysOauthClientService.java b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/service/ISysOauthClientService.java new file mode 100644 index 0000000..941110a --- /dev/null +++ b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/service/ISysOauthClientService.java @@ -0,0 +1,11 @@ +package com.recovery.admin.boot.service; + + +import com.baomidou.mybatisplus.extension.service.IService; +import com.recovery.admin.boot.entity.SysOauthClient; + +/** + * @author: + */ +public interface ISysOauthClientService extends IService { +} diff --git a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/service/ISysPermissionService.java b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/service/ISysPermissionService.java new file mode 100644 index 0000000..b02eb83 --- /dev/null +++ b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/service/ISysPermissionService.java @@ -0,0 +1,17 @@ +package com.recovery.admin.boot.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.recovery.admin.boot.entity.SysPermission; + +import java.util.List; +/** + * @author: + */ +public interface ISysPermissionService extends IService { + /** + * 刷新Redis缓存中角色菜单的权限规则,角色和菜单信息变更调用 + */ + boolean refreshPermRolesRules(); + + List listPermRoles(); +} diff --git a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/service/ISysUserService.java b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/service/ISysUserService.java new file mode 100644 index 0000000..7dee781 --- /dev/null +++ b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/service/ISysUserService.java @@ -0,0 +1,22 @@ +package com.recovery.admin.boot.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.recovery.admin.boot.entity.User; +import com.recovery.common.base.dto.UserAuthDTO; + + +/** + * @author: + */ +public interface ISysUserService extends IService { + + + /** + * 根据用户名获取认证用户信息,携带角色和密码 + * + * @param username + * @return + */ + UserAuthDTO getByUsername(String username); + +} diff --git a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/service/impl/MemberUserServiceImpl.java b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/service/impl/MemberUserServiceImpl.java new file mode 100644 index 0000000..b9762ac --- /dev/null +++ b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/service/impl/MemberUserServiceImpl.java @@ -0,0 +1,24 @@ +package com.recovery.admin.boot.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.recovery.admin.boot.entity.MemberUser; +import com.recovery.admin.boot.mapper.MemberUserMapper; +import com.recovery.admin.boot.service.IMemberUserService; + +import com.recovery.common.base.dto.MemberUserAuthDTO; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +/** + * @author: + */ +@Service +@RequiredArgsConstructor +public class MemberUserServiceImpl extends ServiceImpl implements IMemberUserService { + @Override + public MemberUserAuthDTO getByUsername(String username) { + MemberUserAuthDTO memberUserAuthDTO = this.baseMapper.getByUsername(username); + return memberUserAuthDTO; + } + +} diff --git a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/service/impl/SysOauthClientServiceImpl.java b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/service/impl/SysOauthClientServiceImpl.java new file mode 100644 index 0000000..84fbb98 --- /dev/null +++ b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/service/impl/SysOauthClientServiceImpl.java @@ -0,0 +1,19 @@ +package com.recovery.admin.boot.service.impl; + + + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.recovery.admin.boot.entity.SysOauthClient; +import com.recovery.admin.boot.mapper.SysOauthClientMapper; +import com.recovery.admin.boot.service.ISysOauthClientService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +/** + * @author: + */ +@Service +@RequiredArgsConstructor +public class SysOauthClientServiceImpl extends ServiceImpl implements ISysOauthClientService { + +} diff --git a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/service/impl/SysPermissionServiceImpl.java b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/service/impl/SysPermissionServiceImpl.java new file mode 100644 index 0000000..9e77aae --- /dev/null +++ b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/service/impl/SysPermissionServiceImpl.java @@ -0,0 +1,57 @@ +package com.recovery.admin.boot.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.StrUtil; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.recovery.admin.boot.entity.SysPermission; +import com.recovery.admin.boot.mapper.SysPermissionMapper; +import com.recovery.admin.boot.service.ISysPermissionService; +import com.recovery.common.base.constant.GlobalConstants; +import lombok.RequiredArgsConstructor; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + + +/** + * @author: + */ +@Service +@RequiredArgsConstructor +public class SysPermissionServiceImpl extends ServiceImpl implements ISysPermissionService { + + private final RedisTemplate redisTemplate; + + @Override + public boolean refreshPermRolesRules() { + redisTemplate.delete(Arrays.asList(GlobalConstants.URL_PERM_ROLES_KEY)); + List permissions = this.listPermRoles(); + if (CollectionUtil.isNotEmpty(permissions)) { + // 初始化URL- 角色规则 + List urlPermList = permissions.stream() + .filter(item -> StrUtil.isNotBlank(item.getUrlPerm())) + .collect(Collectors.toList()); + if (CollectionUtil.isNotEmpty(urlPermList)) { + Map> urlPermRoles = new HashMap<>(); + urlPermList.stream().forEach(item -> { + String perm = item.getUrlPerm(); + List roles = item.getRoles(); + urlPermRoles.put(perm, roles); + }); + redisTemplate.opsForHash().putAll(GlobalConstants.URL_PERM_ROLES_KEY, urlPermRoles); + } + } + return true; + } + + @Override + public List listPermRoles() { + return this.baseMapper.listPermRoles(); + } +} diff --git a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/service/impl/SysUserServiceImpl.java b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/service/impl/SysUserServiceImpl.java new file mode 100644 index 0000000..1cffd37 --- /dev/null +++ b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/service/impl/SysUserServiceImpl.java @@ -0,0 +1,25 @@ +package com.recovery.admin.boot.service.impl; + + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.recovery.admin.boot.entity.User; +import com.recovery.admin.boot.mapper.UserMapper; +import com.recovery.admin.boot.service.ISysUserService; + +import com.recovery.common.base.dto.UserAuthDTO; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +/** + * @author: + */ +@Service +@RequiredArgsConstructor +public class SysUserServiceImpl extends ServiceImpl implements ISysUserService { + @Override + public UserAuthDTO getByUsername(String username) { + UserAuthDTO userAuthInfo = this.baseMapper.getByUsername(username); + return userAuthInfo; + } + +} diff --git a/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/util/UserUtil.java b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/util/UserUtil.java new file mode 100644 index 0000000..af76adc --- /dev/null +++ b/hoe-admin/admin-boot/src/main/java/com/recovery/admin/boot/util/UserUtil.java @@ -0,0 +1,33 @@ +package com.recovery.admin.boot.util; + + +import com.recovery.admin.boot.exception.BusinessException; +import com.recovery.common.base.constant.Constants; +import com.recovery.common.base.dto.UserAuthorityDto; +import com.recovery.common.base.result.ResultCode; +import com.recovery.common.base.util.RedisUtil; +import lombok.extern.slf4j.Slf4j; + +import javax.servlet.http.HttpServletRequest; + +@Slf4j +public class UserUtil { + + /** + * 获取登录用户信息 + * @param request + * @param redisUtil + * @return + */ + public static UserAuthorityDto getLoginUserInfo(HttpServletRequest request, RedisUtil redisUtil){ + String loginUserToken = (String) request.getHeader(Constants.LOGIN_USRE_TOKEN); + Boolean rest = redisUtil.hasKey("userToken:" +loginUserToken); + log.info("缓存key:"+"userToken:" +loginUserToken); + log.info("缓存中是否有这条用户:"+rest); + if (!rest) { + throw new BusinessException(ResultCode.TOKEN_INVALID_OR_EXPIRED); + } + UserAuthorityDto userAuthorityDto = (UserAuthorityDto) redisUtil.get("userToken:" +loginUserToken); + return userAuthorityDto; + } +} diff --git a/hoe-admin/admin-boot/src/main/resources/bootstrap.yml b/hoe-admin/admin-boot/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..a4dece1 --- /dev/null +++ b/hoe-admin/admin-boot/src/main/resources/bootstrap.yml @@ -0,0 +1,23 @@ + +spring: + application: + name: hoe-admin + main: + allow-bean-definition-overriding: true + profiles: + active: dev + cloud: + nacos: + discovery: + # metadata: + # serviceGroup: ytChen + server-addr: localhost:8848 + namespace: 11bfd099-10d6-4f2c-b969-58b76e435cce + config: + server-addr: localhost:8848 + file-extension: yaml + prefix: hoe-admin + group: dev + namespace: 11bfd099-10d6-4f2c-b969-58b76e435cce +server: + port: 9002 diff --git a/hoe-admin/admin-boot/src/main/resources/logback-bak.xml b/hoe-admin/admin-boot/src/main/resources/logback-bak.xml new file mode 100644 index 0000000..76945c1 --- /dev/null +++ b/hoe-admin/admin-boot/src/main/resources/logback-bak.xml @@ -0,0 +1,53 @@ + + + logback + + + + + + + + + ${consoleLayoutPattern} + + + + + + ${SYS_LOG_DIR}/${LOG_FILE} + + WARN + ACCEPT + DENY + + + ${SYS_LOG_DIR}/%d{yyyy-MM-dd}/${LOG_FILE}_%d{yyyy-MM-dd}_%i.zip + + 50MB + + + + ${fileLayoutPattern} + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/hoe-admin/admin-boot/src/main/resources/logback-spring.xml b/hoe-admin/admin-boot/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..d2f4072 --- /dev/null +++ b/hoe-admin/admin-boot/src/main/resources/logback-spring.xml @@ -0,0 +1,175 @@ + + + + + + + + + + + + + + + + + + + + + true + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{15}) - %highlight(%msg) %n + + + + + + + + + + ERROR + ACCEPT + DENY + + + + + ${log_dir}/%d{yyyy-MM-dd}/error-log.log + + ${maxHistory} + + + + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n + + + + + + + + + + WARN + ACCEPT + DENY + + + + ${log_dir}/%d{yyyy-MM-dd}/warn-log.log + + + ${maxHistory} + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n + + + + + + + + + + + INFO + ACCEPT + DENY + + + + ${log_dir}/%d{yyyy-MM-dd}/info-log.log + + + ${maxHistory} + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n + + + + + + + + + + + DEBUG + ACCEPT + DENY + + + + ${log_dir}/%d{yyyy-MM-dd}/debug-log.log + + + ${maxHistory} + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n + + + + + + + + + + + TRACE + ACCEPT + DENY + + + + ${log_dir}/%d{yyyy-MM-dd}/trace-log.log + + + ${maxHistory} + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/hoe-admin/admin-boot/src/main/resources/mapper/MemberUserMapper.xml b/hoe-admin/admin-boot/src/main/resources/mapper/MemberUserMapper.xml new file mode 100644 index 0000000..3b83b25 --- /dev/null +++ b/hoe-admin/admin-boot/src/main/resources/mapper/MemberUserMapper.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + id + ,username,nickname, + gender,password,dept_id, + avatar,mobile,status, + email,gmt_create,gmt_modified, + deleted + + + + + + + + + + + + + + + diff --git a/hoe-admin/admin-boot/src/main/resources/mapper/SysPermissionMapper.xml b/hoe-admin/admin-boot/src/main/resources/mapper/SysPermissionMapper.xml new file mode 100644 index 0000000..164658d --- /dev/null +++ b/hoe-admin/admin-boot/src/main/resources/mapper/SysPermissionMapper.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + id + ,name,menu_id, + url_perm,btn_perm,gmt_create, + gmt_modified + + + + + diff --git a/hoe-admin/admin-boot/src/main/resources/mapper/UserMapper.xml b/hoe-admin/admin-boot/src/main/resources/mapper/UserMapper.xml new file mode 100644 index 0000000..fdd9792 --- /dev/null +++ b/hoe-admin/admin-boot/src/main/resources/mapper/UserMapper.xml @@ -0,0 +1,12 @@ + + + + + + + diff --git a/hoe-admin/pom.xml b/hoe-admin/pom.xml new file mode 100644 index 0000000..213d53f --- /dev/null +++ b/hoe-admin/pom.xml @@ -0,0 +1,24 @@ + + + 4.0.0 + + com.recovery + hoe-recovery + 1.0.0 + + + hoe-admin + pom + + admin-boot + + + + 8 + 8 + UTF-8 + + + \ No newline at end of file diff --git a/hoe-auth/pom.xml b/hoe-auth/pom.xml new file mode 100644 index 0000000..25fd989 --- /dev/null +++ b/hoe-auth/pom.xml @@ -0,0 +1,81 @@ + + + 4.0.0 + + com.recovery + hoe-recovery + 1.0.0 + + jar + 1.0.0 + 认证中心 + hoe-auth + + + + + org.springframework.cloud + spring-cloud-starter-bootstrap + + + com.recovery + common-base + ${hoe-version} + + + com.recovery + common-mybatis-plus + ${hoe-version} + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + org.springframework.security + spring-security-oauth2-jose + + + com.recovery + common-web + ${hoe-version} + + + + org.springframework.security.oauth.boot + spring-security-oauth2-autoconfigure + + + + org.springframework.security + spring-security-oauth2-jose + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + com.spotify + docker-maven-plugin + + + + \ No newline at end of file diff --git a/hoe-auth/src/main/java/com/recovery/auth/AuthApp.java b/hoe-auth/src/main/java/com/recovery/auth/AuthApp.java new file mode 100644 index 0000000..2e03075 --- /dev/null +++ b/hoe-auth/src/main/java/com/recovery/auth/AuthApp.java @@ -0,0 +1,24 @@ +package com.recovery.auth; + + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.ComponentScan; + + +/** + * @author: + */ +@SpringBootApplication +@EnableDiscoveryClient +@EnableFeignClients({"com.recovery.auth.feign"}) +@ComponentScan(basePackages = {"com.recovery.auth","com.recovery.common.base"}) +@MapperScan("com.recovery.auth.mapper") +public class AuthApp { + public static void main(String[] args) { + SpringApplication.run(AuthApp.class, args); + } +} diff --git a/hoe-auth/src/main/java/com/recovery/auth/TestController.java b/hoe-auth/src/main/java/com/recovery/auth/TestController.java new file mode 100644 index 0000000..48da64f --- /dev/null +++ b/hoe-auth/src/main/java/com/recovery/auth/TestController.java @@ -0,0 +1,23 @@ +package com.recovery.auth; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author: + * @date: 2022/5/22 + */ +@RestController +@RequestMapping("/oauth") +@AllArgsConstructor +@Slf4j +public class TestController { + + @GetMapping("/public-test") + public String test() { + return "hoe-auth 为您提供服务"; + } +} diff --git a/hoe-auth/src/main/java/com/recovery/auth/aspect/WebLogAspect.java b/hoe-auth/src/main/java/com/recovery/auth/aspect/WebLogAspect.java new file mode 100644 index 0000000..4893570 --- /dev/null +++ b/hoe-auth/src/main/java/com/recovery/auth/aspect/WebLogAspect.java @@ -0,0 +1,77 @@ +package com.recovery.auth.aspect; + + +import com.recovery.common.base.ds.DataSourceContextHolder; +import com.recovery.common.base.util.HspHostUtil; +import com.recovery.common.base.utils.RedisUtils; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.After; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.springframework.core.annotation.Order; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.net.URL; + +/** + * Web层日志切面 + * + * @author RyanWang + * @version 1.0.0 + * @date 16/5/17 上午10:42. + */ +@Aspect +@Order(0) +@Slf4j +@Component +public class WebLogAspect { + @Resource + private StringRedisTemplate stringRedisTemplate; + @Pointcut("execution(public * com.recovery.auth.controller..*.*(..)) || execution(public * com.recovery.auth.rest..*.*(..))") + public void webLog(){} + + @Before("webLog()") + public void doBefore(JoinPoint joinPoint) throws Throwable { + + // 接收到请求,记录请求内容 + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + //获取请求的域名 + HttpServletRequest request = attributes.getRequest(); + // 根据域名获取网站信息 + StringBuffer urlBuf = request.getRequestURL(); + URL url = new URL(urlBuf.toString()); + String path = url.getPath(); + HttpServletResponse response = (HttpServletResponse)attributes.getResponse(); + String hospitalHost = request.getHeader("hospitalHost"); +// if(StringUtils.isEmpty(hospitalHost)){ +// return; +// } + + //切割获取访问目标模块 + String[] split = path.split("/"); + String module = split[0]; + System.out.println(RedisUtils.getDBInfoByHostAndModule(stringRedisTemplate,hospitalHost,"admin")); + //根据域名和请求的模块名查询目标数据库 + HttpSession session = request.getSession(); + /** + * 切换为动态数据源实例 + */ + HspHostUtil.switchDB(hospitalHost,"admin",stringRedisTemplate); + + } + //执行完切面后,将线程共享中的数据源名称清空 + @After("webLog()") + public void after(JoinPoint joinPoint){ + DataSourceContextHolder.clearDBType(); + } +} + diff --git a/hoe-auth/src/main/java/com/recovery/auth/comm/exception/AuthExceptionHandler.java b/hoe-auth/src/main/java/com/recovery/auth/comm/exception/AuthExceptionHandler.java new file mode 100644 index 0000000..4c0ba1d --- /dev/null +++ b/hoe-auth/src/main/java/com/recovery/auth/comm/exception/AuthExceptionHandler.java @@ -0,0 +1,107 @@ +package com.recovery.auth.comm.exception; + + +import com.recovery.common.base.result.ApiResult; +import com.recovery.common.base.result.ResultCode; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.annotation.Order; +import org.springframework.http.HttpStatus; +import org.springframework.security.authentication.InternalAuthenticationServiceException; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.oauth2.common.exceptions.InvalidClientException; +import org.springframework.security.oauth2.common.exceptions.InvalidGrantException; +import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; +import org.springframework.security.oauth2.provider.NoSuchClientException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +/** + * @author: + */ +@RestControllerAdvice +@Slf4j +@Order(-1) +public class AuthExceptionHandler { + + /** + * 用户不存在 + * + * @param e + * @return + */ + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler(UsernameNotFoundException.class) + public ApiResult handleUsernameNotFoundException(UsernameNotFoundException e) { + log.error("错误信息:{}", e.getMessage(),e); + return ApiResult.failed(ResultCode.USER_NOT_EXIST); + } + + /** + * 用户名和密码异常 + * + * @param e + * @return + */ + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler(InvalidGrantException.class) + public ApiResult handleInvalidGrantException(InvalidGrantException e) { + log.error("错误信息:{}", e.getMessage(),e); + return ApiResult.failed(ResultCode.USERNAME_OR_PASSWORD_ERROR); + } + + /** + * 用户名和密码异常 + * + * @param e + * @return + */ + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler(InvalidClientException.class) + public ApiResult handleInvalidGrantException(InvalidClientException e) { + log.error("错误信息:{}", e.getMessage(),e); + return ApiResult.failed(ResultCode.CLIENT_AUTHENTICATION_FAILED); + } + + + /** + * 账户异常(禁用、锁定、过期) + * + * @param e + * @return + */ + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler({InternalAuthenticationServiceException.class}) + public ApiResult handleInternalAuthenticationServiceException(InternalAuthenticationServiceException e) { + log.error("错误信息:{}", e.getMessage(),e); + return ApiResult.failed(e.getMessage()); + } + + /** + * token 无效或已过期 + * + * @param e + * @return + */ + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler({InvalidTokenException.class}) + public ApiResult handleInvalidTokenExceptionException(InvalidTokenException e) { + log.error("错误信息:{}", e.getMessage(),e); + return ApiResult.failed(e.getMessage()); + } + + /** + * token 无效或已过期 + * + * @param e + * @return + */ + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler({NoSuchClientException.class}) + public ApiResult noSuchClientException(NoSuchClientException e) { + log.error("错误信息:{}", e.getMessage(),e); + return ApiResult.failed(e.getMessage()); + } + + +} diff --git a/hoe-auth/src/main/java/com/recovery/auth/comm/test.java b/hoe-auth/src/main/java/com/recovery/auth/comm/test.java new file mode 100644 index 0000000..05a3f24 --- /dev/null +++ b/hoe-auth/src/main/java/com/recovery/auth/comm/test.java @@ -0,0 +1,12 @@ +package com.recovery.auth.comm; + + +import com.recovery.common.base.enums.PasswordEncoderTypeEnum; +import com.recovery.common.base.util.EncryptUtil; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +public class test { + public static void main(String[] args) { + System.out.println(EncryptUtil.encrypt("123")); + } +} diff --git a/hoe-auth/src/main/java/com/recovery/auth/comm/utils/CommonUtils.java b/hoe-auth/src/main/java/com/recovery/auth/comm/utils/CommonUtils.java new file mode 100644 index 0000000..528af4e --- /dev/null +++ b/hoe-auth/src/main/java/com/recovery/auth/comm/utils/CommonUtils.java @@ -0,0 +1,36 @@ +package com.recovery.auth.comm.utils; + +import cn.hutool.core.util.StrUtil; + +import com.recovery.common.base.constant.SecurityConstants; +import org.apache.logging.log4j.util.Strings; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +/** + * @author: + * @date: 2022/5/23 + */ +public class CommonUtils { + public static String getOAuth2ClientId() { + + HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + + String clientId = request.getParameter(SecurityConstants.CLIENT_ID_KEY); + if (StrUtil.isNotBlank(clientId)) { + return clientId; + } + + String basic = request.getHeader(SecurityConstants.AUTHORIZATION_KEY); + if (StrUtil.isNotBlank(basic) && basic.startsWith(SecurityConstants.BASIC_PREFIX)) { + basic = basic.replace(SecurityConstants.BASIC_PREFIX, Strings.EMPTY); + String basicPlainText = new String(Base64.getDecoder().decode(basic.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8); + clientId = basicPlainText.split(":")[0]; + } + return clientId; + } +} diff --git a/hoe-auth/src/main/java/com/recovery/auth/config/AuthorizationServerConfig.java b/hoe-auth/src/main/java/com/recovery/auth/config/AuthorizationServerConfig.java new file mode 100644 index 0000000..520d47c --- /dev/null +++ b/hoe-auth/src/main/java/com/recovery/auth/config/AuthorizationServerConfig.java @@ -0,0 +1,138 @@ +package com.recovery.auth.config; + +import cn.hutool.core.collection.CollectionUtil; + +import com.recovery.auth.security.details.client.ClientDetailsServiceImpl; +import com.recovery.auth.security.details.user.SysUserDetails; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; +import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; +import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; +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.provider.CompositeTokenGranter; +import org.springframework.security.oauth2.provider.TokenGranter; +import org.springframework.security.oauth2.provider.token.DefaultTokenServices; +import org.springframework.security.oauth2.provider.token.TokenEnhancer; +import org.springframework.security.oauth2.provider.token.TokenEnhancerChain; +import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; +import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory; + +import java.security.KeyPair; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * Created with IntelliJ IDEA. + * + * @author + * @date: 2021/11/24 + * @description: + * @modifiedBy: + * @version: 1.0 + */ +@Configuration +@EnableAuthorizationServer +@RequiredArgsConstructor +public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { + + private final AuthenticationManager authenticationManager; + private final ClientDetailsServiceImpl clientDetailsService; + + /** + * OAuth2客户端 + */ + @Override + @SneakyThrows + public void configure(ClientDetailsServiceConfigurer clients) { + clients.withClientDetails(clientDetailsService); + } + + /** + * 配置授权(authorization)以及令牌(token)的访问端点和令牌服务(token services) + */ + @Override + public void configure(AuthorizationServerEndpointsConfigurer endpoints) { + // Token增强 + TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain(); + List tokenEnhancers = new ArrayList<>(); + tokenEnhancers.add(tokenEnhancer()); + tokenEnhancers.add(jwtAccessTokenConverter()); + tokenEnhancerChain.setTokenEnhancers(tokenEnhancers); + + // 获取原有默认授权模式(授权码模式、密码模式、客户端模式、简化模式)的授权者 + List granterList = new ArrayList<>(Arrays.asList(endpoints.getTokenGranter())); + + CompositeTokenGranter compositeTokenGranter = new CompositeTokenGranter(granterList); + endpoints + .authenticationManager(authenticationManager) + .accessTokenConverter(jwtAccessTokenConverter()) + .tokenEnhancer(tokenEnhancerChain) + .tokenGranter(compositeTokenGranter) + .reuseRefreshTokens(true) + .tokenServices(tokenServices(endpoints)) + ; + } + + public DefaultTokenServices tokenServices(AuthorizationServerEndpointsConfigurer endpoints) { + TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain(); + List tokenEnhancers = new ArrayList<>(); + tokenEnhancers.add(tokenEnhancer()); + tokenEnhancers.add(jwtAccessTokenConverter()); + tokenEnhancerChain.setTokenEnhancers(tokenEnhancers); + + DefaultTokenServices tokenServices = new DefaultTokenServices(); + tokenServices.setTokenStore(endpoints.getTokenStore()); + tokenServices.setSupportRefreshToken(true); + tokenServices.setClientDetailsService(clientDetailsService); + tokenServices.setTokenEnhancer(tokenEnhancerChain); + return tokenServices; + + } + + /** + * JWT内容增强 + */ + @Bean + public TokenEnhancer tokenEnhancer() { + return (accessToken, authentication) -> { + Map additionalInfo = CollectionUtil.newHashMap(); + Object principal = authentication.getUserAuthentication().getPrincipal(); + if (principal instanceof SysUserDetails){ + SysUserDetails sysUserDetails = (SysUserDetails) principal; + additionalInfo.put("userId", sysUserDetails.getUserId()); + additionalInfo.put("username", sysUserDetails.getUsername()); + ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo); + } + + return accessToken; + }; + } + + /** + * 使用非对称加密算法对token签名 + */ + @Bean + public JwtAccessTokenConverter jwtAccessTokenConverter() { + JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); + converter.setKeyPair(keyPair()); + return converter; + } + + /** + * 密钥库中获取密钥对(公钥+私钥) + */ + @Bean + public KeyPair keyPair() { + KeyStoreKeyFactory factory = new KeyStoreKeyFactory(new ClassPathResource("jwt.jks"), "afd123".toCharArray()); + KeyPair keyPair = factory.getKeyPair("jwt", "afd123".toCharArray()); + return keyPair; + } +} \ No newline at end of file diff --git a/hoe-auth/src/main/java/com/recovery/auth/config/WebSecurityConfig.java b/hoe-auth/src/main/java/com/recovery/auth/config/WebSecurityConfig.java new file mode 100644 index 0000000..b77aac9 --- /dev/null +++ b/hoe-auth/src/main/java/com/recovery/auth/config/WebSecurityConfig.java @@ -0,0 +1,85 @@ +package com.recovery.auth.config; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +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; + +/** + * Created with IntelliJ IDEA. + * + * @author: + * @date: 2021/11/24 + * @description: + * @modifiedBy: + * @version: 1.0 + */ +@Configuration +@EnableWebSecurity +@Slf4j +@RequiredArgsConstructor +public class WebSecurityConfig extends WebSecurityConfigurerAdapter { + + private final UserDetailsService sysUserDetailsService; + + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + .authorizeRequests().antMatchers("/api/oauth/**").permitAll() + .anyRequest().authenticated() + .and() + .httpBasic() + .and() + .csrf().disable(); + } + + /** + * 认证管理对象 + * + * @return + * @throws Exception + */ + @Bean + public AuthenticationManager authenticationManagerBean() throws Exception { + return super.authenticationManagerBean(); + } + + /** + * 添加自定义认证器 + * + * @param auth + */ + @Override + public void configure(AuthenticationManagerBuilder auth) throws Exception { + auth.authenticationProvider(daoAuthenticationProvider()); + } + + /** + * 设置默认的用户名密码认证授权提供者 + * + * @return + */ + @Bean + public DaoAuthenticationProvider daoAuthenticationProvider() { + DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); + provider.setUserDetailsService(sysUserDetailsService); + provider.setPasswordEncoder(passwordEncoder()); + provider.setHideUserNotFoundExceptions(false); // 是否隐藏用户不存在异常,默认:true-隐藏;false-抛出异常; + return provider; + } + + @Bean + public PasswordEncoder passwordEncoder() { + return PasswordEncoderFactories.createDelegatingPasswordEncoder(); + } +} \ No newline at end of file diff --git a/hoe-auth/src/main/java/com/recovery/auth/config/ds/DataSourceConfig.java b/hoe-auth/src/main/java/com/recovery/auth/config/ds/DataSourceConfig.java new file mode 100644 index 0000000..980e342 --- /dev/null +++ b/hoe-auth/src/main/java/com/recovery/auth/config/ds/DataSourceConfig.java @@ -0,0 +1,95 @@ +package com.recovery.auth.config.ds; + +import com.alibaba.druid.pool.DruidDataSource; +import com.recovery.common.base.ds.DynamicDataSource; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.session.SqlSessionFactory; +import org.mybatis.spring.SqlSessionFactoryBean; +import org.mybatis.spring.SqlSessionTemplate; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; + +import javax.sql.DataSource; +import java.util.HashMap; +import java.util.Map; + +/** + * Created by YHYR on 2017-12-25 + */ +@Slf4j +@Configuration +@MapperScan(basePackages = DataSourceConfig.PACKAGE, sqlSessionFactoryRef = "commonSqlSessionFactory") +public class DataSourceConfig { + + // 精确到模块目录,以便跟其他数据源隔离 + static final String PACKAGE = "com.recovery.admin.boot.mapper"; + static final String MAPPER_LOCATION = "classpath:mapper/*.xml"; + + @Value("${master.datasource.url}") + private String masterDBUrl; + + @Value("${master.datasource.username}") + private String masterDBUser; + + @Value("${master.datasource.password}") + private String masterDBPassword; + + @Value("${master.datasource.driverClassName}") + private String masterDBDreiverName; + + + @Bean(name = "commonDataSource") + @Primary + public DynamicDataSource dynamicDataSource(){ + DynamicDataSource dynamicDataSource = DynamicDataSource.getInstance(); + + DruidDataSource masterDataSource = new DruidDataSource(); + try { + masterDataSource.setUrl(masterDBUrl); + masterDataSource.setUsername(masterDBUser); + masterDataSource.setPassword(masterDBPassword); + masterDataSource.setDriverClassName(masterDBDreiverName); + }catch (Exception e){ + log.error(e.getMessage()); + } + + Map map = new HashMap<>(); + map.put("master", masterDataSource); + dynamicDataSource.setTargetDataSources(map); + + return dynamicDataSource; + } + + @Bean(name = "commonSqlSessionFactory") + @Primary + public SqlSessionFactory sqlSessionFactory( + @Qualifier("commonDataSource") DataSource dynamicDataSource) + throws Exception { + SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); + bean.setDataSource(dynamicDataSource); + bean.setMapperLocations(new PathMatchingResourcePatternResolver() + .getResources(DataSourceConfig.MAPPER_LOCATION)); + return bean.getObject(); + + } + + @Bean(name = "commonTransactionManager") + @Primary + public DataSourceTransactionManager commonTransactionManager() { + return new DataSourceTransactionManager(dynamicDataSource()); + } + + + @Bean(name = "commonSqlSessionTemplate") + public SqlSessionTemplate sqlSessionTemplate( + @Qualifier("commonSqlSessionFactory") SqlSessionFactory sqlSessionFactory) + throws Exception { + return new SqlSessionTemplate(sqlSessionFactory); + } +} diff --git a/hoe-auth/src/main/java/com/recovery/auth/config/ds/TeachingDataSourceConfig.java b/hoe-auth/src/main/java/com/recovery/auth/config/ds/TeachingDataSourceConfig.java new file mode 100644 index 0000000..263c45a --- /dev/null +++ b/hoe-auth/src/main/java/com/recovery/auth/config/ds/TeachingDataSourceConfig.java @@ -0,0 +1,58 @@ +package com.recovery.auth.config.ds; + +import com.alibaba.druid.pool.DruidDataSource; +import org.apache.ibatis.session.SqlSessionFactory; +import org.mybatis.spring.SqlSessionFactoryBean; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; + +import javax.sql.DataSource; + +//@Configuration +//// 扫描 Mapper 接口并容器管理 +//@MapperScan(basePackages = TeachingDataSourceConfig.PACKAGE, sqlSessionFactoryRef = "teachingSqlSessionFactory") +public class TeachingDataSourceConfig { + // 精确到 auth 目录,以便跟其他数据源隔离 + static final String PACKAGE = "com.recovery.auth.dao.mapper"; + static final String MAPPER_LOCATION = "classpath:mapper/*.xml"; + + @Value("${admin.datasource.url}") + private String url; + + @Value("${admin.datasource.username}") + private String user; + + @Value("${admin.datasource.password}") + private String password; + + @Value("${admin.datasource.driverClassName}") + private String driverClass; + + @Bean(name = "adminDataSource") + public DataSource cpdDataSource() { + DruidDataSource dataSource = new DruidDataSource(); + dataSource.setDriverClassName(driverClass); + dataSource.setUrl(url); + dataSource.setUsername(user); + dataSource.setPassword(password); + return dataSource; + } + + @Bean(name = "adminTransactionManager") + public DataSourceTransactionManager courseTransactionManager() { + return new DataSourceTransactionManager(cpdDataSource()); + } + + @Bean(name = "adminSqlSessionFactory") + public SqlSessionFactory cpdSqlSessionFactory(@Qualifier("adminDataSource") DataSource cpdDataSource) + throws Exception { + final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); + sessionFactory.setDataSource(cpdDataSource); + sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver() + .getResources(TeachingDataSourceConfig.MAPPER_LOCATION)); + return sessionFactory.getObject(); + } +} diff --git a/hoe-auth/src/main/java/com/recovery/auth/controller/AuthController.java b/hoe-auth/src/main/java/com/recovery/auth/controller/AuthController.java new file mode 100644 index 0000000..ae5ff67 --- /dev/null +++ b/hoe-auth/src/main/java/com/recovery/auth/controller/AuthController.java @@ -0,0 +1,55 @@ +package com.recovery.auth.controller; + + +import com.nimbusds.jose.jwk.JWKSet; +import com.nimbusds.jose.jwk.RSAKey; +import com.recovery.auth.security.details.user.JwtAuthenticationRequest; +import com.recovery.auth.service.AuthService; +import com.recovery.common.base.result.ApiResult; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import java.security.KeyPair; +import java.security.Principal; +import java.security.interfaces.RSAPublicKey; +import java.util.HashMap; +import java.util.Map; + +/** + * @author: + */ +@RestController +@RequestMapping("/oauth") +@AllArgsConstructor +@Slf4j +public class AuthController { + + @Resource + AuthService authService; + + @PostMapping("/token") + public ApiResult postAccessToken(@RequestBody JwtAuthenticationRequest authenticationRequest, HttpServletRequest request){ + log.info("----------------获取token"); + HashMap map= new HashMap(); + try { + map = authService.login(authenticationRequest, request); + }catch (Exception e){ + log.error("获取token:"+e.getMessage(),e); + return ApiResult.failed(e.getMessage()); + } + return ApiResult.ok(map); + } + +// @GetMapping("/public-key") +// public Map getPublicKey() { +// RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); +// RSAKey key = new RSAKey.Builder(publicKey).build(); +// return new JWKSet(key).toJSONObject(); +// } +} diff --git a/hoe-auth/src/main/java/com/recovery/auth/exception/BusinessException.java b/hoe-auth/src/main/java/com/recovery/auth/exception/BusinessException.java new file mode 100644 index 0000000..f64f2ce --- /dev/null +++ b/hoe-auth/src/main/java/com/recovery/auth/exception/BusinessException.java @@ -0,0 +1,38 @@ +package com.recovery.auth.exception; + + + +import com.recovery.common.base.result.IResultCode; +import lombok.Data; + + +/** + * 自定义异常类 + */ +@Data +public class BusinessException extends RuntimeException{ + + private String code; + + private String msg; + + + public BusinessException(IResultCode apiCode) { + super(apiCode.getMsg()); + this.code = apiCode.getCode(); + this.msg = apiCode.getMsg(); + } + + public BusinessException(String code, String message) { + super(code+":"+message); + this.code = code; + this.msg = message; + } + + public BusinessException(String message) { + super("5001"+":"+message); + this.code = "5001"; + this.msg = message; + } + +} diff --git a/hoe-auth/src/main/java/com/recovery/auth/exception/GlobalExceptionHandler.java b/hoe-auth/src/main/java/com/recovery/auth/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..1397d98 --- /dev/null +++ b/hoe-auth/src/main/java/com/recovery/auth/exception/GlobalExceptionHandler.java @@ -0,0 +1,39 @@ +package com.recovery.auth.exception; + + + +import com.recovery.common.base.result.ApiResult; +import com.recovery.common.base.result.ResultCode; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +/** + * 全局异常处理 + */ +@Slf4j +@RestControllerAdvice +public class GlobalExceptionHandler { + + //自定义异常 + @ExceptionHandler(BusinessException.class) + public ApiResult systemExceptionHandler(BusinessException e) { + log.error("BusinessException全局异常:{}",e); + return ApiResult.failed(e.getCode(), e.getMsg()); + } + + //系统异常 + @ExceptionHandler(Exception.class) + public ApiResult exceptionHandler(Exception e) { + log.error("Exception全局异常:{}",e); + return ApiResult.failed(ResultCode.SYSTEM_EXECUTION_ERROR.getCode(), e.getMessage()); + } + + //Security +// @ExceptionHandler(value = AccessDeniedException.class) +// public void accessDeniedException(AccessDeniedException e) { +// throw e; +// } + + +} diff --git a/hoe-auth/src/main/java/com/recovery/auth/feign/UserFeignClient.java b/hoe-auth/src/main/java/com/recovery/auth/feign/UserFeignClient.java new file mode 100644 index 0000000..da954e1 --- /dev/null +++ b/hoe-auth/src/main/java/com/recovery/auth/feign/UserFeignClient.java @@ -0,0 +1,23 @@ +package com.recovery.auth.feign; + + + +import com.recovery.common.base.config.feign.FeignConfiguration; +import com.recovery.common.base.dto.UserAuthDTO; +import com.recovery.common.base.result.ApiResult; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * @author: + */ +@FeignClient(value = "hoe-admin",configuration = FeignConfiguration.class) +public interface UserFeignClient { + + @GetMapping("/api/rest/users/getUserByUsername") + ApiResult getUserByUsername(@RequestParam String username); + + @GetMapping("/api/rest/users/member/username") + ApiResult getMemberUserByUsername(@RequestParam String username); +} diff --git a/hoe-auth/src/main/java/com/recovery/auth/mapper/OauthClientDetailsMapper.java b/hoe-auth/src/main/java/com/recovery/auth/mapper/OauthClientDetailsMapper.java new file mode 100644 index 0000000..b423749 --- /dev/null +++ b/hoe-auth/src/main/java/com/recovery/auth/mapper/OauthClientDetailsMapper.java @@ -0,0 +1,21 @@ +package com.recovery.auth.mapper; + + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.recovery.common.base.po.base.OauthClientDetails; +import org.apache.ibatis.annotations.Mapper; + +/** +* @author Administrator +* @description 针对表【oauth_client_details(存储客户端的配置信息)】的数据库操作Mapper +* @createDate 2024-03-04 10:33:19 +* @Entity dao.entiy.OauthClientDetails +*/ +@Mapper +public interface OauthClientDetailsMapper extends BaseMapper { + +} + + + + diff --git a/hoe-auth/src/main/java/com/recovery/auth/rest/UserRest.java b/hoe-auth/src/main/java/com/recovery/auth/rest/UserRest.java new file mode 100644 index 0000000..3c4d240 --- /dev/null +++ b/hoe-auth/src/main/java/com/recovery/auth/rest/UserRest.java @@ -0,0 +1,17 @@ +package com.recovery.auth.rest; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +@RestController +@RequestMapping("/api") +@Slf4j +public class UserRest { + @Resource + private StringRedisTemplate stringRedisTemplate; + +} diff --git a/hoe-auth/src/main/java/com/recovery/auth/security/details/client/ClientDetailsServiceImpl.java b/hoe-auth/src/main/java/com/recovery/auth/security/details/client/ClientDetailsServiceImpl.java new file mode 100644 index 0000000..9d3bec7 --- /dev/null +++ b/hoe-auth/src/main/java/com/recovery/auth/security/details/client/ClientDetailsServiceImpl.java @@ -0,0 +1,43 @@ +package com.recovery.auth.security.details.client; + + + +import com.recovery.common.base.enums.PasswordEncoderTypeEnum; +import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.security.oauth2.provider.ClientDetails; +import org.springframework.security.oauth2.provider.ClientDetailsService; +import org.springframework.security.oauth2.provider.client.BaseClientDetails; +import org.springframework.stereotype.Service; + +/** + * Created with IntelliJ IDEA. + * + * @author: AI码师 + * @date: 2021/11/24 + * @description: + * @modifiedBy: + * @version: 1.0 + */ +@Service +@RequiredArgsConstructor +public class ClientDetailsServiceImpl implements ClientDetailsService { + @Override + @Cacheable(cacheNames = "auth", key = "'oauth-client:'+#clientId") + public ClientDetails loadClientByClientId(String clientId) { + // 后面通过feign从管理端获取,目前写死 + BaseClientDetails clientDetails = new BaseClientDetails( + "hoe", + "", + "all", + "password,client_credentials,refresh_token,authorization_code", + "", + "http://www.baidu.com" + + ); + clientDetails.setClientSecret(PasswordEncoderTypeEnum.NOOP.getPrefix() + "hoe"); + clientDetails.setAccessTokenValiditySeconds(3600); + clientDetails.setRefreshTokenValiditySeconds(36000000); + return clientDetails; + } +} diff --git a/hoe-auth/src/main/java/com/recovery/auth/security/details/user/JwtAuthenticationRequest.java b/hoe-auth/src/main/java/com/recovery/auth/security/details/user/JwtAuthenticationRequest.java new file mode 100644 index 0000000..342f09e --- /dev/null +++ b/hoe-auth/src/main/java/com/recovery/auth/security/details/user/JwtAuthenticationRequest.java @@ -0,0 +1,74 @@ +package com.recovery.auth.security.details.user; + +import java.io.Serializable; + +public class JwtAuthenticationRequest implements Serializable { + + private static final long serialVersionUID = -8445943548965154778L; + + private String username; + private String phone; + private String password; + private String verifyCode; + private String loginMethod; + private String visitorsType; + + public JwtAuthenticationRequest(String username,String phone,String password,String verifyCode,String loginMethod,String visitorsType) { + this.username = username; + this.phone = phone; + this.password = password; + this.verifyCode = verifyCode; + this.loginMethod = loginMethod; + this.visitorsType = visitorsType; + } + + public JwtAuthenticationRequest() { + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getUserPhone() { + return phone; + } + + public void setUserPhone(String phone) { + this.phone = phone; + } + + public String getVerifyCode() { + return verifyCode; + } + + public void setVerifyCode(String verifyCode) { + this.verifyCode = verifyCode; + } + + public String getLoginMethod() { + return loginMethod; + } + + public void setLoginMethod(String loginMethod) { + this.loginMethod = loginMethod; + } + public String getVisitorsType() { + return visitorsType; + } + + public void setVisitorsType(String visitorsType) { + this.visitorsType = visitorsType; + } +} diff --git a/hoe-auth/src/main/java/com/recovery/auth/security/details/user/SysUserDetails.java b/hoe-auth/src/main/java/com/recovery/auth/security/details/user/SysUserDetails.java new file mode 100644 index 0000000..89b04c3 --- /dev/null +++ b/hoe-auth/src/main/java/com/recovery/auth/security/details/user/SysUserDetails.java @@ -0,0 +1,76 @@ +package com.recovery.auth.security.details.user; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.Collection; + +/** + * @author: + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class SysUserDetails implements UserDetails { + + /** + * 扩展字段 + */ + private Long userId; + + /** + * 默认字段 + */ + private String username; + private String password; + /** + * 是否启用 + */ + private Boolean enabled; + /** + * 角色 + */ + private Collection authorities; + + + @Override + public Collection getAuthorities() { + return this.authorities; + } + + @Override + public String getPassword() { + return this.password; + } + + @Override + public String getUsername() { + return this.username; + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return this.enabled; + } +} diff --git a/hoe-auth/src/main/java/com/recovery/auth/security/details/user/SysUserDetailsServiceImpl.java b/hoe-auth/src/main/java/com/recovery/auth/security/details/user/SysUserDetailsServiceImpl.java new file mode 100644 index 0000000..626cba7 --- /dev/null +++ b/hoe-auth/src/main/java/com/recovery/auth/security/details/user/SysUserDetailsServiceImpl.java @@ -0,0 +1,79 @@ +package com.recovery.auth.security.details.user; + +import com.recovery.auth.feign.UserFeignClient; +import com.recovery.common.base.dto.UserAuthDTO; +import com.recovery.common.base.enums.PasswordEncoderTypeEnum; +import com.recovery.common.base.result.ApiResult; +import com.recovery.common.base.result.ResultCode; +import com.recovery.common.base.util.RedisUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.authentication.AccountExpiredException; +import org.springframework.security.authentication.DisabledException; +import org.springframework.security.authentication.LockedException; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +/** + * @author: + */ +@Service("sysUserDetailsService") +@Slf4j +@RequiredArgsConstructor +public class SysUserDetailsServiceImpl implements UserDetailsService { + + + @Resource + private UserFeignClient userFeignClient; + @Resource + RedisUtil redisUtil; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + // 后面从管理端获取用户信息 + ApiResult result = userFeignClient.getUserByUsername(username); + SysUserDetails userDetails = null; + if (ApiResult.ok().getCode().equals(result.getCode())) { + UserAuthDTO user = result.getData(); + if (null != user) { + userDetails = SysUserDetails.builder() + .userId(user.getUserId()) + .username(user.getUserName()) + //角色 +// .authorities(handleRoles(user.getRoles())) + .enabled(user.getStatus() == 1) + .password(PasswordEncoderTypeEnum.BCRYPT.getPrefix() + user.getPassword()) + .build(); + } + } + if (Objects.isNull(userDetails)) { + throw new UsernameNotFoundException(ResultCode.USER_NOT_EXIST.getMsg()); + } else if (!userDetails.isEnabled()) { + throw new DisabledException("该账户已被禁用!"); + } else if (!userDetails.isAccountNonLocked()) { + throw new LockedException("该账号已被锁定!"); + } else if (!userDetails.isAccountNonExpired()) { + throw new AccountExpiredException("该账号已过期!"); + } + return userDetails; + } + + private Collection handleRoles(List roles) { + Collection authorities = new ArrayList<>(); + for (String role : roles) { + authorities.add(new SimpleGrantedAuthority(role)); + } + return authorities; + } + + +} diff --git a/hoe-auth/src/main/java/com/recovery/auth/service/AuthService.java b/hoe-auth/src/main/java/com/recovery/auth/service/AuthService.java new file mode 100644 index 0000000..9298180 --- /dev/null +++ b/hoe-auth/src/main/java/com/recovery/auth/service/AuthService.java @@ -0,0 +1,13 @@ +package com.recovery.auth.service; + +import com.recovery.auth.security.details.user.JwtAuthenticationRequest; + +import javax.servlet.http.HttpServletRequest; +import java.util.HashMap; + +public interface AuthService { + + HashMap login(JwtAuthenticationRequest authenticationRequest, HttpServletRequest request); +} + + diff --git a/hoe-auth/src/main/java/com/recovery/auth/service/OauthClientDetailsService.java b/hoe-auth/src/main/java/com/recovery/auth/service/OauthClientDetailsService.java new file mode 100644 index 0000000..d8c96ed --- /dev/null +++ b/hoe-auth/src/main/java/com/recovery/auth/service/OauthClientDetailsService.java @@ -0,0 +1,14 @@ +package com.recovery.auth.service; + + +import com.baomidou.mybatisplus.extension.service.IService; +import com.recovery.common.base.po.base.OauthClientDetails; + +/** +* @author Administrator +* @description 针对表【oauth_client_details(存储客户端的配置信息)】的数据库操作Service +* @createDate 2024-03-04 10:33:19 +*/ +public interface OauthClientDetailsService extends IService { + +} diff --git a/hoe-auth/src/main/java/com/recovery/auth/service/impl/AuthServiceImpl.java b/hoe-auth/src/main/java/com/recovery/auth/service/impl/AuthServiceImpl.java new file mode 100644 index 0000000..e330dbd --- /dev/null +++ b/hoe-auth/src/main/java/com/recovery/auth/service/impl/AuthServiceImpl.java @@ -0,0 +1,96 @@ +package com.recovery.auth.service.impl; + + +import com.alibaba.fastjson.JSON; +import com.recovery.auth.exception.BusinessException; +import com.recovery.auth.feign.UserFeignClient; +import com.recovery.auth.security.details.user.JwtAuthenticationRequest; +import com.recovery.auth.security.details.user.SysUserDetails; +import com.recovery.auth.service.AuthService; +import com.recovery.common.base.dto.UserAuthDTO; +import com.recovery.common.base.dto.UserAuthorityDto; +import com.recovery.common.base.result.ApiResult; +import com.recovery.common.base.result.ResultCode; +import com.recovery.common.base.util.EncryptUtil; +import com.recovery.common.base.util.RedisUtil; +import com.recovery.common.base.utils.JwtUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.authentication.AccountExpiredException; +import org.springframework.security.authentication.DisabledException; +import org.springframework.security.authentication.LockedException; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** +* @author Administrator +* @description 针对表【oauth_client_details(存储客户端的配置信息)】的数据库操作Service +* @createDate 2024-03-04 10:33:19 +*/ +@Slf4j +@Service +public class AuthServiceImpl implements AuthService { + + @Resource + UserFeignClient userFeignClient; + @Resource + RedisUtil redisUtil; + + @Override + public HashMap login(JwtAuthenticationRequest authenticationRequest, HttpServletRequest request) { + HashMap map = new HashMap(); + if(null == authenticationRequest.getUsername() || "".equals(authenticationRequest.getUsername())){ + throw new BusinessException("账户不能为空"); + } + if(null == authenticationRequest.getPassword() || "".equals(authenticationRequest.getPassword())){ + throw new BusinessException("密码不能为空"); + } + + ApiResult result = userFeignClient.getUserByUsername(authenticationRequest.getUsername()); + UserAuthorityDto userDetails = new UserAuthorityDto(); + if (ResultCode.SUCCESS.getCode().equals(result.getCode())) { + UserAuthDTO user = result.getData(); + if (null != user) { + userDetails.setUserName(user.getUserName()); + userDetails.setId(user.getUserId()); + userDetails.setPassword(user.getPassword()); + } + } + + if (Objects.isNull(userDetails)) { + throw new BusinessException(result.getMsg()); + } + //加密 + if (userDetails.getUserName().equals(authenticationRequest.getUsername()) && userDetails.getPassword().equals(EncryptUtil.encrypt(authenticationRequest.getPassword()))) { + log.info("密码校验成功!"); + } else { + throw new BusinessException("密码错误!"); + } + //用户信息 + map.put("userId", userDetails.toString()); + map.put("name", userDetails.getName()); + map.put("userName", userDetails.getUserName()); + map.put("password",userDetails.getPassword()); + //生成token + String token = JwtUtils.generateToken(map); + //token + map.put("userInfo", userDetails); + map.put("token", token); + + //认证通过 使用userid 生成jwt token令牌 + try { + boolean resultRedis = redisUtil.set("userToken:" + token,userDetails , 24 * 600 * 7); + if (!resultRedis) { + throw new RuntimeException("网络链接失败,登录失败"); + } + } catch (Exception e) { + log.error(e.getMessage()); + } + return map; + } +} diff --git a/hoe-auth/src/main/java/com/recovery/auth/service/impl/OauthClientDetailsServiceImpl.java b/hoe-auth/src/main/java/com/recovery/auth/service/impl/OauthClientDetailsServiceImpl.java new file mode 100644 index 0000000..28cae58 --- /dev/null +++ b/hoe-auth/src/main/java/com/recovery/auth/service/impl/OauthClientDetailsServiceImpl.java @@ -0,0 +1,22 @@ +package com.recovery.auth.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; + +import com.recovery.auth.mapper.OauthClientDetailsMapper; +import com.recovery.auth.service.OauthClientDetailsService; +import com.recovery.common.base.po.base.OauthClientDetails; +import org.springframework.stereotype.Service; + +/** +* @author Administrator +* @description 针对表【oauth_client_details(存储客户端的配置信息)】的数据库操作Service实现 +* @createDate 2024-03-04 10:33:19 +*/ +@Service +public class OauthClientDetailsServiceImpl extends ServiceImpl implements OauthClientDetailsService { + +} + + + + diff --git a/hoe-auth/src/main/resources/bootstrap.yml b/hoe-auth/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..16cac24 --- /dev/null +++ b/hoe-auth/src/main/resources/bootstrap.yml @@ -0,0 +1,23 @@ + +spring: + application: + name: hoe-auth + main: + allow-bean-definition-overriding: true + profiles: + active: dev + cloud: + nacos: + discovery: + # metadata: + # serviceGroup: ytChen + server-addr: localhost:8848 + namespace: 11bfd099-10d6-4f2c-b969-58b76e435cce + config: + server-addr: localhost:8848 + file-extension: yaml + prefix: hoe-auth + group: dev + namespace: 11bfd099-10d6-4f2c-b969-58b76e435cce +server: + port: 9001 diff --git a/hoe-auth/src/main/resources/jwt.jks b/hoe-auth/src/main/resources/jwt.jks new file mode 100644 index 0000000..4b797d0 Binary files /dev/null and b/hoe-auth/src/main/resources/jwt.jks differ diff --git a/hoe-auth/src/main/resources/mapper/OauthClientDetailsMapper.xml b/hoe-auth/src/main/resources/mapper/OauthClientDetailsMapper.xml new file mode 100644 index 0000000..65e9535 --- /dev/null +++ b/hoe-auth/src/main/resources/mapper/OauthClientDetailsMapper.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/hoe-common/common-base/pom.xml b/hoe-common/common-base/pom.xml new file mode 100644 index 0000000..10d6a0a --- /dev/null +++ b/hoe-common/common-base/pom.xml @@ -0,0 +1,86 @@ + + + 4.0.0 + + com.recovery + hoe-common + 1.0.0 + + + + common-base + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.core + jackson-core + + + ch.qos.logback + logback-classic + + + org.springframework.boot + spring-boot-starter-data-redis + + + org.apache.commons + commons-pool2 + + + org.springframework.boot + spring-boot-configuration-processor + true + + + org.springframework + spring-jdbc + + + com.baomidou + mybatis-plus-annotation + 3.5.0 + + + com.alibaba + fastjson + 1.2.83 + + + com.alibaba + druid + 1.2.6 + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + + com.auth0 + java-jwt + 3.11.0 + + + + io.github.openfeign + feign-okhttp + + + org.apache.tomcat.embed + tomcat-embed-core + + + commons-codec + commons-codec + + + + \ No newline at end of file diff --git a/hoe-common/common-base/src/main/java/com/recovery/common/base/config/RedisConfig.java b/hoe-common/common-base/src/main/java/com/recovery/common/base/config/RedisConfig.java new file mode 100644 index 0000000..b60d11c --- /dev/null +++ b/hoe-common/common-base/src/main/java/com/recovery/common/base/config/RedisConfig.java @@ -0,0 +1,47 @@ +package com.recovery.common.base.config; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; +/** + * @author: + */ +@Configuration +@AutoConfigureBefore(RedisAutoConfiguration.class) +public class RedisConfig { + @Bean + public RedisTemplate redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) { + + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(lettuceConnectionFactory); + + // 用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值 + StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); + redisTemplate.setKeySerializer(stringRedisSerializer); // key + Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); + ObjectMapper objectMapper = new ObjectMapper(); + // 指定要序列化的域(field,get,set),访问修饰符(public,private,protected) + objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL); + jackson2JsonRedisSerializer.setObjectMapper(objectMapper); + + redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); //value + + redisTemplate.setHashKeySerializer(stringRedisSerializer); + redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer); + + redisTemplate.afterPropertiesSet(); + return redisTemplate; + } + +} + diff --git a/hoe-common/common-base/src/main/java/com/recovery/common/base/config/feign/CommonRequestInterceptor.java b/hoe-common/common-base/src/main/java/com/recovery/common/base/config/feign/CommonRequestInterceptor.java new file mode 100644 index 0000000..7c525e7 --- /dev/null +++ b/hoe-common/common-base/src/main/java/com/recovery/common/base/config/feign/CommonRequestInterceptor.java @@ -0,0 +1,48 @@ +package com.recovery.common.base.config.feign; + + +import com.recovery.common.base.util.HspHostUtil; +import feign.RequestInterceptor; +import feign.RequestTemplate; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; + +public class CommonRequestInterceptor implements RequestInterceptor{ + + @Override + public void apply(RequestTemplate requestTemplate) { + + HttpServletRequest request = getHttpServletRequest(); + if(request != null && request.getHeader("hospitalHost") != null){ + String hospitalHost = request.getHeader("hospitalHost"); + String hospiatlProt = request.getHeader("hospiatlProt"); + String websiteDomain = request.getHeader("websiteDomain"); + String innerInternet = request.getHeader("innerInternet"); + String token = request.getHeader("userTokenHead"); + requestTemplate.header("hospitalHost", hospitalHost); + requestTemplate.header("innerInternet", innerInternet); + requestTemplate.header("websiteDomain",websiteDomain); + requestTemplate.header("hospiatlProt",hospiatlProt); + requestTemplate.header("userTokenHead",token); + } else { + //取当前线程变量中的域名进行传递 + requestTemplate.header("hospitalHost", HspHostUtil.getHspHost()); + } + } + + private HttpServletRequest getHttpServletRequest() { + try { + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + if(attributes != null){ + return attributes.getRequest(); + }else{ + return null; + } + } catch (Exception e) { + return null; + } + } + +} diff --git a/hoe-common/common-base/src/main/java/com/recovery/common/base/config/feign/FeignConfiguration.java b/hoe-common/common-base/src/main/java/com/recovery/common/base/config/feign/FeignConfiguration.java new file mode 100644 index 0000000..776a366 --- /dev/null +++ b/hoe-common/common-base/src/main/java/com/recovery/common/base/config/feign/FeignConfiguration.java @@ -0,0 +1,14 @@ +package com.recovery.common.base.config.feign; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + + +@Configuration +public class FeignConfiguration { + + @Bean + CommonRequestInterceptor getCommonRequestInterceptor(){ + return new CommonRequestInterceptor(); + } +} diff --git a/hoe-common/common-base/src/main/java/com/recovery/common/base/config/redis/BaseRedisCache.java b/hoe-common/common-base/src/main/java/com/recovery/common/base/config/redis/BaseRedisCache.java new file mode 100644 index 0000000..4835929 --- /dev/null +++ b/hoe-common/common-base/src/main/java/com/recovery/common/base/config/redis/BaseRedisCache.java @@ -0,0 +1,56 @@ +package com.recovery.common.base.config.redis; + +import java.util.Set; + +public interface BaseRedisCache { + /** + * 描述:获取缓存 + */ + public String get(String key); + public Object get_obj(String key); + /** + * 描述:删除缓存 + */ + public void remove(String key); + /** + * 描述:添加缓存 + */ + public void put(String key, String value); + public void put_obj(String key, Object value); + + /** + * 描述:添加缓存并且添加失效时间 + */ + public void put(String key, String value, int second); + public void put_obj(String key, Object value, int second); + /** + * 描述:根据缓存value值加减 + */ + public long incr(String key, long value); + + /** + * 描述:设置超时时间 + */ + public void expire(String key); + public void expireDays(String key, int days); + + /** + * 是否存在 + * @param key + * @return + */ + public Boolean isExists(String key); + + /** + * 匹配所有key值 + * @param patten + * @return + */ + public Set keySet(String patten); + + /** + * 清除某类key + * @param patten + */ + public void clean(String patten); +} diff --git a/hoe-common/common-base/src/main/java/com/recovery/common/base/config/redis/BaseRedisCacheImpl.java b/hoe-common/common-base/src/main/java/com/recovery/common/base/config/redis/BaseRedisCacheImpl.java new file mode 100644 index 0000000..c933c94 --- /dev/null +++ b/hoe-common/common-base/src/main/java/com/recovery/common/base/config/redis/BaseRedisCacheImpl.java @@ -0,0 +1,114 @@ +package com.recovery.common.base.config.redis; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.stereotype.Component; + +import java.util.Set; +import java.util.concurrent.TimeUnit; + +@Component("BaseRedisCacheImpl") +public class BaseRedisCacheImpl implements BaseRedisCache { + + @Autowired + private StringRedisTemplate redisTemplate; + + @Autowired + private RedisTemplate redisTemplate_obj; + + + @Override + public String get(String key) { + ValueOperations operations = redisTemplate.opsForValue(); + return operations.get(key); + } + + @Override + public Object get_obj(String key) { + return redisTemplate_obj.opsForValue().get(key); + } + + @Override + public void remove(String key) { + if (redisTemplate.hasKey(key)) { + redisTemplate.delete(key); + } + } + + @Override + public void put(String key, String value) { + try { + if(value == null) { + value = ""; + } + ValueOperations operations = redisTemplate.opsForValue(); + operations.set(key, value); + } catch (Exception err) { + + } + } + + @Override + public void put_obj(String key, Object value) { + redisTemplate_obj.opsForValue().set(key,value); + } + + @Override + public void put(String key, String value, int second) { + if(value == null) { + value = ""; + } + ValueOperations operations = redisTemplate.opsForValue(); + operations.set(key, value, second, TimeUnit.SECONDS); + } + @Override + public void put_obj(String key, Object value, int second) { + redisTemplate_obj.opsForValue().set(key, value, second, TimeUnit.SECONDS); + } + @Override + public long incr(String key, long value) { + ValueOperations operations = redisTemplate.opsForValue(); + return operations.increment(key,value); + } + + @Override + public void expire(String key) { + try { + redisTemplate.expire(key, 15, TimeUnit.DAYS); + } catch (Exception err) { + + } + } + + @Override + public void expireDays(String key, int days) { + try { + redisTemplate.expire(key, days, TimeUnit.DAYS); + } catch (Exception err) { + + } + } + + @Override + public Boolean isExists(String key) { + if(redisTemplate.hasKey(key)){ + return true; + } + return false; + } + + @Override + public Set keySet(String patten) { + return redisTemplate.keys(patten); + } + + @Override + public void clean(String patten) { + Set keys = redisTemplate.keys(patten); + if(keys.size() > 0) { + redisTemplate.delete(keys); + } + } +} diff --git a/hoe-common/common-base/src/main/java/com/recovery/common/base/config/redis/RedisCache.java b/hoe-common/common-base/src/main/java/com/recovery/common/base/config/redis/RedisCache.java new file mode 100644 index 0000000..ffd2beb --- /dev/null +++ b/hoe-common/common-base/src/main/java/com/recovery/common/base/config/redis/RedisCache.java @@ -0,0 +1,47 @@ +package com.recovery.common.base.config.redis; + +public interface RedisCache { + /** + * 描述:获取缓存 + */ + public String get(String key); + public Object get_obj(String key); + /** + * 描述:删除缓存 + */ + public void remove(String key); + /** + * 描述:添加缓存 + */ + public void put(String key, String value); + public void put_obj(String key, Object value); + + /** + * 描述:添加缓存并且添加失效时间 + */ + public void put(String key, String value, int second); + public void put_obj(String key, Object value, int second); + /** + * 描述:根据缓存value值加减 + */ + public long incr(String key, long value); + + /** + * 描述:设置超时时间 + */ + public void expire(String key); + public void expireDays(String key,int days); + + /** + * 是否存在 + * @param key + * @return + */ + Boolean isExists(String key); + + /** + * 清除某类key + * @param patten + */ + public void clean(String patten); +} diff --git a/hoe-common/common-base/src/main/java/com/recovery/common/base/config/redis/RedisCacheImpl.java b/hoe-common/common-base/src/main/java/com/recovery/common/base/config/redis/RedisCacheImpl.java new file mode 100644 index 0000000..c37a6ac --- /dev/null +++ b/hoe-common/common-base/src/main/java/com/recovery/common/base/config/redis/RedisCacheImpl.java @@ -0,0 +1,109 @@ +package com.recovery.common.base.config.redis; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.stereotype.Component; + +import java.util.Set; +import java.util.concurrent.TimeUnit; + +@Component("RedisCacheImpl") +public class RedisCacheImpl implements RedisCache { + + @Autowired + private StringRedisTemplate redisTemplate; + + @Autowired + private RedisTemplate redisTemplate_obj; + + + @Override + public String get(String key) { + if (null == key) { + return null; + } + ValueOperations operations = redisTemplate.opsForValue(); + return operations.get(key); + } + + @Override + public Object get_obj(String key) { + if (null == key) { + return null; + } + return redisTemplate_obj.opsForValue().get(key); + } + + @Override + public void remove(String key) { + if (redisTemplate.hasKey(key)) { + redisTemplate.delete(key); + } + } + + @Override + public void put(String key, String value) { + try { + ValueOperations operations = redisTemplate.opsForValue(); + operations.set(key, value); + } catch (Exception err) { + + } + } + + @Override + public void put_obj(String key, Object value) { + redisTemplate_obj.opsForValue().set(key,value); + } + + @Override + public void put(String key, String value, int second) { + ValueOperations operations = redisTemplate.opsForValue(); + operations.set(key, value, second, TimeUnit.SECONDS); + } + @Override + public void put_obj(String key, Object value, int second) { + redisTemplate_obj.opsForValue().set(key, value, second, TimeUnit.SECONDS); + } + @Override + public long incr(String key, long value) { + ValueOperations operations = redisTemplate.opsForValue(); + return operations.increment(key,value); + } + + @Override + public void expire(String key) { + try { + redisTemplate.expire(key, 15, TimeUnit.DAYS); + } catch (Exception err) { + + } + } + + @Override + public void expireDays(String key, int days) { + try { + redisTemplate.expire(key, days, TimeUnit.DAYS); + } catch (Exception err) { + + } + } + + @Override + public Boolean isExists(String key) { + if(redisTemplate.hasKey(key)){ + return true; + } + return false; + } + + @Override + public void clean(String patten) { + Set keys = redisTemplate.keys(patten); + if(keys.size() > 0) { + redisTemplate.delete(keys); + } + } +} diff --git a/hoe-common/common-base/src/main/java/com/recovery/common/base/constant/Constants.java b/hoe-common/common-base/src/main/java/com/recovery/common/base/constant/Constants.java new file mode 100644 index 0000000..e4664fa --- /dev/null +++ b/hoe-common/common-base/src/main/java/com/recovery/common/base/constant/Constants.java @@ -0,0 +1,48 @@ +package com.recovery.common.base.constant; + +import cn.hutool.core.date.DateUtil; + +import java.util.HashMap; + +public class Constants { + public static int OK = 1; + public static int LOGIN_FAIL = 2; + public static int PARAM_NULL = 3; + public static int PARAM_ERR = 4; + public static int SYSTEM_ERR = 5; + public static int OTHER = 6; + //格式异常 + public static int FORMAT_ERR=7; + public final static String PARAM_NULL_MSG = "参数为空"; + public final static String PARAM_ERR_MSG = "参数错误"; + public final static String SYSTEM_ERR_MSG = "系统异常"; + + + + public final static String UPLOAD_SIZE_ERR_MSG = "上传大小错误"; + + + public static final String LOGIN_USRE_TOKEN = "x-userToken"; + + + public final static String WHOLE_DOMAIN_REDIS_KEY = "HOE_WHOLE_DOMAIN_REDIS_KEY"; + public final static String[] CHINESE_NUM = {"一","二","三","四","五","六","七","八","九","十" + ,"十一","十二","十三","十四","十五","十六","十七","十八","十九","二十" + ,"二十一","二十二","二十三","二十四","二十五","二十六","二十七","二十八","二十九","三十" + ,"三十一","三十一","三十一","三十一","三十一","三十一","三十一","三十一","三十一","四十"}; + + + public final static String[] WEEKS = {"星期日","星期一","星期二","星期三","星期四","星期五","星期六"}; + + + + public static void main(String[] args) { +// String[] distNames = dist_name.split(","); +// Random random=new Random(); +// int number1 = random.nextInt(distNames.length); +// String distName1 = distNames[number1]; +// int number2 = random.nextInt(distNames.length); +// String distName2 = distNames[number2]; +// System.out.println(distName1 + distName2); + } +} diff --git a/hoe-common/common-base/src/main/java/com/recovery/common/base/constant/GlobalConstants.java b/hoe-common/common-base/src/main/java/com/recovery/common/base/constant/GlobalConstants.java new file mode 100644 index 0000000..b8b9218 --- /dev/null +++ b/hoe-common/common-base/src/main/java/com/recovery/common/base/constant/GlobalConstants.java @@ -0,0 +1,5 @@ +package com.recovery.common.base.constant; + +public interface GlobalConstants { + String URL_PERM_ROLES_KEY = "system:perm_roles_rule:url:"; +} diff --git a/hoe-common/common-base/src/main/java/com/recovery/common/base/constant/SecurityConstants.java b/hoe-common/common-base/src/main/java/com/recovery/common/base/constant/SecurityConstants.java new file mode 100644 index 0000000..c22515f --- /dev/null +++ b/hoe-common/common-base/src/main/java/com/recovery/common/base/constant/SecurityConstants.java @@ -0,0 +1,41 @@ +package com.recovery.common.base.constant; +/** + * @author: + */ +public interface SecurityConstants { + + /** + * 认证请求头key + */ + String AUTHORIZATION_KEY = "Authorization"; + + /** + * JWT令牌前缀 + */ + String JWT_PREFIX = "Bearer "; + + + /** + * Basic认证前缀 + */ + String BASIC_PREFIX = "Basic "; + + /** + * JWT载体key + */ + String JWT_PAYLOAD_KEY = "payload"; + + + /** + * JWT存储权限前缀 + */ + String AUTHORITY_PREFIX = "ROLE_"; + + /** + * JWT存储权限属性 + */ + String JWT_AUTHORITIES_KEY = "authorities"; + + + String CLIENT_ID_KEY = "client_id"; +} diff --git a/hoe-common/common-base/src/main/java/com/recovery/common/base/ds/DataSourceContextHolder.java b/hoe-common/common-base/src/main/java/com/recovery/common/base/ds/DataSourceContextHolder.java new file mode 100644 index 0000000..8caf47b --- /dev/null +++ b/hoe-common/common-base/src/main/java/com/recovery/common/base/ds/DataSourceContextHolder.java @@ -0,0 +1,21 @@ +package com.recovery.common.base.ds; + +/** + * Created by RyanWang on 2019-07-24 + */ + +public class DataSourceContextHolder { + private static final ThreadLocal contextHolder = new ThreadLocal(); + + public static synchronized void setDBType(String dbType){ + contextHolder.set(dbType); + } + + public static String getDBType(){ + return contextHolder.get(); + } + + public static void clearDBType(){ + contextHolder.remove(); + } +} diff --git a/hoe-common/common-base/src/main/java/com/recovery/common/base/ds/DynamicDataSource.java b/hoe-common/common-base/src/main/java/com/recovery/common/base/ds/DynamicDataSource.java new file mode 100644 index 0000000..3855f6b --- /dev/null +++ b/hoe-common/common-base/src/main/java/com/recovery/common/base/ds/DynamicDataSource.java @@ -0,0 +1,43 @@ +package com.recovery.common.base.ds; + +import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; + +import java.util.HashMap; +import java.util.Map; + +/** + * Created by RyanWang on 2019-07-24 + */ + +public class DynamicDataSource extends AbstractRoutingDataSource { + private static DynamicDataSource instance; + private static byte[] lock=new byte[0]; + private static Map dataSourceMap=new HashMap(); + + @Override + public void setTargetDataSources(Map targetDataSources) { + super.setTargetDataSources(targetDataSources); + dataSourceMap.putAll(targetDataSources); + super.afterPropertiesSet();// 必须添加该句,否则新添加数据源无法识别到 + } + + public Map getDataSourceMap() { + return dataSourceMap; + } + + public static synchronized DynamicDataSource getInstance(){ + if(instance==null){ + synchronized (lock){ + if(instance==null){ + instance=new DynamicDataSource(); + } + } + } + return instance; + } + //必须实现其方法 + @Override + protected Object determineCurrentLookupKey() { + return DataSourceContextHolder.getDBType(); + } +} diff --git a/hoe-common/common-base/src/main/java/com/recovery/common/base/dto/MemberUserAuthDTO.java b/hoe-common/common-base/src/main/java/com/recovery/common/base/dto/MemberUserAuthDTO.java new file mode 100644 index 0000000..23a0ecd --- /dev/null +++ b/hoe-common/common-base/src/main/java/com/recovery/common/base/dto/MemberUserAuthDTO.java @@ -0,0 +1,41 @@ +package com.recovery.common.base.dto; + +import lombok.Data; + +import java.util.List; + +/** + * @author: + */ +@Data +public class MemberUserAuthDTO { + + /** + * 用户ID + */ + private Long userId; + + /** + * 用户名 + */ + private String username; + + /** + * 用户密码 + */ + private String password; + + /** + * 用户状态:1-有效;0-禁用 + */ + private Integer status; + + /** + * 用户角色编码集合 ["ROOT","ADMIN"] + */ + + private List roles; + + + +} diff --git a/hoe-common/common-base/src/main/java/com/recovery/common/base/dto/OAuth2ClientDTO.java b/hoe-common/common-base/src/main/java/com/recovery/common/base/dto/OAuth2ClientDTO.java new file mode 100644 index 0000000..2f439b0 --- /dev/null +++ b/hoe-common/common-base/src/main/java/com/recovery/common/base/dto/OAuth2ClientDTO.java @@ -0,0 +1,66 @@ +package com.recovery.common.base.dto; + +import lombok.Data; + +/** + * @author: + */ +@Data +public class OAuth2ClientDTO { + + /** + * 客户端ID + */ + private String clientId; + + /** + * 客户端密钥 + */ + private String clientSecret; + + /** + * 资源id列表 + */ + private String resourceIds; + + /** + * 授权范围 + */ + private String scope; + + /** + * 授权方式 + */ + private String authorizedGrantTypes; + + /** + * 回调地址 + */ + private String webServerRedirectUri; + + /** + * 权限列表 + */ + private String authorities; + + /** + * 认证令牌时效 + */ + private Integer accessTokenValidity; + + /** + * 刷新令牌时效 + */ + private Integer refreshTokenValidity; + + /** + * 扩展信息 + */ + private String additionalInformation; + + /** + * 是否自动放行 + */ + private String autoapprove; + +} diff --git a/hoe-common/common-base/src/main/java/com/recovery/common/base/dto/RolePermissionDTO.java b/hoe-common/common-base/src/main/java/com/recovery/common/base/dto/RolePermissionDTO.java new file mode 100644 index 0000000..e8bd03d --- /dev/null +++ b/hoe-common/common-base/src/main/java/com/recovery/common/base/dto/RolePermissionDTO.java @@ -0,0 +1,15 @@ +package com.recovery.common.base.dto; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.List; +/** + * @author: + */ +@Data +@Accessors(chain = true) +public class RolePermissionDTO { + private Long roleId; + private List permissionIds; + private Long menuId; +} diff --git a/hoe-common/common-base/src/main/java/com/recovery/common/base/dto/UserAuthDTO.java b/hoe-common/common-base/src/main/java/com/recovery/common/base/dto/UserAuthDTO.java new file mode 100644 index 0000000..711d9ba --- /dev/null +++ b/hoe-common/common-base/src/main/java/com/recovery/common/base/dto/UserAuthDTO.java @@ -0,0 +1,41 @@ +package com.recovery.common.base.dto; + +import lombok.Data; + +import java.util.List; + +/** + * @author: + */ +@Data +public class UserAuthDTO { + + /** + * 用户ID + */ + private Long userId; + + /** + * 用户名 + */ + private String userName; + + /** + * 用户密码 + */ + private String password; + + /** + * 用户状态:1-有效;0-禁用 + */ + private Integer status; + + /** + * 用户角色编码集合 ["ROOT","ADMIN"] + */ + + private List roles; + + + +} diff --git a/hoe-common/common-base/src/main/java/com/recovery/common/base/dto/UserAuthorityDto.java b/hoe-common/common-base/src/main/java/com/recovery/common/base/dto/UserAuthorityDto.java new file mode 100644 index 0000000..085d5cd --- /dev/null +++ b/hoe-common/common-base/src/main/java/com/recovery/common/base/dto/UserAuthorityDto.java @@ -0,0 +1,34 @@ +package com.recovery.common.base.dto; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class UserAuthorityDto implements Serializable { + + /** + * 主键 + */ + private Long id; + + /** + * 姓名 + */ + private String name; + + /** + * 手机号 + */ + private String phone; + + /** + * 账号 + */ + private String userName; + + /** + * 密码 + */ + private String password; +} diff --git a/hoe-common/common-base/src/main/java/com/recovery/common/base/enums/ClientEnums.java b/hoe-common/common-base/src/main/java/com/recovery/common/base/enums/ClientEnums.java new file mode 100644 index 0000000..875ff3c --- /dev/null +++ b/hoe-common/common-base/src/main/java/com/recovery/common/base/enums/ClientEnums.java @@ -0,0 +1,21 @@ +package com.recovery.common.base.enums; + +import lombok.Getter; + +/** + * @author: + * @date: 2022/5/23 + */ +public enum ClientEnums { + MEMBER_CLIENT("member","会员客户端"), + ADMIN_CLIENT("ams","后台客户端"); + + @Getter + private String name; + @Getter + private String desc; + ClientEnums(String name, String desc) { + this.name = name; + this.desc = desc; + } +} diff --git a/hoe-common/common-base/src/main/java/com/recovery/common/base/enums/PasswordEncoderTypeEnum.java b/hoe-common/common-base/src/main/java/com/recovery/common/base/enums/PasswordEncoderTypeEnum.java new file mode 100644 index 0000000..101e41d --- /dev/null +++ b/hoe-common/common-base/src/main/java/com/recovery/common/base/enums/PasswordEncoderTypeEnum.java @@ -0,0 +1,21 @@ +package com.recovery.common.base.enums; + +import lombok.Getter; + + +/** + * @author: + */ +public enum PasswordEncoderTypeEnum { + // + BCRYPT("{bcrypt}","BCRYPT加密"), + NOOP("{noop}","无加密明文"); + + @Getter + private String prefix; + + PasswordEncoderTypeEnum(String prefix, String desc){ + this.prefix=prefix; + } + +} diff --git a/hoe-common/common-base/src/main/java/com/recovery/common/base/po/base/DBInfo.java b/hoe-common/common-base/src/main/java/com/recovery/common/base/po/base/DBInfo.java new file mode 100644 index 0000000..e22aeb7 --- /dev/null +++ b/hoe-common/common-base/src/main/java/com/recovery/common/base/po/base/DBInfo.java @@ -0,0 +1,36 @@ +package com.recovery.common.base.po.base; + + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.io.Serializable; + +/** + * Created by YHYR on 2017-12-25 + */ +@Data +@TableName("db_info") +public class DBInfo implements Serializable { + + @TableId(value = "id", type = IdType.AUTO) + private int id; + + private String dbName; + + private String dbIp; + + private int dbPort; + + private String dbUser; + + private String dbPasswd; + + private String module; + + private String hospitalHost; + + private String hospitalCode; +} diff --git a/hoe-common/common-base/src/main/java/com/recovery/common/base/po/base/OauthClientDetails.java b/hoe-common/common-base/src/main/java/com/recovery/common/base/po/base/OauthClientDetails.java new file mode 100644 index 0000000..feee7b8 --- /dev/null +++ b/hoe-common/common-base/src/main/java/com/recovery/common/base/po/base/OauthClientDetails.java @@ -0,0 +1,140 @@ +package com.recovery.common.base.po.base; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.io.Serializable; + +/** + * 存储客户端的配置信息 + * @TableName oauth_client_details + */ +@TableName(value ="oauth_client_details") +@Data +public class OauthClientDetails implements Serializable { + /** + * + */ + @TableId + private String clientId; + + /** + * + */ + private String resourceIds; + + /** + * + */ + private String clientSecret; + + /** + * + */ + private String scope; + + /** + * + */ + private String authorizedGrantTypes; + + /** + * + */ + private String webServerRedirectUri; + + /** + * + */ + private String authorities; + + /** + * + */ + private Integer accessTokenValidity; + + /** + * + */ + private Integer refreshTokenValidity; + + /** + * + */ + private String additionalInformation; + + /** + * + */ + private String autoapprove; + + @TableField(exist = false) + private static final long serialVersionUID = 1L; + + @Override + public boolean equals(Object that) { + if (this == that) { + return true; + } + if (that == null) { + return false; + } + if (getClass() != that.getClass()) { + return false; + } + OauthClientDetails other = (OauthClientDetails) that; + return (this.getClientId() == null ? other.getClientId() == null : this.getClientId().equals(other.getClientId())) + && (this.getResourceIds() == null ? other.getResourceIds() == null : this.getResourceIds().equals(other.getResourceIds())) + && (this.getClientSecret() == null ? other.getClientSecret() == null : this.getClientSecret().equals(other.getClientSecret())) + && (this.getScope() == null ? other.getScope() == null : this.getScope().equals(other.getScope())) + && (this.getAuthorizedGrantTypes() == null ? other.getAuthorizedGrantTypes() == null : this.getAuthorizedGrantTypes().equals(other.getAuthorizedGrantTypes())) + && (this.getWebServerRedirectUri() == null ? other.getWebServerRedirectUri() == null : this.getWebServerRedirectUri().equals(other.getWebServerRedirectUri())) + && (this.getAuthorities() == null ? other.getAuthorities() == null : this.getAuthorities().equals(other.getAuthorities())) + && (this.getAccessTokenValidity() == null ? other.getAccessTokenValidity() == null : this.getAccessTokenValidity().equals(other.getAccessTokenValidity())) + && (this.getRefreshTokenValidity() == null ? other.getRefreshTokenValidity() == null : this.getRefreshTokenValidity().equals(other.getRefreshTokenValidity())) + && (this.getAdditionalInformation() == null ? other.getAdditionalInformation() == null : this.getAdditionalInformation().equals(other.getAdditionalInformation())) + && (this.getAutoapprove() == null ? other.getAutoapprove() == null : this.getAutoapprove().equals(other.getAutoapprove())); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((getClientId() == null) ? 0 : getClientId().hashCode()); + result = prime * result + ((getResourceIds() == null) ? 0 : getResourceIds().hashCode()); + result = prime * result + ((getClientSecret() == null) ? 0 : getClientSecret().hashCode()); + result = prime * result + ((getScope() == null) ? 0 : getScope().hashCode()); + result = prime * result + ((getAuthorizedGrantTypes() == null) ? 0 : getAuthorizedGrantTypes().hashCode()); + result = prime * result + ((getWebServerRedirectUri() == null) ? 0 : getWebServerRedirectUri().hashCode()); + result = prime * result + ((getAuthorities() == null) ? 0 : getAuthorities().hashCode()); + result = prime * result + ((getAccessTokenValidity() == null) ? 0 : getAccessTokenValidity().hashCode()); + result = prime * result + ((getRefreshTokenValidity() == null) ? 0 : getRefreshTokenValidity().hashCode()); + result = prime * result + ((getAdditionalInformation() == null) ? 0 : getAdditionalInformation().hashCode()); + result = prime * result + ((getAutoapprove() == null) ? 0 : getAutoapprove().hashCode()); + return result; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(getClass().getSimpleName()); + sb.append(" ["); + sb.append("Hash = ").append(hashCode()); + sb.append(", clientId=").append(clientId); + sb.append(", resourceIds=").append(resourceIds); + sb.append(", clientSecret=").append(clientSecret); + sb.append(", scope=").append(scope); + sb.append(", authorizedGrantTypes=").append(authorizedGrantTypes); + sb.append(", webServerRedirectUri=").append(webServerRedirectUri); + sb.append(", authorities=").append(authorities); + sb.append(", accessTokenValidity=").append(accessTokenValidity); + sb.append(", refreshTokenValidity=").append(refreshTokenValidity); + sb.append(", additionalInformation=").append(additionalInformation); + sb.append(", autoapprove=").append(autoapprove); + sb.append(", serialVersionUID=").append(serialVersionUID); + sb.append("]"); + return sb.toString(); + } +} \ No newline at end of file diff --git a/hoe-common/common-base/src/main/java/com/recovery/common/base/result/ApiResult.java b/hoe-common/common-base/src/main/java/com/recovery/common/base/result/ApiResult.java new file mode 100644 index 0000000..e54f3a6 --- /dev/null +++ b/hoe-common/common-base/src/main/java/com/recovery/common/base/result/ApiResult.java @@ -0,0 +1,95 @@ +package com.recovery.common.base.result; +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; + +import java.io.Serializable; + +/** + * Created with IntelliJ IDEA. + * + * @author: + * @date:2021/11/24 + * @description: + * @modifiedBy: + * @version: 1.0 + */ +@Data +@JsonInclude(JsonInclude.Include.NON_NULL) +public class ApiResult implements Serializable { + + private String code; + + private T data; + + private String msg; + + private Integer total; + + public static ApiResult ok() { + return ok(null); + } + + public static ApiResult ok(T data) { + ResultCode rce = ResultCode.SUCCESS; + if (data instanceof Boolean && Boolean.FALSE.equals(data)) { + rce = ResultCode.SYSTEM_EXECUTION_ERROR; + } + return result(rce, data); + } + + public static ApiResult ok(T data, Long total) { + ApiResult result = new ApiResult<>(); + result.setCode(ResultCode.SUCCESS.getCode()); + result.setMsg(ResultCode.SUCCESS.getMsg()); + result.setData(data); + result.setTotal(total.intValue()); + return result; + } + + + + public static ApiResult failed() { + return result(ResultCode.SYSTEM_EXECUTION_ERROR.getCode(), ResultCode.SYSTEM_EXECUTION_ERROR.getMsg(), null); + } + + public static ApiResult failed(String msg) { + return result(ResultCode.SYSTEM_EXECUTION_ERROR.getCode(), msg, null); + } + + public static ApiResult judge(boolean status) { + if (status) { + return ok(); + } else { + return failed(); + } + } + + public static ApiResult failed(IResultCode resultCode) { + return result(resultCode.getCode(), resultCode.getMsg(), null); + } + + public static ApiResult failed(IResultCode resultCode, String msg) { + return result(resultCode.getCode(), msg, null); + } + + public static ApiResult failed(String code, String msg) { + return result(code, msg, null); + } + + private static ApiResult result(IResultCode resultCode, T data) { + return result(resultCode.getCode(), resultCode.getMsg(), data); + } + + private static ApiResult result(String code, String msg, T data) { + ApiResult result = new ApiResult<>(); + result.setCode(code); + result.setData(data); + result.setMsg(msg); + return result; + } + + + public static boolean isSuccess(ApiResult result) { + return result != null && ResultCode.SUCCESS.getCode().equals(result.getCode()); + } +} diff --git a/hoe-common/common-base/src/main/java/com/recovery/common/base/result/IResultCode.java b/hoe-common/common-base/src/main/java/com/recovery/common/base/result/IResultCode.java new file mode 100644 index 0000000..9e2ab4d --- /dev/null +++ b/hoe-common/common-base/src/main/java/com/recovery/common/base/result/IResultCode.java @@ -0,0 +1,9 @@ +package com.recovery.common.base.result; + +public interface IResultCode { + + + String getCode(); + + String getMsg(); +} diff --git a/hoe-common/common-base/src/main/java/com/recovery/common/base/result/ResultCode.java b/hoe-common/common-base/src/main/java/com/recovery/common/base/result/ResultCode.java new file mode 100644 index 0000000..c142291 --- /dev/null +++ b/hoe-common/common-base/src/main/java/com/recovery/common/base/result/ResultCode.java @@ -0,0 +1,56 @@ +package com.recovery.common.base.result; + +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * Created with IntelliJ IDEA. + * + * @author: + * @date:2021/11/24 + * @description: + * @modifiedBy: + * @version: 1.0 + */ +@AllArgsConstructor +@NoArgsConstructor +public enum ResultCode implements IResultCode, Serializable { + + SUCCESS("200", "成功"), + SYSTEM_EXECUTION_ERROR("999999", "系统执行出错"), + USERNAME_OR_PASSWORD_ERROR("A00100", "用户名或密码错误"), + USER_NOT_EXIST("A00101", "用户不存在"), + CLIENT_AUTHENTICATION_FAILED("A00212", "客户端认证失败"), + ACCESS_UNAUTHORIZED("A00213", "未授权"), + TOKEN_INVALID_OR_EXPIRED("A00214", "token非法或失效"), + TOKEN_ACCESS_FORBIDDEN("A00215", "token禁止访问"), + FLOW_LIMITING("B0210", "系统限流"), + DEGRADATION("B0220", "系统功能降级"), + SERVICE_NO_AUTHORITY("B0221", "服务未授权"), + ; + + @Override + public String getCode() { + return code; + } + + @Override + public String getMsg() { + return msg; + } + + private String code; + + private String msg; + + @Override + public String toString() { + return "{" + + "\"code\":\"" + code + '\"' + + ", \"msg\":\"" + msg + '\"' + + '}'; + } + +} diff --git a/hoe-common/common-base/src/main/java/com/recovery/common/base/util/EncryptUtil.java b/hoe-common/common-base/src/main/java/com/recovery/common/base/util/EncryptUtil.java new file mode 100644 index 0000000..2e8ca82 --- /dev/null +++ b/hoe-common/common-base/src/main/java/com/recovery/common/base/util/EncryptUtil.java @@ -0,0 +1,130 @@ +package com.recovery.common.base.util; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.DESKeySpec; +import java.security.SecureRandom; + +/** + * 加密工具类 + */ +public final class EncryptUtil { + + // 原始密匙 + private static final String PASSWORD_CRYPT_KEY = "avatar-common"; + private final static String DES = "DES"; + + + /** + * 密码解密 + * @return + * @throws Exception + */ + public static String decrypt(String src) { + try { + return new String(decrypt(hex2byte(src.getBytes()), PASSWORD_CRYPT_KEY.getBytes())); + } catch (Exception e) { + } + return null; + } + + /** + * 密码加密 + * @return + * @throws Exception + */ + public static String encrypt(String src) { + try { + return byte2hex(encrypt(src.getBytes(), PASSWORD_CRYPT_KEY.getBytes())); + } catch (Exception e) { + } + return null; + } + + /** + * 加密 + * + * @param src 数据源 + * @param key 密钥,长度必须是8的倍数 + * @return 返回加密后的数据 + * @throws Exception + */ + private static byte[] encrypt(byte[] src, byte[] key) throws Exception { + // DES算法要求有一个可信任的随机数源 + SecureRandom sr = new SecureRandom(); + // 从原始密匙数据创建DESKeySpec对象 + DESKeySpec dks = new DESKeySpec(key); + // 创建一个密匙工厂,然后用它把DESKeySpec转换成一个SecretKey对象 + SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES); + SecretKey securekey = keyFactory.generateSecret(dks); + // Cipher对象实际完成加密操作 + Cipher cipher = Cipher.getInstance(DES); + // 用密匙初始化Cipher对象 + cipher.init(Cipher.ENCRYPT_MODE, securekey, sr); + // 现在,获取数据并加密正式执行加密操作 + return cipher.doFinal(src); + } + + /** + * 解密 + * + * @param src 数据源 + * @param key 密钥,长度必须是8的倍数 + * @return 返回解密后的原始数据 + * @throws Exception + */ + private static byte[] decrypt(byte[] src, byte[] key) throws Exception { + // DES算法要求有一个可信任的随机数源 + SecureRandom sr = new SecureRandom(); + // 从原始密匙数据创建一个DESKeySpec对象 + DESKeySpec dks = new DESKeySpec(key); + // 创建一个密匙工厂,然后用它把DESKeySpec对象转换成一个SecretKey对象 + SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES); + SecretKey securekey = keyFactory.generateSecret(dks); + // Cipher对象实际完成解密操作 + Cipher cipher = Cipher.getInstance(DES); + // 用密匙初始化Cipher对象 + cipher.init(Cipher.DECRYPT_MODE, securekey, sr); + // 现在,获取数据并解密正式执行解密操作 + return cipher.doFinal(src); + } + + /** + * 字符串转二进制 + * @param b + * @return + */ + private static byte[] hex2byte(byte[] b) { + if ((b.length % 2) != 0) + throw new IllegalArgumentException("长度不是偶数"); + byte[] b2 = new byte[b.length / 2]; + for (int n = 0; n < b.length; n += 2) { + String item = new String(b, n, 2); + b2[n / 2] = (byte) Integer.parseInt(item, 16); + } + return b2; + } + + /** + * 二行制转字符串 + * @param b + * @return + */ + private static String byte2hex(byte[] b) { + String hs = ""; + String stmp = ""; + for (int n = 0; n < b.length; n++) { + stmp = (Integer.toHexString(b[n] & 0XFF)); + if (stmp.length() == 1) + hs = hs + "0" + stmp; + else + hs = hs + stmp; + } + return hs.toUpperCase(); + } + + public static void main(String[] args) { + System.out.println(encrypt("123")); + } +} diff --git a/hoe-common/common-base/src/main/java/com/recovery/common/base/util/HspHostUtil.java b/hoe-common/common-base/src/main/java/com/recovery/common/base/util/HspHostUtil.java new file mode 100644 index 0000000..bba139b --- /dev/null +++ b/hoe-common/common-base/src/main/java/com/recovery/common/base/util/HspHostUtil.java @@ -0,0 +1,140 @@ +package com.recovery.common.base.util; + + + +import com.alibaba.druid.pool.DruidDataSource; +import com.alibaba.fastjson.JSON; +import com.recovery.common.base.ds.DataSourceContextHolder; +import com.recovery.common.base.ds.DynamicDataSource; +import com.recovery.common.base.po.base.DBInfo; +import com.recovery.common.base.utils.RedisUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.redis.core.StringRedisTemplate; + +import javax.annotation.Resource; +import java.util.Map; + +@Slf4j +public class HspHostUtil { + + @Resource + private static RedisUtil redisUtil; + //医院代码 + private static final ThreadLocal HSP_HSOT = new ThreadLocal(); + public static String getHspHost() { + return HSP_HSOT.get(); + } + + public static void setHspHost(String hspHost) { + HSP_HSOT.set(hspHost); + } + + + /** + * 跟进域名和模块切库 + * @param host + */ + public static void switchDB(String host, String module, StringRedisTemplate redisTemplate){ + String dataSourceKey = "dynamic-slave"+"_"+host; + Map dataSourceMap = DynamicDataSource.getInstance().getDataSourceMap(); + if (!dataSourceMap.containsKey(dataSourceKey)) { + DBInfo dbInfo = RedisUtils.getDBInfoByHostAndModule(redisTemplate,host,module); + if (UtilTools.isEmpty(dbInfo)){ + log.error("host > {}, module > {}",host,module); + throw new RuntimeException("暂无查到数据库信息"); + } + DruidDataSource dynamicDataSource = new DruidDataSource(); + dynamicDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); + dynamicDataSource.setUrl("jdbc:mysql://" + dbInfo.getDbIp() + ":" + dbInfo.getDbPort() + "/" + dbInfo.getDbName() + "?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false&rewriteBatchedStatements=true&serverTimezone=Asia/Shanghai"); + dynamicDataSource.setUsername(dbInfo.getDbUser()); + dynamicDataSource.setPassword(dbInfo.getDbPasswd()); + //连接配置 +// dynamicDataSource.setTestOnBorrow(Boolean.parseBoolean(EnvironmentConfig.getProperty("master.datasource.testOnBorrow"))); +// dynamicDataSource.setTestWhileIdle(Boolean.parseBoolean(EnvironmentConfig.getProperty("master.datasource.testWhileIdle"))); +// if(StringUtils.isNotBlank(EnvironmentConfig.getProperty("master.datasource.maxWait"))){ +// dynamicDataSource.setMaxWait(Integer.parseInt(EnvironmentConfig.getProperty("master.datasource.maxWait"))); +// } +// if(StringUtils.isNotBlank(EnvironmentConfig.getProperty("master.datasource.maxActive"))){ +// dynamicDataSource.setMaxActive(Integer.parseInt(EnvironmentConfig.getProperty("master.datasource.maxActive"))); +// } +// dynamicDataSource.setValidationQuery(EnvironmentConfig.getProperty("master.datasource.validationQuery")); +// if(StringUtils.isNotBlank(EnvironmentConfig.getProperty("master.datasource.minIdle"))){ +// dynamicDataSource.setMinIdle(Integer.parseInt(EnvironmentConfig.getProperty("master.datasource.minIdle"))); +// } +// if(StringUtils.isNotBlank(EnvironmentConfig.getProperty("master.datasource.minIdle"))){ +// dynamicDataSource.setInitialSize(Integer.parseInt(EnvironmentConfig.getProperty("master.datasource.minIdle"))); +// } + + //log.info("maxActive : " + EnvironmentConfig.getProperty("master.datasource.maxActive")); + //log.info("minIdle : " + EnvironmentConfig.getProperty("master.datasource.minIdle")); + //log.info("maxWait : " + EnvironmentConfig.getProperty("master.datasource.maxWait")); + + +// dynamicDataSource.setBreakAfterAcquireFailure(true);//失败之后中断 +// dynamicDataSource.setConnectionErrorRetryAttempts(3);//失败后重试次数 +// dynamicDataSource.setMaxWait(3000);//超时时间 + dataSourceMap.put(dataSourceKey, dynamicDataSource); + DynamicDataSource.getInstance().setTargetDataSources(dataSourceMap); + } + DruidDataSource druidDataSource = (DruidDataSource) dataSourceMap.get(dataSourceKey); + //log.info("host:{},module:{},url:{}",host,module,druidDataSource.getUrl()); + DataSourceContextHolder.setDBType(dataSourceKey); +// if(RedisUtil.getWholeDomainMap(redisTemplate).containsKey(host)){ +// //传递域名 +// HspHostUtil.setHspHost(host); +// } else{ +// //传递域名 +// HspHostUtil.setHspHost("xxx."+host); +// } + //传递域名 + HspHostUtil.setHspHost(host); + } + + /** + * 根据db信息切库 + * @param dbInfo + */ + public static void switchDB(DBInfo dbInfo,StringRedisTemplate redisTemplate){ + String host = dbInfo.getHospitalCode(); + String dataSourceKey = "dynamic-slave"+"_"+host; + Map dataSourceMap = DynamicDataSource.getInstance().getDataSourceMap(); + if (!dataSourceMap.containsKey(dataSourceKey)) { + DruidDataSource dynamicDataSource = new DruidDataSource(); + dynamicDataSource.setDriverClassName("com.mysql.jdbc.Driver"); + dynamicDataSource.setUrl("jdbc:mysql://" + dbInfo.getDbIp() + ":" + dbInfo.getDbPort() + "/" + dbInfo.getDbName() + "?characterEncoding=utf-8&useSSL=false&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&serverTimezone=Asia/Shanghai"); + dynamicDataSource.setUsername(dbInfo.getDbUser()); + dynamicDataSource.setPassword(dbInfo.getDbPasswd()); +// dynamicDataSource.setBreakAfterAcquireFailure(true);//失败之后中断 +// dynamicDataSource.setConnectionErrorRetryAttempts(3);//失败后重试次数 +// dynamicDataSource.setMaxWait(3000);//超时时间 + dataSourceMap.put(dataSourceKey, dynamicDataSource); + DynamicDataSource.getInstance().setTargetDataSources(dataSourceMap); + } + DataSourceContextHolder.setDBType(dataSourceKey); +// if(RedisUtil.getWholeDomainMap(redisTemplate).containsKey(host)){ +// //传递域名 +// HspHostUtil.setHspHost(host); +// } else{ +// //传递域名 +// HspHostUtil.setHspHost("xxx."+host); +// } + //传递域名 + HspHostUtil.setHspHost(host); + } + + + /** + * 过滤全域名配置获取域名 + * @param hospitalHost + * @param stringRedisTemplate + * @return + */ + public static String getWholeHost(String hospitalHost,StringRedisTemplate stringRedisTemplate){ + String domainStr = stringRedisTemplate.opsForValue().get("HOE_WHOLE_DOMAIN_REDIS_KEY"); + Map domainMap = JSON.parseObject(domainStr,Map.class); + if(domainMap == null || !domainMap.containsKey(hospitalHost)){ + hospitalHost = hospitalHost.substring(hospitalHost.indexOf(".") + 1); + } + return hospitalHost; + } +} diff --git a/hoe-common/common-base/src/main/java/com/recovery/common/base/util/RedisUtil.java b/hoe-common/common-base/src/main/java/com/recovery/common/base/util/RedisUtil.java new file mode 100644 index 0000000..dcafbad --- /dev/null +++ b/hoe-common/common-base/src/main/java/com/recovery/common/base/util/RedisUtil.java @@ -0,0 +1,597 @@ +package com.recovery.common.base.util; + +import cn.hutool.http.server.HttpServerRequest; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +/** + * @author: + */ +@Component +@Slf4j +public class RedisUtil { + + + + /** + * 注入redisTemplate bean + */ + @Autowired + private RedisTemplate redisTemplate; + private HttpServerRequest request; + + + /** + * 指定缓存失效时间 + * + * @param key 键 + * @param time 时间(秒) + * @return + */ + public boolean expire(String key, long time) { + try { + if (time > 0) { + redisTemplate.expire(HspHostUtil.getHspHost()+":"+key, time, TimeUnit.SECONDS); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 根据key获取过期时间 + * + * @param key 键 不能为null + * @return 时间(秒) 返回0代表为永久有效R + */ + public long getExpire(String key) { + return redisTemplate.getExpire(HspHostUtil.getHspHost()+":"+key, TimeUnit.SECONDS); + } + + /** + * 判断key是否存在 + * + * @param key 键 + * @return true 存在 false不存在 + */ + public boolean hasKey(String key) { + try { + return redisTemplate.hasKey(HspHostUtil.getHspHost()+":"+key); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 删除缓存 + * + * @param key 可以传一个值 或多个 + */ + @SuppressWarnings("unchecked") + public void del(String... key) { + if (HspHostUtil.getHspHost()+":"+key != null && key.length > 0) { + if (key.length == 1) { + redisTemplate.delete(HspHostUtil.getHspHost()+":"+key[0]); + } else { + redisTemplate.delete((List) CollectionUtils.arrayToList(HspHostUtil.getHspHost()+":"+key)); + } + } + } + + // ============================String(字符串)============================= + + /** + * 普通缓存获取 + * + * @param key 键 + * @return 值 + */ + public Object get(String key) { + log.info("获取reids数据:key:"+HspHostUtil.getHspHost()+":"+key); + return key == null ? null : redisTemplate.opsForValue().get(HspHostUtil.getHspHost()+":"+key); + } + + /** + * 普通缓存放入 + * + * @param key 键 + * @param value 值 + * @return true成功 false失败 + */ + public boolean set(String key, Object value) { + try { + redisTemplate.opsForValue().set(HspHostUtil.getHspHost()+":"+key, value); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 普通缓存放入并设置时间 + * + * @param key 键 + * @param value 值 + * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期 + * @return true成功 false 失败 + */ + public boolean set(String key, Object value, long time) { + try { + if (time > 0) { + redisTemplate.opsForValue().set(HspHostUtil.getHspHost()+":"+key, value, time, TimeUnit.SECONDS); + } else { + set(HspHostUtil.getHspHost()+":"+key, value); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 递增 + * + * @param key 键 + * @param delta 要增加几(大于0) + * @return + */ + public long incr(String key, long delta) { + if (delta < 0) { + throw new RuntimeException("递增因子必须大于0"); + } + return redisTemplate.opsForValue().increment(HspHostUtil.getHspHost()+":"+key, delta); + } + + /** + * 递减 + * + * @param key 键 + * @param delta 要减少几(小于0) + * @return + */ + public long decr(String key, long delta) { + if (delta < 0) { + throw new RuntimeException("递减因子必须大于0"); + } + return redisTemplate.opsForValue().increment(HspHostUtil.getHspHost()+":"+key, -delta); + } + // ================================Hash(哈希)================================= + + /** + * HashGet + * + * @param key 键 不能为null + * @param item 项 不能为null + * @return 值 + */ + public Object hget(String key, String item) { + return redisTemplate.opsForHash().get(HspHostUtil.getHspHost()+":"+key, item); + } + + /** + * 获取hashKey对应的所有键值 + * + * @param key 键 + * @return 对应的多个键值 + */ + public Map hmget(String key) { + return redisTemplate.opsForHash().entries(HspHostUtil.getHspHost()+":"+key); + } + + /** + * HashSet + * + * @param key 键 + * @param map 对应多个键值 + * @return true 成功 false 失败 + */ + public boolean hmset(String key, Map map) { + try { + redisTemplate.opsForHash().putAll(HspHostUtil.getHspHost()+":"+key, map); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * HashSet 并设置时间 + * + * @param key 键 + * @param map 对应多个键值 + * @param time 时间(秒) + * @return true成功 false失败 + */ + public boolean hmset(String key, Map map, long time) { + try { + redisTemplate.opsForHash().putAll(HspHostUtil.getHspHost()+":"+key, map); + if (time > 0) { + expire(HspHostUtil.getHspHost()+":"+key, time); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 向一张hash表中放入数据,如果不存在将创建 + * + * @param key 键 + * @param item 项 + * @param value 值 + * @return true 成功 false失败 + */ + public boolean hset(String key, String item, Object value) { + try { + redisTemplate.opsForHash().put(HspHostUtil.getHspHost()+":"+key, item, value); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 向一张hash表中放入数据,如果不存在将创建 + * + * @param key 键 + * @param item 项 + * @param value 值 + * @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间 + * @return true 成功 false失败 + */ + public boolean hset(String key, String item, Object value, long time) { + try { + redisTemplate.opsForHash().put(HspHostUtil.getHspHost()+":"+key, item, value); + if (time > 0) { + expire(HspHostUtil.getHspHost()+":"+key, time); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 删除hash表中的值 + * + * @param key 键 不能为null + * @param item 项 可以使多个 不能为null + */ + public void hdel(String key, Object... item) { + redisTemplate.opsForHash().delete(HspHostUtil.getHspHost()+":"+key, item); + } + + /** + * 判断hash表中是否有该项的值 + * + * @param key 键 不能为null + * @param item 项 不能为null + * @return true 存在 false不存在 + */ + public boolean hHasKey(String key, String item) { + return redisTemplate.opsForHash().hasKey(HspHostUtil.getHspHost()+":"+key, item); + } + + /** + * hash递增 如果不存在,就会创建一个 并把新增后的值返回 + * + * @param key 键 + * @param item 项 + * @param by 要增加几(大于0) + * @return + */ + public double hincr(String key, String item, double by) { + return redisTemplate.opsForHash().increment(HspHostUtil.getHspHost()+":"+key, item, by); + } + + /** + * hash递减 + * + * @param key 键 + * @param item 项 + * @param by 要减少记(小于0) + * @return + */ + public double hdecr(String key, String item, double by) { + return redisTemplate.opsForHash().increment(HspHostUtil.getHspHost()+":"+key, item, -by); + } + // ============================Set(集合)============================= + + /** + * 根据key获取Set中的所有值 + * + * @param key 键 + * @return + */ + public Set sGet(String key) { + try { + return redisTemplate.opsForSet().members(HspHostUtil.getHspHost()+":"+key); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * 根据value从一个set中查询,是否存在 + * + * @param key 键 + * @param value 值 + * @return true 存在 false不存在 + */ + public boolean sHasKey(String key, Object value) { + try { + return redisTemplate.opsForSet().isMember(HspHostUtil.getHspHost()+":"+key, value); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 将数据放入set缓存 + * + * @param key 键 + * @param values 值 可以是多个 + * @return 成功个数 + */ + public long sSet(String key, Object... values) { + try { + return redisTemplate.opsForSet().add(HspHostUtil.getHspHost()+":"+key, values); + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 将set数据放入缓存 + * + * @param key 键 + * @param time 时间(秒) + * @param values 值 可以是多个 + * @return 成功个数 + */ + public long sSetAndTime(String key, long time, Object... values) { + try { + Long count = redisTemplate.opsForSet().add(HspHostUtil.getHspHost()+":"+key, values); + if (time > 0) { + expire(HspHostUtil.getHspHost()+":"+key, time); + } + return count; + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 获取set缓存的长度 + * + * @param key 键 + * @return + */ + public long sGetSetSize(String key) { + try { + return redisTemplate.opsForSet().size(HspHostUtil.getHspHost()+":"+key); + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 移除值为value的 + * + * @param key 键 + * @param values 值 可以是多个 + * @return 移除的个数 + */ + public long setRemove(String key, Object... values) { + try { + Long count = redisTemplate.opsForSet().remove(HspHostUtil.getHspHost()+":"+key, values); + return count; + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + // ===============================List(列表)================================= + + /** + * 获取list缓存的内容 + * + * @param key 键 + * @param start 开始 + * @param end 结束 0 到 -1代表所有值 + * @return + */ + public List lGet(String key, long start, long end) { + try { + return redisTemplate.opsForList().range(HspHostUtil.getHspHost()+":"+key, start, end); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * 获取list缓存的长度 + * + * @param key 键 + * @return + */ + public long lGetListSize(String key) { + try { + return redisTemplate.opsForList().size(HspHostUtil.getHspHost()+":"+key); + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 通过索引 获取list中的值 + * + * @param key 键 + * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推 + * @return + */ + public Object lGetIndex(String key, long index) { + try { + return redisTemplate.opsForList().index(HspHostUtil.getHspHost()+":"+key, index); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * 将list放入缓存 + * + * @param key 键 + * @param value 值 + * @return + */ + public boolean lSet(String key, Object value) { + try { + redisTemplate.opsForList().rightPush(HspHostUtil.getHspHost()+":"+key, value); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 将list放入缓存 + * + * @param key 键 + * @param value 值 + * @param time 时间(秒) + * @return + */ + public boolean lSet(String key, Object value, long time) { + try { + redisTemplate.opsForList().rightPush(HspHostUtil.getHspHost()+":"+key, value); + if (time > 0) { + expire(HspHostUtil.getHspHost()+":"+key, time); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 将list放入缓存 + * + * @param key 键 + * @param value 值 + * @return + */ + public boolean lSet(String key, List value) { + try { + redisTemplate.opsForList().rightPushAll(HspHostUtil.getHspHost()+":"+key, value); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 将list放入缓存 + * + * @param key 键 + * @param value 值 + * @param time 时间(秒) + * @return + */ + public boolean lSet(String key, List value, long time) { + try { + redisTemplate.opsForList().rightPushAll(HspHostUtil.getHspHost()+":"+key, value); + if (time > 0) { + expire(HspHostUtil.getHspHost()+":"+key, time); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 根据索引修改list中的某条数据 + * + * @param key 键 + * @param index 索引 + * @param value 值 + * @return + */ + public boolean lUpdateIndex(String key, long index, Object value) { + try { + redisTemplate.opsForList().set(HspHostUtil.getHspHost()+":"+key, index, value); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 移除N个值为value + * + * @param key 键 + * @param count 移除多少个 + * @param value 值 + * @return 移除的个数 + */ + public long lRemove(String key, long count, Object value) { + try { + Long remove = redisTemplate.opsForList().remove(HspHostUtil.getHspHost()+":"+key, count, value); + return remove; + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 根据正则表达式获取key列表 + * + * @param patternKey 正则表达式 + * @return 匹配key列表 + */ + public Set keys(String patternKey) { + try { + Set keys = redisTemplate.keys(patternKey); + return keys; + } catch (Exception e) { + e.printStackTrace(); + return new HashSet<>(); + } + } + + +} diff --git a/hoe-common/common-base/src/main/java/com/recovery/common/base/util/UtilTools.java b/hoe-common/common-base/src/main/java/com/recovery/common/base/util/UtilTools.java new file mode 100644 index 0000000..da545af --- /dev/null +++ b/hoe-common/common-base/src/main/java/com/recovery/common/base/util/UtilTools.java @@ -0,0 +1,867 @@ +package com.recovery.common.base.util; + + + +import cn.hutool.json.JSONObject; +import lombok.extern.slf4j.Slf4j; +import org.springframework.util.StringUtils; + +import java.beans.BeanInfo; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.math.BigDecimal; +import java.net.Inet4Address; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.text.DecimalFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@Slf4j +public class UtilTools { + + //protected final static Log log = LogFactory.getLog(UtilTools.class); + + private final static Pattern DECIMAL_NUMBER_PATTERN = Pattern.compile("[+-]?[0-9]+\\.([0-9]+)?"); + private final static Pattern DECIMAL_NUMBER_PATTERN_P1 = Pattern.compile("^[+]?(([1-9]\\d*[.]?)|(0.))(\\d{0,1})?$"); + private final static Pattern PATTERN_INTEGER = Pattern.compile("^[1-9]\\d*|0$"); + private final static Pattern PATTERN_PHONE = Pattern.compile("^((13[0-9])|(14[5,7,9])|(15([0-3]|[5-9]))|(166)|(17[0,1,3,5,6,7,8])|(18[0-9])|(19[0-9]))\\d{8}$"); + /** + * 验证数字 + * 浮点数保留一位小数 + * @param numberStr + * @return boolean + */ + public static boolean validateNumber(String numberStr){ + //String DECIMALREG = "^[+]?(([1-9]\\d*[.]?)|(0.))(\\d{0,1})?$"; + if(StringUtils.isEmpty(numberStr)) { + return false; + } + //Pattern pattern = Pattern.compile(DECIMALREG); + Matcher matcher = DECIMAL_NUMBER_PATTERN_P1.matcher(numberStr.trim()); + return matcher.matches(); + } + + /** + * 验证数字 + * 正整数包括0 + * @param numberStr + * @return boolean + */ + public static boolean validatePositiveInteger1(String numberStr){ + //String POSITIVEINTEGER1 = "^[1-9]\\d*|0$"; + if(StringUtils.isEmpty(numberStr)) { + return false; + } + //Pattern pattern = Pattern.compile(POSITIVEINTEGER1); + Matcher matcher = PATTERN_INTEGER.matcher(numberStr.trim()); + return matcher.matches(); + } + + //判断空 + public static boolean isEmpty(String str){ + + if(null == str || str.trim().length() <= 0) { + return true; + } + return false; + } + + public static boolean isNotEmpty(String str){ + if(null == str || str.trim().length() <= 0) { + return false; + } + return true; + } + + public static boolean isNotEmpty(Object objStr){ + if(null == objStr || objStr.toString().trim().length() <= 0) { + return false; + } + return true; + } + + public static boolean isNotEmpty(Object... objStr){ + for(Object object : objStr){ + if(object == null){ + return false; + } + } + return true; + } + + + public static boolean isEmpty(Double value){ + if(value == null){ + return true; + } + return false; + } + + public static boolean isEmpty(Float str){ + if(null == str){ + return true; + } + return false; + } + + public static boolean isEmpty(Long value){ + if(value == null){ + return true; + } + return false; + } + + + //判断date空 + public static boolean isEmpty(Date d){ + + if(d == null){ + return true; + } + return false; + } + + /** + * 判断Integer + * @param value + * @return + */ + public static boolean isEmpty(Integer value){ + if(value == null){ + return true; + } + return false; + } + + public static boolean isEmpty(Object objStr){ + if(null == objStr || objStr.toString().trim().length() <= 0) { + return true; + } + return false; + } + + public static boolean isEmpty(List value){ + if(null == value || value.size() <= 0) { + return true; + } + return false; + } + + public static boolean isNotEmpty(List value){ + return !isEmpty(value); + } + + public static Boolean isEmpty(BigDecimal value){ + if(null == value){ + return true; + } + return false; + } + + //判断是否为数据 + public static boolean isNumeric(String str) { + if(UtilTools.isEmpty(str)) { + return false; + } + for (int i = str.length(); --i >= 0;) { + if (!Character.isDigit(str.charAt(i))) { + return false; + } + } + return true; + } + + + // GENERAL_PUNCTUATION 判断中文的“号 + + // CJK_SYMBOLS_AND_PUNCTUATION 判断中文的。号 + + // HALFWIDTH_AND_FULLWIDTH_FORMS 判断中文的,号 + + public static boolean isChinese(char c) { + + Character.UnicodeBlock ub = Character.UnicodeBlock.of(c); + + if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS + + || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS + + || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A + + || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION + + || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION + + || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS) { + + return true; + + } + + return false; + + } + + + // 判断是否为中文 + public static boolean isChinese(String strName) { + boolean flag = false; + char[] ch = strName.toCharArray(); + for (int i = 0; i < ch.length; i++) { + char c = ch[i]; + if (isChinese(c) == true) { + flag = true; + return flag; + } else { + continue; + } + } + return flag; + } + + public static Long parseLong(String value){ + if(value == null){ + return null; + } + try{ + return Long.parseLong(value); + }catch(Exception e){ + return null; + } + } + + public static Short parseShort(String value){ + if(value == null){ + return null; + } + try{ + return Short.parseShort(value); + }catch(Exception e){ + return null; + } + } + + public static Integer parseInteger(String value){ + if(value == null){ + return null; + } + try{ + return Integer.parseInt(value); + }catch(Exception e){ + return null; + } + } + + public static Double parseDouble(String value){ + if(value == null){ + return null; + } + try{ + return Double.parseDouble(value); + }catch(Exception e){ + return null; + } + } + + public static Float parseFloat(String value){ + if(value == null){ + return null; + } + try{ + return Float.parseFloat(value); + }catch(Exception e){ + return null; + } + } + + public static Date parseDate(String value,String pattern){ + if(isEmpty(pattern)){ + pattern = "yyyy-MM-dd"; + } + + if(value == null){ + return null; + } + + try{ + SimpleDateFormat format = new SimpleDateFormat(pattern); + return format.parse(value); + }catch(Exception e){ + return null; + } + } + + + public static Date parseDate2(String value,String pattern) throws ParseException { + if(isEmpty(pattern)){ + pattern = "yyyy-MM-dd"; + } + + if(value == null){ + return null; + } + + SimpleDateFormat format = new SimpleDateFormat(pattern); + return format.parse(value); + } + + public static Date parseDate(String value){ + Date date = parseDate(value,"yyyy-MM-dd"); + if(null == date){ + date = parseDate(value,"yyyy/MM/dd"); + } + return date; + } + + + public static boolean isValidDate(String str,String formatStr) { + boolean convertSuccess=true; + // 指定日期格式为四位年/两位月份/两位日期,注意yyyy/MM/dd区分大小写; + SimpleDateFormat format = new SimpleDateFormat(formatStr); + try{ + format.setLenient(false); + format.parse(str); + } catch (Exception e){ + return false; + } + return convertSuccess; + } + + public static String formatDate(Date value,String pattern){ + if(isEmpty(pattern)){ + pattern = "yyyy-MM-dd"; + } + + if(value == null){ + return null; + } + + try{ + SimpleDateFormat format = new SimpleDateFormat(pattern); + return format.format(value); + }catch(Exception e){ + return null; + } + } + + public static String formatDate(Date value){ + return formatDate(value,"yyyy-MM-dd"); + } + + public static boolean isNumber(String arg){ + final String numbers = "1234567890."; + if(arg == null || "".equals(arg)){ + return false; + } + char[] cs = arg.toCharArray(); + for(char c : cs ){ + int i = (int)c; + if(numbers.indexOf(i) == -1){ + return false; + } + } + return true; + } + + public static boolean isPhone(String phone) { + //String regex = "^((13[0-9])|(14[5,7,9])|(15([0-3]|[5-9]))|(166)|(17[0,1,3,5,6,7,8])|(18[0-9])|(19[0-9]))\\d{8}$"; + if (phone.length() != 11) { + return false; + } else { + //Pattern p = Pattern.compile(regex); + Matcher m = PATTERN_PHONE.matcher(phone); + boolean isMatch = m.matches(); + return isMatch; + } + } + + + + + + public static String currValueConvertStr(String str){ + return UtilTools.isEmpty(str)?null:str.trim(); + } + + public static boolean isNegativeNumber(String arg){ + final String numbers = "-1234567890."; + if(arg == null || "".equals(arg)){ + return false; + } + char[] cs = arg.toCharArray(); + for(char c : cs ){ + int i = (int)c; + if(numbers.indexOf(i) == -1){ + return false; + } + } + return true; + } + public static boolean isInt(String arg){ + final String numbers = "1234567890"; + if(arg == null || "".equals(arg)){ + return false; + } + char[] cs = arg.toCharArray(); + for(char c : cs ){ + int i = (int)c; + if(numbers.indexOf(i) == -1){ + return false; + } + } + return true; + } + + public static String objectToString(Object obj){ + if(obj == null){ + return ""; + } + return obj.toString(); + } + + /** + * + *@描述: 格式化数字 + *@param num + *@param format + *@return + */ + public static String formatNum(Object num, String format){ + if(null==num){ + return ""; + }else if(null==format || format.equals("")){ + return num.toString(); + } + DecimalFormat df = new DecimalFormat(format); + String text = df.format(num); + return text; + } + + public static List stringToList(String str){ + if(!UtilTools.isEmpty(str)){ + String [] strs = str.split(","); + if(null != strs && 0 != strs.length){ + List returnVals = new ArrayList(); + for(int i=0; i(); + } + + public static boolean isEmptyList(List list){ + if(null != list && list.size()>0){ + return false; + } + return true; + } + public static Long getJsonLong(JSONObject jb, String key){ + try { + return jb.getLong(key); + }catch (Exception e){ + return null; + } + } + + + + public static int getJsonInt(JSONObject jb,String key){ + try { + return jb.getInt(key); + }catch (Exception e){ + return 0; + } + } + + + /** + * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 + * 定精度,以后的数字四舍五入。 + * @param v1 被除数 + * @param v2 除数 + * @param scale 表示表示需要精确到小数点以后几位。 + * @return 两个参数的商 + */ + public static double div(double v1,double v2,int scale){ + if(scale<0){ + throw new IllegalArgumentException( + "The scale must be a positive integer or zero"); + } + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + return b2.divide(b1,scale,BigDecimal.ROUND_HALF_UP).doubleValue(); + } + + public static String delHtmlTag(String str){ + String newstr = ""; + newstr = str.replaceAll("<[.[^>]]*>",""); + newstr = newstr.replaceAll(" ", ""); + return newstr; + } + + + + public static String toUserName(String trueName){ + String userName = ""; + if(!isEmpty(trueName)){ + if(trueName.length() > 2){ + String name = trueName.substring(0,2); + String[] arr={"欧阳","太史","上官","端木","司马","东方","独孤","南宫","万俟","闻人","夏侯","诸葛","尉迟","公羊","赫连","澹台","皇甫","宗政","濮阳","公冶","太叔","申屠","公孙","慕容","仲孙","钟离","长孙","宇文","司徒","鲜于","司空","闾丘","子车","亓官","司寇","巫马","公西","颛孙","壤驷","公良","漆雕","乐正","宰父","谷梁","拓跋","夹谷","轩辕","令狐","段干","百里","呼延","东郭","南门","羊舌","微生","公户","公玉","公仪","梁丘","公仲","公上","公门","公山","公坚","左丘","公伯","西门","公祖","第五","公乘","贯丘","公皙","南荣","东里","东宫","仲长","子书","子桑","即墨","达奚","褚师","吴铭"}; + Boolean flag = false; + for (int i = 0; i < arr.length; i++) { + if(name.equals(arr[i])){ + flag = true; + userName = trueName.substring(2); + break; + } + } + if(!flag){ + userName = trueName.substring(1); + } + }else{ + if(trueName.length() > 1){ + userName = trueName.substring(1); + }else{ + userName = trueName; + } + } + } + return userName; + } + + public static String getObjectValue(Object obj){ + return obj==null?"":obj.toString(); + } + + /** + * 将Object对象里面的属性和值转化成Map对象 + * @param obj + * @return + * @throws IllegalAccessException + */ + public static Map objectToMap(Object obj) { + try{ + Map map = new HashMap(); + Class clazz = obj.getClass(); + for (Field field : clazz.getDeclaredFields()) { + field.setAccessible(true); + String fieldName = field.getName(); + //Object value = StringUtils.isEmpty(field.get(obj)); + Object value = field.get(obj); + map.put(fieldName, value); + } + return map; + } catch (Exception e){ + log.error("将Object对象里面的属性和值转化成Map对象,{}", e.getMessage(), e); + return null; + } + } + + /** + * 计算时间差 + * @param startTime + * @param endTime + * @return + */ + public static int getDistanceTime(Date startTime, Date endTime){ + + int days = 0; + long time1 = startTime.getTime(); + long time2 = endTime.getTime(); + + long diff; + if (time1 < time2) { + diff = time2 - time1; + } else { + return 0; + } + days = (int) (diff / (24 * 60 * 60 * 1000)); + return days; + } + + + + + /** + * 合并俩个对象的属性值[如果destination 属性不为空,将属性值合并到target] + * @param target + * @param destination + * @param + * @throws Exception + */ + public static void merge(M target, M destination) throws Exception { + BeanInfo beanInfo = Introspector.getBeanInfo(target.getClass()); + + // Iterate over all the attributes + for (PropertyDescriptor descriptor : beanInfo.getPropertyDescriptors()) { + // Only copy writable attributes + if (descriptor.getWriteMethod() != null) { + Object defaultValue = descriptor.getReadMethod().invoke(destination); + // Only copy values values where the destination values is null + if (defaultValue != null) { + descriptor.getWriteMethod().invoke(target, defaultValue); + } + } + } + } + + /** + * 判断是否有小数 + * @param bd + * @return + */ + public static boolean isIntegerValue(BigDecimal bd) { + return bd.signum() == 0 || bd.scale() <= 0 || bd.stripTrailingZeros().scale() <= 0; + } + + public static List splitToList(String str){ + List result = null; + if(!isEmpty(str)){ + String[] arr = str.split(","); + if(null != arr || arr.length > 0){ + result = new ArrayList<>(); + for(String s : arr){ + if(!isEmpty(s)){ + result.add(s); + } + } + } + } + + return result; + } + + public static void main(String[] args) throws Exception{ + + /*UserViewDto viewDto = new UserViewDto(); + UserDto userDto = new UserDto(); + userDto.setUserKey(DesUtil.encrypt("8")); + viewDto.setUserDto(userDto); + + UtilTools.converCipherKeyToId(viewDto);*/ + + //System.out.println(UtilTools.parseDate("2020-02-28T23:15:23.853","yyyy-MM-dd'T'HH:mm:ss.SSS")); + +// BigDecimal bd = new BigDecimal("1.0"); +// System.out.println(UtilTools.isIntegerValue(bd)); + +// String a = ",a,a,a,"; +// System.out.println(splitToList(a)+"-----length:"+splitToList(a).size()); + +// System.out.println(isNumeric("1500")); +// System.out.println(isPhone("19012391943")); +// System.out.println(isPhone("19112391943")); +// System.out.println(isPhone("19212391943")); +// System.out.println(isPhone("19312391943")); +// System.out.println(isPhone("19412391943")); +// System.out.println(isPhone("19512391943")); +// System.out.println(isPhone("19612391943")); +// System.out.println(isPhone("19712391943")); +// System.out.println(isPhone("19812391943")); +// System.out.println(isPhone("19912391943")); + + + } + + + /** + * 去除字符串中所包含的空格(包括:空格(全角,半角)、制表符、换页符等) + * @param s + * @return + */ + public static String removeAllBlank(String s){ + String result = ""; + if(null!=s && !"".equals(s)){ + result = s.replaceAll("[ *| *| *|//s*]*", ""); + } + return result; + } + + + + + + public static boolean isDecimal(String decimal) { + // 非空校验 + if (StringUtils.isEmpty(decimal)) { + return false; + } + + // 小数校验(可以是+1.2,1.2,-1.2) + //Pattern pattern = Pattern.compile("[+-]?[0-9]+\\.([0-9]+)?"); + if (!DECIMAL_NUMBER_PATTERN.matcher(decimal).matches()) { + return false; + } + + return true; + } + + /** + * 验证是否为子网IP + * @param addr + * @param addr1 + * @return addr is subnet address and addr1 is ip address. Function will return true, if addr1 is within addr(subnet) + */ + public static boolean netMatch(String addr, String addr1){ + + String[] parts = addr.split("/"); + String ip = parts[0]; + int prefix; + + if (parts.length < 2) { + prefix = 0; + } else { + prefix = Integer.parseInt(parts[1]); + } + + Inet4Address a =null; + Inet4Address a1 =null; + try { + a = (Inet4Address) InetAddress.getByName(ip); + a1 = (Inet4Address) InetAddress.getByName(addr1); + } catch (UnknownHostException e){} + + byte[] b = a.getAddress(); + int ipInt = ((b[0] & 0xFF) << 24) | + ((b[1] & 0xFF) << 16) | + ((b[2] & 0xFF) << 8) | + ((b[3] & 0xFF) << 0); + + byte[] b1 = a1.getAddress(); + int ipInt1 = ((b1[0] & 0xFF) << 24) | + ((b1[1] & 0xFF) << 16) | + ((b1[2] & 0xFF) << 8) | + ((b1[3] & 0xFF) << 0); + + int mask = ~((1 << (32 - prefix)) - 1); + + if ((ipInt & mask) == (ipInt1 & mask)) { + return true; + } + else { + return false; + } + } + + /*** + * 判断map是否存在 + * @param value + * @return + */ + public static boolean isEmpty(Map value){ + if(null == value || value.size() <= 0) { + return true; + } + return false; + } + + public static boolean isTrue (Boolean parm){ + if (parm == null){ + return false; + }else { + return parm; + } + } + + public static boolean isEqual (Boolean parm1, Boolean parm2 ){ + if (parm1 == null){ + if (parm2 == null){ + return true; + }else { + return false; + } + }else { + if (parm2 == null){ + return false; + }else { + return Objects.equals(parm1, parm2); + } + } + } + + /** + * 计算指定的日期加指定天数后的日期 + * @param startDate 开始日期 + * @param days 指定天数 + * @return + */ + public static Date calculationDate(Date startDate, int days) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(startDate); + calendar.add(Calendar.DATE,days); + return calendar.getTime(); + } + + /** + * 获取两个日期相隔天数 + * @param startDate 开始时间 + * @param endDate 结束时间 + * @return + */ + public static int getDaysBetween(Date startDate, Date endDate) { + Calendar startCalendar = Calendar.getInstance(); + startCalendar.setTime(startDate); + Calendar endCalendar = Calendar.getInstance(); + endCalendar.setTime(endDate); + if (startCalendar.after(endCalendar)) { + Calendar swap = startCalendar; + startCalendar = endCalendar; + endCalendar = swap; + } + int days = endCalendar.get(Calendar.DAY_OF_YEAR) - startCalendar.get(Calendar.DAY_OF_YEAR); + int y2 = endCalendar.get(Calendar.YEAR); + if (startCalendar.get(Calendar.YEAR) != y2) { + startCalendar = (Calendar) startCalendar.clone(); + do { + days += startCalendar.getActualMaximum(Calendar.DAY_OF_YEAR);//得到当年的实际天数   + startCalendar.add(Calendar.YEAR, 1); + } while (startCalendar.get(Calendar.YEAR) != y2); + } + return days; + } + + + + + /** + * 获取某段时间内的周一(二等等)的日期 + * @param startDate 开始日期 + * @param endDate 结束日期 + * @param weekDays 获取周几,1-6代表周一到周六。0代表周日 + * @return 指定周的一段时间内的天数 在日期间隔内获取星期几的天数 + */ + public static int getDayNumberOfWeekWithinDateInterval(Date startDate, Date endDate, int weekDays) { + SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd"); + String dataBegin = sd.format(startDate); + String dataEnd = sd.format(endDate); + int day = 0; + Calendar cal = Calendar.getInstance(); + String[] dateInterval = {dataBegin, dataEnd}; + Date[] dates = new Date[dateInterval.length]; + for (int i = 0; i < dateInterval.length; i++) { + String[] ymd = dateInterval[i].split("[^\\d]+"); + cal.set(Integer.parseInt(ymd[0]), Integer.parseInt(ymd[1]) - 1, Integer.parseInt(ymd[2])); + dates[i] = cal.getTime(); + } + for (Date date = dates[0]; date.compareTo(dates[1]) <= 0; ) { + cal.setTime(date); + if (cal.get(Calendar.DAY_OF_WEEK) - 1 == weekDays) { + day += 1; + } + cal.add(Calendar.DATE, 1); + date = cal.getTime(); + } + return day; + } +} diff --git a/hoe-common/common-base/src/main/java/com/recovery/common/base/utils/JwtUtils.java b/hoe-common/common-base/src/main/java/com/recovery/common/base/utils/JwtUtils.java new file mode 100644 index 0000000..e239dc9 --- /dev/null +++ b/hoe-common/common-base/src/main/java/com/recovery/common/base/utils/JwtUtils.java @@ -0,0 +1,94 @@ +package com.recovery.common.base.utils; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.JWTCreator; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.interfaces.DecodedJWT; +import org.springframework.stereotype.Component; + +import java.util.Calendar; +import java.util.HashMap; +import java.util.Map; + + +@Component +public class JwtUtils { + private static String secretKey = "cs-token"; + + private static Integer amount = 24 * 600 *7;//jwt的过期周期/秒 默认7天 + + + + /** + * 创建token + * @param payloadMap 存储的内容,自定义,一般是用户id + * @return + */ + public static String generateToken(Map payloadMap) { + + HashMap headers = new HashMap(); + + JWTCreator.Builder builder = JWT.create(); + + //定义jwt过期时间 + Calendar instance = Calendar.getInstance(); + instance.add(Calendar.SECOND, amount); + + + //payload + payloadMap.forEach((k, v) ->{ + builder.withClaim(k, v); + }); + + + // 生成token + String token = builder.withHeader(headers)//header + //.withClaim("second",amount)//jwt的过期周期/秒,可以用于jwt快过期的时候自动刷新 + .withExpiresAt(instance.getTime())//指定令牌的过期时间 + .sign(Algorithm.HMAC256(secretKey));//签名 + + + return token; + } + + + /** + * 校验token是否合法 + * @param token + * @return + */ + public static DecodedJWT verifyToken(String token) { + + /* + 如果有任何验证异常,此处都会抛出异常 + SignatureVerificationException 签名不一致异常 + TokenExpiredException 令牌过期异常 + AlgorithmMismatchException 算法不匹配异常 + InvalidClaimException 失效的payload异常 + */ + DecodedJWT decodedJWT = JWT.require(Algorithm.HMAC256(secretKey)).build().verify(token); + + + + return decodedJWT; + } + + /** + * 获取token信息 + * @param token + * @return + */ + public static DecodedJWT getTokenInfo(String token) { + DecodedJWT decodedJWT = JWT.require(Algorithm.HMAC256(secretKey)).build().verify(token); + return decodedJWT; + + } + + /** + * 获取token信息方法 + */ + /*public static Map getTokenInfo(String token) { + + return JWT.require(Algorithm.HMAC256(secretKey)).build().verify(token).getClaims(); + }*/ +} diff --git a/hoe-common/common-base/src/main/java/com/recovery/common/base/utils/RedisUtils.java b/hoe-common/common-base/src/main/java/com/recovery/common/base/utils/RedisUtils.java new file mode 100644 index 0000000..4103f0c --- /dev/null +++ b/hoe-common/common-base/src/main/java/com/recovery/common/base/utils/RedisUtils.java @@ -0,0 +1,42 @@ +package com.recovery.common.base.utils; + +import com.alibaba.fastjson.JSONObject; +import com.recovery.common.base.po.base.DBInfo; +import com.recovery.common.base.util.UtilTools; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +/** + * @author: + */ +@Slf4j +public class RedisUtils { + + /** + * 获取指定DB + * @param redisTemplate + * @param host + * @param module + * @return + */ + public static DBInfo getDBInfoByHostAndModule(StringRedisTemplate redisTemplate, String host, String module){ + log.info("查询数据库:"+"hoe:hospital:dbInfo:"+host+"_"+module); + String dbStr = redisTemplate.opsForValue().get("hoe:hospital:dbInfo:"+host+"_"+module); + if(UtilTools.isNotEmpty(dbStr)){ + return JSONObject.parseObject(dbStr, DBInfo.class); + }else{ + return null; + } + } + +} diff --git a/hoe-common/common-mybatis-plus/pom.xml b/hoe-common/common-mybatis-plus/pom.xml new file mode 100644 index 0000000..16b624e --- /dev/null +++ b/hoe-common/common-mybatis-plus/pom.xml @@ -0,0 +1,31 @@ + + + 4.0.0 + + com.recovery + hoe-common + 1.0.0 + + + common-mybatis-plus + + + + + mysql + mysql-connector-java + + + + com.alibaba + druid-spring-boot-starter + + + + com.baomidou + mybatis-plus-boot-starter + + + \ No newline at end of file diff --git a/hoe-common/common-mybatis-plus/src/main/java/com/recovery/common/mybatis/config/MybatisPlusConfig.java b/hoe-common/common-mybatis-plus/src/main/java/com/recovery/common/mybatis/config/MybatisPlusConfig.java new file mode 100644 index 0000000..ed4a81b --- /dev/null +++ b/hoe-common/common-mybatis-plus/src/main/java/com/recovery/common/mybatis/config/MybatisPlusConfig.java @@ -0,0 +1,41 @@ +package com.recovery.common.mybatis.config; + + +import com.baomidou.mybatisplus.annotation.DbType; +import com.baomidou.mybatisplus.core.config.GlobalConfig; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import com.recovery.common.mybatis.handler.MyMetaObjectHandler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +/** + * @author: + */ +@Configuration +@EnableTransactionManagement +public class MybatisPlusConfig { + + + /** + * 分页插件 + */ + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); + return interceptor; + } + + /** + * 自动填充数据库创建人、创建时间、更新人、更新时间 + */ + @Bean + public GlobalConfig globalConfig() { + GlobalConfig globalConfig = new GlobalConfig(); + globalConfig.setMetaObjectHandler(new MyMetaObjectHandler()); + return globalConfig; + } + +} \ No newline at end of file diff --git a/hoe-common/common-mybatis-plus/src/main/java/com/recovery/common/mybatis/handler/MyMetaObjectHandler.java b/hoe-common/common-mybatis-plus/src/main/java/com/recovery/common/mybatis/handler/MyMetaObjectHandler.java new file mode 100644 index 0000000..7f65fb5 --- /dev/null +++ b/hoe-common/common-mybatis-plus/src/main/java/com/recovery/common/mybatis/handler/MyMetaObjectHandler.java @@ -0,0 +1,36 @@ +package com.recovery.common.mybatis.handler; + +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import org.apache.ibatis.reflection.MetaObject; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; + +/** + * @author: + */ +@Component +public class MyMetaObjectHandler implements MetaObjectHandler { + + /** + * 新增填充创建时间 + * + * @param metaObject + */ + @Override + public void insertFill(MetaObject metaObject) { + this.strictInsertFill(metaObject, "createTime", () -> LocalDateTime.now(), LocalDateTime.class); + this.strictUpdateFill(metaObject, "updateTime", () -> LocalDateTime.now(), LocalDateTime.class); + } + + /** + * 更新填充更新时间 + * + * @param metaObject + */ + @Override + public void updateFill(MetaObject metaObject) { + this.strictUpdateFill(metaObject, "updateTime", () -> LocalDateTime.now(), LocalDateTime.class); + } + +} diff --git a/hoe-common/common-mybatis-plus/src/main/resources/META-INF/spring.factories b/hoe-common/common-mybatis-plus/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000..69cad5c --- /dev/null +++ b/hoe-common/common-mybatis-plus/src/main/resources/META-INF/spring.factories @@ -0,0 +1,4 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + com.recovery.common.mybatis.config.MybatisPlusConfig,\ + com.recovery.common.mybatis.handler.MyMetaObjectHandler + diff --git a/hoe-common/common-redis/pom.xml b/hoe-common/common-redis/pom.xml new file mode 100644 index 0000000..81158dc --- /dev/null +++ b/hoe-common/common-redis/pom.xml @@ -0,0 +1,36 @@ + + + 4.0.0 + + com.recovery + hoe-common + 1.0.0 + + + common-redis + + + + com.recovery + common-base + ${hoe-version} + + + + org.springframework.boot + spring-boot-starter-data-redis + + + org.apache.commons + commons-pool2 + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + \ No newline at end of file diff --git a/hoe-common/common-redis/src/main/java/com/recovery/common/redis/config/RedisConfig.java b/hoe-common/common-redis/src/main/java/com/recovery/common/redis/config/RedisConfig.java new file mode 100644 index 0000000..602a654 --- /dev/null +++ b/hoe-common/common-redis/src/main/java/com/recovery/common/redis/config/RedisConfig.java @@ -0,0 +1,47 @@ +package com.recovery.common.redis.config; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; +/** + * @author: + */ +@Configuration +@AutoConfigureBefore(RedisAutoConfiguration.class) +public class RedisConfig { + @Bean + public RedisTemplate redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) { + + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(lettuceConnectionFactory); + + // 用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值 + StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); + redisTemplate.setKeySerializer(stringRedisSerializer); // key + Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); + ObjectMapper objectMapper = new ObjectMapper(); + // 指定要序列化的域(field,get,set),访问修饰符(public,private,protected) + objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL); + jackson2JsonRedisSerializer.setObjectMapper(objectMapper); + + redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); //value + + redisTemplate.setHashKeySerializer(stringRedisSerializer); + redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer); + + redisTemplate.afterPropertiesSet(); + return redisTemplate; + } + +} + diff --git a/hoe-common/common-redis/src/main/java/com/recovery/common/redis/config/redis/RedisCache.java b/hoe-common/common-redis/src/main/java/com/recovery/common/redis/config/redis/RedisCache.java new file mode 100644 index 0000000..5e2003b --- /dev/null +++ b/hoe-common/common-redis/src/main/java/com/recovery/common/redis/config/redis/RedisCache.java @@ -0,0 +1,47 @@ +package com.recovery.common.redis.config.redis; + +public interface RedisCache { + /** + * 描述:获取缓存 + */ + public String get(String key); + public Object get_obj(String key); + /** + * 描述:删除缓存 + */ + public void remove(String key); + /** + * 描述:添加缓存 + */ + public void put(String key, String value); + public void put_obj(String key, Object value); + + /** + * 描述:添加缓存并且添加失效时间 + */ + public void put(String key, String value, int second); + public void put_obj(String key, Object value, int second); + /** + * 描述:根据缓存value值加减 + */ + public long incr(String key, long value); + + /** + * 描述:设置超时时间 + */ + public void expire(String key); + public void expireDays(String key,int days); + + /** + * 是否存在 + * @param key + * @return + */ + Boolean isExists(String key); + + /** + * 清除某类key + * @param patten + */ + public void clean(String patten); +} diff --git a/hoe-common/common-redis/src/main/java/com/recovery/common/redis/config/redis/RedisCacheImpl.java b/hoe-common/common-redis/src/main/java/com/recovery/common/redis/config/redis/RedisCacheImpl.java new file mode 100644 index 0000000..01978ff --- /dev/null +++ b/hoe-common/common-redis/src/main/java/com/recovery/common/redis/config/redis/RedisCacheImpl.java @@ -0,0 +1,109 @@ +package com.recovery.common.redis.config.redis; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.stereotype.Component; + +import java.util.Set; +import java.util.concurrent.TimeUnit; + +@Component("RedisCacheImpl") +public class RedisCacheImpl implements RedisCache { + + @Autowired + private StringRedisTemplate redisTemplate; + + @Autowired + private RedisTemplate redisTemplate_obj; + + + @Override + public String get(String key) { + if (null == key) { + return null; + } + ValueOperations operations = redisTemplate.opsForValue(); + return operations.get(key); + } + + @Override + public Object get_obj(String key) { + if (null == key) { + return null; + } + return redisTemplate_obj.opsForValue().get(key); + } + + @Override + public void remove(String key) { + if (redisTemplate.hasKey(key)) { + redisTemplate.delete(key); + } + } + + @Override + public void put(String key, String value) { + try { + ValueOperations operations = redisTemplate.opsForValue(); + operations.set(key, value); + } catch (Exception err) { + + } + } + + @Override + public void put_obj(String key, Object value) { + redisTemplate_obj.opsForValue().set(key,value); + } + + @Override + public void put(String key, String value, int second) { + ValueOperations operations = redisTemplate.opsForValue(); + operations.set(key, value, second, TimeUnit.SECONDS); + } + @Override + public void put_obj(String key, Object value, int second) { + redisTemplate_obj.opsForValue().set(key, value, second, TimeUnit.SECONDS); + } + @Override + public long incr(String key, long value) { + ValueOperations operations = redisTemplate.opsForValue(); + return operations.increment(key,value); + } + + @Override + public void expire(String key) { + try { + redisTemplate.expire(key, 15, TimeUnit.DAYS); + } catch (Exception err) { + + } + } + + @Override + public void expireDays(String key, int days) { + try { + redisTemplate.expire(key, days, TimeUnit.DAYS); + } catch (Exception err) { + + } + } + + @Override + public Boolean isExists(String key) { + if(redisTemplate.hasKey(key)){ + return true; + } + return false; + } + + @Override + public void clean(String patten) { + Set keys = redisTemplate.keys(patten); + if(keys.size() > 0) { + redisTemplate.delete(keys); + } + } +} diff --git a/hoe-common/common-redis/src/main/java/com/recovery/common/redis/util/RedisUtils.java b/hoe-common/common-redis/src/main/java/com/recovery/common/redis/util/RedisUtils.java new file mode 100644 index 0000000..e009a7d --- /dev/null +++ b/hoe-common/common-redis/src/main/java/com/recovery/common/redis/util/RedisUtils.java @@ -0,0 +1,606 @@ +package com.recovery.common.redis.util; + +import ch.qos.logback.core.encoder.EchoEncoder; +import com.recovery.common.base.po.base.DBInfo; +import com.recovery.common.base.util.UtilTools; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * @author: + */ +@Component +public class RedisUtils { + + /** + * 注入redisTemplate bean + */ + @Autowired + private RedisTemplate redisTemplate; + + /** + * 获取指定DB + * @param redisTemplate + * @param host + * @param module + * @return + */ + public static DBInfo getDBInfoByHostAndModule(StringRedisTemplate redisTemplate, String host, String module){ + String dbStr = redisTemplate.opsForValue().get("hoe:hospital:dbInfo:"+host+"_"+module); + if(UtilTools.isNotEmpty(dbStr)){ + return JSONObject.parseObject(dbStr, DBInfo.class); + }else{ + return null; + } + } + + /** + * 指定缓存失效时间 + * + * @param key 键 + * @param time 时间(秒) + * @return + */ + public boolean expire(String key, long time) { + try { + if (time > 0) { + redisTemplate.expire(key, time, TimeUnit.SECONDS); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 根据key获取过期时间 + * + * @param key 键 不能为null + * @return 时间(秒) 返回0代表为永久有效 + */ + public long getExpire(String key) { + return redisTemplate.getExpire(key, TimeUnit.SECONDS); + } + + /** + * 判断key是否存在 + * + * @param key 键 + * @return true 存在 false不存在 + */ + public boolean hasKey(String key) { + try { + return redisTemplate.hasKey(key); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 删除缓存 + * + * @param key 可以传一个值 或多个 + */ + @SuppressWarnings("unchecked") + public void del(String... key) { + if (key != null && key.length > 0) { + if (key.length == 1) { + redisTemplate.delete(key[0]); + } else { + redisTemplate.delete((List) CollectionUtils.arrayToList(key)); + } + } + } + + // ============================String(字符串)============================= + + /** + * 普通缓存获取 + * + * @param key 键 + * @return 值 + */ + public Object get(String key) { + return key == null ? null : redisTemplate.opsForValue().get(key); + } + + /** + * 普通缓存放入 + * + * @param key 键 + * @param value 值 + * @return true成功 false失败 + */ + public boolean set(String key, Object value) { + try { + redisTemplate.opsForValue().set(key, value); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 普通缓存放入并设置时间 + * + * @param key 键 + * @param value 值 + * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期 + * @return true成功 false 失败 + */ + public boolean set(String key, Object value, long time) { + try { + if (time > 0) { + redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS); + } else { + set(key, value); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 递增 + * + * @param key 键 + * @param delta 要增加几(大于0) + * @return + */ + public long incr(String key, long delta) { + if (delta < 0) { + throw new RuntimeException("递增因子必须大于0"); + } + return redisTemplate.opsForValue().increment(key, delta); + } + + /** + * 递减 + * + * @param key 键 + * @param delta 要减少几(小于0) + * @return + */ + public long decr(String key, long delta) { + if (delta < 0) { + throw new RuntimeException("递减因子必须大于0"); + } + return redisTemplate.opsForValue().increment(key, -delta); + } + // ================================Hash(哈希)================================= + + /** + * HashGet + * + * @param key 键 不能为null + * @param item 项 不能为null + * @return 值 + */ + public Object hget(String key, String item) { + return redisTemplate.opsForHash().get(key, item); + } + + /** + * 获取hashKey对应的所有键值 + * + * @param key 键 + * @return 对应的多个键值 + */ + public Map hmget(String key) { + return redisTemplate.opsForHash().entries(key); + } + + /** + * HashSet + * + * @param key 键 + * @param map 对应多个键值 + * @return true 成功 false 失败 + */ + public boolean hmset(String key, Map map) { + try { + redisTemplate.opsForHash().putAll(key, map); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * HashSet 并设置时间 + * + * @param key 键 + * @param map 对应多个键值 + * @param time 时间(秒) + * @return true成功 false失败 + */ + public boolean hmset(String key, Map map, long time) { + try { + redisTemplate.opsForHash().putAll(key, map); + if (time > 0) { + expire(key, time); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 向一张hash表中放入数据,如果不存在将创建 + * + * @param key 键 + * @param item 项 + * @param value 值 + * @return true 成功 false失败 + */ + public boolean hset(String key, String item, Object value) { + try { + redisTemplate.opsForHash().put(key, item, value); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 向一张hash表中放入数据,如果不存在将创建 + * + * @param key 键 + * @param item 项 + * @param value 值 + * @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间 + * @return true 成功 false失败 + */ + public boolean hset(String key, String item, Object value, long time) { + try { + redisTemplate.opsForHash().put(key, item, value); + if (time > 0) { + expire(key, time); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 删除hash表中的值 + * + * @param key 键 不能为null + * @param item 项 可以使多个 不能为null + */ + public void hdel(String key, Object... item) { + redisTemplate.opsForHash().delete(key, item); + } + + /** + * 判断hash表中是否有该项的值 + * + * @param key 键 不能为null + * @param item 项 不能为null + * @return true 存在 false不存在 + */ + public boolean hHasKey(String key, String item) { + return redisTemplate.opsForHash().hasKey(key, item); + } + + /** + * hash递增 如果不存在,就会创建一个 并把新增后的值返回 + * + * @param key 键 + * @param item 项 + * @param by 要增加几(大于0) + * @return + */ + public double hincr(String key, String item, double by) { + return redisTemplate.opsForHash().increment(key, item, by); + } + + /** + * hash递减 + * + * @param key 键 + * @param item 项 + * @param by 要减少记(小于0) + * @return + */ + public double hdecr(String key, String item, double by) { + return redisTemplate.opsForHash().increment(key, item, -by); + } + // ============================Set(集合)============================= + + /** + * 根据key获取Set中的所有值 + * + * @param key 键 + * @return + */ + public Set sGet(String key) { + try { + return redisTemplate.opsForSet().members(key); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * 根据value从一个set中查询,是否存在 + * + * @param key 键 + * @param value 值 + * @return true 存在 false不存在 + */ + public boolean sHasKey(String key, Object value) { + try { + return redisTemplate.opsForSet().isMember(key, value); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 将数据放入set缓存 + * + * @param key 键 + * @param values 值 可以是多个 + * @return 成功个数 + */ + public long sSet(String key, Object... values) { + try { + return redisTemplate.opsForSet().add(key, values); + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 将set数据放入缓存 + * + * @param key 键 + * @param time 时间(秒) + * @param values 值 可以是多个 + * @return 成功个数 + */ + public long sSetAndTime(String key, long time, Object... values) { + try { + Long count = redisTemplate.opsForSet().add(key, values); + if (time > 0) { + expire(key, time); + } + return count; + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 获取set缓存的长度 + * + * @param key 键 + * @return + */ + public long sGetSetSize(String key) { + try { + return redisTemplate.opsForSet().size(key); + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 移除值为value的 + * + * @param key 键 + * @param values 值 可以是多个 + * @return 移除的个数 + */ + public long setRemove(String key, Object... values) { + try { + Long count = redisTemplate.opsForSet().remove(key, values); + return count; + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + // ===============================List(列表)================================= + + /** + * 获取list缓存的内容 + * + * @param key 键 + * @param start 开始 + * @param end 结束 0 到 -1代表所有值 + * @return + */ + public List lGet(String key, long start, long end) { + try { + return redisTemplate.opsForList().range(key, start, end); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * 获取list缓存的长度 + * + * @param key 键 + * @return + */ + public long lGetListSize(String key) { + try { + return redisTemplate.opsForList().size(key); + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 通过索引 获取list中的值 + * + * @param key 键 + * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推 + * @return + */ + public Object lGetIndex(String key, long index) { + try { + return redisTemplate.opsForList().index(key, index); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + /** + * 将list放入缓存 + * + * @param key 键 + * @param value 值 + * @return + */ + public boolean lSet(String key, Object value) { + try { + redisTemplate.opsForList().rightPush(key, value); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 将list放入缓存 + * + * @param key 键 + * @param value 值 + * @param time 时间(秒) + * @return + */ + public boolean lSet(String key, Object value, long time) { + try { + redisTemplate.opsForList().rightPush(key, value); + if (time > 0) { + expire(key, time); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 将list放入缓存 + * + * @param key 键 + * @param value 值 + * @return + */ + public boolean lSet(String key, List value) { + try { + redisTemplate.opsForList().rightPushAll(key, value); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 将list放入缓存 + * + * @param key 键 + * @param value 值 + * @param time 时间(秒) + * @return + */ + public boolean lSet(String key, List value, long time) { + try { + redisTemplate.opsForList().rightPushAll(key, value); + if (time > 0) { + expire(key, time); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 根据索引修改list中的某条数据 + * + * @param key 键 + * @param index 索引 + * @param value 值 + * @return + */ + public boolean lUpdateIndex(String key, long index, Object value) { + try { + redisTemplate.opsForList().set(key, index, value); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + /** + * 移除N个值为value + * + * @param key 键 + * @param count 移除多少个 + * @param value 值 + * @return 移除的个数 + */ + public long lRemove(String key, long count, Object value) { + try { + Long remove = redisTemplate.opsForList().remove(key, count, value); + return remove; + } catch (Exception e) { + e.printStackTrace(); + return 0; + } + } + + /** + * 根据正则表达式获取key列表 + * + * @param patternKey 正则表达式 + * @return 匹配key列表 + */ + public Set keys(String patternKey) { + try { + Set keys = redisTemplate.keys(patternKey); + return keys; + } catch (Exception e) { + e.printStackTrace(); + return new HashSet<>(); + } + } + + +} diff --git a/hoe-common/common-redis/src/main/resources/META-INF/spring.factories b/hoe-common/common-redis/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000..804b64f --- /dev/null +++ b/hoe-common/common-redis/src/main/resources/META-INF/spring.factories @@ -0,0 +1,4 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + com.recovery.common.base.config.RedisConfig,\ + com.recovery.common.base.util.RedisUtil + diff --git a/hoe-common/common-web/pom.xml b/hoe-common/common-web/pom.xml new file mode 100644 index 0000000..2ad7654 --- /dev/null +++ b/hoe-common/common-web/pom.xml @@ -0,0 +1,66 @@ + + + 4.0.0 + + com.recovery + hoe-common + 1.0.0 + + + common-web + + + net.logstash.logback + logstash-logback-encoder + 5.3 + + + com.recovery + common-base + ${hoe-version} + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-aop + true + + + io.github.openfeign + feign-core + + + + io.github.openfeign + feign-httpclient + + + + com.nimbusds + nimbus-jose-jwt + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + + + + com.alibaba.csp + sentinel-datasource-nacos + + + + \ No newline at end of file diff --git a/hoe-common/common-web/src/main/java/com/recovery/common/web/config/FeignConfig.java b/hoe-common/common-web/src/main/java/com/recovery/common/web/config/FeignConfig.java new file mode 100644 index 0000000..fcb8891 --- /dev/null +++ b/hoe-common/common-web/src/main/java/com/recovery/common/web/config/FeignConfig.java @@ -0,0 +1,63 @@ +package com.recovery.common.web.config; + +import feign.RequestInterceptor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.web.servlet.ServletRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import org.springframework.web.servlet.DispatcherServlet; + +import javax.servlet.http.HttpServletRequest; +import java.util.Enumeration; + +/** + * @author: + */ +@Configuration +public class FeignConfig { + @Value("${spring.application.name}") + private String applicationName; + /** + * 让DispatcherServlet向子线程传递RequestContext + * + * @param servlet servlet + * @return 注册bean + */ + @Bean + public ServletRegistrationBean dispatcherRegistration(DispatcherServlet servlet) { + servlet.setThreadContextInheritable(true); + return new ServletRegistrationBean<>(servlet, "/**"); + } + + /** + * 覆写拦截器,在feign发送请求前取出原来的header并转发 + * + * @return 拦截器 + */ + @Bean + public RequestInterceptor requestInterceptor() { + return (template) -> { + ServletRequestAttributes + attributes = (ServletRequestAttributes) RequestContextHolder + .getRequestAttributes(); + HttpServletRequest request = attributes.getRequest(); + //获取请求头 + Enumeration headerNames = request.getHeaderNames(); + if (headerNames != null) { + while (headerNames.hasMoreElements()) { + String name = headerNames.nextElement(); + String values = request.getHeader(name); + //将请求头保存到模板中 + if (!name.equalsIgnoreCase("serviceName")){ + template.header(name, values); + } + + } + template.header("serviceName",applicationName); + } + + }; + } +} \ No newline at end of file diff --git a/hoe-common/common-web/src/main/java/com/recovery/common/web/exception/BizException.java b/hoe-common/common-web/src/main/java/com/recovery/common/web/exception/BizException.java new file mode 100644 index 0000000..cfc96bc --- /dev/null +++ b/hoe-common/common-web/src/main/java/com/recovery/common/web/exception/BizException.java @@ -0,0 +1,30 @@ +package com.recovery.common.web.exception; + + +import com.recovery.common.base.result.IResultCode; +import lombok.Getter; +/** + * @author: + */ +@Getter +public class BizException extends RuntimeException { + + public IResultCode resultCode; + + public BizException(IResultCode errorCode) { + super(errorCode.getMsg()); + this.resultCode = errorCode; + } + + public BizException(String message) { + super(message); + } + + public BizException(String message, Throwable cause) { + super(message, cause); + } + + public BizException(Throwable cause) { + super(cause); + } +} diff --git a/hoe-common/common-web/src/main/java/com/recovery/common/web/exception/GlobalExceptionHandler.java b/hoe-common/common-web/src/main/java/com/recovery/common/web/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..f4f0a54 --- /dev/null +++ b/hoe-common/common-web/src/main/java/com/recovery/common/web/exception/GlobalExceptionHandler.java @@ -0,0 +1,23 @@ +package com.recovery.common.web.exception; + + +import com.recovery.common.base.result.ApiResult; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; +/** + * @author: + */ +@Slf4j +@RestControllerAdvice +public class GlobalExceptionHandler { + @ResponseStatus(HttpStatus.OK) + @ExceptionHandler(IllegalArgumentException.class) + public ApiResult handleIllegalArgumentException(IllegalArgumentException e) { + log.error("非法参数异常,异常原因:{}", e.getMessage(), e); + e.printStackTrace(); + return ApiResult.failed(e.getMessage()); + } +} diff --git a/hoe-common/common-web/src/main/java/com/recovery/common/web/log/HttpAspect.java b/hoe-common/common-web/src/main/java/com/recovery/common/web/log/HttpAspect.java new file mode 100644 index 0000000..18d95b0 --- /dev/null +++ b/hoe-common/common-web/src/main/java/com/recovery/common/web/log/HttpAspect.java @@ -0,0 +1,95 @@ +package com.recovery.common.web.log; + +import cn.hutool.core.exceptions.ExceptionUtil; +import com.alibaba.fastjson.JSON; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; +import java.text.SimpleDateFormat; +import java.util.*; + +/** + * @author: + */ +@Aspect +@Component +@Slf4j +public class HttpAspect { + + @Value("${spring.application.name}") + private String appName; + + /** + * 这样写是将重复的代码提取出来方便处理 + */ + @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping) " + + "|| @annotation(org.springframework.web.bind.annotation.GetMapping)" + + "|| @annotation(org.springframework.web.bind.annotation.PostMapping)" + + "|| @annotation(org.springframework.web.bind.annotation.DeleteMapping)" + ) + public void log() { + } + + + @Around("log()") + public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable { + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + HttpServletRequest request = attributes.getRequest(); + + Date d = new Date(); + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String date = simpleDateFormat.format(d); + + StringBuffer url = request.getRequestURL(); + String method = request.getMethod(); + String ip = request.getRemoteAddr(); + String class_method = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName(); + Object[] args = joinPoint.getArgs(); + String[] argNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();//参数名 + + long start = System.currentTimeMillis(); + Map map = new HashMap(); + map.put("currentTime", start); + map.put("class_method", class_method); + map.put("url", url); + map.put("method", method); + map.put("app_name", appName); + map.put("ip", ip); + List a = new ArrayList<>(); + Map params = new HashMap<>(); + for (int i = 0; i < argNames.length; i++) { + String n = argNames[i]; + String v = ""; + if (args[i] != null) { + v = (args[i]).toString(); + } else { + v = "null"; + } + params.put(n, v); + } + map.put("request", params); + Object result = null; + try { + result = joinPoint.proceed(); + map.put("response", JSON.toJSONString(result)); + } catch (Throwable throwable) { + map.put("exception", ExceptionUtil.getRootCauseMessage(throwable)); + throw throwable; + } finally { + map.put("cost", System.currentTimeMillis() - start); + log.info(JSON.toJSONString(map)); + } + return result; + } + + +} \ No newline at end of file diff --git a/hoe-common/common-web/src/main/java/com/recovery/common/web/sentinel/DefaultBlockExceptionHandler.java b/hoe-common/common-web/src/main/java/com/recovery/common/web/sentinel/DefaultBlockExceptionHandler.java new file mode 100644 index 0000000..d4821fc --- /dev/null +++ b/hoe-common/common-web/src/main/java/com/recovery/common/web/sentinel/DefaultBlockExceptionHandler.java @@ -0,0 +1,44 @@ +package com.recovery.common.web.sentinel; + +import cn.hutool.http.HttpStatus; +import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler; +import com.alibaba.csp.sentinel.slots.block.BlockException; +import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException; +import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException; +import com.alibaba.csp.sentinel.slots.block.flow.FlowException; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.recovery.common.base.result.ApiResult; +import com.recovery.common.base.result.ResultCode; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + + +/** + * @author: + */ +@Component +public class DefaultBlockExceptionHandler implements BlockExceptionHandler { + + @Override + public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception { + response.setStatus(HttpStatus.HTTP_OK); + response.setCharacterEncoding("UTF-8"); + response.setContentType("application/json;charset=utf-8"); + + ObjectMapper objectMapper = new ObjectMapper(); + + // 流控 + if (e instanceof FlowException) { + objectMapper.writeValue(response.getWriter(), ApiResult.failed(ResultCode.FLOW_LIMITING)); + // 降级 + } else if (e instanceof DegradeException) { + objectMapper.writeValue(response.getWriter(), ApiResult.failed(ResultCode.DEGRADATION)); + // 未授权 + } else if (e instanceof AuthorityException) { + objectMapper.writeValue(response.getWriter(), ApiResult.failed(ResultCode.SERVICE_NO_AUTHORITY)); + } + } +} diff --git a/hoe-common/common-web/src/main/java/com/recovery/common/web/sentinel/RequestOriginParserDefinition.java b/hoe-common/common-web/src/main/java/com/recovery/common/web/sentinel/RequestOriginParserDefinition.java new file mode 100644 index 0000000..07ada7b --- /dev/null +++ b/hoe-common/common-web/src/main/java/com/recovery/common/web/sentinel/RequestOriginParserDefinition.java @@ -0,0 +1,18 @@ +package com.recovery.common.web.sentinel; + +import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +/** + * @author: + */ +@Component +public class RequestOriginParserDefinition implements RequestOriginParser { + // 获取调用方标识信息并返回 + @Override + public String parseOrigin(HttpServletRequest request) { + String serviceName = request.getHeader("serviceName"); + return serviceName; + } +} \ No newline at end of file diff --git a/hoe-common/common-web/src/main/resources/META-INF/spring.factories b/hoe-common/common-web/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000..df84551 --- /dev/null +++ b/hoe-common/common-web/src/main/resources/META-INF/spring.factories @@ -0,0 +1,4 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + com.recovery.common.web.config.FeignConfig,\ + com.recovery.common.web.exception.GlobalExceptionHandler + diff --git a/hoe-common/pom.xml b/hoe-common/pom.xml new file mode 100644 index 0000000..d6b4aca --- /dev/null +++ b/hoe-common/pom.xml @@ -0,0 +1,22 @@ + + + 4.0.0 + + com.recovery + hoe-recovery + 1.0.0 + + + hoe-common + pom + + common-base + common-redis + common-mybatis-plus + common-web + + + + \ No newline at end of file diff --git a/hoe-gateway/pom.xml b/hoe-gateway/pom.xml new file mode 100644 index 0000000..33e695d --- /dev/null +++ b/hoe-gateway/pom.xml @@ -0,0 +1,88 @@ + + + 4.0.0 + + com.recovery + hoe-recovery + 1.0.0 + + + hoe-gateway + + + 8 + 8 + UTF-8 + + + + org.springframework.cloud + spring-cloud-starter-bootstrap + + + + org.springframework.cloud + spring-cloud-starter-gateway + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + org.springframework + spring-jdbc + + + + + mysql + mysql-connector-java + ${mysql.version} + + + + org.springframework.security + spring-security-config + + + org.springframework.security + spring-security-oauth2-resource-server + + + org.springframework.security + spring-security-oauth2-jose + + + com.recovery + common-base + ${hoe-version} + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + com.spotify + docker-maven-plugin + + + + diff --git a/hoe-gateway/src/main/java/com/recovery/gateway/GatewayApp.java b/hoe-gateway/src/main/java/com/recovery/gateway/GatewayApp.java new file mode 100644 index 0000000..c04dea7 --- /dev/null +++ b/hoe-gateway/src/main/java/com/recovery/gateway/GatewayApp.java @@ -0,0 +1,18 @@ +package com.recovery.gateway; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.context.annotation.ComponentScan; + +/** + * @author: + */ +@EnableDiscoveryClient +@SpringBootApplication +@ComponentScan(basePackages = {"com.recovery.gateway","com.recovery.common.base"}) +public class GatewayApp { + public static void main(String[] args) { + SpringApplication.run(GatewayApp.class, args); + } +} diff --git a/hoe-gateway/src/main/java/com/recovery/gateway/config/SecretStoreRunner.java b/hoe-gateway/src/main/java/com/recovery/gateway/config/SecretStoreRunner.java new file mode 100644 index 0000000..a187bd1 --- /dev/null +++ b/hoe-gateway/src/main/java/com/recovery/gateway/config/SecretStoreRunner.java @@ -0,0 +1,21 @@ +package com.recovery.gateway.config; + + +import com.recovery.gateway.task.DataInitTask; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + +@Component +public class SecretStoreRunner implements CommandLineRunner { + @Resource + private DataInitTask dataInitTask; + + @Override + public void run(String... args) throws Exception { + //初始化DB信息 + dataInitTask.initHospitalDB(); + + } +} diff --git a/hoe-gateway/src/main/java/com/recovery/gateway/security/ResourceServerConfig.java b/hoe-gateway/src/main/java/com/recovery/gateway/security/ResourceServerConfig.java new file mode 100644 index 0000000..4a869a3 --- /dev/null +++ b/hoe-gateway/src/main/java/com/recovery/gateway/security/ResourceServerConfig.java @@ -0,0 +1,80 @@ +package com.recovery.gateway.security; + + +import com.recovery.common.base.constant.SecurityConstants; +import com.recovery.common.base.result.ResultCode; +import com.recovery.gateway.util.ResponseUtils; +import lombok.AllArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.convert.converter.Converter; +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; +import org.springframework.security.config.web.server.ServerHttpSecurity; +import org.springframework.security.oauth2.jwt.Jwt; +import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter; +import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter; +import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverterAdapter; +import org.springframework.security.web.server.SecurityWebFilterChain; +import org.springframework.security.web.server.ServerAuthenticationEntryPoint; +import org.springframework.security.web.server.authorization.ServerAccessDeniedHandler; +import reactor.core.publisher.Mono; +/** + * @author: + */ +@AllArgsConstructor +@Configuration +@EnableWebFluxSecurity +public class ResourceServerConfig { + + private final ResourceServerManager resourceServerManager; + + @Bean + public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) { + http.oauth2ResourceServer().jwt().jwtAuthenticationConverter(jwtAuthenticationConverter()); + http.oauth2ResourceServer().authenticationEntryPoint(authenticationEntryPoint()); + http.authorizeExchange() + .anyExchange().access(resourceServerManager) + .and() + .exceptionHandling() + .accessDeniedHandler(accessDeniedHandler()) // 处理未授权 + .authenticationEntryPoint(authenticationEntryPoint()) //处理未认证 + .and().csrf().disable(); + return http.build(); + } + + /** + * 自定义未授权响应 + */ + @Bean + ServerAccessDeniedHandler accessDeniedHandler() { + return (exchange, denied) -> { + Mono mono = Mono.defer(() -> Mono.just(exchange.getResponse())) + .flatMap(response -> ResponseUtils.writeErrorInfo(response, ResultCode.ACCESS_UNAUTHORIZED)); + return mono; + }; + } + + /** + * token无效或者已过期自定义响应 + */ + @Bean + ServerAuthenticationEntryPoint authenticationEntryPoint() { + return (exchange, e) -> { + Mono mono = Mono.defer(() -> Mono.just(exchange.getResponse())) + .flatMap(response -> ResponseUtils.writeErrorInfo(response, ResultCode.TOKEN_INVALID_OR_EXPIRED)); + return mono; + }; + } + + @Bean + public Converter> jwtAuthenticationConverter() { + JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter(); + jwtGrantedAuthoritiesConverter.setAuthorityPrefix(SecurityConstants.AUTHORITY_PREFIX); + jwtGrantedAuthoritiesConverter.setAuthoritiesClaimName(SecurityConstants.JWT_AUTHORITIES_KEY); + + JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter(); + jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter); + return new ReactiveJwtAuthenticationConverterAdapter(jwtAuthenticationConverter); + } +} \ No newline at end of file diff --git a/hoe-gateway/src/main/java/com/recovery/gateway/security/ResourceServerManager.java b/hoe-gateway/src/main/java/com/recovery/gateway/security/ResourceServerManager.java new file mode 100644 index 0000000..637e013 --- /dev/null +++ b/hoe-gateway/src/main/java/com/recovery/gateway/security/ResourceServerManager.java @@ -0,0 +1,122 @@ +package com.recovery.gateway.security; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.StrUtil; + +import com.recovery.common.base.constant.GlobalConstants; +import com.recovery.common.base.constant.SecurityConstants; +import com.recovery.gateway.util.UrlPatternUtils; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.http.HttpMethod; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.security.authorization.AuthorizationDecision; +import org.springframework.security.authorization.ReactiveAuthorizationManager; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.web.server.authorization.AuthorizationContext; +import org.springframework.stereotype.Component; +import org.springframework.util.AntPathMatcher; +import org.springframework.util.PathMatcher; +import reactor.core.publisher.Mono; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +/** + * Created with IntelliJ IDEA. + * + * @author: AI码师 关注公众号"AI码师"获取完整源码 + * @date: 2021/11/24 + * @description: + * @modifiedBy: + * @version: 1.0 + */ +@Component +@RequiredArgsConstructor +@Slf4j +@ConfigurationProperties(prefix = "security") +public class ResourceServerManager implements ReactiveAuthorizationManager { + private final RedisTemplate redisTemplate; + + @Setter + private List ignoreUrls; + + @Override + public Mono check(Mono mono, AuthorizationContext authorizationContext) { + ServerHttpRequest request = authorizationContext.getExchange().getRequest(); + if (request.getMethod() == HttpMethod.OPTIONS) { // 预检请求放行 + return Mono.just(new AuthorizationDecision(true)); + } + PathMatcher pathMatcher = new AntPathMatcher(); + String method = request.getMethodValue(); + String path = request.getURI().getPath(); + + // 跳过token校验,放在这里去做是为了能够动态刷新 +// if (skipValid(path)) { + return Mono.just(new AuthorizationDecision(true)); +// } + +// // 如果token为空 或者token不合法 则进行拦截 +// String restfulPath = method + ":" + path; // RESTFul接口权限设计 @link https://www.cnblogs.com/haoxianrui/p/14961707.html +// String token = request.getHeaders().getFirst(SecurityConstants.AUTHORIZATION_KEY); +// if (StrUtil.isBlank(token) || !StrUtil.startWithIgnoreCase(token, SecurityConstants.JWT_PREFIX)) { +// return Mono.just(new AuthorizationDecision(false)); +// } +// +// // 从redis中获取资源权限 +// Map urlPermRolesRules = redisTemplate.opsForHash().entries(GlobalConstants.URL_PERM_ROLES_KEY); +// List authorizedRoles = new ArrayList<>(); // 拥有访问权限的角色 +// boolean requireCheck = false; // 是否需要鉴权,默认未设置拦截规则不需鉴权 +// +// // 获取当前资源 所需要的角色 +// for (Map.Entry permRoles : urlPermRolesRules.entrySet()) { +// String perm = permRoles.getKey(); +// if (pathMatcher.match(perm, restfulPath)) { +// List roles = Convert.toList(String.class, permRoles.getValue()); +// authorizedRoles.addAll(Convert.toList(String.class, roles)); +// if (requireCheck == false) { +// requireCheck = true; +// } +// } +// } +// +// // 如果资源不需要权限 则直接返回授权成功 +// if (!requireCheck) { +// return Mono.just(new AuthorizationDecision(true)); +// } +// +// // 判断JWT中携带的用户角色是否有权限访问 +// Mono authorizationDecisionMono = mono +// .filter(Authentication::isAuthenticated) +// .flatMapIterable(Authentication::getAuthorities) +// .map(GrantedAuthority::getAuthority) +// .any(authority -> { +// String roleCode = authority.substring(SecurityConstants.AUTHORITY_PREFIX.length()); // 用户的角色 +// boolean hasAuthorized = CollectionUtil.isNotEmpty(authorizedRoles) && authorizedRoles.contains(roleCode); +// return hasAuthorized; +// }) +// .map(AuthorizationDecision::new) +// .defaultIfEmpty(new AuthorizationDecision(false)); +// return authorizationDecisionMono; + } + + /** + * 跳过校验 + * + * @param path + * @return + */ + private boolean skipValid(String path) { + for (String skipPath : ignoreUrls) { + if (UrlPatternUtils.match(skipPath, path)) { + return true; + } + } + return false; + } +} \ No newline at end of file diff --git a/hoe-gateway/src/main/java/com/recovery/gateway/security/SecurityGlobalFilter.java b/hoe-gateway/src/main/java/com/recovery/gateway/security/SecurityGlobalFilter.java new file mode 100644 index 0000000..5dc9a9c --- /dev/null +++ b/hoe-gateway/src/main/java/com/recovery/gateway/security/SecurityGlobalFilter.java @@ -0,0 +1,62 @@ +package com.recovery.gateway.security; + +import cn.hutool.core.util.StrUtil; + +import com.nimbusds.jose.JWSObject; +import com.recovery.common.base.constant.SecurityConstants; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.apache.logging.log4j.util.Strings; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.core.Ordered; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +import java.net.URLEncoder; +/** + * Created with IntelliJ IDEA. + * + * @author: AI码师 关注公众号"AI码师"获取完整源码 + * @date: 2021/11/24 + * @description: + * @modifiedBy: + * @version: 1.0 + */ +@Component +@Slf4j +@RequiredArgsConstructor +public class SecurityGlobalFilter implements GlobalFilter, Ordered { + + @SneakyThrows + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + + ServerHttpRequest request = exchange.getRequest(); + log.info("接受到请求地址:"+request.getURI()); + ServerHttpResponse response = exchange.getResponse(); + // 不是正确的的JWT不做解析处理 + String token = request.getHeaders().getFirst(SecurityConstants.AUTHORIZATION_KEY); + if (StrUtil.isBlank(token) || !StrUtil.startWithIgnoreCase(token, SecurityConstants.JWT_PREFIX)) { + return chain.filter(exchange); + } + // 解析JWT获取jti,以jti为key判断redis的黑名单列表是否存在,存在则拦截访问 + token = StrUtil.replaceIgnoreCase(token, SecurityConstants.JWT_PREFIX, Strings.EMPTY); + String payload = StrUtil.toString(JWSObject.parse(token).getPayload()); + request = exchange.getRequest().mutate() + .header(SecurityConstants.JWT_PAYLOAD_KEY, URLEncoder.encode(payload, "UTF-8")) + .build(); + exchange = exchange.mutate().request(request).build(); + return chain.filter(exchange); + } + + @Override + public int getOrder() { + return 0; + } +} \ No newline at end of file diff --git a/hoe-gateway/src/main/java/com/recovery/gateway/task/DataInitTask.java b/hoe-gateway/src/main/java/com/recovery/gateway/task/DataInitTask.java new file mode 100644 index 0000000..3d1e661 --- /dev/null +++ b/hoe-gateway/src/main/java/com/recovery/gateway/task/DataInitTask.java @@ -0,0 +1,89 @@ +package com.recovery.gateway.task; + +import com.alibaba.fastjson.JSONObject; +import com.recovery.common.base.config.redis.BaseRedisCache; +import com.recovery.common.base.po.base.DBInfo; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.DriverManagerDataSource; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Component +@Slf4j +public class DataInitTask { + @Resource + private BaseRedisCache redisCache; + @Value("${master.datasource.url}") + private String masterUrl; + @Value("${master.datasource.username}") + private String user; + @Value("${master.datasource.password}") + private String password; + @Value("${master.datasource.driverClassName}") + private String driverClass; + /** + * 初始医院DB配置 + */ + public void initHospitalDB(){ + try { + JdbcTemplate jdbcTemplate=new JdbcTemplate(generateDataSource()); + List> mapList = jdbcTemplate.queryForList("SELECT id,db_name,db_ip,db_port,db_user,db_passwd,`module`,hospital_host,hospital_code FROM db_info"); + if(mapList.size() > 0){ + Map> dbMap = new HashMap<>(); + for(Map map : mapList){ + DBInfo dbInfo = new DBInfo(); + dbInfo.setId(Integer.parseInt(map.get("id").toString())); + dbInfo.setDbName(map.get("db_name").toString()); + dbInfo.setDbIp(map.get("db_ip").toString()); + dbInfo.setDbPort(Integer.parseInt(map.get("db_port").toString())); + dbInfo.setDbUser(map.get("db_user").toString()); + dbInfo.setDbPasswd(map.get("db_passwd").toString()); + dbInfo.setModule(map.get("module").toString()); + + if(map.get("hospital_host") != null) { + dbInfo.setHospitalHost(map.get("hospital_host").toString()); + } + dbInfo.setHospitalCode((String)map.get("hospital_code")); + String codeKey = "hoe:hospital:dbInfo:"+dbInfo.getHospitalCode()+"_"+dbInfo.getModule(); + redisCache.put(codeKey, JSONObject.toJSONString(dbInfo)); + /** 废弃host字段 迁移至新表 website_info + if(UtilTools.isNotEmpty(dbInfo.getModule()) && dbInfo.getModule().equals("common")){ + String hostKey = "hoe:hospital:host:"+dbInfo.getHospitalHost(); + redisCache.put(hostKey,dbInfo.getHospitalCode()); + redisCache.expireDays(hostKey,365); + } + **/ + if(dbMap.containsKey(dbInfo.getModule())){ + dbMap.get(dbInfo.getModule()).add(dbInfo); + }else{ + List dbs = new ArrayList<>(); + dbs.add(dbInfo); + dbMap.put(dbInfo.getModule(),dbs); + } + } + dbMap.forEach((k,v)->{ + String redisKey = "hoe:hospital:dbs:"+k; + redisCache.put(redisKey,JSONObject.toJSONString(v)); + }); + } + }catch (Exception e){ + log.error("ERROR:initHospitalDB",e); + } + } + + private DriverManagerDataSource generateDataSource()throws Exception{ + DriverManagerDataSource dataSource=new DriverManagerDataSource(); + dataSource.setDriverClassName(driverClass); + dataSource.setUrl(masterUrl); + dataSource.setUsername(user); + dataSource.setPassword(password); + return dataSource; + } +} diff --git a/hoe-gateway/src/main/java/com/recovery/gateway/util/ResponseUtils.java b/hoe-gateway/src/main/java/com/recovery/gateway/util/ResponseUtils.java new file mode 100644 index 0000000..2e3ef5c --- /dev/null +++ b/hoe-gateway/src/main/java/com/recovery/gateway/util/ResponseUtils.java @@ -0,0 +1,43 @@ +package com.recovery.gateway.util; + +import cn.hutool.json.JSONUtil; + +import com.recovery.common.base.result.ApiResult; +import com.recovery.common.base.result.ResultCode; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.core.io.buffer.DataBufferUtils; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.server.reactive.ServerHttpResponse; +import reactor.core.publisher.Mono; + +import java.nio.charset.StandardCharsets; +/** + * @author: + */ +public class ResponseUtils { + + public static Mono writeErrorInfo(ServerHttpResponse response, ResultCode resultCode) { + switch (resultCode) { + case ACCESS_UNAUTHORIZED: + case TOKEN_INVALID_OR_EXPIRED: + response.setStatusCode(HttpStatus.UNAUTHORIZED); + break; + case TOKEN_ACCESS_FORBIDDEN: + response.setStatusCode(HttpStatus.FORBIDDEN); + break; + default: + response.setStatusCode(HttpStatus.BAD_REQUEST); + break; + } + response.getHeaders().set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); + response.getHeaders().set("Access-Control-Allow-Origin", "*"); + response.getHeaders().set("Cache-Control", "no-cache"); + String body = JSONUtil.toJsonStr(ApiResult.failed(resultCode)); + DataBuffer buffer = response.bufferFactory().wrap(body.getBytes(StandardCharsets.UTF_8)); + return response.writeWith(Mono.just(buffer)) + .doOnError(error -> DataBufferUtils.release(buffer)); + } + +} diff --git a/hoe-gateway/src/main/java/com/recovery/gateway/util/UrlPatternUtils.java b/hoe-gateway/src/main/java/com/recovery/gateway/util/UrlPatternUtils.java new file mode 100644 index 0000000..68c8d63 --- /dev/null +++ b/hoe-gateway/src/main/java/com/recovery/gateway/util/UrlPatternUtils.java @@ -0,0 +1,22 @@ +package com.recovery.gateway.util; + +import cn.hutool.core.util.StrUtil; +import org.springframework.util.AntPathMatcher; +import org.springframework.util.PathMatcher; + +/** + * @author: + */ +public class UrlPatternUtils { + public static boolean match(String patternUrl, String requestUrl) { + if (StrUtil.isBlank(patternUrl) || StrUtil.isBlank(requestUrl)) { + return false; + } + PathMatcher matcher = new AntPathMatcher(); + return matcher.match(patternUrl, requestUrl); + } + + public static void main(String[] args) { + System.out.println(match("/a/b/c/**","/a/b/c/d/e")); + } +} diff --git a/hoe-gateway/src/main/resources/bootstrap.yml b/hoe-gateway/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..1f4767e --- /dev/null +++ b/hoe-gateway/src/main/resources/bootstrap.yml @@ -0,0 +1,23 @@ + +spring: + application: + name: hoe-gateway + main: + allow-bean-definition-overriding: true + profiles: + active: dev + cloud: + nacos: + discovery: + # metadata: + # serviceGroup: ytChen + server-addr: localhost:8848 + namespace: 11bfd099-10d6-4f2c-b969-58b76e435cce + config: + server-addr: localhost:8848 + file-extension: yaml + prefix: hoe-gateway + group: dev + namespace: 11bfd099-10d6-4f2c-b969-58b76e435cce +server: + port: 9000 diff --git a/hoe-order/pom.xml b/hoe-order/pom.xml new file mode 100644 index 0000000..8edc820 --- /dev/null +++ b/hoe-order/pom.xml @@ -0,0 +1,94 @@ + + + 4.0.0 + + com.recovery + hoe-recovery + 1.0.0 + + + hoe-order + + + 8 + 8 + UTF-8 + + + + + org.springframework.boot + spring-boot-configuration-processor + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + org.springframework.cloud + spring-cloud-starter-bootstrap + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + com.nimbusds + nimbus-jose-jwt + + + + com.recovery + common-redis + ${hoe-version} + + + com.recovery + common-mybatis-plus + ${hoe-version} + + + com.recovery + common-web + ${hoe-version} + + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + com.spotify + docker-maven-plugin + + + + diff --git a/hoe-order/src/main/resources/bootstrap.yml b/hoe-order/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..53f6548 --- /dev/null +++ b/hoe-order/src/main/resources/bootstrap.yml @@ -0,0 +1,23 @@ + +spring: + application: + name: hoe-order + main: + allow-bean-definition-overriding: true + profiles: + active: dev + cloud: + nacos: + discovery: + # metadata: + # serviceGroup: ytChen + server-addr: localhost:8848 + namespace: 11bfd099-10d6-4f2c-b969-58b76e435cce + config: + server-addr: localhost:8848 + file-extension: yaml + prefix: hoe-order + group: dev + namespace: 11bfd099-10d6-4f2c-b969-58b76e435cce +server: + port: 9002 diff --git a/hoe-order/src/main/resources/logback-bak.xml b/hoe-order/src/main/resources/logback-bak.xml new file mode 100644 index 0000000..d895647 --- /dev/null +++ b/hoe-order/src/main/resources/logback-bak.xml @@ -0,0 +1,53 @@ + + + logback + + + + + + + + + ${consoleLayoutPattern} + + + + + + ${SYS_LOG_DIR}/${LOG_FILE} + + WARN + ACCEPT + DENY + + + ${SYS_LOG_DIR}/%d{yyyy-MM-dd}/${LOG_FILE}_%d{yyyy-MM-dd}_%i.zip + + 50MB + + + + ${fileLayoutPattern} + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/hoe-order/src/main/resources/logback-spring.xml b/hoe-order/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..20a4666 --- /dev/null +++ b/hoe-order/src/main/resources/logback-spring.xml @@ -0,0 +1,175 @@ + + + + + + + + + + + + + + + + + + + + + true + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{15}) - %highlight(%msg) %n + + + + + + + + + + ERROR + ACCEPT + DENY + + + + + ${log_dir}/%d{yyyy-MM-dd}/error-log.log + + ${maxHistory} + + + + + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n + + + + + + + + + + WARN + ACCEPT + DENY + + + + ${log_dir}/%d{yyyy-MM-dd}/warn-log.log + + + ${maxHistory} + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n + + + + + + + + + + + INFO + ACCEPT + DENY + + + + ${log_dir}/%d{yyyy-MM-dd}/info-log.log + + + ${maxHistory} + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n + + + + + + + + + + + DEBUG + ACCEPT + DENY + + + + ${log_dir}/%d{yyyy-MM-dd}/debug-log.log + + + ${maxHistory} + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n + + + + + + + + + + + TRACE + ACCEPT + DENY + + + + ${log_dir}/%d{yyyy-MM-dd}/trace-log.log + + + ${maxHistory} + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..ad9c009 --- /dev/null +++ b/pom.xml @@ -0,0 +1,93 @@ + + + 4.0.0 + + com.recovery + hoe-recovery + 1.0.0 + pom + + hoe-common + hoe-auth + hoe-admin + hoe-gateway + hoe-order + + + + 8 + 8 + UTF-8 + 2020.0.3 + 2021.1 + 2.5.4 + 1.0.0 + 5.5.8 + 8.0.22 + 1.2.4 + 3.4.3 + + + org.springframework.boot + spring-boot-starter-parent + 2.5.4 + + + + + + org.projectlombok + lombok + + + cn.hutool + hutool-all + ${hutool-version} + + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + + com.alibaba.cloud + spring-cloud-alibaba-dependencies + ${spring-cloud-alibaba.version} + pom + import + + + + + mysql + mysql-connector-java + ${mysql.version} + + + com.alibaba + druid-spring-boot-starter + ${druid.version} + + + com.baomidou + mybatis-plus-boot-starter + ${mybatis-plus.version} + + + + + + + + \ No newline at end of file