本次分享Oauth2的第三個實踐——SSO單點登錄。這個場景經常用於企業開發,集成多方系統認證。
CAS與Oauth2?
業界實現SSO一般採用CAS方案,案例也很多,也有很多其它標準和實現。CAS與Oauth2在實現SSO上的區別,可參考CAS的單點登錄和oauth2的最大區別、 SSO with CAS or OAuth?兩篇文章,相信讀者讀完後會有收穫。
CAS是側重於認證,Oauth2是認證、授權或控制對某些資源的訪問權限。CAS客戶端登錄後獲取到用戶的資源後,判斷是否有權限訪問自己,而Oauth2客戶端是需要獲取用戶資源,且需要Oauth2認證的客戶端纔可以訪問。
在實現上CAS一般是不會關心具體是哪個客戶端(可以自定義實現),而Oauth2服務端是給每個客戶端都分配一個appId和secret才能訪問Oauth2Resource並獲取用戶信息。
個人覺得,在企業SSO開發中,如果僅僅只需要認證,各系統間不需要授權訪問,則可以採用CAS;如果需要認證、SSO,並且各系統間有資源訪問認證需求、還有APP端對接,可以採用Oauth2實現SSO+授權。
客戶端實現
相同的對接方式實現兩個不同客戶端,展示SSO的實踐。
Maven依賴
springboot、spring-boot-starter-security版本均爲2.1.11,spring-security-oauth2-autoconfigure版本爲2.1.4.RELEASE,各版本對應匹配信息可參考maven中央倉庫查詢。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<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>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
項目配置
對接Oauth2需要在Oauth2服務端註冊客戶端,添加對應的客戶端信息,並在客戶端項目中配置。
客戶端A
server:
port: 8081
servlet:
context-path: /clientA
session:
cookie:
name: clientA-SESSION
security:
oauth2:
client:
clientId: 980f9e83e04a4196a01300c9ff2a5899
clientSecret: fe5b9d71f29147dbbeb4dcea39b81e50
accessTokenUri: http://localhost:9011/auth/oauth/token
userAuthorizationUri: http://localhost:9011/auth/oauth/authorize
tokenName: access_token
resource:
userInfoUri: http://localhost:9011/auth/userInfo
server:
logoutUrl: http://localhost:9011/auth/logout
spring:
thymeleaf:
cache: false
logging:
level:
root: debug
客戶端B
server:
port: 8082
servlet:
context-path: /clientB
session:
cookie:
name: clientB-SESSION
security:
oauth2:
client:
clientId: 83f49cf610f54d94b7e773049f353719
clientSecret: abd6d9d167c345ceb1ae1426a22dcc06
accessTokenUri: http://localhost:9011/auth/oauth/token
userAuthorizationUri: http://localhost:9011/auth/oauth/authorize
tokenName: access_token
resource:
userInfoUri: http://localhost:9011/auth/userInfo
server:
logoutUrl: http://localhost:9011/auth/logout
spring:
thymeleaf:
cache: false
logging:
level:
root: debug
@EnableOAuth2Sso接入配置
@EnableOAuth2Sso配置表示注入Oauth2Sso客戶端自動配置
@Configuration
@EnableOAuth2Sso
public class SecurityConfig extends WebSecurityConfigurerAdapter{
@Override
public void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**")
.authorizeRequests()
.anyRequest()
.authenticated();
}
}
客戶端服務
客戶端提供以下服務用於測試,可用於前端交互獲取登錄用戶信息。
@Slf4j
@Controller
public class IndexController {
@RequestMapping("loginUserInfo")
@ResponseBody
public Object loginUserInfo(Principal principal, HttpServletRequest request) {
if (null == principal) {
log.info("====> 用戶未登錄");
return null;
}
HttpSession session = request.getSession(false);
log.info("====> session id is {}", session.getId());
return principal;
}
}
驗證
- 登錄客戶端A
訪問服務http://localhost:8081/clientA/loginUserInfo
跳轉到登錄頁面,登錄後api返回json信息如下。
- 訪問客戶端B
訪問客戶端Bhttp://localhost:8082/clientB/loginUserInfo
時,發現並沒有跳轉到Oauth2服務端登錄頁面,而是授權後直接跳轉到api並返回json信息
如何登出Logout?
logout登出在CAS中是需要強制實現,客戶端不用具體開發,將session失效即可,具體交互則交給Cas client sdk
框架實現;而在Oauth2中並沒有強制標準,客戶端需要開發邏輯讓Oauth2服務端登出實現,一般是採用302轉發到服務端。
客戶端配置
修改客戶端配置如下
@Configuration
@EnableOAuth2Sso
public class SecurityConfig extends WebSecurityConfigurerAdapter{
@Value("${security.oauth2.server.logoutUrl}")
private String serverLogoutUrl;
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl(serverLogoutUrl)
.and().csrf().disable();
}
}
Oauth2服務端配置
Oauth2Server端需要實現登出接口,Client對接時跳轉到此處即可
@Controller
public class LogoutController {
@RequestMapping("/logout")
public void exit(HttpServletRequest request, HttpServletResponse response) {
new SecurityContextLogoutHandler().logout(request, null, null);
try {
//sending back to client app
response.sendRedirect(request.getHeader("referer"));
} catch (IOException e) {
e.printStackTrace();
}
}
}
小結
文章介紹CAS與Oauth2實現SSO單點登錄的區別,給出Oauth2實現SSO的實踐案例,結尾處給出登出的最佳實踐,但是這個登出並不能保證單點登出,即A系統登出後,B系統無法訪問,而CAS有這個實現機制,可能是其中任意系統登出後向其它客戶系統發出logoutRequest,客戶系統SDK攔截處理本地session失效。Oauth2也可以實現類似機制,需要客戶端開發進行攔截本地session失效。