• API网关与社保模块


    API网关与社保模块

    理解zuul网关的作用完成zuul网关的搭建

    实现社保模块的代码开发

    zuul网关

    在学习完前面的知识后,微服务架构已经初具雏形。但还有一些问题:不同的微服务一般会有不同的网 络地址,客户端在访问这些微服务时必须记住几十甚至几百个地址,这对于客户端方来说太复杂也难以 维护。如下图:

    如果让客户端直接与各个微服务通讯,可能会有很多问题:

    • 客户端会请求多个不同的服务,需要维护不同的请求地址,增加开发难度

    • 在某些场景下存在跨域请求的问题

    • 加大身份认证的难度,每个微服务需要独立认证

    因此,我们需要一个微服务网关,介于客户端与服务器之间的中间层,所有的外部请求都会先经过微服

    务网关。客户端只需要与网关交互,只知道一个网关地址即可,这样简化了开发还有以下优点:

    1、易于监控

    2、易于认证

    3、减少了客户端与各个微服务之间的交互次数

    API网关是一个服务器,是系统对外的唯一入口。API网关封装了系统内部架构,为每个客户端提供 一个定制的API。API网关方式的核心要点是,所有的客户端和消费端都通过统一的网关接入微服务,在 网关层处理所有的非业务功能。通常,网关也是提供REST/HTTP的访问API。服务端通过API-GW注册和管理服务。

    什么是zuul网关

    ZUUL是Netflflix开源的微服务网关,它可以和Eureka、Ribbon、Hystrix等组件配合使用,Zuul组件的核心是一系列的过滤器,这些过滤器可以完成以下功能:

    • 动态路由:动态将请求路由到不同后端集群

    • 压力测试:逐渐增加指向集群的流量,以了解性能

    • 负载分配:为每一种负载类型分配对应容量,并弃用超出限定值的请求静态响应处理:边缘位置进行响应,避免转发到内部集群

    • 身份认证和安全: 识别每一个资源的验证要求,并拒绝那些不符的请求。Spring Cloud对Zuul进行了整合和增强。

    Spring Cloud对Zuul进行了整合和增强

    Zuul加入后的架构

    不管是来自于客户端(PC或移动端)的请求,还是服务内部调用。一切对服务的请求都会经过Zuul这个 网关,然后再由网关来实现 鉴权、动态路由等等操作。Zuul就是我们服务的统一入口。

    快速入门

    工程搭建

    创建工程 ihrm_gate ,并导入zuul网关的响应依赖

    1. org.springframework.cloud
    2. spring-cloud-starter-netflix-eureka-client
    3. org.springframework.cloud
    4. spring-cloud-starter-netflix-zuul
    5. org.springframework.boot
    6. spring-boot-starter-actuator
    7. com.ihrm
    8. ihrm_common
    9. 1.0-SNAPSHOT
    10. org.springframework.boot
    11. spring-boot-starter-data-jpa

    编写启动类

    在网关工程中的 com.ihrm.gate 下创建启动类 GateApplication

    1. package com.ihrm.gate;
    2. import org.springframework.boot.SpringApplication;
    3. import org.springframework.boot.autoconfigure.SpringBootApplication;
    4. import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    5. import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
    6. //声明boot工程
    7. @SpringBootApplication(scanBasePackages="com.ihrm")
    8. //开启zuul网关功能
    9. @EnableZuulProxy
    10. public class GateApplication {
    11. public static void main(String[] args) {
    12. SpringApplication.run(GateApplication.class);
    13. }
    14. }

    @EnableZuulProxy注解开启Zuul网关功能

    编写配置文件

    1. #注册到eureka的服务地址
    2. eureka:
    3. client:
    4. service-url:
    5. defaultZone: http://127.0.0.1:6868/eureka/
    6. instance:
    7. preferIpAddress : true
    8. instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
    9. #服务配置
    10. server:
    11. port: 9090
    12. #spring配置
    13. spring:
    14. #1.应用配置
    15. application:
    16. name: ihrm-gate #指定服务名
    17. redis:
    18. host: 127.0.0.1
    19. port: 6379
    20. #配置路由规则
    21. zuul:
    22. routes:
    23. #路由id,随便写
    24. ihrm-company:
    25. path: /company/** #需要映射的路径地址
    26. #url: http://127.0.0.1:9001 #映射路径对应的实际微服务的url路径
    27. serviceId: ihrm-company
    28. #zuul 自动的会删除请求的前缀 http://127.0.0.1:9001/company
    29. stripPrefix: false #不删除请求前缀

    配置路由规则

    通过zuul网关请求企业微服务,需要在application.yml中配置路由转发规则如下

    1. #配置路由规则
    2. zuul:
    3. routes:
    4. #路由id,随便写
    5. ihrm-company:
    6. path: /company/** #需要映射的路径地址
    7. url: http://127.0.0.1:9001 #映射路径对应的实际微服务的url路径
    8. #zuul 自动的会删除请求的前缀 http://127.0.0.1:9001/company
    9. stripPrefix: false #不删除请求前缀

    骚戴理解:例如前端发的请求是http://localhost:9090/company,其中9090是zuul网关的端口号,然后就会匹配到path路径,因为符合/company/**,匹配上后会把http://localhost:9090/替换成http://127.0.0.1:9001,然后把/company拼接在后面,最终成http://127.0.0.1:9001/company,这是在配置了stripPrefix: false的情况下,如果没有配置这个,那就需要发请求的时候是http://localhost:9090/company/company,因为zuul网关会自动删除掉前缀/company

    注释

    代码

    作用

    配置路由规则

    zuul:

    配置Zuul网关的路由规则

    路由id,随便写

    ihrm-company:

    设置路由的id,可以随意命名

    需要映射的路径地址

    path: /company/**

    配置需要映射的路径地址,即客户端请求的路径地址

    映射路径对应的实际微服务的url路径

    url: http://127.0.0.1:9001

    配置映射路径对应的实际微服务的url路径,即请求需要转发到的服务地址

    zuul 自动的会删除请求的前缀 http://127.0.0.1:9001/company

    stripPrefix: false

    配置是否删除请求前缀,这里设置为false,表示不删除请求前缀

    路由配置

    在刚才的路由规则中,我们把路径对应的服务地址写死了!如果同一服务有多个实例的话,这样做显然 就不合理了。我们应该根据服务的名称,去Eureka注册中心查找服务对应的所有实例列表,然后进行动态路由才对!

    添加Eureka客户端发现功能

    • 添加eureka依赖

    1. org.springframework.cloud
    2. spring-cloud-starter-netflix-eureka-client
    3. org.springframework.cloud
    4. spring-cloud-starter-netflix-zuul
    5. org.springframework.boot
    6. spring-boot-starter-actuator
    7. com.ihrm
    8. ihrm_common
    9. 1.0-SNAPSHOT
    10. org.springframework.boot
    11. spring-boot-starter-data-jpa
    • 修改启动类,开启服务发现功能

    1. package com.ihrm.gate;
    2. import org.springframework.boot.SpringApplication;
    3. import org.springframework.boot.autoconfigure.SpringBootApplication;
    4. import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    5. import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
    6. //声明boot工程
    7. @SpringBootApplication(scanBasePackages="com.ihrm")
    8. //开启zuul网关功能
    9. @EnableZuulProxy
    10. //开启服务发现功能
    11. @EnableDiscoveryClient
    12. public class GateApplication {
    13. public static void main(String[] args) {
    14. SpringApplication.run(GateApplication.class);
    15. }
    16. }

    修改配置文件

    • 在application.xml中添加eureka服务发现的相关配置信息

    1. #注册到eureka的服务地址
    2. eureka:
    3. client:
    4. service-url:
    5. defaultZone: http://127.0.0.1:6868/eureka/
    6. instance:
    7. preferIpAddress : true
    8. instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
    9. #服务配置
    10. server:
    11. port: 9090
    12. #spring配置
    13. spring:
    14. #1.应用配置
    15. application:
    16. name: ihrm-gate #指定服务名
    17. redis:
    18. host: 127.0.0.1
    19. port: 6379
    20. #配置路由规则
    21. zuul:
    22. routes:
    23. #路由id,随便写
    24. ihrm-company:
    25. path: /company/** #需要映射的路径地址
    26. #url: http://127.0.0.1:9001 #映射路径对应的实际微服务的url路径
    27. serviceId: ihrm-company
    28. #zuul 自动的会删除请求的前缀 http://127.0.0.1:9001/company
    29. stripPrefix: false #不删除请求前缀

    zuul网关从eureka注册中心中获取服务提供者的所有数据

    骚戴理解:解释一下下面的配置内容

    1. #注册到eureka的服务地址
    2. eureka:
    3. client:
    4. service-url:
    5. defaultZone: http://127.0.0.1:6868/eureka/
    6. instance:
    7. preferIpAddress : true
    8. instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}

    注释

    代码

    作用

    注册到eureka的服务地址

    eureka:

    配置Eureka注册中心的相关信息

    配置Eureka客户端的服务地址

    eureka.client.service-url.defaultZone: http://127.0.0.1:6868/eureka/

    配置Eureka客户端注册中心的服务地址,即Eureka Server的地址

    配置Eureka客户端实例的信息

    eureka.instance

    配置Eureka客户端实例的信息,包括优先使用IP地址、实例ID等信息

    优先使用IP地址

    preferIpAddress : true

    配置Eureka客户端实例优先使用IP地址

    实例ID

    instance-id: {spring.cloud.client.ip-address}:spring.cloud.client.ip−address:{spring.application.name}:${server.port}

    配置Eureka客户端实例的ID,使用${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}表达式,其中${spring.cloud.client.ip-address}表示客户端的IP地址,${spring.application.name}表示应用名称,${server.port}表示应用端口号。

    • 修改zuul路由的映射规则

    因为已经有了Eureka客户端,我们可以从Eureka获取服务的地址信息,因此映射时无需指定IP地址,而 是通过serviceId: ihrm-company来指定服务名称访问,而且Zuul已经集成了Ribbon的负载均衡功能。

    1. zuul:
    2. routes:
    3. ihrm-company: # 这里是路由id,随意写
    4. path: /ihrm-company/** # 这里是映射路径
    5. serviceId: ihrm-company # 指定服务名称

    简化配置

    在刚才的配置中,我们的规则是这样的:

    • zuul.routes..path=/xxx/** : 来指定映射路径。 是自定义的路由名

    • zuul.routes..serviceId=/user-service :来指定服务名。

    而大多数情况下,我们的 路由名称往往和 服务名会写成一样的。因此Zuul就提供了一种简化的配置语法: zuul.routes.=

    比方说上面我们关于user-service的配置可以简化为一条(省去了对服务名称的配置):

    1. zuul:
    2. routes:
    3. ihrm-company: /ihrm-company/** # 这里是映射路径

    在使用Zuul的过程中,上面讲述的规则已经大大的简化了配置项。但是当服务较多时,配置也是比较繁 琐的。因此Zuul就指定了默认的路由规则:

    • 默认情况下,一切服务的映射路径就是服务名本身。例如服务名为: ihrm-company ,则默认的映射路径就是: /ihrm-company/**

    也就是说,刚才的映射规则我们完全不配置也是可以的

    前端修改

    由于使用了zuul网关统一对所有微服务进行转发,那么我们在前端系统调用的时候,只需要向网关服务 器发送请求即可。在前端框架中的 config/index.js 中修改请求URL

    基于Zuul的统一鉴权

    spring cloud Zuul包含了对请求的路由和过滤2个功能。路由功能负责将请求转发到具体的微服务上, 而过滤器负责对请求的处理过程进行干预,是实现权限校验、服务聚合等功能的基础。

    Zuul的过滤器

    ZuulFilter

    ZuulFilter是过滤器的顶级父类。在这里我们看一下其中定义的4个最重要的方法:

    1. public abstract ZuulFilter implements IZuulFilter{
    2. abstract public String filterType();
    3. abstract public int filterOrder();
    4. boolean shouldFilter();// 来自IZuulFilter
    5. Object run() throws ZuulException;// IZuulFilter
    6. }

    shouldFilter :返回一个 Boolean 值,判断该过滤器是否需要执行。返回true执行,返回false 不执行。

    run :过滤器的具体业务逻辑。

    filterType :返回字符串,代表过滤器的类型。包含以下4种:

    • pre :请求在被路由之前执行

    • routing :在路由请求时调用

    • post :在routing和errror过滤器之后调用

    • error :处理请求时发生错误调用

    filterOrder :通过返回的int值来定义过滤器的执行顺序,数字越小优先级越高。

    自定义过滤器

    接下来我们来自定义一个过滤器,用于深入理解zuul过滤器的执行过程

    1. package com.ihrm.gate.filter;
    2. import com.netflix.zuul.ZuulFilter;
    3. import com.netflix.zuul.context.RequestContext;
    4. import com.netflix.zuul.exception.ZuulException;
    5. import org.springframework.http.HttpStatus;
    6. import org.springframework.stereotype.Component;
    7. import javax.servlet.http.HttpServletRequest;
    8. /**
    9. * 自定义过滤器
    10. */
    11. //@Component
    12. public class LoginFilter extends ZuulFilter {
    13. /**
    14. * String类型的返回值
    15. * 定义过滤器类型的
    16. * pre : 在执行路由请求之前执行
    17. * routing : 在路由请求时调用
    18. * post : 在routing和error过滤器之后执行
    19. * error : 处理请求出现异常的时候执行
    20. */
    21. public String filterType() {
    22. return "pre";
    23. }
    24. /**
    25. * int类型的返回值
    26. * 定义过滤器的优先级 : 数字越小,优先级越高
    27. */
    28. public int filterOrder() {
    29. return 2;
    30. }
    31. /**
    32. * boolean类型的返回值
    33. * 判断过滤器是否需要执行
    34. *
    35. */
    36. public boolean shouldFilter() {
    37. //对某些请求过滤(不执行过滤器)
    38. return true;
    39. }
    40. /**
    41. * run方法 : 过滤器中负责的具体业务逻辑
    42. * 使用过滤器进行jwt的鉴权
    43. */
    44. public Object run() throws ZuulException {
    45. //System.out.println("执行了LoginFilter的run方法");
    46. //1.获取请求对象request
    47. //1.1 获取Zuul提供的请求上下文的对象(工具类)
    48. RequestContext rc = RequestContext.getCurrentContext();
    49. //1.2 从上下文对象获取request对象
    50. HttpServletRequest request = rc.getRequest();
    51. //2.从request中获取Authorization的头信息
    52. String token = request.getHeader("Authorization");
    53. //3.判断
    54. if(token == null || "".equals(token)) {
    55. //没有传递token信息,需要登录,拦截
    56. rc.setSendZuulResponse(false);
    57. //返回错误的401状态码
    58. rc.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
    59. }
    60. return null;
    61. }
    62. }

    过滤器执行生命周期

    这张是Zuul官网提供的请求生命周期图,清晰的表现了一个请求在各个过滤器的执行顺序。

    正常流程:

    • 请求到达首先会经过pre类型过滤器,而后到达routing类型,进行路由,请求就到达真正的 服务提供者,执行请求,返回结果后,会到达post过滤器。而后返回响应。

    异常流程:

    • 整个过程中,pre或者routing过滤器出现异常,都会直接进入error过滤器,再error处理完毕 后,会将请求交给POST过滤器,最后返回给用户。

    • 如果是error过滤器自己出现异常,最终也会进入POST过滤器,而后返回。

    • 如果是POST过滤器出现异常,会跳转到error过滤器,但是与pre和routing不同的时,请求 不会再到达POST过滤器了。

    所有内置过滤器列表:

    使用场景

    场景非常多:

    • 请求鉴权:一般放在pre类型,如果发现没有访问权限,直接就拦截了

    • 异常处理:一般会在error类型和post类型过滤器中结合来处理。

    • 服务调用时长统计:pre和post结合使用。

    统一鉴权

    基于JWT的统一鉴权

    在某些应用中,往往使用JWT的形式进行无状态的用户鉴权。对于JWT的鉴权,只需要使用Zuul的自定义过滤器,在过滤器中判断是否携带JWT的token信息即可。

    1. public Object run() throws ZuulException {
    2. //System.out.println("执行了LoginFilter的run方法");
    3. //1.获取请求对象request
    4. //1.1 获取Zuul提供的请求上下文的对象(工具类)
    5. RequestContext rc = RequestContext.getCurrentContext();
    6. //1.2 从上下文对象获取request对象
    7. HttpServletRequest request = rc.getRequest();
    8. //2.从request中获取Authorization的头信息
    9. String token = request.getHeader("Authorization");
    10. //3.判断
    11. if(token == null || "".equals(token)) {
    12. //没有传递token信息,需要登录,拦截
    13. rc.setSendZuulResponse(false);
    14. //返回错误的401状态码
    15. rc.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value());
    16. }
    17. return null;
    18. }

    基于Shiro的统一鉴权

    由于我们的系统使用shiro结合自定义session的形式,相当于将用户数据存储到了分布式缓存redis中。 那么只需要再zuul中使用shiro即可完成统一用户权限校验

    • 引入shiro依赖

    1.     com.ihrm
    2.     ihrm_common
    3.     1.0-SNAPSHOT
    • 配置shiro

    1. package com.ihrm.gate;
    2. import com.ihrm.common.shiro.realm.IhrmRealm;
    3. import com.ihrm.common.shiro.session.CustomSessionManager;
    4. import org.apache.shiro.mgt.SecurityManager;
    5. import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
    6. import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
    7. import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    8. import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
    9. import org.crazycake.shiro.RedisCacheManager;
    10. import org.crazycake.shiro.RedisManager;
    11. import org.crazycake.shiro.RedisSessionDAO;
    12. import org.springframework.beans.factory.annotation.Value;
    13. import org.springframework.context.annotation.Bean;
    14. import org.springframework.context.annotation.Configuration;
    15. import java.util.LinkedHashMap;
    16. import java.util.Map;
    17. @Configuration
    18. public class ShiroConfiguration {
    19. //1.创建realm
    20. @Bean
    21. public IhrmRealm getRealm() {
    22. return new IhrmRealm();
    23. }
    24. //2.创建安全管理器
    25. @Bean
    26. public SecurityManager getSecurityManager(IhrmRealm realm) {
    27. DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    28. securityManager.setRealm(realm);
    29. //将自定义的会话管理器注册到安全管理器中
    30. securityManager.setSessionManager(sessionManager());
    31. //将自定义的redis缓存管理器注册到安全管理器中
    32. securityManager.setCacheManager(cacheManager());
    33. return securityManager;
    34. }
    35. //3.配置shiro的过滤器工厂
    36. /**
    37. * 再web程序中,shiro进行权限控制全部是通过一组过滤器集合进行控制
    38. *
    39. */
    40. @Bean
    41. public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
    42. //1.创建过滤器工厂
    43. ShiroFilterFactoryBean filterFactory = new ShiroFilterFactoryBean();
    44. //2.设置安全管理器
    45. filterFactory.setSecurityManager(securityManager);
    46. //3.通用配置(跳转登录页面,未授权跳转的页面)
    47. filterFactory.setLoginUrl("/autherror?code=1");//跳转url地址
    48. filterFactory.setUnauthorizedUrl("/autherror?code=2");//未授权的url
    49. //4.设置过滤器集合
    50. Map filterMap = new LinkedHashMap<>();
    51. //anon -- 匿名访问
    52. filterMap.put("/sys/login","anon");
    53. filterMap.put("/autherror","anon");
    54. //注册
    55. //authc -- 认证之后访问(登录)
    56. filterMap.put("/**","authc");
    57. //perms -- 具有某中权限 (使用注解配置授权)
    58. filterFactory.setFilterChainDefinitionMap(filterMap);
    59. return filterFactory;
    60. }
    61. @Value("${spring.redis.host}")
    62. private String host;
    63. @Value("${spring.redis.port}")
    64. private int port;
    65. /**
    66. * 1.redis的控制器,操作redis
    67. */
    68. public RedisManager redisManager() {
    69. RedisManager redisManager = new RedisManager();
    70. redisManager.setHost(host);
    71. redisManager.setPort(port);
    72. return redisManager;
    73. }
    74. /**
    75. * 2.sessionDao
    76. */
    77. public RedisSessionDAO redisSessionDAO() {
    78. RedisSessionDAO sessionDAO = new RedisSessionDAO();
    79. sessionDAO.setRedisManager(redisManager());
    80. return sessionDAO;
    81. }
    82. /**
    83. * 3.会话管理器
    84. */
    85. public DefaultWebSessionManager sessionManager() {
    86. CustomSessionManager sessionManager = new CustomSessionManager();
    87. //sessionManager.setSessionIdCookieEnabled(false);
    88. sessionManager.setSessionIdUrlRewritingEnabled(false);
    89. sessionManager.setSessionDAO(redisSessionDAO());
    90. return sessionManager;
    91. }
    92. /**
    93. * 4.缓存管理器
    94. */
    95. public RedisCacheManager cacheManager() {
    96. RedisCacheManager redisCacheManager = new RedisCacheManager();
    97. redisCacheManager.setRedisManager(redisManager());
    98. return redisCacheManager;
    99. }
    100. //开启对shior注解的支持
    101. @Bean
    102. public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
    103. AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
    104. advisor.setSecurityManager(securityManager);
    105. return advisor;
    106. }
    107. }

    由于再zuul网关中已经进行了统一的权限校验,那么其它微服务的权限校验就可以关闭了。

    传递敏感header

    经过测试可以发现在网关中明明已经具备了权限,可以在具体的微服务中还是会告知权限不足或者没有 找到相关用户。这是因为在Zuul进行请求转发的时候,会把header清空,为了传递原始的header信息 到最终的微服务,在配置加上:

    1. zuul.routes.xxx.sentiviteHeaders: #将指定路由的敏感头设置为空
    2. zuul.routes.xxx.customSensitiveHeaders: true #对指定路由开启自定义敏感头

    完整的gate配置文件

    1. #注册到eureka的服务地址
    2. eureka:
    3. client:
    4. service-url:
    5. defaultZone: http://127.0.0.1:6868/eureka/
    6. instance:
    7. preferIpAddress : true
    8. instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
    9. #服务配置
    10. server:
    11. port: 9090
    12. #spring配置
    13. spring:
    14. #1.应用配置
    15. application:
    16. name: ihrm-gate #指定服务名
    17. redis:
    18. host: 127.0.0.1
    19. port: 6379
    20. #配置路由规则
    21. zuul:
    22. routes:
    23. #路由id,随便写
    24. ihrm-company:
    25. path: /company/** #需要映射的路径地址
    26. #url: http://127.0.0.1:9001 #映射路径对应的实际微服务的url路径
    27. serviceId: ihrm-company
    28. #zuul 自动的会删除请求的前缀 http://127.0.0.1:9001/company
    29. stripPrefix: false #不删除请求前缀
    30. #处理敏感头信息
    31. sentiviteHeaders: #将指定路由的敏感头设置为空
    32. customSensitiveHeaders: true #对指定路由开启自定义敏感头
    33. ihrm-system: #系统
    34. path: /sys/** #配置请求URL的请求规则
    35. serviceId: ihrm-system #指定Eureka注册中心中的服务id
    36. stripPrefix: false
    37. sentiviteHeaders: #将指定路由的敏感头设置为空
    38. customSensitiveHeaders: true #对指定路由开启自定义敏感头
    39. ihrm-employee: #员工
    40. path: /employees/** #配置请求URL的请求规则
    41. serviceId: ihrm-employee #指定Eureka注册中心中的服务id
    42. stripPrefix: false
    43. sentiviteHeaders: #将指定路由的敏感头设置为空
    44. customSensitiveHeaders: true #对指定路由开启自定义敏感头
    45. ihrm-social-securitys: #社保
    46. path: /social_securitys/** #配置请求URL的请求规则
    47. serviceId: ihrm-social-securitys #指定Eureka注册中心中的服务id
    48. stripPrefix: false
    49. sentiviteHeaders: #将指定路由的敏感头设置为空
    50. customSensitiveHeaders: true #对指定路由开启自定义敏感头
    51. ihrm-atte-cfg: #社保
    52. path: /cfg/** #配置请求URL的请求规则
    53. serviceId: ihrm-attendance #指定Eureka注册中心中的服务id
    54. stripPrefix: false
    55. sentiviteHeaders: #将指定路由的敏感头设置为空
    56. customSensitiveHeaders: true #对指定路由开启自定义敏感头
    57. ihrm-atte-attendances: #社保
    58. path: /attendances/** #配置请求URL的请求规则
    59. serviceId: ihrm-attendance #指定Eureka注册中心中的服务id
    60. stripPrefix: false
    61. sentiviteHeaders: #将指定路由的敏感头设置为空
    62. customSensitiveHeaders: true #对指定路由开启自定义敏感头

    社保管理

    需求分析

    完成社保模板相关代码开发:

    • 企业员工参保设置

    • 企业月度社保明细

    • 企业社保归档数据

    数据库表

    • 社保归档表

    1. CREATE TABLE `ss_archive` (
    2. `id` varchar(40) NOT NULL COMMENT 'id',
    3. `company_id` varchar(40) NOT NULL COMMENT '企业id',
    4. `years_month` varchar(255) NOT NULL COMMENT '年月',
    5. `creation_time` date NOT NULL COMMENT '创建时间',
    6. `enterprise_payment` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '企业缴纳',
    7. `personal_payment` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '个人缴纳',
    8. `total` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '合计', PRIMARY KEY (`id`)
    9. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='社保-归档表'
    • 社保-归档详情表

    1. CREATE TABLE `ss_archive_detail` (
    2. `id` varchar(40) NOT NULL COMMENT 'id',
    3. `archive_id` varchar(40) NOT NULL COMMENT '归档id',
    4. `user_id` varchar(40) DEFAULT NULL COMMENT '用户id',
    5. `username` varchar(255) DEFAULT NULL COMMENT '用户名称',
    6. `time_of_entry` varchar(255) DEFAULT NULL COMMENT '入职时间',
    7. `mobile` varchar(255) DEFAULT NULL COMMENT '手机号',
    8. `id_number` varchar(255) DEFAULT NULL COMMENT '身份证号',
    9. `the_highest_degree_of_education` varchar(255) DEFAULT NULL COMMENT '学历',
    10. `opening_bank` varchar(255) DEFAULT NULL COMMENT '开户行',
    11. `bank_card_number` varchar(255) DEFAULT NULL COMMENT '银行卡号',
    12. `first_level_department` varchar(255) DEFAULT NULL COMMENT '一级部门',
    13. `two_level_department` varchar(255) DEFAULT NULL COMMENT '二级部门',
    14. `working_city` varchar(255) DEFAULT NULL COMMENT '工作城市',
    15. `social_security_computer_number` varchar(255) DEFAULT NULL COMMENT '社保电脑号',
    16. `provident_fund_account` varchar(255) DEFAULT NULL COMMENT '公积金账号',
    17. `leave_date` varchar(255) DEFAULT NULL COMMENT '离职时间',
    18. `household_registration_type` varchar(255) DEFAULT NULL COMMENT '户籍类型',
    19. `participating_in_the_city` varchar(255) DEFAULT NULL COMMENT '参保城市',
    20. `social_security_month` varchar(255) DEFAULT NULL COMMENT '社保月份',
    21. `social_security_base` decimal(10,2) DEFAULT NULL COMMENT '社保基数',
    22. `social_security` decimal(10,2) DEFAULT NULL COMMENT '社保合计',
    23. `social_security_enterprise` decimal(10,2) DEFAULT NULL COMMENT '社保企业',
    24. `social_security_individual` decimal(10,2) DEFAULT NULL COMMENT '社保个人',
    25. `provident_fund_city` varchar(255) DEFAULT NULL COMMENT '公积金城市',
    26. `provident_fund_month` varchar(255) DEFAULT NULL COMMENT '公积金月份',
    27. `provident_fund_base` decimal(10,2) DEFAULT NULL COMMENT '公积金基数',
    28. `accumulation_fund_enterprise_base` decimal(10,2) DEFAULT NULL COMMENT '公积金企业基数',
    29. `proportion_of_provident_fund_enterprises` decimal(10,2) DEFAULT NULL COMMENT '公积金企业比例',
    30. `individual_base_of_provident_fund` decimal(10,2) DEFAULT NULL COMMENT '公积金
    31. 个人基数',
    32. `personal_ratio_of_provident_fund` decimal(10,2) DEFAULT NULL COMMENT '公积金个人比例',
    33. `total_provident_fund` decimal(10,2) DEFAULT NULL COMMENT '公积金合计',
    34. `provident_fund_enterprises` decimal(10,2) DEFAULT NULL COMMENT '公积金企业',
    35. `provident_fund_individual` decimal(10,2) DEFAULT NULL COMMENT '公积金个人',
    36. `pension_enterprise_base` decimal(10,2) DEFAULT NULL COMMENT '养老企业基数',
    37. `proportion_of_pension_enterprises` decimal(10,2) DEFAULT NULL COMMENT '养老企业比例',
    38. `pension_enterprise` decimal(10,2) DEFAULT NULL COMMENT '养老企业',
    39. `personal_pension_base` decimal(10,2) DEFAULT NULL COMMENT '养老个人基数',
    40. `personal_pension_ratio` decimal(10,2) DEFAULT NULL COMMENT '养老个人比例',
    41. `old_age_individual` decimal(10,2) DEFAULT NULL COMMENT '养老个人',
    42. `unemployment_enterprise_base` decimal(10,2) DEFAULT NULL COMMENT '失业企业基数',
    43. `proportion_of_unemployed_enterprises` decimal(10,2) DEFAULT NULL COMMENT '失业企业比例',
    44. `unemployed_enterprise` decimal(10,2) DEFAULT NULL COMMENT '失业企业',
    45. `the_number_of_unemployed_individuals` decimal(10,2) DEFAULT NULL COMMENT '失业个人基数',
    46. `percentage_of_unemployed_individuals` decimal(10,2) DEFAULT NULL COMMENT '失业个人比例',
    47. `unemployed_individual` decimal(10,2) DEFAULT NULL COMMENT '失业个人',
    48. `medical_enterprise_base` decimal(10,2) DEFAULT NULL COMMENT '医疗企业基数',
    49. `proportion_of_medical_enterprises` decimal(10,2) DEFAULT NULL COMMENT '医疗企业比例',
    50. `medical_enterprise` decimal(10,2) DEFAULT NULL COMMENT '医疗企业',
    51. `medical_personal_base` decimal(10,2) DEFAULT NULL COMMENT '医疗个人基数',
    52. `medical_personal_ratio` decimal(10,2) DEFAULT NULL COMMENT '医疗个人比例',
    53. `medical_individual` decimal(10,2) DEFAULT NULL COMMENT '医疗个人',
    54. `base_of_industrial_injury_enterprises` decimal(10,2) DEFAULT NULL COMMENT '工伤企业基数',
    55. `proportion_of_industrial_injury_enterprises` decimal(10,2) DEFAULT NULL COMMENT '工伤企业比例',
    56. `industrial_injury_enterprise` decimal(10,2) DEFAULT NULL COMMENT '工伤企业',
    57. `fertility_enterprise_base` decimal(10,2) DEFAULT NULL COMMENT '生育企业基数',
    58. `proportion_of_fertility_enterprises` decimal(10,2) DEFAULT NULL COMMENT '生育企业比例',
    59. `childbearing_enterprise` decimal(10,2) DEFAULT NULL COMMENT '生育企业',
    60. `base_of_serious_illness` decimal(10,2) DEFAULT NULL COMMENT '大病企业基数',
    61. `proportion_of_seriously_ill_enterprises` decimal(10,2) DEFAULT NULL COMMENT '大病企业比例',
    62. `big_disease_enterprise` decimal(10,2) DEFAULT NULL COMMENT '大病企业',
    63. `personal_base_of_serious_illness` decimal(10,2) DEFAULT NULL COMMENT '大病个人基数',
    64. `personal_proportion_of_serious_illness` decimal(10,2) DEFAULT NULL COMMENT '大病个人比例',
    65. `a_person_of_great_disease` decimal(10,2) DEFAULT NULL COMMENT '大病个人',
    66. `provident_fund_notes` text COMMENT '公积金备注',
    67. `social_security_notes` text COMMENT '社保备注', PRIMARY KEY (`id`)
    68. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='社保-归档详情'
    • 社保-城市与缴费项目关联表

    1. CREATE TABLE `ss_city_payment_item` (
    2. `id` varchar(40) NOT NULL,
    3. `city_id` varchar(40) NOT NULL COMMENT '城市id',
    4. `payment_item_id` varchar(40) NOT NULL COMMENT '缴费项目id',
    5. `switch_company` tinyint(1) NOT NULL COMMENT '企业是否缴纳开关,0为禁用,1为启用',
    6. `scale_company` decimal(6,2) DEFAULT NULL COMMENT '企业比例',
    7. `switch_personal` tinyint(1) NOT NULL COMMENT '个人是否缴纳开关,0为禁用,1为启用',
    8. `scale_personal` decimal(6,2) DEFAULT NULL COMMENT '个人比例', PRIMARY KEY (`id`),
    9. UNIQUE KEY `UK_CID_PIID` (`city_id`,`payment_item_id`) USING BTREE COMMENT '城市id与缴费项目id组合唯一'
    10. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='社保-城市与缴费项目关联表'
    • 社保-企业设置信息

    1. CREATE TABLE `ss_company_settings` (
    2. `company_id` varchar(40) NOT NULL COMMENT '企业id',
    3. `is_settings` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0是未设置,1是已设置',
    4. `data_month` varchar(40) NOT NULL COMMENT '当前显示报表月份', PRIMARY KEY (`company_id`)
    5. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='社保-企业设置信息'
    • 社保-缴费项目

    1. CREATE TABLE `ss_payment_item` (
    2. `id` varchar(40) NOT NULL COMMENT 'id',
    3. `name` varchar(255) NOT NULL COMMENT '缴费项目名称',
    4. `switch_company` tinyint(1) NOT NULL DEFAULT '0' COMMENT '企业是否缴纳开关,0为禁用,1为启用',
    5. `scale_company` decimal(6,2) NOT NULL DEFAULT '0.00' COMMENT '企业比例',
    6. `switch_personal` tinyint(1) NOT NULL DEFAULT '0' COMMENT '个人是否缴纳开关,0为禁用,1为启用',
    7. `scale_personal` decimal(6,2) NOT NULL DEFAULT '0.00' COMMENT '个人比例', PRIMARY KEY (`id`)
    8. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='社保-缴费项目'
    • 社保-用户社保信息表

    1. CREATE TABLE `ss_user_social_security` (
    2. `user_id` varchar(40) NOT NULL COMMENT '用户id',
    3. `enterprises_pay_social_security_this_month` tinyint(1) NOT NULL DEFAULT '0' COMMENT '本月是否缴纳社保 0为不缴纳 1为缴纳',
    4. `enterprises_pay_the_provident_fund_this_month` tinyint(1) NOT NULL DEFAULT '0' COMMENT '本月是否缴纳公积金 0为不缴纳 1为缴纳',
    5. `participating_in_the_city_id` varchar(40) NOT NULL COMMENT '参保城市id',
    6. `social_security_type` tinyint(1) NOT NULL DEFAULT '2' COMMENT '参保类型 1为首次开户 2为非首次开户',
    7. `household_registration_type` tinyint(1) NOT NULL COMMENT '户籍类型 1为本市城镇 2
    8. 为本市农村 3为外埠城镇 4为外埠农村',
    9. `social_security_base` int(8) NOT NULL COMMENT '社保基数',
    10. `industrial_injury_ratio` decimal(6,2) DEFAULT NULL COMMENT '工伤比例',
    11. `social_security_notes` varchar(300) DEFAULT NULL COMMENT '社保备注',
    12. `provident_fund_city_id` varchar(40) NOT NULL COMMENT '公积金城市id',
    13. `provident_fund_base` int(8) NOT NULL COMMENT '公积金基数',
    14. `enterprise_proportion` decimal(6,2) NOT NULL COMMENT '公积金企业比例',
    15. `personal_proportion` decimal(6,2) NOT NULL COMMENT '公积金个人比例',
    16. `enterprise_provident_fund_payment` decimal(8,2) NOT NULL COMMENT '公积金企业缴纳数额',
    17. `personal_provident_fund_payment` decimal(8,2) NOT NULL COMMENT '公积金个人缴纳数额',
    18. `provident_fund_notes` varchar(300) DEFAULT NULL COMMENT '公积金备注',
    19. `create_time` datetime NOT NULL COMMENT '创建时间',
    20. `last_modify_time` datetime NOT NULL COMMENT '最后修改时间',
    21. `social_security_switch_update_time` datetime NOT NULL COMMENT '社保是否缴纳变更时间',
    22. `provident_fund_switch_update_time` datetime NOT NULL COMMENT '公积金是否缴纳变更时间',
    23. PRIMARY KEY (`user_id`)
    24. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='社保-用户社保信息表'

    搭建环境

    1. 使用代码生成工具,根据数据库表生成最基本的实体类,dao接口和service层代码。 略

    1. 创建社保管理模块ihrm_social_securitys,并将自动生成的代码依次copy到响应的包下。

    1. 修改zuul网关,添加社保相关的请求转发

    1. ihrm-social-securitys: #企业
    2. path: /social_securitys/** #配置请求URL的请求规则
    3. serviceId: ihrm-social-securitys #指定Eureka注册中心中的服务id strip-prefix: false
    4. sentiviteHeaders: customSensitiveHeaders: true

    社保列表

    企业社保设置

    企业第一次进入社保页面,会要求输入制作社保记录的日期

    1. /**
    2. * 获取企业是否设置社保
    3. * @return
    4. */
    5. @RequestMapping(value = "/settings", method = RequestMethod.GET)
    6. public Result getSettings() throws Exception {
    7. CompanySettings companySettings =
    8. companySettingsService.findById(companyId);
    9. return new Result(ResultCode.SUCCESS, companySettings);
    10. }
    11. /**
    12. * 保存企业社保设置
    13. */
    14. @RequestMapping(value = "/settings", method = RequestMethod.POST)
    15. public Result saveSettings(@RequestBody CompanySettings companySettings){
    16. companySettings.setCompanyId(companyId);
    17. companySettingsService.save(companySettings);
    18. return new Result(ResultCode.SUCCESS);
    19. }

    查询所有参保人员数据列表

    社保设置

    获取不同城市的社保缴费项目

    1. findPaymentItemByCityId

    展示员工社保数据

    1. findById

    设置员工参保数据

    1. /**
    2. * 保存用户社保信息
    3. */
    4. @ApiOperation(value=" 保 存 用 户 社 保 信 息 ",httpMethod = "PUT") @RequestMapping(value = "/{userId}", method = RequestMethod.PUT)
    5. public Result save(@RequestBody UserSocialSecurity userSocialSecurity) { userSocialService.save(userSocialSecurity);
    6. return new Result(ResultCode.SUCCESS);
    7. }
  • 相关阅读:
    用PHP实现极验验证功能
    基于卷积神经网络与双向长短时融合的锂离子电池剩余使用寿命预测
    推荐动漫《头文字D》
    JupyterNotebook中导出当前环境,并存储为requirements.txt
    想知道对象有没有在外面搞暧昧?简单几步就能查清楚
    我幼儿园的弟看了都直呼简单的【栈和队列】
    java面向对象.day29(内部类)
    接口
    Istio 流量管理 virtualservice
    (原创)自定义Drawable
  • 原文地址:https://blog.csdn.net/qq_50954361/article/details/129544355