前面文章我們對比過網關受權與微服務受權的區別,文章也提到了,若是要實現微服務受權,通常會構建一個獨立的資源服務器配置模塊,不然每一個後端業務都須要進行資源服務器的配置,那本節內容咱們就來完成此功能。html
因爲間隔時間較久,建議先閱讀下面兩篇相關文章回顧一下。
話很少說,咱們直接開始代碼改造。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.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 }
以上,但願對你有所幫助!
這裏爲你們準備了一份小小的禮物,關注公衆號,輸入以下代碼,便可得到百度網盤地址,無套路領取!
001:《程序員必讀書籍》
002:《從無到有搭建中小型互聯網公司後臺服務架構與運維架構》
003:《互聯網企業高併發解決方案》
004:《互聯網架構教學視頻》
006:《SpringBoot實現點餐系統》
007:《SpringSecurity實戰視頻》
008:《Hadoop實戰教學視頻》
009:《騰訊2019Techo開發者大會PPT》
010: 微信交流羣
本文分享自微信公衆號 - JAVA日知錄(javadaily)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。