微信小程序登录与Spring Security结合的思路

开发 前端
本来不需要长篇大论OAuth 2.0,之所以写出来是让你明白开发中要善于发现一些相似的东西,通过差异对比来探讨他们结合的可能性,这也是一种自我提升的方法。方法远比结果重要,形成自己的方法论就能富有创造力。

[[385213]]

 1. 前言

原本打算把Spring Security中OAuth2.0的机制讲完后,用小程序登录来实战一下,发现小程序登录流程和Spring Security中OAuth 2.0登录的流程有点不一样,就把写了半天的东西全部推翻了。但是,但是过了一天之后,突然感觉又可以了。我们来一起试一试。

2. 小程序登录流程分析

小程序的登录流程是这样的:

微信小程序登录时序图

而在Spring Security中的OAuth 2.0 Code模式是这样的:

Spring Security OAuth2.0 Code模式时序图

从这两张图上看最大的差别就是微信小程序中获取code不需要通过后端服务器的调用,而Spring Security中需要(第1步,第2步,第3步)。腾讯应该也是借鉴了OAuth 2.0,但是做了一些改动。

让我放弃的也是这个差别,有没有人能想到解决方法呢?

假如说小程序已经持有了code,它依然需要将code传递给后端服务器来执行后面的流程。那么我们能不能利用图2中第3个调用redirectUri的步骤呢?换个角度来看问题第三方就是小程序反正它也是将一个code传递给了后端服务器,只要返回登录状态就行了,反正剩下的登录流程都跟小程序无关。我觉得它是可以的。在Spring Security中我们可以使用code通过tokenUri来换取token。那么在微信小程序登录流程中,code最终换取的只是登录态,没有特定的要求。但是后端肯定需要去获取用户的一些信息,比如openId,用户微信信息之类的。总之要根据微信平台提供的API来实现。通过改造tokenUri和userInfoUri可以做到这一点。

3. 思路借鉴

所有的猜想都没有错,而且我也实现了,但是改造成本过高了,写了很多兼容性的代码,如果不深入Spring Security,很难实现这一点,而且也不好理解。

为了简化实现,我决定借鉴Spring Security中OAuth 2.0的思路。Filter拦截小程序登录URL,然后通过RestTemplate执行向微信服务器请求获取结果,处理后返回登录态。时序图如下:

小程序登录开发时序图

对应的伪代码实现:

  1. package cn.felord.spring.security.filter; 
  2.  
  3. import org.springframework.http.ResponseEntity; 
  4. import org.springframework.security.web.util.matcher.AntPathRequestMatcher; 
  5. import org.springframework.security.web.util.matcher.RequestMatcher; 
  6. import org.springframework.util.Assert; 
  7. import org.springframework.util.LinkedMultiValueMap; 
  8. import org.springframework.util.MultiValueMap; 
  9. import org.springframework.web.client.RestTemplate; 
  10. import org.springframework.web.filter.OncePerRequestFilter; 
  11. import org.springframework.web.util.UriComponentsBuilder; 
  12.  
  13. import javax.servlet.FilterChain; 
  14. import javax.servlet.ServletException; 
  15. import javax.servlet.http.HttpServletRequest; 
  16. import javax.servlet.http.HttpServletResponse; 
  17. import java.io.IOException; 
  18. import java.net.URI; 
  19.  
  20. /** 
  21.  * 小程序登录过滤器 
  22.  * 
  23.  * @author felord.cn 
  24.  * @since 1.0.4.RELEASE 
  25.  */ 
  26. public class WeChatAppLoginFilter extends OncePerRequestFilter { 
  27.  
  28.     private final RequestMatcher requiresAuthenticationRequestMatcher; 
  29.     private final RestTemplate restTemplate; 
  30.     private String appId; 
  31.     private String secret; 
  32.     private static final String WX_URL = "https://api.weixin.qq.com/sns/jscode2session"
  33.  
  34.     public WeChatAppLoginFilter(String loginProcessingUrl, String appId, String secret) { 
  35.         this.appId = appId; 
  36.         this.secret = secret; 
  37.         Assert.notNull(loginProcessingUrl, "loginProcessingUrl must not be null"); 
  38.         this.requiresAuthenticationRequestMatcher = new AntPathRequestMatcher(loginProcessingUrl, "POST"); 
  39.         this.restTemplate = new RestTemplate(); 
  40.     } 
  41.  
  42.     @Override 
  43.     protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { 
  44.  
  45.         // 拦截微信登录 
  46.         if (requiresAuthenticationRequestMatcher.matches(request)) { 
  47.             //todo 从request中获取 code 参数 这里逻辑根据你的情况自行实现 
  48.             String jsCode = "你自行实现从request中获取"
  49.             //todo 必要的校验自己写 
  50.             MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>(); 
  51.             queryParams.add("appid", this.appId); 
  52.             queryParams.add("secret", this.secret); 
  53.             queryParams.add("js_code", jsCode); 
  54.             queryParams.add("grant_type""authorization_code"); 
  55.  
  56.  
  57.             URI uri = UriComponentsBuilder.fromHttpUrl(WX_URL) 
  58.                     .queryParams(queryParams) 
  59.                     .build() 
  60.                     .toUri(); 
  61.             //todo 这里 Object 自行封装为具体对象 
  62.             ResponseEntity<Object> result = this.restTemplate.getForEntity(uri, Object.class); 
  63.  
  64.             //todo 处理 result 比如后端存储、后端授权、角色资源处理、注册、对session_key的处理等等你需要的业务逻辑 
  65.             // 最后放入HttpServletResponse中返回前端返回 
  66.  
  67.         } else { 
  68.             filterChain.doFilter(request, response); 
  69.         } 
  70.     } 

最后一定别忘了把过滤器配置到WebSecurityConfigurerAdapter的HttpSecurity中去。

4. 总结

本篇讲解了Spring Security和微信小程序登录相结合的思路历程。本来不需要长篇大论OAuth 2.0,之所以写出来是让你明白开发中要善于发现一些相似的东西,通过差异对比来探讨他们结合的可能性,这也是一种自我提升的方法。方法远比结果重要,形成自己的方法论就能富有创造力。

 本文转载自微信公众号「码农小胖哥」,可以通过以下二维码关注。转载本文请联系码农小胖哥公众号。

 

责任编辑:武晓燕 来源: 码农小胖哥
相关推荐

2021-10-26 00:25:14

程序登录流程

2023-03-26 00:00:00

2023-07-28 22:14:51

Spring多租户配置

2017-05-08 15:03:07

微信小程序开发实战

2017-02-06 13:32:12

微信小程序思想

2016-10-20 21:02:12

微信小程序javascript

2017-06-09 12:58:20

微信小程序架构分析

2017-01-09 10:01:49

微信小程序

2017-06-09 10:06:54

微信小程序架构分析

2016-09-28 18:10:59

微信程序MINA

2017-06-09 10:40:00

微信小程序架构分析

2016-11-04 10:31:49

微信程序指南

2016-11-22 11:23:52

微信小程序腾讯微信

2016-11-04 10:49:48

微信小程序

2021-06-10 10:51:27

程序基础架构

2016-09-27 15:40:58

微信程序前端

2016-09-27 16:38:24

JavaScript微信Web

2021-02-25 10:01:19

鸿蒙HarmonyOS应用开发

2016-11-07 10:30:07

微信小程序安装配置

2016-11-19 18:06:44

微信小程序张小龙
点赞
收藏

51CTO技术栈公众号