基于Spring Security OAuth2.0實現單點登錄SSO

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容