本來打算把Spring Security中OAuth 2.0的機制講完後,用小程序登陸來實戰一下,發現小程序登陸流程和Spring Security中OAuth 2.0登陸的流程有點不同,就把寫了半天的東西所有推翻了。可是,可是過了一天以後,忽然感受又能夠了。咱們來一塊兒試一試。前端
小程序的登陸流程是這樣的:java
而在Spring Security中的OAuth 2.0 Code
模式是這樣的:web
從這兩張圖上看最大的差異就是微信小程序中獲取code
不須要經過後端服務器的調用,而Spring Security中須要(第1步,第2步,第3步)。騰訊應該也是借鑑了OAuth 2.0,可是作了一些改動。spring
讓我放棄的也是這個差異,有沒有人能想到解決方法呢?編程
假如說小程序已經持有了code
,它依然須要將code
傳遞給後端服務器來執行後面的流程。那麼咱們能不能利用圖2中第3個調用redirectUri
的步驟呢?換個角度來看問題第三方就是小程序反正它也是將一個code
傳遞給了後端服務器,只要返回登陸狀態就好了,反正剩下的登陸流程都跟小程序無關。我以爲它是能夠的。在Spring Security中咱們可使用code
經過tokenUri
來換取token
。那麼在微信小程序登陸流程中,code
最終換取的只是登陸態,沒有特定的要求。可是後端確定須要去獲取用戶的一些信息,好比openId
,用戶微信信息之類的。總之要根據微信平臺提供的API來實現。經過改造tokenUri
和userInfoUri
能夠作到這一點。小程序
全部的猜測都沒有錯,並且我也實現了,可是改形成本太高了,寫了不少兼容性的代碼,若是不深刻Spring Security,很難實現這,並且也很差理解。後端
爲了簡化實現,我決定借鑑Spring Security中OAuth 2.0的思路。Filter攔截小程序登陸URL,而後經過RestTemplate
執行向微信服務器請求獲取結果,處理後返回登陸態。時序圖以下:微信小程序
對應的僞代碼實現:api
package cn.felord.spring.security.filter; import org.springframework.http.ResponseEntity; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.util.Assert; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestTemplate; import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.util.UriComponentsBuilder; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.net.URI; /** * 小程序登陸過濾器 * * @author felord.cn * @since 1.0.4.RELEASE */ public class WeChatAppLoginFilter extends OncePerRequestFilter { private final RequestMatcher requiresAuthenticationRequestMatcher; private final RestTemplate restTemplate; private String appId; private String secret; private static final String WX_URL = "https://api.weixin.qq.com/sns/jscode2session"; public WeChatAppLoginFilter(String loginProcessingUrl, String appId, String secret) { this.appId = appId; this.secret = secret; Assert.notNull(loginProcessingUrl, "loginProcessingUrl must not be null"); this.requiresAuthenticationRequestMatcher = new AntPathRequestMatcher(loginProcessingUrl, "POST"); this.restTemplate = new RestTemplate(); } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // 攔截微信登陸 if (requiresAuthenticationRequestMatcher.matches(request)) { //todo 從request中獲取 code 參數 這裏邏輯根據你的狀況自行實現 String jsCode = "你自行實現從request中獲取"; //todo 必要的校驗本身寫 MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>(); queryParams.add("appid", this.appId); queryParams.add("secret", this.secret); queryParams.add("js_code", jsCode); queryParams.add("grant_type", "authorization_code"); URI uri = UriComponentsBuilder.fromHttpUrl(WX_URL) .queryParams(queryParams) .build() .toUri(); //todo 這裏 Object 自行封裝爲具體對象 ResponseEntity<Object> result = this.restTemplate.getForEntity(uri, Object.class); //todo 處理 result 好比後端存儲、後端受權、角色資源處理、註冊、對session_key的處理等等你須要的業務邏輯 // 最後放入HttpServletResponse中返回前端返回 } else { filterChain.doFilter(request, response); } } }
最後必定別忘了把過濾器配置到WebSecurityConfigurerAdapter
的HttpSecurity
中去。服務器
本篇講解了Spring Security和微信小程序登陸相結合的思路歷程。原本不須要長篇大論OAuth 2.0,之因此寫出來是讓你明白開發中要善於發現一些類似的東西,經過差別對比來探討他們結合的可能性,這也是一種自我提高的方法。方法遠比結果重要,造成本身的方法論就能富有創造力。我是:碼農小胖哥,多多關注,分享更多原創編程乾貨。
關注公衆號:Felordcn 獲取更多資訊