javascript
Springboot简便的配置微信小程序
Springboot簡便的配置微信小程序
ShareNotes
最近在完成一個微信小程序項目,即將上線
歡迎star
Github–ShareNotes
issue
寫小程序接口遇到的具體情況
- 通過openId登錄。也就是所謂的微信點擊直接登錄。不需要輸入賬戶密碼
- 使用微信自帶的api過濾不合法的字符或者圖片
- 商戶,支付接口
- 更多issue后期遇到情況會添加
微信直接登錄
在最早的時候我?guī)瓦^朋友寫過一個花店賣花的程序。
最開始的時候大多數(shù)都是通過
微信官方文檔API
找到api。然后RestTemplate來訪問url。
RestTemplate是Spring提供的用于訪問Rest服務(wù)的客戶端。
十分的煩瑣
引入別人寫好的微信封裝工具類
Github-Binary Wang
開始操作
引入依賴
<dependency><groupId>com.github.binarywang</groupId><artifactId>weixin-java-miniapp</artifactId><version>3.3.0</version> </dependency> <!--如果不需要支付功能--> <dependency><groupId>com.github.binarywang</groupId><artifactId>weixin-java-pay</artifactId><version>3.3.0</version> </dependency>jwt的依賴
<dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.4.1</version> </dependency>創(chuàng)建用戶表
DROP TABLE IF EXISTS `user`; CREATE TABLE `user` (`id` int(11) NOT NULL AUTO_INCREMENT,`username` varchar(63) NOT NULL COMMENT '用戶名稱',`password` varchar(63) NOT NULL DEFAULT '' COMMENT '用戶密碼',`gender` tinyint(3) NOT NULL DEFAULT '0' COMMENT '性別:0 未知, 1男, 1 女',`birthday` date DEFAULT NULL COMMENT '生日',`last_login_time` datetime DEFAULT NULL COMMENT '最近一次登錄時間',`last_login_ip` varchar(63) NOT NULL DEFAULT '' COMMENT '最近一次登錄IP地址',`nickname` varchar(63) NOT NULL DEFAULT '' COMMENT '用戶昵稱或網(wǎng)絡(luò)名稱',`mobile` varchar(20) NOT NULL DEFAULT '' COMMENT '用戶手機號碼',`avatar` varchar(255) NOT NULL DEFAULT '' COMMENT '用戶頭像圖片',`weixin_openid` varchar(63) NOT NULL DEFAULT '' COMMENT '微信登錄openid',`session_key` varchar(100) NOT NULL DEFAULT '' COMMENT '微信登錄會話KEY',`status` tinyint(3) NOT NULL DEFAULT '0' COMMENT '0 可用, 1 禁用, 2 注銷',`add_time` datetime DEFAULT NULL COMMENT '創(chuàng)建時間',`update_time` datetime DEFAULT NULL COMMENT '更新時間',`deleted` tinyint(1) DEFAULT '0' COMMENT '邏輯刪除',PRIMARY KEY (`id`),UNIQUE KEY `user_name` (`username`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COMMENT='用戶表';本質(zhì)是 weixin_openid,session_key。
因為登錄不需要賬戶密碼。是直接登錄。所以如果第一次登錄創(chuàng)建用戶的時候username和password可以設(shè)置為 weixin_openid,或者任意值。
配置wxConfig。
如果weixin-java-miniapp依賴已經(jīng)導(dǎo)入完成就可以開始配置你的appid和appsecret,其在到微信開發(fā)者平臺里頭
在之前先配置好properties(我這里是yml格式)
wx:app-id: xxxxxapp-secret: xxxxxmch-id: xxxxxxxxmch-key: xxxxxxnotify-url: http://www.example.com/wx/order/pay-notify# 商戶證書文件路徑# 請參考“商戶證書”一節(jié) https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=4_3key-path: xxxxx獲取properties的值
@Configuration @ConfigurationProperties(prefix = "wx") public class WxProperties {private String appId;private String appSecret;private String mchId;private String mchKey;private String notifyUrl;private String keyPath;public String getNotifyUrl() {return notifyUrl;}public void setNotifyUrl(String notifyUrl) {this.notifyUrl = notifyUrl;}public String getMchKey() {return mchKey;}public void setMchKey(String mchKey) {this.mchKey = mchKey;}public String getAppId() {return this.appId;}public void setAppId(String appId) {}public String getAppSecret() {return appSecret;}}}this.keyPath = keyPath;public void setKeyPath(String keyPath) {}return keyPath;public String getKeyPath() {}this.mchId = mchId;public void setMchId(String mchId) {}return mchId;public String getMchId() {}this.appSecret = appSecret;public void setAppSecret(String appSecret) {配置wxConfig
@Configuration public class WxConfig {@Autowiredprivate WxProperties properties;@Beanpublic WxMaConfig wxMaConfig() {WxMaInMemoryConfig config = new WxMaInMemoryConfig();config.setAppid(properties.getAppId());config.setSecret(properties.getAppSecret());return config;}@Beanpublic WxMaService wxMaService(WxMaConfig maConfig) {WxMaService service = new WxMaServiceImpl();service.setWxMaConfig(maConfig);return service;}@Beanpublic WxPayConfig wxPayConfig() {WxPayConfig payConfig = new WxPayConfig();payConfig.setAppId(properties.getAppId());payConfig.setMchId(properties.getMchId());payConfig.setMchKey(properties.getMchKey());payConfig.setNotifyUrl(properties.getNotifyUrl());payConfig.setKeyPath(properties.getKeyPath());payConfig.setTradeType("JSAPI");payConfig.setSignType("MD5");return payConfig;}@Beanpublic WxPayService wxPayService(WxPayConfig payConfig) {WxPayService wxPayService = new WxPayServiceImpl();wxPayService.setConfig(payConfig);return wxPayService;} }所有關(guān)于微信小程序配置都在這里了。
然后我們可以使用他封裝好的wxMaService了。
生成(寫好)對應(yīng)的 實體類,mapper。
User.java
@Data public class User { private Integer id; private String username; private String password; private Byte gender; private Date birthday; private Date lastLoginTime; private String lastLoginIp; private String nickname; private String mobile; private String avatar; private String weixinOpenid; private String sessionKey; private Byte status; private Date addTime; private Date updateTime; private Boolean deleted; }WxLoginInfo.java
public class WxLoginInfo {private String code;private UserDto userInfo;public String getCode() {return code;}public void setCode(String code) {this.code = code;}public UserDto getUserInfo() {return userInfo;}public void setUserInfo(UserDto userInfo) {this.userInfo = userInfo;} }UserDto.java
@Data public class UserDto {private String nickName;private String avatarUrl;private String country;private String province;private String city;private String language;private Byte gender;UserService.java
需要放這兩個方法
實現(xiàn)
UserServiceImpl.java
Controller
WxAuthController.java
@Slf4j @RestController @RequestMapping("/wx/auth") @Validated public class WxAuthController {@Autowiredprivate UserService userService;//這里是之前添加依賴里面的方法@Autowiredprivate WxMaService wxService;@PostMapping("login_by_weixin") public Object loginByWeixin(@RequestBody WxLoginInfo wxLoginInfo, HttpServletRequest request) {String code = wxLoginInfo.getCode();UserDto userDto = wxLoginInfo.getUserInfo();if (code == null || userDto == null) {return ResponseUtil.badArgument();}String sessionKey = null;String openId = null;try {WxMaJscode2SessionResult result = this.wxService.getUserService().getSessionInfo(code);sessionKey = result.getSessionKey();openId = result.getOpenid();} catch (Exception e) {e.printStackTrace();}if (sessionKey == null || openId == null) {return ResponseUtil.fail();}User user = userService.queryByOid(openId);if (user == null) {user = new User();user.setUsername(openId);user.setPassword(openId);user.setWeixinOpenid(openId);user.setAvatar(userDto.getAvatarUrl());user.setNickname(userDto.getNickName());user.setGender(userDto.getGender());user.setStatus((byte) 0);user.setLastLoginTime(new Date());user.setLastLoginIp(IpUtil.getIpAddr(request));user.setSessionKey(sessionKey);userService.add(user);} else {user.setLastLoginTime(new Date());user.setLastLoginIp(IpUtil.getIpAddr(request));user.setSessionKey(sessionKey);if (userService.updateById(user) == 0) {return ResponseUtil.updatedDataFailed();}}// tokenString token = UserTokenManager.generateToken(user.getId());Map<Object, Object> result = new HashMap<Object, Object>();result.put("token", token);result.put("userInfo", userDto);return ResponseUtil.ok(result); }這里的ResponseUtil.ok(result)是我自己的返回值封裝類。
舉個例子
JWT配置
接上面的 UserTokenManager
JwtHelper----> UserTokenManager
JwtHelper.java
public class JwtHelper {// 秘鑰static final String SECRET = "YOUR-SECRET-TOKEN";// 簽名是有誰生成static final String ISSUSER = "SECRET";// 簽名的主題static final String SUBJECT = "this is you token";// 簽名的觀眾static final String AUDIENCE = "MINIAPP";public String createToken(Integer userId){try {Algorithm algorithm = Algorithm.HMAC256(SECRET);Map<String, Object> map = new HashMap<String, Object>();Date nowDate = new Date();// 過期時間:7天2小時Date expireDate = getAfterDate(nowDate,0,0,7,2,0,0);map.put("alg", "HS256");map.put("typ", "JWT");String token = JWT.create()// 設(shè)置頭部信息 Header.withHeader(map)// 設(shè)置 載荷 Payload.withClaim("userId", userId).withIssuer(ISSUSER).withSubject(SUBJECT).withAudience(AUDIENCE)// 生成簽名的時間 .withIssuedAt(nowDate)// 簽名過期的時間 .withExpiresAt(expireDate)// 簽名 Signature.sign(algorithm);return token;} catch (JWTCreationException exception){exception.printStackTrace();}return null;}public Integer verifyTokenAndGetUserId(String token) {try {Algorithm algorithm = Algorithm.HMAC256(SECRET);JWTVerifier verifier = JWT.require(algorithm).withIssuer(ISSUSER).build();DecodedJWT jwt = verifier.verify(token);Map<String, Claim> claims = jwt.getClaims();Claim claim = claims.get("userId");return claim.asInt();} catch (JWTVerificationException exception){ // exception.printStackTrace();} return 0;}public Date getAfterDate(Date date, int year, int month, int day, int hour, int minute, int second){if(date == null){date = new Date();} Calendar cal = new GregorianCalendar(); cal.setTime(date);if(year != 0){cal.add(Calendar.YEAR, year);}if(month != 0){cal.add(Calendar.MONTH, month);}if(day != 0){cal.add(Calendar.DATE, day);}if(hour != 0){cal.add(Calendar.HOUR_OF_DAY, hour);}if(minute != 0){cal.add(Calendar.MINUTE, minute);}if(second != 0){cal.add(Calendar.SECOND, second);}return cal.getTime();} }UserTokenManager.java
public static String generateToken(Integer id) {JwtHelper jwtHelper = new JwtHelper();return jwtHelper.createToken(id); } public static Integer getUserId(String token) {JwtHelper jwtHelper = new JwtHelper();Integer userId = jwtHelper.verifyTokenAndGetUserId(token);if(userId == null || userId == 0){return null;}return userId; }小程序前端
user.js
function loginByWeixin(userInfo) {return new Promise(function (resolve, reject) {return login().then((res) => {//這里的api.AuthLoginByWeixin 為 localhost:8080/wx/auth/login_by_weixinutil.request(api.AuthLoginByWeixin, {code: res.code,userInfo: userInfo}, 'POST').then(res => {if (res.errno === 0) {//存儲用戶信息wx.setStorageSync('userInfo', res.data.userInfo);wx.setStorageSync('token', res.data.token);resolve(res);} else {reject(res);}}).catch((err) => {reject(err);});}).catch((err) => {reject(err);})}); }utils.js
function request(url, data = {}, method = "GET") {return new Promise(function (resolve, reject) {wx.request({url: url,data: data,method: method,header: {'Content-Type': 'application/json', //剛剛你設(shè)置的token'YOUR-SECRET-TOKEN': wx.getStorageSync('token')},success: function (res) {if (res.statusCode == 200) {if (res.data.errno == 501) {// 清除登錄相關(guān)內(nèi)容try {wx.removeStorageSync('userInfo');wx.removeStorageSync('token');} catch (e) {// Do something when catch error}// 切換到登錄頁面wx.navigateTo({url: '/pages/ucenter/index/index'});} else {resolve(res.data);}} else {reject(res.errMsg);}},fail: function (err) {reject(err)}})}); }小程序頁面js的使用
user.loginByWeixin(e.detail.userInfo).then(res => {app.globalData.hasLogin = true;this.onShow(); }).catch((err) => {app.globalData.hasLogin = false;util.showErrorToast('微信登錄失敗'); });關(guān)于登錄就到這里了。可以打開我的GitHub源碼其分別對應(yīng)
Github–ShareNotes
-
實體類和mapper還有sql
share-Notes-db----->sql文件夾
share-Notes-db----->src–>main–>java–>cn.sharenotes.db---->domain/mapper/model -
wxconfig配置
share-Notes-core----->src–>main–>java–>cn.sharenotes.core---->config
share-Notes-core----->src–>main–>java–>cn.sharenotes.core---->utils
share-Notes-core----->src–>main–>resources---->wxconfig.properties(我這里不是yml) -
controller使用
share-Notes-wx-api----->src–>main–>java–>cn.sharenotes.wxapi---->web
share-Notes-wx-api----->src–>main–>java–>cn.sharenotes.wxapi---->service
- 微信小程序
shareNotes---->config—>api.js 地址配置
shareNotes---->utils—>user.js /utils工具類
shareNotes---->page---->uncenter---->index—>index.js 登錄js
用binarywang的工具類使用過濾
我們回到wxconfig中
重新添加上wxmaSecCheckService
@Slf4j @Configuration @PropertySource(value = "classpath:wxconf.properties") public class WxConfig {@Value("${APP_ID}")private String appId;@Value("${APP_SERCET}")private String appSecret;@Beanpublic WxMaConfig wxMaConfig() {WxMaInMemoryConfig config = new WxMaInMemoryConfig();config.setAppid(appId);config.setSecret(appSecret);log.info("id"+appId);log.info("key"+appSecret);return config;}@Beanpublic WxMaService wxMaService(WxMaConfig maConfig) {WxMaService service = new WxMaServiceImpl();service.setWxMaConfig(maConfig);return service;}@Beanpublic WxMaSecCheckService wxMaSecCheckService(){WxMaSecCheckService wxMaSecCheckService = new WxMaSecCheckServiceImpl(wxMaService(wxMaConfig()));return wxMaSecCheckService;}@Beanpublic WxPayConfig wxPayConfig() {WxPayConfig payConfig = new WxPayConfig();payConfig.setAppId(appId);payConfig.setTradeType("JSAPI");payConfig.setSignType("MD5");return payConfig;}@Beanpublic WxPayService wxPayService(WxPayConfig payConfig) {WxPayService wxPayService = new WxPayServiceImpl();wxPayService.setConfig(payConfig);return wxPayService;} }使用
文字過濾
@Autowiredprivate WxMaSecCheckService wxMaSecCheckService;if(!wxMaSecCheckService.checkMessage(JacksonUtil.parseString(body, "name"))){ResponseUtil.fail(500,"違法違規(guī)標題");}圖片
wxMaSecCheckService.checkImage(f)他的方法都是返回布爾值。
分析原理
https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/sec-check/security.imgSecCheck.html這里的本質(zhì)是使用微信開發(fā)文檔中的api
POST https://api.weixin.qq.com/wxa/img_sec_check?access_token=ACCESS_TOKEN通過post請求這段api返回是否是內(nèi)容安全。
官方文檔很詳細了。返回errcode和errMsg
通過為0失敗為87014
Post的body中放圖片。
不過我們看一下整句api,后面有一個accessToken。
這個accesstoken怎么獲取呢。
回到官方文檔
獲取小程序全局唯一后臺接口調(diào)用憑據(jù)(access_token)。調(diào)調(diào)用絕大多數(shù)后臺接口時都需使用 access_token,開發(fā)者需要進行妥善保存。
api為
GET https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET這里需要我們的appid和appsecret。
請求后可以過去accesstoken。但是請求的token只會支持2個小時,后他將重新生成。2個小時后得再次請求。
tips
微信官方api中關(guān)于服務(wù)端的有趣api很多,可以查閱。雖然有請求次數(shù)限制。但是已經(jīng)足夠了
總結(jié)
以上是生活随笔為你收集整理的Springboot简便的配置微信小程序的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 有一种选择叫女程(2)
- 下一篇: 台球能做什么?