認證鑑權與API權限控制在微服務架構中的設計與實現:升級

概述

在之前的系列文章認證鑑權與API權限控制在微服務架構中的設計與實現中,我們有四篇文章講解了微服務下的認證鑑權與API權限控制的實現。當時基於的Spring Cloud版本爲Dalston.SR4,當前最新的Spring Cloud版本爲Finchley.SR1,對應的Spring Boot也升級到了2.0.x。Spring Cloud版本爲Finchley和Spring Boot2.0相對之前的版本有較大的變化,至於具體的changes,請參見官網。本次會將項目升級到最新版本,下面具體介紹其中的變化。與使用之前的版本,請切換到1.0-RELEASE

升級依賴

將Spring Boot的依賴升級爲2.0.4.RELEASE

1<parent>
2    <groupId>org.springframework.boot</groupId>
3    <artifactId>spring-boot-starter-parent</artifactId>
4    <version>2.0.4.RELEASE</version>
5</parent>

升級dependencyManagement中的spring-cloud依賴爲Finchley.RELEASE

 1<dependencyManagement>
2     <dependencies>
3         <dependency>
4             <groupId>org.springframework.cloud</groupId>
5             <artifactId>spring-cloud-dependencies</artifactId>
6             <version>Finchley.RELEASE</version>
7             <type>pom</type>
8             <scope>import</scope>
9         </dependency>
10     </dependencies>
11</dependencyManagement>

刪除spring-cloud-starter-oauth2依賴,只留下spring-cloud-starter-security依賴。

工具升級

flyway

我們在項目中,引入了flyway的依賴,用以初始化數據庫的增量腳本,具體可以參見數據庫版本管理工具Flyway應用。

docker容器

爲了更加簡便的體驗本項目,筆者在項目中提供了docker compose腳本。在本地安裝好docker compose的情況下,進入項目根目錄執行docker-compose up命令。

41ee4f7b92642d97448737cb10219ce276ab73a4


即可啓動我們所需要的mysql和redis。

Mybatis和HikariCP

在Spring Boot 2.0.X版本中,選擇了HikariCP作爲默認數據庫連接池。所以我們並不需要額外配置DataSource。

Mybatis的mapper和config-location配置也通過配置文件的形式,因此DatasourceConfig大大簡化。

application.yml

 1spring:
2  flyway:
3    baseline-on-migrate: true
4    locations: classpath:db
5  datasource:
6    hikari:
7      connection-test-query: SELECT 1
8      minimum-idle: 1
9      maximum-pool-size: 5
10      pool-name: dbcp1
11    driver-class-name: com.mysql.jdbc.Driver
12    url: jdbc:mysql://localhost:3306/auth?autoReconnect=true&useSSL=false
13    username: ${AUTH_DB_PWD:root}
14    password: ${AUTH_DB_USER:_123456_}
15#    schema[0]: classpath:/auth.sql
16#    initialization-mode: ALWAYS
17    type: com.zaxxer.hikari.HikariDataSource
18  redis:
19    database: 0
20    host: localhost
21    port: 6379
22
23mybatis:
24  mapper-locations: classpath:/mybatis/mapper/*Mapper.xml
25  config-location: classpath:/mybatis/config.xml

配置類升級

AuthenticationManagerConfig

棄用,由於循環依賴的問題,將AuthenticationManager的配置放置到WebSecurityConfig中。

WebSecurityConfig

添加了來自AuthenticationManagerConfigAuthenticationManager配置。

由於Spring Security5默認PasswordEncoder不是NoOpPasswordEncoder,需要手動指定。原來的auth項目中沒有對密碼進行加密,NoOpPasswordEncoder已經被廢棄,只適合在測試環境中使用,本次我們使用SCryptPasswordEncoder密碼加密器對密碼進行加解密,更貼近產線的使用。其他的算法還有Pbkdf2PasswordEncoderBCryptPasswordEncoder

關於Scrpyt算法,可以肯定的是其很難被攻擊。

Scrpyt算法是由著名的FreeBSD黑客 Colin Percival爲他的備份服務 Tarsnap開發的,當初的設計是爲了降低CPU負荷,儘量少的依賴cpu計算,利用CPU閒置時間進行計算,因此scrypt不僅計算所需時間長,而且佔用的內存也多,使得並行計算多個摘要異常困難,因此利用rainbow table進行暴力攻擊更加困難。Scrpyt沒有在生產環境中大規模應用,並且缺乏仔細的審察和廣泛的函數庫支持。所以Scrpyt一直沒有推廣開,但是由於其內存依賴的設計特別符合當時對抗專業礦機的設計,成爲數字貨幣算法發展的一個主要應用方向。

而BCrypt相對出現的時間更久,也很安全。Spring Security中的BCryptPasswordEncoder方法採用SHA-256 + 隨機鹽 + 密鑰對密碼進行加密。SHA系列是Hash算法,不是加密算法,使用加密算法意味着可以解密(這個與編碼/解碼一樣),但是採用Hash處理,其過程是不可逆的。

  1. 加密(encode):註冊用戶時,使用SHA-256+隨機鹽+密鑰把用戶輸入的密碼進行hash處理,得到密碼的hash值,然後將其存入數據庫中。

  2. 密碼匹配(matches):用戶登錄時,密碼匹配階段並沒有進行密碼解密(因爲密碼經過Hash處理,是不可逆的),而是使用相同的算法把用戶輸入的密碼進行hash處理,得到密碼的hash值,然後將其與從數據庫中查詢到的密碼hash值進行比較。如果兩者相同,說明用戶輸入的密碼正確。

關於怎麼初始化密碼呢,和註冊用戶的時候怎麼給密碼加密,我們可以在初始化密碼時調用如下的方法:

1SCryptPasswordEncoder sCryptPasswordEncoder = new SCryptPasswordEncoder();
2sCryptPasswordEncoder.encode("frontend");

此時需要對數據庫中的client_secret進行修改,如把frontend修改爲:

1$e0801$65x9sjjnRPuKmqaFn3mICtPYnSWrjE7OB/pKzKTAI4ryhmVoa04cus+9sJcSAFKXZaJ8lcPO1I9H22TZk6EN4A==$o+ZWccaWXSA2t7TxE5VBRvz2W8psujU3RPPvejvNs4U=

並修改配置如下:

 1    @Autowired
2    CustomAuthenticationProvider customAuthenticationProvider;
3    @Autowired
4    CodeAuthenticationProvider codeAuthenticationProvider;
5
6    @Override
7    public void configure(AuthenticationManagerBuilder auth) throws Exception {
8        auth.authenticationProvider(customAuthenticationProvider);
9        auth.authenticationProvider(codeAuthenticationProvider);
10    }
11
12    @Bean
13    @Override
14    public AuthenticationManager authenticationManagerBean() throws Exception {
15        return super.authenticationManagerBean();
16    }
17
18   @Bean
19    public PasswordEncoder passwordEncoder(){
20        return new SCryptPasswordEncoder();
21    }

ResourceServerConfig

棄用,auth項目不啓用資源服務器的功能。

OAuth2Config

由於當前版本的spring-boot-redis中的RedisConnection缺少#set方法,直接使用RedisTokenStore會出現以下異常:

1java.lang.NoSuchMethodError: org.springframework.data.redis.connection.RedisConnection.set([B[B)V

因此自定義CustomRedisTokenStore類,與RedisTokenStore代碼一致,只是將RedisConnection#set方法的調用替換爲RedisConnection#stringCommands#set,如下所示:

1    conn.stringCommands().set(accessKey, serializedAccessToken);
2    conn.stringCommands().set(authKey, serializedAuth);
3    conn.stringCommands().set(authToAccessKey, serializedAccessToken);

完整代碼見文末的GitHub地址。

結果驗證

經過如上的升級改造,我們將驗證如下的API端點:

  • password模式獲取token:/oauth/token?grant_type=password

  • 刷新token:/oauth/token?grant_type=refresh_token&refresh_token=…

  • 檢驗token:/oauth/check_token

  • 登出:/logout

  • 授權:/oauth/authorize

  • 授權碼模式獲取token:/oauth/token?grant_type=authorization_code

結果就不展示了,都可以正常使用。

小結

OAuth鑑權服務是微服務架構中的一個基礎服務,項目公開之後得到了好多同學的關注,好多同學在加入QQ羣之後也提出了自己關於這方面的疑惑或者建議,一起討論和解決疑惑的地方。隨着Spring Boot和Spring Cloud的版本升級,筆者也及時更新了本項目,希望能夠幫到一些童鞋。筆者籌劃的一本關於Spring Cloud應用的書籍,本月即將出版面世,其中關於Spring Cloud Security部分,有着詳細的解析,各位同學可以支持一下正版。



原文發佈時間爲:2018-09-14
本文作者:aoho
本文來自雲棲社區合作伙伴“雲時代架構”,瞭解相關信息可以關注“雲時代架構”。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章