前幾節簡單介紹了OAuth,以及spring secuirty OAuth的一些基本構成。文中有一些關聯知識點的引用,若是要深刻研究,能夠點進去詳細瞭解一下,這樣能夠加深當即。若是你只想快速瀏覽OAuth server的搭建能夠跳過前面幾節,直接看下面的代碼實例。html
OAuth(開放受權)是一個開放標準,容許用戶受權第三方移動應用訪問他們存儲在另外的服務提供者上的信息,而不須要將用戶名和密碼提供給第三方移動應用或分享他們數據的全部內容。
Oauth2 受權方式java
關於OAuth不一樣受權的具體流程能夠參考[理解OAuth 2.0
-阮一峯](http://www.ruanyifeng.com/blo...。算法
spring security OAuth 是對OAuth2協議的一個實現。是在spring security的基礎上發展而來,以前是spring security的一個子項目,如今已經獨立出來。詳細見官網.
spring OAuth使用相似spring secuirty的機制來實現OAuth2 .根據OAuth協議規定,一個完整的受權服務器應當包含:受權服務器和資源服務器。受權服務器負責認證和受權,資源服務器負責根據用戶提供的受權憑證(好比給以token)。這裏有一篇官方文檔介紹了spring OAuth的使用.[OAuth 2 Developers Guide
](http://projects.spring.io/spr...spring
一個受權服務大體幾個模塊:client管理、受權接口、用戶認證、令牌管理。
spring security OAuth授端口數據庫
受權是使用 AuthorizationEndpoint 這個端點來進行控制的,你可以使用 AuthorizationServerEndpointsConfigurer 這個對象的實例來進行配置 ,若是你不進行設置的話,默認是除了資源全部者密碼(password)受權類型之外,支持其他全部標準受權類型的(RFC6749),咱們來看一下這個配置對象有哪些屬性能夠設置吧,以下列表:後端
令牌主要有兩種解決方案:使用隨機算法生成惟一標示與用戶受權關聯,而後保存起來供校驗時查詢,爲了方便資源服務方便驗證令牌,這種方案經常是受權服務和資源服務共存的,若是不共存,那麼資源服務的tokenStore服務於受權服務tokenStore要作到數據互通,spring 的解決方案是提供/oauth/check_token接口來完成。第二種是受權服務器使用某種算法生成字符串,資源服務器使用約定好的算法對令牌進行解析校驗,以驗證 他的合法性。在spring security OAuth2中提供了InMemoryTokenStore、JdbcTokenStore、JwtTokenStore。前面二者須要將令牌存在起來,最後一個JwtTokenStore是jwt令牌TokenStore的實現,他不存儲令牌,只根據必定的規則和祕鑰驗證令牌的合法性。jwt令牌分爲三段:頭部信息、playload、簽名。每段使用base64編碼,每段之間使用"."鏈接。api
一個資源服務(能夠和受權服務在同一個應用中,固然也能夠分離開成爲兩個不一樣的應用程序)提供一些受token令牌保護的資源,Spring OAuth提供者是經過Spring Security authentication filter來驗證過濾器來實現的保護(OAuth2AuthenticationProcessingFilter),你能夠經過 @EnableResourceServer 註解到一個 @Configuration 配置類上來標記應用是一個資源服務器,你能夠經過配置 ResourceServerConfigurer 配置對象來進行資源服務器的一些自定義配置(能夠選擇繼承自 ResourceServerConfigurerAdapter 而後覆寫其中的方法,參數就是這個對象的實例),下面是一些能夠配置的屬性:緩存
@EnableResourceServer 註解自動增長了一個類型爲 OAuth2AuthenticationProcessingFilter 的過濾器鏈。
ResourceServerTokenServices 是組成受權服務的另外一半,若是你的受權服務和資源服務在同一個應用程序上的話,你可使用 DefaultTokenServices ,這樣的話,你就不用考慮關於實現全部必要的接口的一致性問題,這一般是很困難的。若是你的資源服務器是分離開的,那麼你就必需要確保可以有匹配受權服務提供的 ResourceServerTokenServices,它知道如何對令牌進行解碼。
在受權服務器上,你一般可使用 DefaultTokenServices 而且選擇一些主要的表達式經過 TokenStore(後端存儲或者本地編碼)。
RemoteTokenServices 能夠做爲一個替代,它將容許資源服務器經過HTTP請求來解碼令牌(也就是受權服務的 /oauth/check_token 端點)。若是你的資源服務沒有太大的訪問量的話,那麼使用RemoteTokenServices 將會很方便(全部受保護的資源請求都將請求一次受權服務用以檢驗token值),或者你能夠經過緩存來保存每個token驗證的結果。安全
關於spring OAuth的更多配置細節能夠參考官方文檔: OAuth2 Autoconfig.spring Secuirty OAuth2配置比較簡單,關鍵仍是要理解OAuth2協議,以及他的使用場景。
<dependencies> <!-- ... other dependency elements ... --> <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> <version>2.1.1.RELEASE</version> </dependency> </dependencies>
受權服務配置主要須要完成如下幾個配置:bash
咱們能夠繼承AuthorizationServerConfigurerAdapter而且覆寫其中的三個configure方法來進行配置。下面是簡單的
配置demo,配置來源於官方文檔。
@Component public class CustomAuthorizationServerConfigurer extends AuthorizationServerConfigurerAdapter { AuthenticationManager authenticationManager; public CustomAuthorizationServerConfigurer( AuthenticationConfiguration authenticationConfiguration ) throws Exception { this.authenticationManager = authenticationConfiguration.getAuthenticationManager(); } @Override public void configure( ClientDetailsServiceConfigurer clients ) throws Exception { //下面配置了一個簡單的客服端 clients.inMemory()// 使用內存保存客服端信息,建議編寫本身的clientService來配置客服端 .withClient("client")//客服端的clientid .authorizedGrantTypes("password","client_credentials","refresh_token")//支持的受權方式 .secret("secret")//該客服端訪問時的密碼 .scopes("all");//scope範圍 } @Override public void configure( AuthorizationServerEndpointsConfigurer endpoints ) throws Exception { endpoints.authenticationManager(authenticationManager); endpoints.tokenStore(tokenStore()); } @Bean public TokenStore tokenStore() { return new InMemoryTokenStore(); } }
以上完成了一個簡單的受權服務配置,咱們的配置重點放在了client的配置上,因爲咱們爲配置資源擁有者(用戶),咱們沒法使用password受權,應爲用戶信不存在,咱們不管輸入用戶名和密碼都不正確。所以我暫且只能使用,client_credentials,這個僅僅是client受權,不牽扯到用戶信息。使用下面命令
curl client:secret@localhost:8080/oauth/token -d grant_type=client_credentials
一切順利的話咱們能夠獲得token相關信息。若是要使用其餘好比password等用戶受權,那麼須要在受權服務器配置用戶相關信息,並把相關的userDetailService配置到AuthorizationServerEndpointsConfigurer對象上,上面有提到endponint配置相關規則。
資源服務的做用前面已經介紹過了,資源服務主要關注兩個事情,對token進行鑑權,根據具體權限包含資源。因爲token多是其餘服務器生成,所以要作到ResourceServerTokenServices與受權服務的tokenService相匹配。下面是簡單的配置.
@EnableResourceServer public static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter { // 注意這個TokenStore須要和以前配置的一致 private final TokenStore tokenStore; public ResourceServerConfiguration(TokenStore tokenStore) { this.tokenStore = tokenStore; } // 資源服務器安全規則配置 @Override public void configure(HttpSecurity http) throws Exception { http .exceptionHandling() .authenticationEntryPoint((request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED)) .and() .csrf() .disable() .headers() .frameOptions() .disable() .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .antMatchers("/api/register").permitAll() .antMatchers("/api/activate").permitAll() .antMatchers("/api/authenticate").permitAll() .antMatchers("/api/account/reset-password/init").permitAll() .antMatchers("/api/account/reset-password/finish").permitAll() .antMatchers("/api/**").authenticated() // 配置scope權限訪問 // .antMatchers("/api/**").access("#oauth2.hasScope('read') or (!#oauth2.isOAuth() and hasRole('ROLE_USER'))"); } @Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { resources.resourceId("jhipster-uaa") .tokenStore(tokenStore); } }
上面是資源服務和受權服務在一臺機器上時的配置。因爲共享tokenStore,所以資源服務在驗證token的時候不會出任何問題。
@Configuration @EnableResourceServer @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) public class SecurityConfiguration extends ResourceServerConfigurerAdapter { private final OAuth2Properties oAuth2Properties; public SecurityConfiguration(OAuth2Properties oAuth2Properties) { this.oAuth2Properties = oAuth2Properties; } // 主要配置資源的保護規則 @Override public void configure(HttpSecurity http) throws Exception { http .csrf() .disable() .headers() .frameOptions() .disable() .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .antMatchers("/api/**").authenticated() .antMatchers("/management/health").permitAll() .antMatchers("/management/info").permitAll() .antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN); } // 配置tokenstore,資源服務器在進行token驗證的時候須要, @Bean public TokenStore tokenStore(JwtAccessTokenConverter jwtAccessTokenConverter) { return new JwtTokenStore(jwtAccessTokenConverter); } @Bean public JwtAccessTokenConverter jwtAccessTokenConverter(OAuth2SignatureVerifierClient signatureVerifierClient) { return new OAuth2JwtAccessTokenConverter(oAuth2Properties, signatureVerifierClient); } }
上面是採用jwt,資源/受權服務分離的配置,以上配置沒法和上面的受權服務配置配合使用。若要配置使用,須要修改受權服務的TokenStore爲JwtTokenStore,而且在OAuth2JwtAccessTokenConverter中需訪問受權服務器/token/access_key獲取RSA公鑰,該公鑰會在token驗證時用到。