概述
在之前的系列文章認證鑒權與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
指令。

即可啟動我們所需要的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
添加了來自
AuthenticationManagerConfig
的
AuthenticationManager
配置。
由于Spring Security5預設
PasswordEncoder
不是
NoOpPasswordEncoder
,需要手動指定。原來的auth項目中沒有對密碼進行加密,
NoOpPasswordEncoder
已經被廢棄,隻适合在測試環境中使用,本次我們使用
SCryptPasswordEncoder
密碼加密器對密碼進行加解密,更貼近産線的使用。其他的算法還有
Pbkdf2PasswordEncoder
和
BCryptPasswordEncoder
。
關于Scrpyt算法,可以肯定的是其很難被攻擊。
Scrpyt算法是由著名的FreeBSD黑客 Colin Percival為他的備份服務 Tarsnap開發的,當初的設計是為了降低CPU負荷,盡量少的依賴cpu計算,利用CPU閑置時間進行計算,是以scrypt不僅計算所需時間長,而且占用的記憶體也多,使得并行計算多個摘要異常困難,是以利用rainbow table進行暴力攻擊更加困難。Scrpyt沒有在生産環境中大規模應用,并且缺乏仔細的審察和廣泛的函數庫支援。是以Scrpyt一直沒有推廣開,但是由于其記憶體依賴的設計特别符合當時對抗專業礦機的設計,成為數字貨币算法發展的一個主要應用方向。
而BCrypt相對出現的時間更久,也很安全。Spring Security中的
BCryptPasswordEncoder
方法采用SHA-256 + 随機鹽 + 密鑰對密碼進行加密。SHA系列是Hash算法,不是加密算法,使用加密算法意味着可以解密(這個與編碼/解碼一樣),但是采用Hash處理,其過程是不可逆的。
- 加密(encode):注冊使用者時,使用SHA-256+随機鹽+密鑰把使用者輸入的密碼進行hash處理,得到密碼的hash值,然後将其存入資料庫中。
- 密碼比對(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部分,有着詳細的解析,各位同學可以支援一下正版。
本文的源碼位址
GitHub:https://github.com/keets2012/Auth-service
碼雲: https://gitee.com/keets/Auth-Service
參考
scrypt算法的前世今生(從零開始學區塊鍊 192)https://www.sohu.com/a/167016356_99901444