赞
踩
最近在完成一个微信小程序项目,即将上线
欢迎star
Github–ShareNotes
写小程序接口遇到的具体情况
在最早的时候我帮过朋友写过一个花店卖花的程序。
最开始的时候大多数都是通过
微信官方文档API
找到api。然后RestTemplate来访问url。
RestTemplate是Spring提供的用于访问Rest服务的客户端。
@Autowird
private RestTemplate restTemplate;
WxConfig wxConfig= restTemplate.getForObject("https://api.weixin.qq.com/xxxxx/" , WxConfig.class);
十分的烦琐
开始操作
<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>
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 '用户昵称或网络名称', `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 '创建时间', `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='用户表';
本质是 weixin_openid,session_key。
因为登录不需要账户密码。是直接登录。所以如果第一次登录创建用户的时候username和password可以设置为 weixin_openid,或者任意值。
如果weixin-java-miniapp依赖已经导入完成就可以开始配置你的appid和appsecret,其在到微信开发者平台里头
wx:
app-id: xxxxx
app-secret: xxxxx
mch-id: xxxxxxxx
mch-key: xxxxxx
notify-url: http://www.example.com/wx/order/pay-notify
# 商户证书文件路径
# 请参考“商户证书”一节 https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=4_3
key-path: xxxxx
@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) {
@Configuration public class WxConfig { @Autowired private WxProperties properties; @Bean public WxMaConfig wxMaConfig() { WxMaInMemoryConfig config = new WxMaInMemoryConfig(); config.setAppid(properties.getAppId()); config.setSecret(properties.getAppSecret()); return config; } @Bean public WxMaService wxMaService(WxMaConfig maConfig) { WxMaService service = new WxMaServiceImpl(); service.setWxMaConfig(maConfig); return service; } @Bean public 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; } @Bean public WxPayService wxPayService(WxPayConfig payConfig) { WxPayService wxPayService = new WxPayServiceImpl(); wxPayService.setConfig(payConfig); return wxPayService; } }
所有关于微信小程序配置都在这里了。
然后我们可以使用他封装好的wxMaService了。
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
需要放这两个方法
public interface UserService {
//根据openId查询
public User queryByOid(String openId);
//加入user表
public void add(User user);
实现
UserServiceImpl.java
@Override
public User queryByOid(String openId) {
//这里用了Example
UserExample example = new UserExample();
example.or().andWeixinOpenidEqualTo(openId).andDeletedEqualTo(false);
return userMapper.selectOneByExample(example);
}
@Override
public void add(User user) {
user.setAddTime(new Date());
user.setUpdateTime(new Date());
userMapper.insertSelective(user);
}
WxAuthController.java
@Slf4j @RestController @RequestMapping("/wx/auth") @Validated public class WxAuthController { @Autowired private UserService userService; //这里是之前添加依赖里面的方法 @Autowired private 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(); } } // token String 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)是我自己的返回值封装类。
举个例子
public static Object ok(Object data) {
Map<String, Object> obj = new HashMap<String, Object>();
obj.put("errno", 0);
obj.put("errmsg", "成功");
obj.put("data", data);
return obj;
}
接上面的 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() // 设置头部信息 Header .withHeader(map) // 设置 载荷 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_weixin util.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', //刚刚你设置的token 'YOUR-SECRET-TOKEN': wx.getStorageSync('token') }, success: function (res) { if (res.statusCode == 200) { if (res.data.errno == 501) { // 清除登录相关内容 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('微信登录失败');
});
关于登录就到这里了。可以打开我的GitHub源码其分别对应
实体类和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
我们回到wxconfig中
重新添加上wxmaSecCheckService
@Slf4j @Configuration @PropertySource(value = "classpath:wxconf.properties") public class WxConfig { @Value("${APP_ID}") private String appId; @Value("${APP_SERCET}") private String appSecret; @Bean public WxMaConfig wxMaConfig() { WxMaInMemoryConfig config = new WxMaInMemoryConfig(); config.setAppid(appId); config.setSecret(appSecret); log.info("id"+appId); log.info("key"+appSecret); return config; } @Bean public WxMaService wxMaService(WxMaConfig maConfig) { WxMaService service = new WxMaServiceImpl(); service.setWxMaConfig(maConfig); return service; } @Bean public WxMaSecCheckService wxMaSecCheckService(){ WxMaSecCheckService wxMaSecCheckService = new WxMaSecCheckServiceImpl(wxMaService(wxMaConfig())); return wxMaSecCheckService; } @Bean public WxPayConfig wxPayConfig() { WxPayConfig payConfig = new WxPayConfig(); payConfig.setAppId(appId); payConfig.setTradeType("JSAPI"); payConfig.setSignType("MD5"); return payConfig; } @Bean public WxPayService wxPayService(WxPayConfig payConfig) { WxPayService wxPayService = new WxPayServiceImpl(); wxPayService.setConfig(payConfig); return wxPayService; } }
文字过滤
@Autowired
private WxMaSecCheckService wxMaSecCheckService;
if(!wxMaSecCheckService.checkMessage(JacksonUtil.parseString(body, "name"))){
ResponseUtil.fail(500,"违法违规标题");
}
图片
wxMaSecCheckService.checkImage(f)
他的方法都是返回布尔值。
https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/sec-check/security.imgSecCheck.html
这里的本质是使用微信开发文档中的api
POST https://api.weixin.qq.com/wxa/img_sec_check?access_token=ACCESS_TOKEN
通过post请求这段api返回是否是内容安全。
官方文档很详细了。返回errcode和errMsg
通过为0失败为87014
Post的body中放图片。
不过我们看一下整句api,后面有一个accessToken。
这个accesstoken怎么获取呢。
回到官方文档
获取小程序全局唯一后台接口调用凭据(access_token)。调调用绝大多数后台接口时都需使用 access_token,开发者需要进行妥善保存。
api为
GET https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
这里需要我们的appid和appsecret。
请求后可以过去accesstoken。但是请求的token只会支持2个小时,后他将重新生成。2个小时后得再次请求。
微信官方api中关于服务端的有趣api很多,可以查阅。虽然有请求次数限制。但是已经足够了
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。