Spring Security OAuth 實現OAuth 2.0 受權

OAuth 2.0 簡介

OAuth 2.0是一種工業級的受權協議。OAuth 2.0是從建立於2006年的OAuth 1.0繼承而來的。OAuth 2.0致力於幫助開發者簡化受權併爲web應用、桌面應用、移動應用、嵌入式應用提供具體的受權流程。java

OAuth 2.0 is the industry-standard protocol for authorization. OAuth 2.0 supersedes the work done on the original OAuth protocol created in 2006. OAuth 2.0 focuses on client developer simplicity while providing specific authorization flows for web applications, desktop applications, mobile phones, and living room devices.git

OAuth 2.0的四個角色

爲了方便理解,以經常使用的使用微信登陸爲例github

  • Resource Ownerweb

    資源擁有者,對應微信的每一個用戶微信上設置的我的信息是屬於每一個用戶的,不屬於騰訊。spring

  • Resource Serverapi

    資源服務器,通常就是用戶數據的一些操做(增刪改查)的REST API,好比微信的獲取用戶基本信息的接口。安全

  • Client Application服務器

    第三方客戶端,對比微信中就是各類微信公衆號開發的應用,第三方應用通過認證服務器受權後便可訪問資源服務器的REST API來獲取用戶的頭像、性別、地區等基本信息。微信

  • Authorization Servercookie

    認證服務器,驗證第三方客戶端是否合法。若是合法就給客戶端頒佈token,第三方經過token來調用資源服務器的API。

四種受權方式(Grant Type)

  • anthorization_code

    受權碼類型,適用於Web Server Application。模式爲:客戶端先調用/oauth/authorize/進到用戶受權界面,用戶受權後返回code,客戶端而後根據code和appSecret獲取access token

  • implicit 簡化類型,相對於受權碼類型少了受權碼獲取的步驟。客戶端應用受權後認證服務器會直接將access token放在客戶端的url。客戶端解析url獲取token。這種方式實際上是不太安全的,能夠經過https安全通道縮短access token的有效時間來較少風險。

  • password

    密碼類型,客戶端應用經過用戶的username和password獲access token。適用於資源服務器、認證服務器與客戶端具備徹底的信任關係,由於要將用戶要將用戶的用戶名密碼直接發送給客戶端應用,客戶端應用經過用戶發送過來的用戶名密碼獲取token,而後訪問資源服務器資源。好比支付寶就能夠直接用淘寶用戶名和密碼登陸,由於它們屬於同一家公司,彼此充分信任

  • client_credentials

    客戶端類型,是不須要用戶參與的一種方式,用於不一樣服務之間的對接。好比本身開發的應用程序要調用短信驗證碼服務商的服務,調用地圖服務商的服務、調用手機消息推送服務商的服務。當須要調用服務是能夠直接使用服務商給的appIDappSecret來獲取token,獲得token以後就能夠直接調用服務。

其餘概念

  • scope:訪問資源服務器的哪些做用域。
  • refresh token:當access token 過時後,能夠經過refresh token從新獲取access token。

實現

有的時候資源服務器和認證服務器是兩個不一樣的應用,有的時候資源服務器和認證服務器在通一個應用中,不一樣之處在於資源服務器是否須要檢查token的有效性,前者須要檢查,後者不須要。這裏實現後者。

Application的安全配置

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
                .and().csrf().disable()
                .authorizeRequests().anyRequest().authenticated();
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        super.configure(web);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("lyt").password("lyt").authorities("ROLE_USER")
                .and().withUser("admin").password("admin").authorities("ROLE_ADMIN");
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

認證服務器配置

@EnableAuthorizationServer
@Configuration
public class AuthorizationServerConfiguration  extends AuthorizationServerConfigurerAdapter {

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory().withClient("client")
                .scopes("read","write")
                .secret("secret")
                .authorizedGrantTypes("authorization_code","password","implicit","client_credentials");}

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        super.configure(security);
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
       endpoints.authenticationManager(authenticationManager);
    }

    @Autowired
    @Qualifier("authenticationManagerBean")
    private AuthenticationManager authenticationManager;
}

資源服務器配置

@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableResourceServer
@Configuration
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
	@Override
	public void configure(HttpSecurity http) throws Exception {
		http.antMatcher("/oauth2/api/**").authorizeRequests()
			.antMatchers(HttpMethod.GET, "/read/**").access("#oauth2.hasScope('read')")
			.antMatchers(HttpMethod.POST, "/write/**").access("#oauth2.hasScope('write')")
			.antMatchers(HttpMethod.PUT, "/write/**").access("#oauth2.hasScope('write')")
			.antMatchers(HttpMethod.DELETE, "/write/**").access("#oauth2.hasScope('write')");
	}

}

資源服務器filter-order設置

須要在application.yml中將filter-order設置成3,具體緣由參考 連接

防止cookie衝突

爲了不認證服務器的cookie和客戶端的cookie衝突,出現錯誤,最好修改cookie name 或者設置contextPath

測試

postman中提供OAuth 2.0的認證方式,能夠獲取到token以後再把認證加入http請求中,便可請求資源服務器的REST API

  • 客戶端信息

輸入圖片說明

  • 受權

輸入圖片說明

  • 獲取的token

輸入圖片說明

  • 訪問資源服務器API

輸入圖片說明

最後

測試代碼github地址。有興趣能夠關注微信公衆帳號獲取最新推送文章。

輸入圖片說明

相關文章
相關標籤/搜索