本来打算把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 获取更多资讯