講解基於先後端分離和App模式的token受權開發,因此咱們代碼主要寫在spring-security-app裏面。 以前的spring-security-demo項目一直是依賴咱們的spring-security-web項目,咱們如今改爲spring-security-demo項目依賴咱們的:spring-security-app項目:html
<!-- <dependency> <artifactId>spring-security-web</artifactId> <groupId>com.yxm.security</groupId> <version>${project.version}</version> </dependency>--> <dependency> <groupId>com.yxm.security</groupId> <artifactId>spring-security-app</artifactId> <version>${project.version}</version> </dependency>
由於spring-security-app是一個空的項目,咱們如今啓動下,看demo項目是否能夠正常啓動
啓動項目時候報錯:web
*************************** APPLICATION FAILED TO START *************************** Description: Field authenticationFailureHandler in com.yxm.security.core.validate.code.ValidateCodeFilter required a bean of type 'org.springframework.security.web.authentication.AuthenticationFailureHandler' that could not be found. Action: Consider defining a bean of type 'org.springframework.security.web.authentication.AuthenticationFailureHandler' in your configuration.
報錯緣由是咱們的的spring-security-core須要一個類:
spring
以前的認證成功和失敗是在spring-security-web裏面進行的,因爲咱們web和app對認證失敗後的處理是不同的,因此咱們在app裏面咱們單獨定義本身的認證失敗,認證成功處理器。 數據庫
而後咱們把SimpleResponse移動到spring-security-core裏面的support包中。
後端
*************************** APPLICATION FAILED TO START *************************** Description: Field passwordEncoder in com.yxm.security.service.MyUserDetailsService required a bean of type 'org.springframework.security.crypto.password.PasswordEncoder' that could not be found. Action: Consider defining a bean of type 'org.springframework.security.crypto.password.PasswordEncoder' in your configuration.
咱們以前是在spring-security-web裏面配置了瀏覽器
@Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); }
如今咱們在spring-security-web和spring-security-app都須要使用PasswordEncoder,因此咱們將其配置在spring-security-core裏面的:SecurityCoreConfig服務器
@Configuration//配置類 @EnableConfigurationProperties(SecurityProperties.class)//讓配置類生效 public class SecurityCoreConfig { @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } }
如今咱們服務正常啓動。微信
@Configuration @EnableAuthorizationServer public class MyAuthorizationServerConfig { }
咱們知道以前受權碼模式對於服務提供商而言:他要提供2個服務地址,一個是讓用戶跳過來去點擊「受權」的服務地址;另一個就是:點完受權以後,帶着受權碼過來換access_token的地址。 app
咱們啓動應用,發現console控制檯打印出了不少地址:
前後端分離
/oauth/authorize(第一個地址):讓用戶跳過來去確認受權的路徑。
/oauth/token(第二個地址):讓用戶去拿着受權碼過來換access_token的地址路徑。
咱們在瀏覽器訪問下第一個地址:
咱們進入oauth2的官網:https://tools.ietf.org/html/rfc6749
去查找OAuth2的獲取受權碼的參數說明:
咱們如今主要是組裝這幾個參數:
response_type:值:code
client_id:值從咱們服務啓動時候會打印出client_id:e0fb9540-74d1-4477-a8b7-b7bc89986176
redirect_uri:http://example.com
scope:all
state:可選
則url地址爲:http://localhost:8088/oauth/authorize?response_type=code&client_id=e0fb9540-74d1-4477-a8b7-b7bc89986176&redirect_uri=http://example.com&scope=all
訪問以後,彈出一個basic的信息,須要咱們輸入用戶名和密碼:
咱們如今扮演的是服務提供商:qq,微信這種角色,那麼這個地址帳號密碼其實是咱們服務提供商提供給第三方應用的,讓他去引導用戶受權的,做爲我來講,我須要知道3件事:
總結就是:哪一個第三方應用須要哪一個用戶進行什麼權限。
用戶名/密碼是使用:UserDetailsService去校驗
因此咱們輸入的時候,用戶名能夠隨便輸入,面必須是123456便可。
而後咱們進入403頁面:
由於在默認條件下:認證服務器請求必定要有一個ROLE_USER的角色才能訪問
因此咱們在後面加一個ROLE_USER配置。
private SocialUserDetails buildUser(String userId){ // 根據用戶名查找用戶信息 //根據查找到的用戶信息判斷用戶是否被凍結 String password = passwordEncoder.encode("123456"); logger.info("數據庫密碼是:"+password); return new SocialUser(userId, password, true, true, true, true, AuthorityUtils.commaSeparatedStringToAuthorityList("admin,ROLE_USER")); }
在重啓服務以前咱們作一個一個配置,以前咱們服務提供商提供的client_id是在應用啓動時候,寫死的。每次都要變,咱們本身來生成如下。
//咱們配置服務提供商給哪些第三方(client)應用去提供受權服務。咱們配置給yxm提供服務 security: oauth2: client: clientId: yxm secret: yxmsecret
重啓服務此時咱們訪問的url爲:
http://localhost:8088/oauth/authorize?response_type=code&client_id=yxm&redirect_uri=http://example.com&scope=all
咱們就能夠看到相似qq受權登陸,微信受權掃碼的頁面:
贊成或者拒絕受權是咱們服務提供方作的:咱們點擊贊成:
而後帶着咱們的受權碼跳轉到redirect_uri指定的頁面-便是咱們第三方的頁面。
第三方應用拿到受權碼以後,應該拿着這個受權碼去發一個請求換取:access_token;也就是調用咱們/oauth/token(第二個地址)
而後咱們使用瀏覽器請求工具發送獲取access_token請求
咱們須要攜帶請求頭Header:
Authorization:
request_body中內容以下:
grant_type:authorization_code
client_id:yxm
code:NBP3tW
redirect_uri:http://example.com
scope:all
而後咱們發送請求:這個時候返回以下:
密碼模式至關因而服務提供方微信/qq把用戶名/密碼告訴了第三方,第三方拿着這個用戶名/密碼去獲取access_token。服務提供方無法判斷此用戶名/密碼來源。在微信這種服務提供方和第三方不是同一廠家的話,這種方式是不可行的,可是在咱們經常使用業務系統開發中用於咱們服務提供方和第三方App都是同一廠家是一夥的,因此可行。
與受權碼的url和headers一致;只有BODY不同:
從地址:https://tools.ietf.org/html/r...
POST /token HTTP/1.1 Host:server.example.com Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW Content-Type: application/x-www-form-urlencoded
http://127.0.0.1:8088/oauth/token?grant_type=password&username=johndoe&password=123456&scope=all請求響應:
注意: 1.咱們發現使用受權碼模式和使用密碼模式獲取的access_token是相同的。雖然受權模式不一樣,可是使用的都是同一個用戶:yxm;同一個用戶他在去反覆請求access_token時候,Spring Security OAuth會判斷當前用戶是否是發過響應請求access_token;只要這個token沒有過時,只是這個expires_in在變小。
2.如今全部的4種受權都實現了,token的存儲也實現了。實際上就用了一行代碼:@EnableAuthorizationServer
自定義類,添加註解:@EnableResourceServer
@Configuration @EnableResourceServer public class MyResourceServerConfig { }
添加以上資源服務器配置以後,咱們訪問下以前的用戶接口
報401:
此時沒有認證,咱們經過攜帶下access_token便可訪問資源,可是重啓服務後access_token已通過期了,咱們須要從新獲取access_token而後再攜帶參數請求。
經過token_type與access_token合併。
如今流程走通了,可是仍是存在不少不足: