Spring Security OAuth2.0認證授權四:分散式系統認證授權

itread01 2021-01-14 15:48:56
spring oauth2.0 Security 分散 oauth


Spring Security OAuth2.0認證授權系列文章> [Spring Security OAuth2.0認證授權一:框架搭建和認證測試](https://blog.kdyzm.cn/post/24) > [Spring Security OAuth2.0認證授權二:搭建資源服務](https://blog.kdyzm.cn/post/25) > [Spring Security OAuth2.0認證授權三:使用JWT令牌](https://blog.kdyzm.cn/post/26)前面幾篇文章講解了如何從頭開始搭建認證服務和資源服務,從頒發普通令牌到頒發jwt令牌,最終完成了jwt令牌的頒發和校驗。本篇文章將會講解分散式環境下如何進行認證和授權。## 一、設計思路![分散式授權圖.png](https://img2020.cnblogs.com/news/516671/202101/516671-20210114093941879-2038643737.png;%20charset=UTF-8)一般來說,一個典型的分散式系統架構如上圖所示,這裡進行一個簡單的設計,來完成分散式系統下的認證和授權。整體設計思路是使用OAuth2.0頒發令牌,使用JWT對令牌簽名並頒發JWT令牌給客戶端。既然決定使用JWT令牌了,則不需要再呼叫認證伺服器對令牌進行驗證了,因為JWT本身就包含了所需要的資訊,而且只要驗籤成功,則可認為令牌可信任且有效。如上所述,則可以如此設計:1. 使用者請求登陸之後認證服務頒發令牌給使用者,瀏覽器將令牌儲存下來。2. 瀏覽器請求資源的的時候攜帶著令牌,閘道器攔截請求對令牌驗證,驗證的方法很簡單,不請求認證服務而是直接使用金鑰(對稱或非對稱)驗籤,只要驗證成功則將jwt payload中的資訊解析成明文放到請求頭中轉發請求到資源服務。3. 資源服務拿到明文資訊,根據明文資訊中的許可權資訊驗證是否有許可權訪問該資源,有許可權則返回資源資訊,無許可權則返回401。綜上,整體思路就是閘道器認證,資源服務鑑權。典型的微服務架構下會有註冊中心、閘道器等服務,接下來會依次介紹和搭建相關服務。## 二、註冊中心搭建為了方便程式本地除錯方便,這裡使用eureka server作為服務註冊中心,使用起來也非常簡單### 1.新增maven依賴``` xml org.springframework.cloud spring-cloud-starter-netflix-eureka-server org.springframework.boot spring-boot-starter-actuator ```### 2.新建啟動類``` java@SpringBootApplication@EnableEurekaServerpublic class RegisterServer { public static void main(String[] args) { SpringApplication.run(RegisterServer.class,args); }}```### 3.新建配置檔案``` yamlspring: application: name: register-serverserver: port: 8765 #啟動埠eureka: server: enable-self-preservation: false #關閉伺服器自我保護,客戶端心跳檢測15分鐘內錯誤達到80%服務會保護,導致別人還認為是好用的服務 eviction-interval-timer-in-ms: 10000 #清理間隔(單位毫秒,預設是60*1000)5秒將客戶端剔除的服務在服務註冊列表中剔除# shouldUseReadOnlyResponseCache: true #eureka是CAP理論種基於AP策略,為了保證強一致性關閉此切換CP 預設不關閉 false關閉 client: register-with-eureka: false #false:不作為一個客戶端註冊到註冊中心 fetch-registry: false #為true時,可以啟動,但報異常:Cannot execute request on any known server instance-info-replication-interval-seconds: 10 serviceUrl: defaultZone: http://localhost:${server.port}/eureka/ instance: hostname: ${spring.cloud.client.ip-address} prefer-ip-address: true instance-id: ${spring.application.name}:${spring.cloud.client.ip-address}:${spring.application.instance_id:${server.port}}```然後啟動啟動類,訪問瀏覽器,[http://127.0.0.1:8765](http://127.0.0.1:8765),出現如下頁面即表示已經成功![eureka server.png](https://img2020.cnblogs.com/blog/516671/202101/516671-20210114093944072-1906516697.png;%20charset=UTF-8)## 二、閘道器搭建這裡選用spring cloud gateway作為閘道器(不是zuul)### 1.新增maven依賴``` xml org.springframework.cloud spring-cloud-starter-netflix-eureka-client org.springframework.cloud spring-cloud-starter-gateway 2.2.5.RELEASE org.springframework.boot spring-boot-starter-actuator org.projectlombok lombok org.springframework.security spring-security-jwt ```### 2.新建啟動類``` java@SpringBootApplicationpublic class GatewayServer { public static void main(String[] args) { SpringApplication.run(GatewayServer.class, args); }}```### 3.新建配置檔案``` yamlserver: port: 8761spring: cloud: gateway: routes: - id: resource_server uri: "lb://resource-server" predicates: - Path=/r** application: name: gateway-servereureka: client: service-url: defaultZone: http://127.0.0.1:8765/eureka instance: prefer-ip-address: true instance-id: ${spring.application.name}:${spring.cloud.client.ip‐address}:${spring.application.instance_id:${server.port}}```如此,一個閘道器就已經搭建好了,但是還不具備我們想要的認證功能。### 4.新增token全域性過濾器知識點有以下幾點:- 全域性過濾器要實現GlobalFilter介面- 為了實現token過濾器最先被呼叫,要實現Order介面並將優先順序調到最大- 使用JwtHelper工具類對jwt驗籤,簽名的key必須和認證中心中配置的key保持一致- 驗籤成功後將jwt中payload明文資訊放到token-info的header值中傳遞給目標服務實現程式碼如下:```java@Component@Slf4jpublic class TokenFilter implements GlobalFilter, Ordered { private static final String BEAR_HEADER = "Bearer "; /** * 該值要和auth-server中配置的簽名相同 * * com.kdyzm.spring.security.auth.center.config.TokenConfig#SIGNING_KEY */ private static final String SIGNING_KEY = "auth123"; @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { String token = exchange.getRequest().getHeaders().getFirst(HttpHeaders.AUTHORIZATION); //如果沒有token,則直接返回401 if(StringUtils.isEmpty(token)){ return unAuthorized(exchange); } //驗籤並獲取PayLoad String payLoad; try { Jwt jwt = JwtHelper.decodeAndVerify(token.replace(BEAR_HEADER,""), new MacSigner(SIGNING_KEY)); payLoad = jwt.getClaims(); } catch (Exception e) { log.error("驗籤失敗",e); return unAuthorized(exchange); } //將PayLoad資料放到header ServerHttpRequest.Builder builder = exchange.getRequest().mutate(); builder.header("token-info", payLoad).build(); //繼續執行 return chain.filter(exchange.mutate().request(builder.build()).build()); } private Mono unAuthorized(ServerWebExchange exchange){ exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); return exchange.getResponse().setComplete(); } /** * 將該過濾器的優先順序設定為最高,因為只要認證不通過,就不能做任何事情 * * @return */ @Override public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; }}```## 三、資源服務修改原來資源服務已經集成了OAuth2.0、Spring Security、JWT等元件,根據現在的設計方案,需要刪除OAuth2.0和JWT元件,只留下Spring Security元件。### 1.移除OAuth2.0、JWT元件這裡要刪除maven依賴,同時將相關配置刪除**第一步,刪除maven依賴**,直接將以下兩個依賴移除就好``` xml org.springframework.cloud spring-cloud-starter-oauth2 org.springframework.security spring-security-jwt ```**第二步,刪除相關配置**將ResouceServerConfig、TokenConfig兩個類直接刪除 即可。### 2.新增過濾器這裡需要使用過濾器做,首先寫一個過濾器,實現OncePerRequestFilter介面,該過濾器的作用就是獲取閘道器傳過來的token-info明文資料,封裝成JwtTokenInfo物件,並將該相關資訊新增到SpringSecurity上下文以備之後的鑑權使用。程式碼實現如下:``` java@Component@Slf4jpublic class AuthFilterCustom extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { ObjectMapper objectMapper = new ObjectMapper(); String tokenInfo=request.getHeader("token-info"); if(StringUtils.isEmpty(tokenInfo)){ log.info("未找到token資訊"); filterChain.doFilter(request,response); return; } JwtTokenInfo jwtTokenInfo = objectMapper.readValue(tokenInfo, JwtTokenInfo.class); log.info("tokenInfo={}",objectMapper.writeValueAsString(jwtTokenInfo)); List authorities1 = jwtTokenInfo.getAuthorities(); String[] authorities=new String[authorities1.size()]; authorities1.toArray(authorities); //將使用者資訊和許可權填充 到使用者身份token物件中 UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(jwtTokenInfo.getUser_name(),null, AuthorityUtils.createAuthorityList(authorities)); authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); //將authenticationToken填充到安全上下文 SecurityContextHolder.getContext().setAuthentication(authenticationToken); filterChain.doFilter(request,response); }}```### 3.將過濾器註冊到過濾器鏈修改WebSecurityConfig類,使用如下方法註冊過濾器:``` java.addFilterAfter(authFilterCustom, BasicAuthenticationFilter.class)//新增過濾器```同時,一定要關閉session功能,否則會出現上下文快取問題``` java.sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS);//禁用session```完整程式碼如下:``` java @Autowired private AuthFilterCustom authFilterCustom; @Override protected void configure(HttpSecurity http) throws Exception { http.csrf() .disable() .authorizeRequests()// .antMatchers("/r/r1").hasAuthority("p2")// .antMatchers("/r/r2").hasAuthority("p2") .antMatchers("/**").authenticated()//所有的請求必須認證通過 .anyRequest().permitAll()//其它所有請求都可以隨意訪問 .and() .addFilterAfter(authFilterCustom, BasicAuthenticationFilter.class)//新增過濾器 .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS);//禁用session }```## 四、其他注意事項認證服務auth-server以及資源服務resource-server、閘道器服務gateway-server都要整合eureka client元件## 五、測試測試前需要將各個服務依次啟動起來:- 啟動註冊中心 register-server:[https://gitee.com/kdyzm/spring-security-oauth-study/tree/v5.0.0/register-server](https://gitee.com/kdyzm/spring-security-oauth-study/tree/v5.0.0/register-server)- 啟動閘道器 gateway-server:[https://gitee.com/kdyzm/spring-security-oauth-study/tree/v5.0.0/gateway-server](https://gitee.com/kdyzm/spring-security-oauth-study/tree/v5.0.0/gateway-server)- 啟動認證服務 auth-server:[https://gitee.com/kdyzm/spring-security-oauth-study/tree/v5.0.0/auth-server](https://gitee.com/kdyzm/spring-security-oauth-study/tree/v5.0.0/auth-server)- 啟動資源服務 resource-server:[https://gitee.com/kdyzm/spring-security-oauth-study/tree/v5.0.0/resource-server](https://gitee.com/kdyzm/spring-security-oauth-study/tree/v5.0.0/resource-server)**第一步,獲取token**這裡使用password模式直接獲取token,POST請求如下介面:[http://127.0.0.1:30000/oauth/token?client_id=c1&client_secret=secret&grant_type=password&username=zhangsan&password=123](http://127.0.0.1:30000/oauth/token?client_id=c1&client_secret=secret&grant_type=password&username=zhangsan&password=123)即可獲取token。**第二步,訪問資源**通過閘道器請求資源服務的r1介面,GET請求如下介面:[http://127.0.0.1:8761/r1](http://127.0.0.1:8761/r1)需要帶上Header,key為`Authorization`,value格式如下:``` textBearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsicmVzMSJdLCJ1c2VyX25hbWUiOiJ6aGFuZ3NhbiIsInNjb3BlIjpbIlJPTEVfQURNSU4iLCJST0xFX1VTRVIiLCJST0xFX0FQSSJdLCJleHAiOjE2MTAzNzI5MzUsImF1dGhvcml0aWVzIjpbInAxIiwicDIiXSwianRpIjoiOWQzMzRmZGMtOTcwZC00YmJkLWI2MmMtZDU4MDZkNTgzM2YwIiwiY2xpZW50X2lkIjoiYzEifQ.gZraRNeX-o_jKiH7XQgg3TlUQBpxUcXa2-qR_Treu8U```如果相應結果如下,則表示測試通過```訪問資源r1```否則,會返回401狀態碼。## 六、專案原始碼專案原始碼:[https://gitee.com/kdyzm/spring-security-oauth-study/tree/v5.0.0](https://gitee.com/kdyzm/spring-security-oauth-study/tree/v5.0.0)我的部落格原文地址:[https://blog.kdyzm.cn/post/30](https://blog.kdyzm.cn/p
版权声明
本文为[itread01]所创,转载请带上原文链接,感谢
https://www.itread01.com/content/1610607664.html

  1. Centos7 one click installation of JDK1.8 shell script
  2. Mounting of file system in Linux (centos7)
  3. How does serverless deal with the resource supply demand of k8s in the offline scenario
  4. Detailed explanation of HBase basic principle
  5. Spring security oauth2.0 authentication and authorization 4: distributed system authentication and authorization
  6. Redis performance Part 5 redis buffer
  7. JavaScript this keyword
  8. Summary of Java multithreading (3)
  9. Sentry(v20.12.1) K8S 云原生架构探索, SENTRY FOR JAVASCRIPT 手动捕获事件基本用法
  10. Sentry(v20.12.1) K8S 云原生架构探索, SENTRY FOR JAVASCRIPT 手动捕获事件基本用法
  11. (10) Spring from the beginning to the end
  12. Summary of Java multithreading (2)
  13. Spring source notes! From the introduction to the source code, let you really understand the source code
  14. A stormy sunny day
  15. Zookeeper (curator), the implementation of distributed lock
  16. Show the sky! Tencent T4's core Java Dictionary (framework + principle + Notes + map)
  17. Spring boot project, how to gracefully replace the blank value in the interface parameter with null value?
  18. Spring boot project, how to gracefully replace the blank value in the interface parameter with null value?
  19. docker+mysql集群+读写分离+mycat管理+垂直分库+负载均衡
  20. docker+mysql集群+读写分离+mycat管理+垂直分库+负载均衡
  21. To what extent can I go out to work?
  22. Java 使用拦截器无限转发/重定向无限循环/重定向次数过多报错(StackOverflowError) 解决方案
  23. Implementation of rocketmq message sending based on JMeter
  24. How to choose the ticket grabbing app in the Spring Festival? We have measured
  25. Implementation of rocketmq message sending based on JMeter
  26. My programmer's Road: self study java
  27. My programmer's Road: self study java
  28. All in one, one article talks about the use of virtual machine VirtualBox and Linux
  29. All in one, one article talks about the use of virtual machine VirtualBox and Linux
  30. Java 使用拦截器无限转发/重定向无限循环/重定向次数过多报错(StackOverflowError) 解决方案
  31. [Java training project] Java ID number recognition system
  32. How does serverless deal with the resource supply demand of k8s in the offline scenario
  33. Detailed explanation of HBase basic principle
  34. Explain the function of thread pool and how to use it in Java
  35. Kubernetes official java client 8: fluent style
  36. 010_MySQL
  37. Vibrant special purchases for the Spring Festival tiktok section, hundreds of good things to make the year more rich flavor.
  38. 010_MySQL
  39. Of the 4 million docker images, 51% have high-risk vulnerabilities
  40. Rocketmq CPP client visual studio 2019 compilation
  41. Rocketmq CPP client visual studio 2019 compilation
  42. Usage of data custom attribute in jquery
  43. Common decompression in Linux
  44. Upload large files in Java
  45. Sentry (v20.12.1) k8s cloud native architecture exploration, sentry for JavaScript manual capture event basic usage
  46. Sentry (v20.12.1) k8s cloud native architecture exploration, sentry for JavaScript manual capture event basic usage
  47. Docker + MySQL Cluster + read / write separation + MYCAT Management + vertical sub database + load balancing
  48. Docker + MySQL Cluster + read / write separation + MYCAT Management + vertical sub database + load balancing
  49. Java use interceptor infinite forwarding / redirection infinite loop / redirection times too many error (stack overflow error) solution
  50. Java use interceptor infinite forwarding / redirection infinite loop / redirection times too many error (stack overflow error) solution
  51. 010_ MySQL
  52. 010_ MySQL
  53. Fast integration of imsdk and Huawei offline push
  54. 消息队列之RabbitMQ
  55. Rabbitmq of message queue
  56. 初学java进制转换方面补充学习
  57. Learn java base conversion supplementary learning
  58. 了解一下RPC,为何诞生RPC,和HTTP有什么不同?
  59. 了解一下RPC,为何诞生RPC,和HTTP有什么不同?
  60. 初学java进制转换方面补充学习