赞
踩
package com.ckm.ball.utils; import java.util.Base64; import java.util.Date; import io.jsonwebtoken.Claims; import io.jsonwebtoken.JwtBuilder; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.SignatureException; public class JwtUtilChat { private static final String SECRET_KEY = "secret"; // 秘钥 private static final long VALID_TIME = 30 * 60 * 1000; // 有效时间30分钟 /** * 生成Token * @param openId 用户唯一标识 * @return Token */ public static String generateToken(String openId) { Date now = new Date(); Date expireTime = new Date(now.getTime() + VALID_TIME); byte[] secretBytes = Base64.getEncoder().encode(SECRET_KEY.getBytes()); JwtBuilder builder = Jwts.builder() .setId(openId) .setIssuedAt(now) .setExpiration(expireTime) .setSubject("jwt") .signWith(SignatureAlgorithm.HS256, secretBytes); return builder.compact(); } /** * 更新Token * @param token 原始Token * @return 新的Token * @throws SignatureException 无效Token异常 */ public static String refreshToken(String token) throws SignatureException { Claims claims = Jwts.parser() .setSigningKey(Base64.getEncoder().encode(SECRET_KEY.getBytes())) .parseClaimsJws(token) .getBody(); Date now = new Date(); Date expireTime = new Date(now.getTime() + VALID_TIME); JwtBuilder builder = Jwts.builder() .setId(claims.getId()) .setIssuedAt(now) .setExpiration(expireTime) .setSubject("jwt") .signWith(SignatureAlgorithm.HS256, Base64.getEncoder().encode(SECRET_KEY.getBytes())); return builder.compact(); } /** * 验证Token是否过期 如果返回false则代表token没有过期,如果返回true则代表token已经过期 * @param token 要验证的Token * @return 是否过期 * @throws SignatureException 无效Token异常 */ public static boolean isTokenExpired(String token) throws SignatureException { Claims claims = Jwts.parser() .setSigningKey(Base64.getEncoder().encode(SECRET_KEY.getBytes())) .parseClaimsJws(token) .getBody(); return claims.getExpiration().before(new Date()); } /** * 根据Token获取openId * @param token 要解析的Token * @return openId * @throws SignatureException 无效Token异常 */ public static String getOpenIdFromToken(String token) throws SignatureException { Claims claims = Jwts.parser() .setSigningKey(Base64.getEncoder().encode(SECRET_KEY.getBytes())) .parseClaimsJws(token) .getBody(); return claims.getId(); } }
微信小程序js代码
微信小程序点击登录按钮调用该方法
login() { let that = this //调用wx登录接口获取openId wx.login({ success: (res) => { //调用getUserCode获取openId getUserCode(res.code).then((data) => { //生成Token userLogin(data.openid).then((result) => { that.setData({ userInfo: result }) //保存到缓存中,后续做判断 wx.setStorageSync('userInfo', result) wx.showLoading() setTimeout(function () { wx.hideLoading() wx.navigateBack({ delta: 0, }) }, 500) }) }) }, }) },
java后端代码
@ApiOperation(value = "获取用户openId",tags = "用户接口") @GetMapping(value="/getUserCode") public JSONObject getUserCode(@RequestParam("code") String code){ System.out.println(code); JSONObject jsonObject = new JSONObject(); try{ if(code == null || code.equals("")){ jsonObject.put("errcode","10001"); jsonObject.put("errmsg","数据传输错误,code为空"); return jsonObject; } Map<String, String> data = new HashMap<String, String>(); data.put("appid", "wx4ab3xxxxxxxx52b"); data.put("secret", "33ae24be19xxxxxxxxxx77b990"); data.put("js_code", code); data.put("grant_type", "authorization_code"); String response = HttpRequest.get("https://api.weixin.qq.com/sns/jscode2session").form(data).body(); jsonObject = JSON.parseObject(response); jsonObject.remove("session_key"); //删除session_key 避免泄露用户信息 }catch (Exception ex){ jsonObject.put("errcode","10004"); jsonObject.put("errmsg","获取失败,发生未知错误"); } System.out.println(jsonObject); return jsonObject; } @GetMapping("/userLogin") @ApiOperation(value = "用户登录",tags = "用户接口") public BallUserVo userLogin(@RequestParam("openId") String openId){ return ballUserService.userLogin(openId); }
userLogin实现类方法
@Override public BallUserVo userLogin(String openId) { //根据openId生成Token String token = JwtUtilChat.generateToken(openId); BallUser ballUser = this.getOne(new LambdaQueryWrapper<BallUser>().eq(BallUser::getOpenId, openId)); //没有记录,首次授权登录 if (ObjectUtils.isEmpty(ballUser)){ BallUser newBallUser = new BallUser(); newBallUser.setCreateTime(new Date()); newBallUser.setAvatarUrl(avatarUrl); newBallUser.setNickName("微信用户"+RandomStringGenerator.generateRandomString()); newBallUser.setOpenId(openId); this.save(newBallUser); BallUserVo ballUserVo = new BallUserVo(); //token赋值 ballUserVo.setToken(token); BeanUtils.copyProperties(newBallUser,ballUserVo); return ballUserVo; } BallUserVo ballUserVo = new BallUserVo(); //token赋值 ballUserVo.setToken(token); BeanUtils.copyProperties(ballUser,ballUserVo); return ballUserVo; }
封装了wx的request请求,每次发起请求的时候都走一遍更新Token的接口/user/updateTokenTime,如果接口返回offline则代表已经过期,则跳转到登录页面,否则就是没有过期,则更新缓存userInfo中的token。
微信小程序js代码
//经过token验证 function request(options) { let userInfo = wx.getStorageSync('userInfo'); if (userInfo == null || userInfo == undefined || userInfo == '') { const userInfoNologin = { avatarUrl: "/images/nologin.png", nickName: "授权登录" }; wx.setStorageSync('userInfo', userInfoNologin); console.log("请登录!"); wx.navigateTo({ url: '/pages/login/login', }); return Promise.reject("用户未登录"); } else { const API_ROOT = "http://192.168.1.2:8088"; return new Promise((resolve, reject) => { refreshToken(API_ROOT, userInfo) .then(() => { wx.request({ ...options, url: API_ROOT + options.url, success: function (res) { if (res.statusCode === 200) { resolve(res.data); } else { reject(res.errMsg); } }, fail: function (err) { reject(err.errMsg); }, }); }) .catch(error => { reject(error); }); }); } } // 更新 Token function refreshToken(url, userInfo) { return new Promise((resolve, reject) => { wx.request({ url: url + "/user/updateTokenTime", method: 'GET', data: { 'token': userInfo.token }, success: res => { if (res.data == "offline") { const userInfoNologin = { avatarUrl: "/images/nologin.png", nickName: "授权登录" }; wx.setStorageSync('userInfo', userInfoNologin); wx.navigateTo({ url: '/pages/login/login', }); reject("用户已离线"); } else { userInfo.token = res.data; wx.setStorageSync('userInfo', userInfo); resolve(); } }, fail: error => { console.log(error); reject(error); } }); }); } export default request;
java后端代码
@GetMapping("/updateTokenTime")
@ApiOperation(value = "更新token",tags = "用户接口")
public String updateTokenTime(@RequestParam("token") String token){
return ballUserService.updateTokenTime(token);
}
updateTokenTime实现类方法
@Override public String updateTokenTime(String token) { // 判断token是否为空 if (StringUtils.isEmpty(token)) { // Token为空,则返回未认证错误 return "offline"; } try { //判断token是否过期,返回false则代表没有过期,反正true则代表过期 boolean flag = JwtUtilChat.isTokenExpired(token); if (!flag){ // 更新token return JwtUtilChat.refreshToken(token); }else{ return "offline"; } }catch (Exception e){ //异常代表token已经过期 return "offline"; } }
如果你想有一些接口不要经过这个request封装,就比如用户没有登录是可以进入到首页等页面的,开放一些允许用户没有登录状态可以访问的接口。我们可以再封装一个request,专门开放接口不经过Token验证。
//不经过token验证 function onlineRequest(options) { const API_ROOT = "http://192.168.1.2:8088"; return new Promise((resolve, reject) => { wx.request({ ...options, url: API_ROOT + options.url, // 在请求的 URL 前加上 API_ROOT success: function (res) { if (res.statusCode === 200) { resolve(res.data); } else { reject(res.errMsg); } }, fail: function (err) { reject(err.errMsg); }, }); }); } export default onlineRequest;
API怎么编写?
import request from "../../utils/request"; //经过token的封装 import onlineRequest from "../../utils/onlineRequest"; //不经过token的封装 //用户没有登录,但是可以访问这个接口 export function getBallCourtInfoByPoint(lat,lng) { return onlineRequest({ url: '/court/getBallCourtInfoByPoint', method: 'get', data:{ lat, lng } }); } //用户必须登录才能访问这个接口 export function getBallCourtInfoByPointById(id) { return request({ url: '/court/getBallCourtInfoByPointById', method: 'get', data:{id} }); }
接口怎么调用?
import { getBallCourtInfoByPoint, getBallCourtInfoByPointById } from "../../../api/home/home.js"; getBallCourtInfoByPoint(res){ let that = this //该方法不经过Token验证 getBallCourtInfoByPoint(res.latitude, res.longitude).then((res) => { res.forEach(element => { element['id'] = Number(element.id) element['markerId'] = Number(element.id) element['title'] = element.ballCourtName element['width'] = 45 element['height'] = 45 element['latitude'] = Number(element.lat) element['longitude'] = Number(element.lng) element['lat'] = Number(element.lat) element['lng'] = Number(element.lng) element['iconPath'] = "/images/ball.png" }); that.setData({ markers: res }) }); }, onMarkerTap(e) { let that = this console.log(e); //该方法经过Token验证 getBallCourtInfoByPointById(e.detail.markerId).then((res) => { that.setData({ showPopup: true, ballCourtInfo: res }) }) }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。