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

简介: SpringCloud Alibaba微服务实战三十 | 统一资源服务器配置模块

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

由于间隔时间较久,建议先阅读下面两篇相关文章回顾一下。

SpringCloud Alibaba微服务实战十九 - 集成RBAC授权

SpringCloud Alibaba微服务实战二十八 - 网关授权VS微服务授权

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


认证服务器改造


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

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


网关改造


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

@Configuration
public class SecurityConfig {
    @Bean
    SecurityWebFilterChain webFluxSecurityFilterChain(ServerHttpSecurity http) throws Exception{
        http
   .httpBasic().disable()
   .csrf().disable();
        return http.build();
    }
}


独立资源服务器配置模块


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

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

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

public class CloudResourceServerConfigure extends ResourceServerConfigurerAdapter {
    private CustomAccessDeniedHandler accessDeniedHandler;
    private CustomAuthenticationEntryPoint exceptionEntryPoint;
    private TokenStore tokenStore;
    @Value("${security.oauth2.resource.id}")
    private String resourceId ;
    @Autowired(required = false)
    public void setAccessDeniedHandler(CustomAccessDeniedHandler accessDeniedHandler) {
        this.accessDeniedHandler = accessDeniedHandler;
    }
    @Autowired(required = false)
    public void setExceptionEntryPoint(CustomAuthenticationEntryPoint exceptionEntryPoint) {
        this.exceptionEntryPoint = exceptionEntryPoint;
    }
    @Autowired(required = false)
    public void setTokenStore(TokenStore tokenStore) {
        this.tokenStore = tokenStore;
    }
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll()
                .antMatchers(
                        "/v2/api-docs/**",
                        "/swagger-resources/**",
                        "/swagger-ui.html",
                        "/webjars/**"
                ).permitAll()
                .anyRequest().authenticated()
                .and()
                .csrf().disable();
    }
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter();
        UserAuthenticationConverter userTokenConverter = new CustomUserAuthenticationConverter();
        accessTokenConverter.setUserTokenConverter(userTokenConverter);
        if (exceptionEntryPoint != null) {
            resources.authenticationEntryPoint(exceptionEntryPoint);
        }
        if (accessDeniedHandler != null) {
            resources.accessDeniedHandler(accessDeniedHandler);
        }
        resources.resourceId(resourceId).tokenStore(tokenStore);
    }
}


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

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

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

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

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

@EnableGlobalMethodSecurity(prePostEnabled = true)
public class CloudSecurityAutoConfigure extends GlobalMethodSecurityConfiguration {
    @Bean
    @ConditionalOnMissingBean(name = "accessDeniedHandler")
    public CustomAccessDeniedHandler accessDeniedHandler() {
        return new CustomAccessDeniedHandler();
    }
    @Bean
    @ConditionalOnMissingBean(name = "authenticationEntryPoint")
    public CustomAuthenticationEntryPoint authenticationEntryPoint() {
        return new CustomAuthenticationEntryPoint();
    }
    @Override
    protected MethodSecurityExpressionHandler createExpressionHandler() {
        return new CustomMethodSecurityExpressionHandler();
    }
}

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


微服务改造


  • 在maven中删除原oauth2.0的相关配置,引入自定义cloud-component-security-starter
<dependency>
 <groupId>com.jianzh5.cloud</groupId>
 <artifactId>cloud-component-security-starter</artifactId>
</dependency>


  • 删除所有资源服务器相关代码(此过程略)
  • 修改主启动类,通过@EnableCloudResourceServer引入资源服务器配置
@EnableDiscoveryClient
@SpringCloudApplication
@EnableCloudResourceServer
public class AccountServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(AccountServiceApplication.class, args);
    }
}


  • 在需要拦截的Controller方法中添加自定义权限拦截注解@PreAuthorize("hasPrivilege('queryAccount')")
    当然也可以使用SpringSecurity原生注解 @PreAuthorize("hasAuthority('queryAccount')") ,两者作用一样。
@GetMapping("/account/getByCode/{accountCode}")
@PreAuthorize("hasPrivilege('queryAccount')")
//@PreAuthorize("hasAuthority('queryAccount')")
public ResultData<AccountDTO> getByCode(@PathVariable(value = "accountCode") String accountCode){
 AccountDTO accountDTO = accountService.selectByCode(accountCode);
 return ResultData.success(accountDTO);
}


测试


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

{
  "status": 500,
  "message": "不允许访问",
  "data": null,
  "success": false,
  "timestamp": 1619052359563
}


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

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

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

{
  "status": 2003,
  "message": "没有权限访问该资源",
  "data": null,
  "success": false,
  "timestamp": 1619052359563
}



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

目录
相关文章
|
9月前
|
人工智能 JavaScript API
零基础构建MCP服务器:TypeScript/Python双语言实战指南
作为一名深耕技术领域多年的博主摘星,我深刻感受到了MCP(Model Context Protocol)协议在AI生态系统中的革命性意义。MCP作为Anthropic推出的开放标准,正在重新定义AI应用与外部系统的交互方式,它不仅解决了传统API集成的复杂性问题,更为开发者提供了一个统一、安全、高效的连接框架。在过去几个月的实践中,我发现许多开发者对MCP的概念理解透彻,但在实际动手构建MCP服务器时却遇到了各种技术壁垒。从环境配置的细节问题到SDK API的深度理解,从第一个Hello World程序的调试到生产环境的部署优化,每一个环节都可能成为初学者的绊脚石。因此,我决定撰写这篇全面的实
2113 67
零基础构建MCP服务器:TypeScript/Python双语言实战指南
|
6月前
|
弹性计算 人工智能 前端开发
在阿里云ECS上部署n8n自动化工作流:U2实例实战
本文介绍如何在阿里云ECS的u2i/u2a实例上部署开源工作流自动化平台n8n,利用Docker快速搭建并配置定时任务,实现如每日抓取MuleRun新AI Agent并推送通知等自动化流程。内容涵盖环境准备、安全组设置、实战案例与优化建议,助力高效构建低维护成本的自动化系统。
1644 5
|
11月前
|
人工智能 安全 Shell
Jupyter MCP服务器部署实战:AI模型与Python环境无缝集成教程
Jupyter MCP服务器基于模型上下文协议(MCP),实现大型语言模型与Jupyter环境的无缝集成。它通过标准化接口,让AI模型安全访问和操作Jupyter核心组件,如内核、文件系统和终端。本文深入解析其技术架构、功能特性及部署方法。MCP服务器解决了传统AI模型缺乏实时上下文感知的问题,支持代码执行、变量状态获取、文件管理等功能,提升编程效率。同时,严格的权限控制确保了安全性。作为智能化交互工具,Jupyter MCP为动态计算环境与AI模型之间搭建了高效桥梁。
721 2
Jupyter MCP服务器部署实战:AI模型与Python环境无缝集成教程
|
9月前
|
JSON 前端开发 Go
Go语言实战:创建一个简单的 HTTP 服务器
本篇是《Go语言101实战》系列之一,讲解如何使用Go构建基础HTTP服务器。涵盖Go语言并发优势、HTTP服务搭建、路由处理、日志记录及测试方法,助你掌握高性能Web服务开发核心技能。
|
12月前
|
Go API 定位技术
MCP 实战:用 Go 语言开发一个查询 IP 信息的 MCP 服务器
随着 MCP 的快速普及和广泛应用,MCP 服务器也层出不穷。大多数开发者使用的 MCP 服务器开发库是官方提供的 typescript-sdk,而作为 Go 开发者,我们也可以借助优秀的第三方库去开发 MCP 服务器,例如 ThinkInAIXYZ/go-mcp。 本文将详细介绍如何在 Go 语言中使用 go-mcp 库来开发一个查询 IP 信息的 MCP 服务器。
754 2
|
弹性计算 资源调度 搜索推荐
阿里云ECS中长期成本节省计划解析:从原理到实战,助力企业降本提效
阿里云ECS节省计划的推出为企业用户提供了一种全新的成本优化方案。通过一次性购买的方式享受长期按量付费的折扣权益,客户不仅可以大幅降低ECS资源的使用成本还可以享受更高的灵活性和便捷性。本文将从多个维度深入剖析阿里云ECS节省计划,包括其核心优势、详尽的购买使用指引、与传统付费模式的全面对比,以及一客户成功案例,以供大家了解和参考。
|
负载均衡 Dubbo Java
Spring Cloud Alibaba与Spring Cloud区别和联系?
Spring Cloud Alibaba与Spring Cloud区别和联系?
|
人工智能 SpringCloudAlibaba 自然语言处理
SpringCloud Alibaba AI整合DeepSeek落地AI项目实战
在现代软件开发领域,微服务架构因其灵活性、可扩展性和模块化特性而受到广泛欢迎。微服务架构通过将大型应用程序拆分为多个小型、独立的服务,每个服务运行在其独立的进程中,服务与服务间通过轻量级通信机制(通常是HTTP API)进行通信。这种架构模式有助于提升系统的可维护性、可扩展性和开发效率。
5047 2