1. 概述
本文簡要總結一下如果使用Spring Security OAuth和Spring Boot來實現SSO,文末有樣例代碼。不了解OAuth2.0協議的同學請參考《OAuth2.0協議原理詳解》
整個工程包括三個獨立的應用,一個認證服務和兩個客戶端應用,結構非常簡單。當一個用戶訪問客戶端應用中被防護的API時,系統會被自動重定向到認證服務,之后我們使用OAuth2.0的Authorization code授權方式來實現認證授權。
2. 客戶端APP
我們先從客戶端應用入手,使用Spring Boot可以最大程度簡化配置
2.1 Maven依賴
<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</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
</dependency>
2.2 安全配置
客戶端的安全配置如下:
@Configuration
@EnableOAuth2Sso
public class UiSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**")
.authorizeRequests()
.antMatchers("/", "/login**")
.permitAll()
.anyRequest()
.authenticated();
}
}
配置最核心的部分是 @EnableOAuth2Sso注解來開啟SSO。這里要注意,我們需要重寫WebSecurityConfigurerAdapter 否則所有的路徑都會受到SSO的保護,這樣無論用戶訪問哪個頁面都會被重定向到登錄頁面,在這個例子里,index和login頁面是唯一不需要被防護的。
最后,我們定義一個RequestContextListener bean來處理request scopes。
application.yml:
server:
port: 8082
context-path: /ui
session:
cookie:
name: UISESSION
security:
basic:
enabled: false
oauth2:
client:
clientId: SampleClientId
clientSecret: secret
accessTokenUri: http://localhost:8081/auth/oauth/token
userAuthorizationUri: http://localhost:8081/auth/oauth/authorize
resource:
userInfoUri: http://localhost:8081/auth/user/me
spring:
thymeleaf:
cache: false
說明:
我們禁用了default Basic Authentication
accessTokenUri 是獲取訪問令牌的URL
userAuthorizationUri是授權用戶被重定向的目標URL
userInfoUri是用戶終端訪問用戶信息的URL
在這個case里,認證服務是我們自己建設的,但是在實際的應用場景下認證服務往往是第三方提供的比如Facebook 或者 gitHub
2.3 前端
前端并非本文的重點,這里簡單提一下。客戶端有一個非常簡單的前端頁面
index.html:
<h1>Spring Security SSO</h1>
<a href="securedPage">Login</a>
securedPage.html:
<h1>Secured Page</h1>
Welcome, <span th:text="${#authentication.name}">Name</span>
securedPage.html需要用戶通過授權才能訪問,如果一個未授權的用戶訪問這個頁面,他會被重定向到login頁面
3. 認證服務器
3.1 Maven依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
3.2 OAuth配置
本例中我們把AS(認證服務)器和RS放(資源服務器)在一個實例中部署。
RS配置如下:
@EnableResourceServer
public class AuthorizationServerApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(AuthorizationServerApplication.class, args);
}
}
AS配置如下:
@Configuration
@EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("SampleClientId")
.secret("secret")
.authorizedGrantTypes("authorization_code")
.scopes("user_info")
.autoApprove(true) ;
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
}
這里我們只開啟authorization_code授權模式,另外這里注意到autoApprove=true,這意味著用戶不會被重定向到授權的頁面,也不需要手動給請求授權。
3.3 安全配置
這里需要定義一個簡單的認證機制
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatchers()
.antMatchers("/login", "/oauth/authorize")
.and()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().permitAll();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.parentAuthenticationManager(authenticationManager)
.inMemoryAuthentication()
.withUser("john").password("123").roles("USER");
}
}
注意,我們用了in-memory認證,這里只是做一個簡單的例子,我們直接把賬號密碼寫到內存里了,真正使用的時候這里要換成自定義的userDetailsService.
3.4 用戶終端
一個很簡單的返回JSON消息的接口
@RestController
public class UserController {
@GetMapping("/user/me")
public Principal user(Principal principal) {
return principal;
}
}
完整的代碼下載鏈接:Over On Github
參考資料:
http://blog.csdn.net/j754379117/article/details/70175198
http://www.baeldung.com/sso-spring-security-oauth2