forSpringCloudAlibaba微服务实战转载请联系JAVA日知录公众号。在上一篇文章中,我们比较了网关授权和微服务授权的区别。文章中也提到,如果要实现微服务授权,一般会搭建一个独立的资源服务器配置模块。否则,每个后端业务都需要配置资源服务器,那么我们将在本节中完成这个功能。话不多说,我们直接开始代码改造。修改认证服务器首先我们需要修改认证服务器,要求认证服务器在构造用户授权时使用授权标识字段。代码只需要在UserDetailServiceImpl#loadUserByUsername()中修改即可。@OverridepublicUserDetailsloadUserByUsername(StringuserName)throwsUsernameNotFoundException{//获取本地用户SysUsersysUser=sysUserMapper.selectByUserName(userName);if(sysUser!=null){//获取当前用户的所有角色ListroleList=sysRoleService.listRolesByUserId(sysUser.getId());sysUser.setRoles(roleList.stream().map(SysRole::getRoleCode).collect(Collectors.toList()));ListroleIds=roleList.stream().map(SysRole::getId).collect(Collectors.toList());//获取所有角色的权限ListpermissionList=sysPermissionService.listPermissionsByRoles(roleIds);//基于方法拦截。将用户权限ID填入ListpermissionMethodList=permissionList.stream().map(SysPermission::getPermission).collect(Collectors.toList());sysUser.setPermissions(permissionMethodList);//构建oauth2的用户returnbuildUserDetails(sysUser);}else{thrownewUsernameNotFoundException("user["+userName+"]doesnotexist");}}网关改造网关服务器不再需要进行用户权限验证,所以我们需要删除所有相关验证逻辑.@ConfigurationpublicclassSecurityConfig{@BeanSecurityWebFilterChainwebFluxSecurityFilterChain(ServerHttpSecurityhttp)throwsException{http.httpBasic().disable().csrf().disable();returnhttp.build();}}独立资源服务器配置模块完成以上两步后,你将走到最后一个重要的步骤是创建一个独立的资源服务器配置模块,供其他模块引用。首先,我们要创建一个单独的资源服务模块cloud-component-security-starter,如下图是修改后的代码结构图。那么,要让一个普通的后端服务成为资源服务器,需要配置类继承ResourceServerConfigurerAdapter并进行相关配置。在我们独立的资源服务器模块中,首先要创建这样一个配置类。这个比较简单,从Makeacopyofthepreviousmodule开始。publicclassCloudResourceServerConfigureextendsResourceServerConfigurerAdapter{privateCustomAccessDeniedHandleraccessDeniedHandler;privateCustomAuthenticationEntryPointexceptionEntryPoint;privateTokenStoretokenStore;@Value("${security.oauth2.resource.id}")privateStringresourceId;@Autowired(required=false)publicvoidsetAccessDeniedHandler(CustomAccessDeniedHandleraccessDeniedHandler){this.accessDeniedHandler=accessDeniedHandler;}@Autowired(required=false)publicvoidsetExceptionEntryPoint(CustomAuthenticationEntryPointexceptionEntryPoint){this.exceptionEntryPoint=exceptionEntryPoint;}@Autowired(required=false)publicvoidsetTokenStore(TokenStoretokenStore){this.tokenStore=tokenStore;}@Overridepublicvoidconfigure(HttpSecurityhttp)throwsException{http.authorizeRequests().requestMatchers.(EndpointRequest)toAnyEndpoint()).permitAll().antMatchers("/v2/api-docs/**","/swagger-resources/**","/swagger-ui.html","/webjars/**").permitAll().anyRequest().authenticated().and().csrf().disable();}@Overridepublicvoidconfigure(ResourceServerSecurityConfigurerresources){DefaultAccessTokenConverteraccessTokenConverter=newDefaultAccessTokenConverter();UserAuthenticationConverteruserCakenConverter(newUserAuthenticationConverterusTokenConverter)UserAuthenticationConverter=accessTokenConverter.setUserTokenConverter(userTokenConverter);if(exceptionEntryPoint!=null){resources.authenticationEntryPoint(exceptionEntryPoint);}if(accessDeniedHandler!=null){resources.accessDeniedHandler(accessDeniedHandler);}resources.resourceId(resourceId).tokenStore;}既然资源服务器配置好了,那么其他模块如何引入这个配置类呢,这里可以利用SpringBoot的Enable模块驱动能力,通过@EnableXXX注解导入配置类我们创建自定义注解类EnableCloudResourceServer,其他模块可以通过@EnableCloudResourceServer注解导入资源服务器配置@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@EnableResourceServer//启用资源服务器@Import({CloudResourceServerConfigure.class,TokenStoreConfigure.class})public@interfaceEnableCloudResourceServer{}最后,我们知道微服务授权是基于方法拦截的。基于方法拦截,我们需要开启@EnableGlobalMethodSecurity,需要迁移我们自定义的权限注解功能。所以我们再创建一个配置类来配置上??面的功能。@EnableGlobalMethodSecurity(prePostEnabled=true)publicclassCloudSecurityAutoConfigureextendsGlobalMethodSecurityConfiguration{@Bean@ConditionalOnMissingBean(name="accessDeniedHandler")publicCustomAccessDeniedHandleraccessDeniedHandler(){returnnewCustomAccessDeniedHandler();}@Bean@ConditionalOnMissingBean(name="authenticationEntryPoint")publicCustomAuthenticationEntryPointauthenticationEntryPoint(){returnnewCustomAuthenticationEntryPoint();}@OverrideprotectedMethodSecurityExpressionHandlercreateExpressionHandler(){returnnewCustomMethodSecurityExpressionHandler();}}经过上面的修改,一个独立的资源服务器就创建成功了,接下来就是修改微服务了。微服务改造删除maven中原有oauth2.0的相关配置,引入自定义的cloud-component-security-startercom.jianzh5.cloudcloud-component-security-starter删除所有资源服务器相关代码(此过程省略)修改主启动类,通过@EnableCloudResourceServer@EnableDiscoveryClient@SpringCloudApplication@EnableCloudResourceServerpublicclassAccountServiceApplication{publicstaticvoidmain(String[]args){引入资源服务器配置SpringApplication.run(AccountServiceApplication.class,args);}}在需要拦截的Controller方法中添加自定义权限拦截注解@PreAuthorize("hasPrivilege('queryAccount')")当然也可以使用SpringSecurity原生注解@PreAuthorize("hasPrivilege('queryAccount')")PreAuthorize("hasAuthority('queryAccount')"),两者的工作原理相同。@GetMapping("/account/getByCode/{accountCode}")@PreAuthorize("hasPrivilege('queryAccount')")//@PreAuthorize("hasAuthority('queryAccount')")publicResultDatagetByCode(@PathVariable(value)="accountCode")StringaccountCode){AccountDTOaccountDTO=accountService.selectByCode(accountCode);returnResultData.success(accountDTO);}测试当我们访问一个没有权限的方法时,会出现如下错误信息,说明独立资源服务器配置成功{"status":500,"message":"Accessnotallowed","data":null,"success":false,"timestamp":1619052359563}提示:@PreAuthorize注解的异常抛出AccessDeniedException,不会被捕获通过accessDeniedHandler。被全局异常捕获。如果需要自定义@PreAuthorize错误异常,可以通过全局@RestControllerAdvice拦截异常。拦截到的自定义异常如下:{"status":2003,"message":"Nopermissiontoaccessthisresource","data":null,"success":false,"timestamp":1619052359563}以上,希望它会帮助你!【责任编辑:吴晓燕电话:(010)68476606】