文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

SpringCloud Alibaba微服务实战之统一资源服务器配置模块

2024-12-03 05:48

关注

本文转载自微信公众号「JAVA日知录」,作者单一色调   。转载本文请联系JAVA日知录公众号。

前面文章咱们对比过网关授权与微服务授权的区别,文章也提到了,如果要实现微服务授权,一般会构建一个独立的资源服务器配置模块,否则每个后端业务都需要进行资源服务器的配置,那本节内容我们就来完成此功能。

话不多说,我们直接开始代码改造。

认证服务器改造

首先我们需要改造认证服务器,需要认证服务器在构建用户权限的时候使用的是权限标识字段。对于代码而言只需要 UserDetailServiceImpl#loadUserByUsername()中修改即可。

  1. @Override 
  2. public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException { 
  3.  //获取本地用户 
  4.  SysUser sysUser = sysUserMapper.selectByUserName(userName); 
  5.  if(sysUser != null){ 
  6.   //获取当前用户的所有角色 
  7.   List roleList = sysRoleService.listRolesByUserId(sysUser.getId()); 
  8.   sysUser.setRoles(roleList.stream().map(SysRole::getRoleCode).collect(Collectors.toList())); 
  9.   List<Integer> roleIds = roleList.stream().map(SysRole::getId).collect(Collectors.toList()); 
  10.   //获取所有角色的权限 
  11.   List permissionList = sysPermissionService.listPermissionsByRoles(roleIds); 
  12.  
  13.   //基于方法拦截.只需放入用户权限标识即可 
  14.   List permissionMethodList = permissionList.stream() 
  15.     .map(SysPermission::getPermission) 
  16.     .collect(Collectors.toList()); 
  17.   sysUser.setPermissions(permissionMethodList); 
  18.   //构建oauth2的用户 
  19.   return buildUserDetails(sysUser); 
  20.  
  21.  }else
  22.   throw  new UsernameNotFoundException("用户["+userName+"]不存在"); 
  23.  } 

网关改造

网关服务器不再需要进行用户权限校验,所以我们需要将相关校验逻辑全部删除。

  1. @Configuration 
  2. public class SecurityConfig { 
  3.     @Bean 
  4.     SecurityWebFilterChain webFluxSecurityFilterChain(ServerHttpSecurity http) throws Exception{ 
  5.  
  6.         http 
  7.    .httpBasic().disable() 
  8.    .csrf().disable(); 
  9.  
  10.         return http.build(); 
  11.     } 

独立资源服务器配置模块

完成了上面两步后就到了最重要的步骤了,需要建立一个独立的资源服务器配置模块,用于其他模块引用。

首先我们得建立一个单独的资源服务模块 cloud-component-security-starter ,如下为改造后的代码结构图。

然后,要让一个普通后端服务成为资源服务器,需要有一个配置类继承 ResourceServerConfigurerAdapter并进行相关配置,那在我们独立的资源服务器模块我们首先得创建一个这样的配置类,这个比较简单,只需从之前的模块中拷贝一份出来。

  1. public class CloudResourceServerConfigure extends ResourceServerConfigurerAdapter { 
  2.     private CustomAccessDeniedHandler accessDeniedHandler; 
  3.     private CustomAuthenticationEntryPoint exceptionEntryPoint; 
  4.  
  5.     private TokenStore tokenStore; 
  6.  
  7.     @Value("${security.oauth2.resource.id}"
  8.     private String resourceId ; 
  9.  
  10.     @Autowired(required = false
  11.     public void setAccessDeniedHandler(CustomAccessDeniedHandler accessDeniedHandler) { 
  12.         this.accessDeniedHandler = accessDeniedHandler; 
  13.     } 
  14.  
  15.     @Autowired(required = false
  16.     public void setExceptionEntryPoint(CustomAuthenticationEntryPoint exceptionEntryPoint) { 
  17.         this.exceptionEntryPoint = exceptionEntryPoint; 
  18.     } 
  19.  
  20.     @Autowired(required = false
  21.     public void setTokenStore(TokenStore tokenStore) { 
  22.         this.tokenStore = tokenStore; 
  23.     } 
  24.  
  25.  
  26.     @Override 
  27.     public void configure(HttpSecurity http) throws Exception { 
  28.         http 
  29.                 .authorizeRequests() 
  30.                 .requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll() 
  31.                 .antMatchers( 
  32.                         "/v2/api-docs/**"
  33.                         "/swagger-resources/**"
  34.                         "/swagger-ui.html"
  35.                         "/webjars/**" 
  36.                 ).permitAll() 
  37.                 .anyRequest().authenticated() 
  38.                 .and() 
  39.                 .csrf().disable(); 
  40.     } 
  41.  
  42.  
  43.     @Override 
  44.     public void configure(ResourceServerSecurityConfigurer resources) { 
  45.         DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter(); 
  46.         UserAuthenticationConverter userTokenConverter = new CustomUserAuthenticationConverter(); 
  47.         accessTokenConverter.setUserTokenConverter(userTokenConverter); 
  48.  
  49.         if (exceptionEntryPoint != null) { 
  50.             resources.authenticationEntryPoint(exceptionEntryPoint); 
  51.         } 
  52.         if (accessDeniedHandler != null) { 
  53.             resources.accessDeniedHandler(accessDeniedHandler); 
  54.         } 
  55.  
  56.         resources.resourceId(resourceId).tokenStore(tokenStore); 
  57.     } 
  58.    

现在有了资源服务器配置,那其他模块如何引入这个配置类呢?

这里我们可以借助SpringBoot的Enable模块驱动能力,通过@EnableXXX注解导入配置类。

我们创建一个自定义注解类 EnableCloudResourceServer,其他模块通过 @EnableCloudResourceServer注解即可导入资源服务器配置

  1. @Target({ElementType.TYPE}) 
  2. @Retention(RetentionPolicy.RUNTIME) 
  3. @Documented 
  4. @EnableResourceServer //开启资源服务器 
  5. @Import({CloudResourceServerConfigure.class, TokenStoreConfigure.class}) 
  6. public @interface EnableCloudResourceServer { 
  7.  

最后我们知道微服务授权是基于方法拦截,基于方法拦截我们就需要开启 @EnableGlobalMethodSecurity,并且需要将我们自定义的权限注解功能迁移过来。所以我们再创建一个配置类用于配置上述功能。

  1. @EnableGlobalMethodSecurity(prePostEnabled = true
  2. public class CloudSecurityAutoConfigure extends GlobalMethodSecurityConfiguration { 
  3.  
  4.     @Bean 
  5.     @ConditionalOnMissingBean(name = "accessDeniedHandler"
  6.     public CustomAccessDeniedHandler accessDeniedHandler() { 
  7.         return new CustomAccessDeniedHandler(); 
  8.     } 
  9.  
  10.     @Bean 
  11.     @ConditionalOnMissingBean(name = "authenticationEntryPoint"
  12.     public CustomAuthenticationEntryPoint authenticationEntryPoint() { 
  13.         return new CustomAuthenticationEntryPoint(); 
  14.     } 
  15.  
  16.     @Override 
  17.     protected MethodSecurityExpressionHandler createExpressionHandler() { 
  18.         return new CustomMethodSecurityExpressionHandler(); 
  19.     } 
  20.  

经过上面的改造,一个独立的资源服务器创建成功了,现在剩下的就是对微服务的改造。

微服务改造

在maven中删除原oauth2.0的相关配置,引入自定义cloud-component-security-starter

  1.  
  2.  com.jianzh5.cloud 
  3.  cloud-component-security-starter 
  4.  

删除所有资源服务器相关代码(此过程略)

修改主启动类,通过@EnableCloudResourceServer引入资源服务器配置

  1. @EnableDiscoveryClient 
  2. @SpringCloudApplication 
  3. @EnableCloudResourceServer 
  4. public class AccountServiceApplication { 
  5.     public static void main(String[] args) { 
  6.         SpringApplication.run(AccountServiceApplication.class, args); 
  7.     } 

在需要拦截的Controller方法中添加自定义权限拦截注解@PreAuthorize("hasPrivilege('queryAccount')")

当然也可以使用SpringSecurity原生注解 @PreAuthorize("hasAuthority('queryAccount')") ,两者作用一样。

  1. @GetMapping("/account/getByCode/{accountCode}"
  2. @PreAuthorize("hasPrivilege('queryAccount')"
  3. //@PreAuthorize("hasAuthority('queryAccount')"
  4. public ResultData getByCode(@PathVariable(value = "accountCode") String accountCode){ 
  5.  AccountDTO accountDTO = accountService.selectByCode(accountCode); 
  6.  return ResultData.success(accountDTO); 

测试

我们访问一个没有权限的方法会出现如下错误提示,表明独立资源服务器成功配置

  1.   "status": 500, 
  2.   "message""不允许访问"
  3.   "data"null
  4.   "success"false
  5.   "timestamp": 1619052359563 

提示:@PreAuthorize 注解的异常,抛出AccessDeniedException异常,不会被accessDeniedHandler捕获,而是会被全局异常捕获。

如果需要自定义 @PreAuthorize错误异常,可以通过全局的 @RestControllerAdvice进行异常拦截

拦截后的自定义异常如下:

  1.   "status": 2003, 
  2.   "message""没有权限访问该资源"
  3.   "data"null
  4.   "success"false
  5.   "timestamp": 1619052359563 

以上,希望对你有所帮助!

【责任编辑:武晓燕 TEL:(010)68476606】

 

来源:JAVA日知录内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯