在學習Spring Cloud 時,遇到了受權服務oauth 相關內容時,老是隻知其一;不知其二,所以決定先把Spring Security 、Spring Security Oauth2 等權限、認證相關的內容、原理及設計學習並整理一遍。本系列文章就是在學習的過程當中增強印象和理解所撰寫的,若有侵權請告知。html
項目環境:git
- JDK1.8github
- Spring boot 2.xspring
- Spring Security 5.x瀏覽器
前期基本上已經將 Spring Security相關的內容寫得差很少了,因此最近在整理Spring Sexurity Oauh2 相關的內容,但在進行到單點登錄(OSS)時,有一個問題一直困擾了我好久,因爲網上有關於Spring Boot 1.x 升級到Spring Boot 2.x 後單點登錄相關的問題解決資料不多,特此在這裏專門列一篇文章來描述升級過程當中遇到的一些問題、問題表現現象以及我是如何解決這些問題的。安全
首先很明確的告訴你,並無!!但爲何引入了 spring-security-oauth2 maven依賴 IDEA提示 @EnableOAuth2Sso 找不到呢? 首先咱們找到官方Spring Boot 2.x 升級文檔,咱們會發現其中有關於Oauth2 相關的介紹:服務器
OAuth 2.0 Support
Functionality from the Spring Security OAuth project is being migrated to core Spring Security. OAuth 2.0 client support has already been added and additional features will be migrated in due course.cookieIf you depend on Spring Security OAuth features that have not yet been migrated you will need to add org.springframework.security.oauth:spring-security-oauth2 and configure things manually. If you only need OAuth 2.0 client support you can use the auto-configuration provided by Spring Boot 2.0. We’re also continuing to support Spring Boot 1.5 so older applications can continue to use that until an upgrade path is provided.session
咱們能夠大體明白 官方 2.x 正在將 Spring Security OAuth項目的功能遷移到 Spring Security 中。 但最值得注意的是其中有這麼一段話 If you only need OAuth 2.0 client support you can use the auto-configuration provided by Spring Boot 2.0.(若是你想要在Spring Boot 2.0(及以上)版本中使用 Oauth2 客戶端 相關的功能 須要使用 auto-configuration)。app
根據這個提示,咱們找到 官方 auto-configuration文檔 ,一進來 就告訴咱們須要用到的最小maven依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.1.7.RELEASE</version>
</dependency>複製代碼
按照官方文檔配置成功引用到了@EnableOAuth2Sso ,至此,該問題獲得解決!
@Configuration
@EnableOAuth2Sso // SSo自動配置引用
public class ClientSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").permitAll()
.anyRequest().authenticated()
.and()
.csrf().disable();
}
}複製代碼
這個配置是按照 官方 auto-configuration文檔 推薦的配置。
auth-server: http://localhost:9090 # authorization服務地址
security:
oauth2:
client:
user-authorization-uri: ${auth-server}/oauth/authorize #請求認證的地址
access-token-uri: ${auth-server}/oauth/token #請求令牌的地址
resource:
jwt:
key-uri: ${auth-server}/oauth/token_key #解析jwt令牌所須要密鑰的地址,服務啓動時會調用 受權服務該接口獲取jwt key,因此務必保證受權服務正常
sso:
login-path: /login #指向登陸頁面的路徑,即OAuth2受權服務器觸發重定向到客戶端的路徑 ,默認爲 /login
spring:
profiles:
active: client1複製代碼
因爲咱們要多客戶端單點測試,這裏使用Spring boot 的多環境配置,這裏有關受權服務的配置不在描述,以及默認搭建好了一個可用的受權服務(若是不清楚如何搭建Oauth2的受權服務和資源服務,能夠關注我,後續會出相關文章)。
server:
port: 8091
security:
oauth2:
client:
client-id: client1
client-secret: 123456複製代碼
@RestController
@Slf4j
public class TestController {
@GetMapping("/client/{clientId}")
public String getClient(@PathVariable String clientId) {
return clientId;
}
}複製代碼
至此問咱們完成了一個最基本的SSO客戶端,啓動項目。
瀏覽器上訪問測試接口 localhost:8091/client/1 ,跳轉到受權服務登錄界面,登錄成功後,跳轉回到客戶端的 /login 地址 (即 咱們 配置的 spring.security.sso.login-path ),正常狀況下會再次跳轉到 localhost:8091/client/1(此次已是認證成功後訪問)。這整個流程就是Oauth2 的受權碼模式流程。但如今有這麼一個問題,在受權服務回調到客戶端的 /login 地址時,瀏覽器顯示 HTTP ERROR 401, 以下圖:
從圖中咱們能夠看到,受權服務成功的返回了受權碼,但因爲咱們客戶端存在問題,出現 401 ,致使整個受權碼模式流程中斷。 在看 官方 auto-configuration文檔 過程當中,無心間發現
Also note that since all endpoints are secure by default, this includes any default error handling endpoints, for example, the endpoint "/error". This means that if there is some problem during Single Sign On that requires the application to redirect to the "/error" page, then this can cause an infinite redirect between the identity provider and the receiving application.
First, think carefully about making an endpoint insecure as you may find that the behavior is simply evidence of a different problem. However, this behavior can be addressed by configuring the application to permit "/error":
@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/error").permitAll()
.anyRequest().authenticated();
}
}複製代碼
大體意思就是:因爲默認狀況下全部端點都是安全的,所以這包括任何默認錯誤處理端點,例如端點「/ error」。這意味着若是單點登陸期間存在某些問題,須要應用程序重定向到「/ error」頁面,則這會致使身份提供程序和接收應用程序之間的無限重定向。
根據這個提示,我開始DEBUG,果真正如文檔所說,單點登陸期間存在某些問題重定向到了/error,因此咱們將 /error 配置成無權限訪問,重啓再次訪問測試接口,此次的錯誤界面提示就很明顯了:
既然明顯的提示 Unauthorized 了,那咱們就來一步一步的DEBUG 看看單點期間出現的問題點是什麼。
從以前的現象描述咱們能夠知道問題點在受權碼回來後去調用獲取token這裏出現問題了,那麼根據源碼查看,獲取token這塊步驟在 OAuth2ClientAuthenticationProcessingFilter 過濾器內部,其關鍵代碼以下:
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException, ServletException {
OAuth2AccessToken accessToken;
try {
accessToken = restTemplate.getAccessToken(); // 1 調用受權服務獲取token
} catch (OAuth2Exception e) {
BadCredentialsException bad = new BadCredentialsException("Could not obtain access token", e);
publish(new OAuth2AuthenticationFailureEvent(bad));
throw bad;
}
try {
OAuth2Authentication result = tokenServices.loadAuthentication(accessToken.getValue()); // 成功後從token中解析 OAuth2Authentication 信息
if (authenticationDetailsSource!=null) {
request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_VALUE, accessToken.getValue());
request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_TYPE, accessToken.getTokenType());
result.setDetails(authenticationDetailsSource.buildDetails(request));
}
publish(new AuthenticationSuccessEvent(result));
return result;
}
catch (InvalidTokenException e) {
BadCredentialsException bad = new BadCredentialsException("Could not obtain user details from token", e);
publish(new OAuth2AuthenticationFailureEvent(bad));
throw bad;
}
}複製代碼
咱們把斷點打到這裏,Debug下,果真不出所料,在獲取token時異常了,異常信息爲 : Possible CSRF detected - state parameter was required but no state could be found ,debug截圖以下:
查閱網上資料有一下說法:
本地開發,auth server與client都是localhost,形成JSESSIONID相互影響問題。能夠經過配置client的context-path或者session名稱來解決
根據這個描述,我嘗試經過修改 session名稱來解決:
server:
servlet:
session:
cookie:
name: OAUTH2CLIENTSESSION # 解決 Possible CSRF detected - state parameter was required but no state could be found 問題複製代碼
重啓項目,測試SSO,完美解決!!!
本文介紹短信登陸開發的代碼能夠訪問代碼倉庫中的 security 模塊 ,項目的github 地址 : https://github.com/BUG9/spring-security
若是您對這些感興趣,歡迎star、follow、收藏、轉發給予支持!