如果不瞭解Oauth2 是什麼、工作流程的可以看我上一篇文章:
SpringCloud-OAuth2(一):基礎篇
這篇講的內容是:Oauth2在SpringBoot/SpringCloud中的實戰。
SpringBoot版本:2.2.5.Release
SpringCloud版本:Hoxton.SR9
JDK版本:1.8
1:POM配置
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework.security.oauth/spring-security-oauth2 -->
<dependency>
<artifactId>spring-cloud-starter-oauth2</artifactId>
<groupId>org.springframework.cloud</groupId>
</dependency>
<!--使用redis存放token-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--密碼加密解密依賴包-->
<dependency>
<groupId>org.jasypt</groupId>
<artifactId>jasypt</artifactId>
<version>1.9.2</version>
</dependency>
</dependencies>
2:關鍵配置
2.1:認證服務配置-WebAuthorizationConfig
@Configuration
@EnableAuthorizationServer
public class WebAuthorizationConfig extends AuthorizationServerConfigurerAdapter {
private final AuthenticationManager authenticationManager;
private final UserDetailsService userDetailsService;
private final PasswordEncoder passwordEncoder;
private final TokenStore tokenStore;
private final AuthorizationCodeServices authorizationCodeServices;
private final AuthTokenExceptionHandler authTokenExceptionHandler;
public WebAuthorizationConfig(AuthenticationManager authenticationManager,
UserDetailsService userDetailsService,
PasswordEncoder passwordEncoder,
TokenStore tokenStore,
AuthorizationCodeServices authorizationCodeServices,
AuthTokenExceptionHandler authTokenExceptionHandler) {
this.authenticationManager = authenticationManager;
this.userDetailsService = userDetailsService;
this.passwordEncoder = passwordEncoder;
this.tokenStore = tokenStore;
this.authorizationCodeServices = authorizationCodeServices;
this.authTokenExceptionHandler = authTokenExceptionHandler;
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
String secret = PasswordHelper.encryptPassword(Oauth2ClientUserEnums.ADMIN.getClientSecret());
clients.inMemory()
.withClient(Oauth2ClientUserEnums.ADMIN.getClientId())
.secret(secret)
.scopes("all", "test")
.resourceIds("admin")
// autoApprove 可跳過授權頁直接返回code
.autoApprove("all")
.redirectUris("http://www.baidu.com")
//客戶端認證所支援的授權型別 1:客戶端憑證 2:賬號密碼 3:授權碼 4:token重新整理 5:簡易模式
.authorizedGrantTypes(CLIENT_CREDENTIALS, PASSWORD, REFRESH_TOKEN, AUTHORIZATION_CODE, IMPLICIT)
//使用者角色
.authorities("admin")
//允許自動授權
.autoApprove(false)
//token 過期時間
.accessTokenValiditySeconds((int) TimeUnit.HOURS.toSeconds(12))
//refresh_token 過期時間
.refreshTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(30))
;
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
.passwordEncoder(passwordEncoder) //設定密碼編輯器
.allowFormAuthenticationForClients()
.tokenKeyAccess("permitAll()") //開啟 /oauth/token_key 的訪問許可權控制
.checkTokenAccess("permitAll()") //開啟 /oauth/check_token 驗證埠認證許可權訪問
;
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// 配置授權伺服器端點的屬性
endpoints.authenticationManager(authenticationManager) //認證管理器
.tokenStore(tokenStore)
.authorizationCodeServices(authorizationCodeServices)
.userDetailsService(userDetailsService)
.exceptionTranslator(authTokenExceptionHandler);
}
}
註解:@EnableAuthorizationServer表明當前服務是認證服務。
介紹一下幾個基礎元件
①:authenticationManager
認證管理器,對客戶端憑證、使用者進行認證的地方。
②:tokenStore
存放token的地方,預設是存放在Inmemory(記憶體)中的。
③:authorizationCodeServices
code生成服務,使用預設的即可。
④:userDetailsService
使用者詳情服務,可重寫實現,使用者資訊從資料庫中載入。
⑤:authTokenExceptionHandler
自定義的 token 鑑別失敗異常處理器。
⑥:authClientExceptionHandler
自定義的 客戶端憑證 鑑別失敗異常處理器。
2.2:資源服務配置-WebResourceConfig
@Configuration
@EnableResourceServer
public class WebResourceConfig extends ResourceServerConfigurerAdapter {
private final AuthClientExceptionHandler authClientExceptionHandler;
public WebResourceConfig(AuthClientExceptionHandler authClientExceptionHandler) {
this.authClientExceptionHandler = authClientExceptionHandler;
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId("admin").stateless(true).authenticationEntryPoint(authClientExceptionHandler);
}
@Override
public void configure(HttpSecurity http) throws Exception {
// 資源鏈路
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.and().formLogin().permitAll()
// 登入放通
.and()
.authorizeRequests()
.antMatchers("/oauth/**", "/favicon.ico")
//.authenticated()
.permitAll()
// 其他請求都需認證
.and()
.authorizeRequests()
.anyRequest()
.authenticated()
// 跨域
.and()
.cors()
// 關閉跨站請求防護
.and()
.csrf()
.disable();
}
}
註解:@EnableResourceServer表明當前服務是認證服務。
筆記:
為什麼要放開 /favicon.ico 的訪問許可權?因為在進行授權碼模式的時候,一直無法跳轉到我定義的redirect_uri 地址中去。
使用 logging.level.org.springframework.security=debug 發現會訪問 /favicon.ico ,然後報錯,再然後就是調到登入頁去了。
因此 /favicon.ico 放開之後,認證正常,成功調到redirect_uri 地址並返回了code。(這是OAuth2的小坑嗎?)
2.3:安全配置-WebSecurityConfig
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final PasswordEncoder passwordEncoder;
public WebSecurityConfig(PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService())
.passwordEncoder(passwordEncoder); //為認證管理器配置passwordEncoder,無論客戶端憑證密碼還是使用者密碼都通過passwordEncoder進行密碼匹配
}
@Bean
@Override
protected UserDetailsService userDetailsService() {
return new VipUserDetailService();
}
@Bean(name = BeanIds.AUTHENTICATION_MANAGER)
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
詳細配置可以翻閱我的GitHub:GitHub地址
3:認證授權演示
3.1:授權碼模式
注意!!!一定要配置redirect_uri,並且一定要允許表單登入!!!
以下是步驟:
①:瀏覽器輸入以下連結地址,會重定向到登入頁
http://localhost:8123/oauth/authorize?client_id=admin&client_secret=admin&response_type=code&redirect_uri=http://www.baidu.com&scope=test
②:輸入配置的使用者名稱密碼,回撥到授權頁
如果我們配置的scope是autoApprove的,即可跳過這步,直接到第③步,拿到code。
③:選擇 Approve,點選Authorize 即可調到redirect_uri地址
④:拿code 換 token
我的請求地址:127.0.0.1:8123/oauth/token?grant_type=authorization_code&code=h35Fh1&redirect_uri=http://www.baidu.com
需要配置Authorization,請求時會將客戶端憑證以base64格式放在請求頭中。
3.2:使用者密碼模式
我的請求地址:127.0.0.1:8123/oauth/token?grant_type=password&username=found&password=123456
需要配置Authorization,請求時會將客戶端憑證以base64格式放在請求頭中。
3.3:客戶端憑證模式
我的請求地址:127.0.0.1:8123/oauth/token?grant_type=client_credentials
需要配置Authorization,請求時會將客戶端憑證以base64格式放在請求頭中。
3.4:隱式授權模式
瀏覽器輸入以下地址即可拿到token:
http://localhost:8123/oauth/authorize?client_id=admin&client_secret=admin&response_type=token&redirect_uri=http://www.baidu.com
3.5:token 重新整理
我的請求地址:127.0.0.1:8123/oauth/token?grant_type=refresh_token&refresh_token=dde5f388-4ad7-4781-a1b7-aaafb3c34b10
refresh_token是伺服器頒發給客戶端的,作用就是在一定時間內,讓使用者不用重新登入。
需要配置Authorization,請求時會將客戶端憑證以base64格式放在請求頭中。
4:聊聊第三方登入
個人認為做第三方登入是當前比較流行的做法。
4.1:流程
①:比如我們現在登入ProcessOn,就可以選擇第三方登入。
②:點選QQ登入後會調到這個頁面↓↓↓↓↓↓↓↓↓↓↓↓
③:上面這個client_id 應該就是ProcessOn 官方申請的第三方客戶端憑證了。
選擇賬號密碼登入,輸入賬號密碼之後就可以拿到token,(我手速快截了個圖)
④:可以看到返回了access_token、過期時間。
隨後processOn既發起請求進行登入操作,登入成功進入使用者首頁,失敗則重定向到登入頁。
4.2:uml圖
認真辨別了一下,QQ第三方登入採用不是授權碼模式,而是Implicat Grant Type。(差點看走眼)
總結 :
學習總要從身邊找例子,這樣才能更好的理解。