# 实训课程:基于Spring Security OAuth2 + Jwt的微信小程序用户授权认证整合演示Demo
**Repository Path**: y1ip/wechat-miniprogram-login-demo
## Basic Information
- **Project Name**: 实训课程:基于Spring Security OAuth2 + Jwt的微信小程序用户授权认证整合演示Demo
- **Description**: 实现了简单版的uaa服务器;使用Spring Security oauth2resourceServer校验jwt;基于spring security的跨域cors的解决方法。
- **Primary Language**: Java
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 9
- **Created**: 2021-01-11
- **Last Updated**: 2021-01-11
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 基于Spring Security OAuth2 + Jwt的微信小程序用户授权认证整合演示Demo
## 项目背景
在验收2020-2021学年的企业级开发框架实践课的大作业时,发现同学们在处理微信用户登录和在前、后端项目使用jwt认证授权时有很多问题:
- 小程序的后台应用程序的所有接口没有安全规则保护,靠openid参数识别用户。
- 管理后台使用jwt认证和限权,但所有逻辑是自己实现的。最新版本Spring Security内部已经对Jwt支持,自己实现的逻辑与Spring Security不能很好地融合在一起。
- Jwt的密钥是HS256,并没有使用更安全RS256非对称加密方案。
- 小程序的后台应用程序的所有接口应该要用户授权认证后才可以访问,这样可以保护api接口免受机器人拉数据。
- 小程序的大部分外部请求都是异步的,在处理先access_token,再访问后台Api接口获取显示内容时,不知道如何处理同步问题。
- 一般在微服务架构中,会有一个uaa,统一用户账号和认证中心服务器,用于分发基于jwt的access_token。
基于上面的问题,本项目实施了一个整合演示Demo,希望同学们可以加深对Spring Security框架的理解,并提高开发小程序相关应用的架构能力。
## 项目结构
wechat-miniprogram-login-demo
├── miniprogram-app 小程序项目
├── miniprogram-service-a service-a Api微服务
├── miniprogram-uaa 简单版的用户帐户和身份验证(UAA)服务器
└── pom.xml
## 运行项目
- 在idea中运行 miniprogram-uaa 和 miniprogram-service-a 项目。
- 在微信开发者工具中打开 miniprogram-app 项目,编译项目,即可看到如下首页内容:
## 代码注释与参考资源
为了方便同学们自我学习,在编写项目时,在内部插入了大量注释,这些注释是每一项配置和功能实现的解释,或老师在编码时看过的源码引用。
在理解和阅读本项目代码时,必须认真阅读代码的注释。
## UAA服务器生成accessToken与内部认证流程
小程序app启动时,会调用 wx.login() 获取用户授权,用户授权后把code发送到uaa服务器。
uaa服务器调用code2session获取用户的openid和session_key,uaa把这个openid作为subject字段,生成 Jwt 。uaa服务器把这个 jwt 返回给 小程序。 Jwt 的有效期为 1 天。
小程序在请求后台api微服务时,都需要把Jwt添加到请求的header字段(如:wx.request),后台引用了Spring Security oauth2ResourceServer模块,会使用RS256验签,并解释jwt,把openid解释出来,最终生成 JwtAuthenticationToken 存储在SecurityContext中完成认证。
Spring Security会检查JwtAuthenticationToken的权限信息,决定用户是否有权限访问api。
## 小程序中调用外部Api时的同步问题
后台的api都是受Spring Security的保护的,会先检查http请求的header是否包含jwt的token。
小程序在加载app时应该先完成用户授权登录,并获取uaa服务器返回的Jwt token。
但wx.login()异步的,在获取到jwt之前,可能其它Page.onload已经执行了,导致不能调用后台api获取数据成完page内容的显示。
项目里的解释方案是参考小程序起手架项目的userInfo处理方式。在Page.onLoad里 检查app的全局变量中的accesstoken有没有初始化,如果没有的话(wx.request还没有返回)就定义一个 app 的函数变量 app.accessTokenReadCallBack ,函数的逻辑使用token获取api的数据并显示在index首页。
在app.js里,会检查 app.accessTokenReadCallBack 有没有定义,如果有的话,说明Page.onload已经执行过了,然后回调这个app.accessTokenReadCallBack,完成Page的数据赋值,从而把数据初始化到首页。
## 前、后端分离项目中的跨域问题的解释方法
小程序 或 ajax的异常请求,不存在跨域问题。
跨域问题主要存在需要使用浏览器访问的后台管理系统。后台管理系统前端使用vue开发和npm编译打包的,后端使用spring boot开发。
如果前端vue独立部署,并且与后端的api微服务不同域名或不同端口时,就会出来跨域问题,浏览器会禁止访问api。
关于跨域的详细解释,可参考:
- [跨源资源共享(CORS)](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS)
- [Spring Security官文档 - CORS](https://docs.spring.io/spring-security/site/docs/current/reference/html5/#cors)
如果后端使用spring security框架的话,解释跨域问题很简单:
```java
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// by default uses a Bean by the name of corsConfigurationSource
.cors(withDefaults());
//....
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("https://example.com"));
configuration.setAllowedMethods(Arrays.asList("GET","POST"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
```
## Q & A
由于时间关系,项目并不完善,内容只是实现了一个简单版的基于Spring Security OAuth2 + Jwt的微信小程序用户授权认证整合演示Demo。
如果大家遇到什么问题,可以随时与我联系,QQ:2231068
如果觉得项目对你有帮助,欢迎 fork 和 star 。
## 参考资源
- [jwt.io](http://jwt.io/)
- https://docs.cloudfoundry.org/concepts/architecture/uaa.html
- https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html
- https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html