1. 概述
本文簡(jiǎn)要總結(jié)一下如果使用Spring Security OAuth和Spring Boot來(lái)實(shí)現(xiàn)SSO,文末有樣例代碼。不了解OAuth2.0協(xié)議的同學(xué)請(qǐng)參考《OAuth2.0協(xié)議原理詳解》
整個(gè)工程包括三個(gè)獨(dú)立的應(yīng)用,一個(gè)認(rèn)證服務(wù)和兩個(gè)客戶(hù)端應(yīng)用,結(jié)構(gòu)非常簡(jiǎn)單。當(dāng)一個(gè)用戶(hù)訪問(wèn)客戶(hù)端應(yīng)用中被防護(hù)的API時(shí),系統(tǒng)會(huì)被自動(dòng)重定向到認(rèn)證服務(wù),之后我們使用OAuth2.0的Authorization code授權(quán)方式來(lái)實(shí)現(xiàn)認(rèn)證授權(quán)。
2. 客戶(hù)端APP
我們先從客戶(hù)端應(yīng)用入手,使用Spring Boot可以最大程度簡(jiǎn)化配置
2.1 Maven依賴(lài)
<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 安全配置
客戶(hù)端的安全配置如下:
@Configuration
@EnableOAuth2Sso
public class UiSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**")
.authorizeRequests()
.antMatchers("/", "/login**")
.permitAll()
.anyRequest()
.authenticated();
}
}
配置最核心的部分是 @EnableOAuth2Sso注解來(lái)開(kāi)啟SSO。這里要注意,我們需要重寫(xiě)WebSecurityConfigurerAdapter 否則所有的路徑都會(huì)受到SSO的保護(hù),這樣無(wú)論用戶(hù)訪問(wèn)哪個(gè)頁(yè)面都會(huì)被重定向到登錄頁(yè)面,在這個(gè)例子里,index和login頁(yè)面是唯一不需要被防護(hù)的。
最后,我們定義一個(gè)RequestContextListener bean來(lái)處理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
說(shuō)明:
我們禁用了default Basic Authentication
accessTokenUri 是獲取訪問(wèn)令牌的URL
userAuthorizationUri是授權(quán)用戶(hù)被重定向的目標(biāo)URL
userInfoUri是用戶(hù)終端訪問(wèn)用戶(hù)信息的URL
在這個(gè)case里,認(rèn)證服務(wù)是我們自己建設(shè)的,但是在實(shí)際的應(yīng)用場(chǎng)景下認(rèn)證服務(wù)往往是第三方提供的比如Facebook 或者 gitHub
2.3 前端
前端并非本文的重點(diǎn),這里簡(jiǎn)單提一下。客戶(hù)端有一個(gè)非常簡(jiǎn)單的前端頁(yè)面
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需要用戶(hù)通過(guò)授權(quán)才能訪問(wèn),如果一個(gè)未授權(quán)的用戶(hù)訪問(wèn)這個(gè)頁(yè)面,他會(huì)被重定向到login頁(yè)面
3. 認(rèn)證服務(wù)器
3.1 Maven依賴(lài)
<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(認(rèn)證服務(wù))器和RS放(資源服務(wù)器)在一個(gè)實(shí)例中部署。
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);
}
}
這里我們只開(kāi)啟authorization_code授權(quán)模式,另外這里注意到autoApprove=true,這意味著用戶(hù)不會(huì)被重定向到授權(quán)的頁(yè)面,也不需要手動(dòng)給請(qǐng)求授權(quán)。
3.3 安全配置
這里需要定義一個(gè)簡(jiǎn)單的認(rèn)證機(jī)制
@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認(rèn)證,這里只是做一個(gè)簡(jiǎn)單的例子,我們直接把賬號(hào)密碼寫(xiě)到內(nèi)存里了,真正使用的時(shí)候這里要換成自定義的userDetailsService.
3.4 用戶(hù)終端
一個(gè)很簡(jiǎn)單的返回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