SpringCloud Alibaba微服務實戰三十 | 統一資源服務器配置模塊

 

前面文章我們對比過網關受權與微服務受權的區別,文章也提到了,若是要實現微服務受權,通常會構建一個獨立的資源服務器配置模塊,不然每一個後端業務都須要進行資源服務器的配置,那本節內容咱們就來完成此功能。html

因爲間隔時間較久,建議先閱讀下面兩篇相關文章回顧一下。

SpringCloud Alibaba微服務實戰十九 - 集成RBAC受權java

SpringCloud Alibaba微服務實戰二十八 - 網關受權VS微服務受權程序員

話很少說,咱們直接開始代碼改造。web

認證服務器改造

首先咱們須要改造認證服務器,須要認證服務器在構建用戶權限的時候使用的是權限標識字段。對於代碼而言只須要 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+"]不存在");
 }
}

網關改造

網關服務器再也不須要進行用戶權限校驗,因此咱們須要將相關校驗邏輯所有刪除。api

@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.classTokenStoreConfigure.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.classargs);
    }
}

 

  • 在須要攔截的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
}

 

 

以上,但願對你有所幫助!

 

這裏爲你們準備了一份小小的禮物,關注公衆號,輸入以下代碼,便可得到百度網盤地址,無套路領取!

001:《程序員必讀書籍》
002:《從無到有搭建中小型互聯網公司後臺服務架構與運維架構》
003:《互聯網企業高併發解決方案》
004:《互聯網架構教學視頻》
006:《SpringBoot實現點餐系統》
007:《SpringSecurity實戰視頻》
008:《Hadoop實戰教學視頻》
009:《騰訊2019Techo開發者大會PPT》

010: 微信交流羣

 

 

 

本文分享自微信公衆號 - JAVA日知錄(javadaily)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索