实现微信小程序授权登录

      • 1、前端技术栈
        • 1.1、uniapp
        • 1.2、前端封装工具
        • 1.3、Hbuilderx构建uniapp项目
      • 2、后端技术栈
        • 2.1、创建springboot后端项目
        • 2.2、数据库准备
        • 2.3、创建实体类
        • 2.4、后端工具类
        • 2.5、mapper和service接口
        • 2.5、Service实现类
        • 2.6、微信用户的控制层Controller

微信小程序官方登录流程图:

参考微信小程序登录官网文档

1、前端技术栈

1.1、uniapp

使用uniapp构建一套代码多端使用的前端框架项目

1.2、前端封装工具
  • dateUtil.js:
    功能:
    1. 时间日期格式化
    2. 传入日期是否和当前日期的比较
    完整代码:

    // 判断传入日期是否和当前日期比较  const judgeDate=(toDate)=>{return new Date().getTime()-new Date(toDate).getTime();}var timeFormat = function (msTime) {let time = new Date(msTime);let yy = time.getFullYear();let MM = time.getMonth() + 1;let dd = time.getDate();let hh = time.getHours() < 10 " />"0" + time.getHours() : time.getHours();let min =time.getMinutes() < 10 ? "0" + time.getMinutes() : time.getMinutes();let sec =time.getSeconds() < 10 ? "0" + time.getSeconds() : time.getSeconds();return yy + "-" + MM + "-" + dd + " " + hh + ":" + min + ":" + sec;}export {timeFormat,judgeDate}
  • requestUtil.js:
    功能:
    1. 定义公共的url
    2. 后端请求工具封装
    完整代码:

    // 同时发送异步代码的次数let ajaxTimes = 0;// 定义公共的urlconst baseUrl = "http://localhost:8866";/** * 返回baseUrl */export const getBaseUrl = () => {return baseUrl;}/** * 后端请求工具类 * @param {*} params 请求参数 */export const requestUtil = (params) => {let header = {...params.header};// 拼接header 带上tokenheader["token"] = uni.getStorageSync("token");ajaxTimes++;// 显示加载中 效果wx.showLoading({title: "加载中",mask: true});var start = new Date().getTime();// 模拟网络延迟加载while (true)if (new Date().getTime() - start > 1000 * 1) break;return new Promise((resolve, reject) => {wx.request({...params,header: header,url: baseUrl + params.url,success: (result) => {resolve(result.data);},fail: (err) => {uni.showToast({icon: 'error',title: '连接服务器失败',duration: 3000})reject(err);},complete: () => {ajaxTimes--;if (ajaxTimes === 0) {//关闭正在等待的图标wx.hideLoading();}}});})}
  • stringUtil.js:
    功能:
    1. 判断字符串是否为空
    完整代码:

    //判断字符串是否为空export const isEmpty = (str) => {if (str === '' || str.trim().length === 0) {return true} else {return false;}}
1.3、Hbuilderx构建uniapp项目


项目结构:

app.vue中,写两个方法:

  1. 在onLaunch生命周期函数中调用wx.login()获取code(前提是在微信开发者工具中登录微信账号,而且在uniapp中设置微信小程序AppId),code的作用是后端接受到code,通过code参数向微信后台发送请求,它是实现微信临时登录的url中的一个非常重要的参数。
  2. 三个重要参数
  • appid:应用ID
  • secret:应用密钥
  • js_code:前台传给我们的code
  1. wxlogin方法
    携带code参数发送请求给后端来获取token和openid
<script>import {requestUtil} from "./utils/requestUtil.js"export default {onLaunch: function() {console.log('App Launch')wx.login({timeout: 5000,success: (res) => {console.log(res)this.wxlogin(res.code);}});},onShow: function() {console.log('App Show')},onHide: function() {console.log('App Hide')},methods: {/** * 请求后端获取用户token * @param {} code*/async wxlogin(code) {console.log("code=" + code)// 发送请求 获取用户的tokenconst result = await requestUtil({url: "/user/wxlogin",data: {code: code},method: "post"});console.log("token=" + result.token);console.log("openid=" + result.openid);if (result.code == 0) {console.log("登录成功")uni.setStorageSync("token", result.token);uni.setStorageSync("openid", result.openid);} else {console.log("登录失败,报错信息:" + result.msg);uni.showToast({icon: 'error',title: result.msg,duration: 3000})}}}}</script><style>/*每个页面公共css */</style>

2、后端技术栈

  • springboot后端技术框架
  • mybatis-plus数据持久层框架
2.1、创建springboot后端项目

利用idea工具,使用spring initializr初始化创建一个空的springboot项目

springboot版本选择2.3.2.RELEASE。

  1. 修改pom.xml
 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><!-- 连接池 --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.10</version></dependency><!-- mybatis-plus --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.3.2</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.40</version></dependency><!-- JWT --><dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.2.0</version></dependency><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.5</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!-- spring boot redis 缓存引入 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- lettuce pool 缓存连接池 --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency><!-- hutool工具类--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.3.3</version></dependency><!-- 验证码依赖--><dependency><groupId>com.github.axet</groupId><artifactId>kaptcha</artifactId><version>0.0.9</version></dependency><!-- 添加Httpclient支持 --><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.2</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>2.3.2.RELEASE</version></plugin></plugins></build>
  1. 创建application.yml
server:port: 8866servlet:context-path: /spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/db-wxlogin" />=Asia/Shanghaiusername: rootpassword: 123456mybatis-plus:global-config:db-config:id-type: autoconfiguration:map-underscore-to-camel-case: trueauto-mapping-behavior: fulllog-impl: org.apache.ibatis.logging.stdout.StdOutImplmapper-locations: classpath:mapper/*.xmlweixin:jscode2sessionUrl: https://api.weixin.qq.com/sns/jscode2sessionappid: wxa4aa78831ea93858#修改自己的微信小程序 appIdsecret: a2efb3b611d96b2dee615b7a4dee451a #修改自己的微信小程序 appSecret
2.2、数据库准备

创建名称db-wxlogin的数据库

创建t_wxuserinfo数据表
CREATE TABLE `t_wxuserinfo` (`id` int NOT NULL AUTO_INCREMENT COMMENT '用户编号',`openid` varchar(30) DEFAULT NULL COMMENT '用户唯一标识',`nick_name` varchar(50) DEFAULT NULL COMMENT '用户昵称',`avatar_url` varchar(200) DEFAULT NULL COMMENT '用户头像图片的 URL',`register_date` datetime DEFAULT NULL COMMENT '注册日期',`last_login_date` datetime DEFAULT NULL COMMENT '最后登录日期',`status` char(1) DEFAULT '1' COMMENT '状态 0 可用 1 封禁',PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
2.3、创建实体类
  • WxUserInfo类
    对应数据库表t_wxuserinfo
package com.tigerhhzz.springbootvote.entity;import com.baomidou.mybatisplus.annotation.TableField;import com.baomidou.mybatisplus.annotation.TableName;import com.fasterxml.jackson.databind.annotation.JsonSerialize;import lombok.Data;import java.io.Serializable;import java.util.Date;/** * 微信用户信息实体 * * @author tigerhhzz * @date 2023/5/17 15:34 */@TableName("t_wxUserInfo")@Datapublic class WxUserInfo implements Serializable {private Integer id; // 用户编号private String openid; // 用户唯一标识private String nickName="微信用户"; // 用户昵称private String avatarUrl="default.png"; // 用户头像图片的 URL@JsonSerialize(using=CustomDateTimeSerializer.class)private Date registerDate; // 注册日期@JsonSerialize(using=CustomDateTimeSerializer.class)private Date lastLoginDate; // 最后登录日期private String status="0"; // 用户状态 状态 0 可用 1 封禁//查询时,则不返回该字段的值设置该字段在数据库表中不存在@TableField(select = false,exist = false)private String code; // 微信用户code 前端传来的}
  • 页面响应实体类
package com.tigerhhzz.springbootvote.entity;import java.util.HashMap;import java.util.Map;/** * 页面响应entity * * @author tigerhhzz * @date 2023/5/17 15:34 */public class R extends HashMap<String, Object> {private static final long serialVersionUID = 1L;public R() {put("code", 0);}public static R error() {return error(500, "未知异常,请联系管理员");}public static R error(String msg) {return error(500, msg);}public static R error(int code, String msg) {R r = new R();r.put("code", code);r.put("msg", msg);return r;}public static R ok(String msg) {R r = new R();r.put("msg", msg);return r;}public static R ok(Map<String, Object> map) {R r = new R();r.putAll(map);return r;}public static R ok() {return new R();}@Overridepublic R put(String key, Object value) {super.put(key, value);return this;}}
  • jwt验证信息
package com.tigerhhzz.springbootvote.entity;import io.jsonwebtoken.Claims;/** * jwt验证信息 * * @author tigerhhzz * @date 2023/5/17 17:05 */public class CheckResult {private int errCode;private boolean success;private Claims claims;public int getErrCode() {return errCode;}public void setErrCode(int errCode) {this.errCode = errCode;}public boolean isSuccess() {return success;}public void setSuccess(boolean success) {this.success = success;}public Claims getClaims() {return claims;}public void setClaims(Claims claims) {this.claims = claims;}}
  • 微信小程序配置文件
package com.tigerhhzz.springbootvote.properties;import lombok.Data;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.stereotype.Component;/** * 微信小程序配置文件 * * @author tigerhhzz * @date 2023/5/17 16:55 */@Component@ConfigurationProperties(prefix = "weixin")@Datapublic class WeixinProperties {private String jscode2sessionUrl; // 登录凭证校验请求地址private String appid; // 小程序 appIdprivate String secret; // 小程序 appSecret}
2.4、后端工具类
  • 日期工具类 DateUtil
package com.tigerhhzz.springbootvote.util;import java.text.SimpleDateFormat;import java.util.Date;/** * 日期工具类 * * @author tigerhhzz * @date 2023/5/17 15:34 */public class DateUtil {/** * 日期对象转字符串 * @param date * @param format * @return */public static String formatDate(Date date,String format){String result="";SimpleDateFormat sdf=new SimpleDateFormat(format);if(date!=null){result=sdf.format(date);}return result;}/** * 字符串转日期对象 * @param str * @param format * @return * @throws Exception */public static Date formatString(String str,String format) throws Exception{if(StringUtil.isEmpty(str)){return null;}SimpleDateFormat sdf=new SimpleDateFormat(format);return sdf.parse(str);}public static String getCurrentDateStr(){Date date=new Date();SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMddhhmmssSSSSSSSSS");return sdf.format(date);}public static String getCurrentDatePath()throws Exception{Date date=new Date();SimpleDateFormat sdf=new SimpleDateFormat("yyyy/MM/dd/");return sdf.format(date);}public static void main(String[] args) {try {System.out.println(getCurrentDateStr());} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
  • httpClient 工具类
package com.tigerhhzz.springbootvote.util;import org.apache.http.HttpEntity;import org.apache.http.HttpResponse;import org.apache.http.NameValuePair;import org.apache.http.client.ClientProtocolException;import org.apache.http.client.HttpClient;import org.apache.http.client.config.RequestConfig;import org.apache.http.client.entity.UrlEncodedFormEntity;import org.apache.http.client.methods.CloseableHttpResponse;import org.apache.http.client.methods.HttpGet;import org.apache.http.client.methods.HttpPost;import org.apache.http.conn.ssl.DefaultHostnameVerifier;import org.apache.http.conn.util.PublicSuffixMatcher;import org.apache.http.conn.util.PublicSuffixMatcherLoader;import org.apache.http.entity.StringEntity;import org.apache.http.impl.client.CloseableHttpClient;import org.apache.http.impl.client.HttpClients;import org.apache.http.message.BasicNameValuePair;import org.apache.http.util.EntityUtils;import org.springframework.stereotype.Component;import java.io.*;import java.net.URL;import java.util.ArrayList;import java.util.List;import java.util.Map;/** * httpClient 工具类 * * @author tigerhhzz * @date 2023/5/17 15:34 */@Componentpublic class HttpClientUtil {/** * 默认参数设置 * setConnectTimeout:设置连接超时时间,单位毫秒。 * setConnectionRequestTimeout:设置从connect Manager获取Connection 超时时间,单位毫秒。 * setSocketTimeout:请求获取数据的超时时间,单位毫秒。访问一个接口,多少时间内无法返回数据,就直接放弃此次调用。 暂时定义15分钟 */private RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(600000).setConnectTimeout(600000).setConnectionRequestTimeout(600000).build();/** * 静态内部类---作用:单例产生类的实例 * @author Administrator * */private static class LazyHolder { private static final HttpClientUtil INSTANCE = new HttpClientUtil(); }private HttpClientUtil(){}public static HttpClientUtil getInstance(){return LazyHolder.INSTANCE;}/** * 发送 post请求 * @param httpUrl 地址 */public String sendHttpPost(String httpUrl) {HttpPost httpPost = new HttpPost(httpUrl);// 创建httpPostreturn sendHttpPost(httpPost);}/** * 发送 post请求 * @param httpUrl 地址 * @param params 参数(格式:key1=value1&key2=value2) */public String sendHttpPost(String httpUrl, String params) {HttpPost httpPost = new HttpPost(httpUrl);// 创建httpPosttry {//设置参数StringEntity stringEntity = new StringEntity(params, "UTF-8");stringEntity.setContentType("application/x-www-form-urlencoded");httpPost.setEntity(stringEntity);} catch (Exception e) {e.printStackTrace();}return sendHttpPost(httpPost);}/** * 发送 post请求 * @param httpUrl 地址 * @param maps 参数 */public String sendHttpPost(String httpUrl, Map<String, String> maps) {HttpPost httpPost = new HttpPost(httpUrl);// 创建httpPost// 创建参数队列List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();for (String key : maps.keySet()) {nameValuePairs.add(new BasicNameValuePair(key, maps.get(key)));}try {httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs, "UTF-8"));} catch (Exception e) {e.printStackTrace();}return sendHttpPost(httpPost);}/** * 发送Post请求 * @param httpPost * @return */private String sendHttpPost(HttpPost httpPost) {CloseableHttpClient httpClient = null;CloseableHttpResponse response = null;HttpEntity entity = null;String responseContent = null;try {// 创建默认的httpClient实例httpClient = HttpClients.createDefault();httpPost.setConfig(requestConfig);// 执行请求long execStart = System.currentTimeMillis();response = httpClient.execute(httpPost);long execEnd = System.currentTimeMillis();System.out.println("=================执行post请求耗时:"+(execEnd-execStart)+"ms");long getStart = System.currentTimeMillis();entity = response.getEntity();responseContent = EntityUtils.toString(entity, "UTF-8");long getEnd = System.currentTimeMillis();System.out.println("=================获取响应结果耗时:"+(getEnd-getStart)+"ms");} catch (Exception e) {e.printStackTrace();} finally {try {// 关闭连接,释放资源if (response != null) {response.close();}if (httpClient != null) {httpClient.close();}} catch (IOException e) {e.printStackTrace();}}return responseContent;}/** * 发送 get请求 * @param httpUrl */public String sendHttpGet(String httpUrl) {HttpGet httpGet = new HttpGet(httpUrl);// 创建get请求return sendHttpGet(httpGet);}/** * 发送 get请求Https * @param httpUrl */public String sendHttpsGet(String httpUrl) {HttpGet httpGet = new HttpGet(httpUrl);// 创建get请求return sendHttpsGet(httpGet);}/** * 发送Get请求 * @param httpGet * @return */private String sendHttpGet(HttpGet httpGet) {CloseableHttpClient httpClient = null;CloseableHttpResponse response = null;HttpEntity entity = null;String responseContent = null;try {// 创建默认的httpClient实例.httpClient = HttpClients.createDefault();httpGet.setConfig(requestConfig);// 执行请求response = httpClient.execute(httpGet);entity = response.getEntity();responseContent = EntityUtils.toString(entity, "UTF-8");} catch (Exception e) {e.printStackTrace();} finally {try {// 关闭连接,释放资源if (response != null) {response.close();}if (httpClient != null) {httpClient.close();}} catch (IOException e) {e.printStackTrace();}}return responseContent;}/** * 发送Get请求Https * @param httpGet * @return */private String sendHttpsGet(HttpGet httpGet) {CloseableHttpClient httpClient = null;CloseableHttpResponse response = null;HttpEntity entity = null;String responseContent = null;try {// 创建默认的httpClient实例.PublicSuffixMatcher publicSuffixMatcher = PublicSuffixMatcherLoader.load(new URL(httpGet.getURI().toString()));DefaultHostnameVerifier hostnameVerifier = new DefaultHostnameVerifier(publicSuffixMatcher);httpClient = HttpClients.custom().setSSLHostnameVerifier(hostnameVerifier).build();httpGet.setConfig(requestConfig);// 执行请求response = httpClient.execute(httpGet);entity = response.getEntity();responseContent = EntityUtils.toString(entity, "UTF-8");} catch (Exception e) {e.printStackTrace();} finally {try {// 关闭连接,释放资源if (response != null) {response.close();}if (httpClient != null) {httpClient.close();}} catch (IOException e) {e.printStackTrace();}}return responseContent;}/** * 发送xml数据 * @param url * @param xmlData * @return * @throws ClientProtocolException * @throws IOException */public static HttpResponse sendXMLDataByPost(String url, String xmlData)throws ClientProtocolException, IOException {HttpClient httpClient = HttpClients.createDefault();HttpPost httppost = new HttpPost(url);StringEntity entity = new StringEntity(xmlData);httppost.setEntity(entity);httppost.setHeader("Content-Type", "text/xml;charset=UTF-8");HttpResponse response = httpClient.execute(httppost);return response;}/** * 获得响应HTTP实体内容 * * @param response * @return * @throws IOException * @throws UnsupportedEncodingException */public static String getHttpEntityContent(HttpResponse response) throws IOException, UnsupportedEncodingException {HttpEntity entity = response.getEntity();if (entity != null) {InputStream is = entity.getContent();BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"));String line = br.readLine();StringBuilder sb = new StringBuilder();while (line != null) {sb.append(line + "\n");line = br.readLine();}return sb.toString();}return "";}}
  • jwt加密和解密的工具类
package com.tigerhhzz.springbootvote.util;import com.tigerhhzz.springbootvote.constant.JwtConstant;import com.tigerhhzz.springbootvote.entity.CheckResult;import io.jsonwebtoken.*;import org.bouncycastle.util.encoders.Base64;import javax.crypto.SecretKey;import javax.crypto.spec.SecretKeySpec;import java.util.Date;/** * jwt加密和解密的工具类 * * @author tigerhhzz * @date 2023/5/17 15:34 */public class JwtUtils {/** * 签发JWT * @param id * @param subject 可以是JSON数据 尽可能少 * @param ttlMillis * @return */public static String createJWT(String id, String subject, long ttlMillis) {SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;long nowMillis = System.currentTimeMillis();Date now = new Date(nowMillis);SecretKey secretKey = generalKey();JwtBuilder builder = Jwts.builder().setId(id).setSubject(subject) // 主题.setIssuer("tigerhhzz") // 签发者.setIssuedAt(now)// 签发时间.signWith(signatureAlgorithm, secretKey); // 签名算法以及密匙if (ttlMillis >= 0) {long expMillis = nowMillis + ttlMillis;Date expDate = new Date(expMillis);builder.setExpiration(expDate); // 过期时间}return builder.compact();}/** * 生成jwt token * @param username * @return */public static String genJwtToken(String username){return createJWT(username,username,60*60*1000);}/** * 验证JWT * @param jwtStr * @return */public static CheckResult validateJWT(String jwtStr) {CheckResult checkResult = new CheckResult();Claims claims = null;try {claims = parseJWT(jwtStr);checkResult.setSuccess(true);checkResult.setClaims(claims);} catch (ExpiredJwtException e) {checkResult.setErrCode(JwtConstant.JWT_ERRCODE_EXPIRE);checkResult.setSuccess(false);} catch (SignatureException e) {checkResult.setErrCode(JwtConstant.JWT_ERRCODE_FAIL);checkResult.setSuccess(false);} catch (Exception e) {checkResult.setErrCode(JwtConstant.JWT_ERRCODE_FAIL);checkResult.setSuccess(false);}return checkResult;}/** * 生成加密Key * @return */public static SecretKey generalKey() {byte[] encodedKey = Base64.decode(JwtConstant.JWT_SECERT);SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");return key;}/** * 解析JWT字符串 * @param jwt * @return * @throws Exception */public static Claims parseJWT(String jwt) {SecretKey secretKey = generalKey();return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwt).getBody();}public static void main(String[] args) throws InterruptedException {//小明失效 10sString sc = createJWT("1","小明", 60 * 60 * 1000);System.out.println(sc);System.out.println(validateJWT(sc).getErrCode());System.out.println(validateJWT(sc).getClaims().getId());System.out.println(validateJWT(sc).getClaims().getSubject());//Thread.sleep(3000);System.out.println(validateJWT(sc).getClaims());Claims claims = validateJWT(sc).getClaims();String sc2 = createJWT(claims.getId(),claims.getSubject(), JwtConstant.JWT_TTL);System.out.println(sc2);}}
  • 字符串工具类
package com.tigerhhzz.springbootvote.util;import java.util.ArrayList;import java.util.List;import java.util.Random;/** * 字符串工具类 * @author* */public class StringUtil {/** * 判断是否是空 * @param str * @return */public static boolean isEmpty(String str){if(str==null||"".equals(str.trim())){return true;}else{return false;}}/** * 判断是否不是空 * @param str * @return */public static boolean isNotEmpty(String str){if((str!=null)&&!"".equals(str.trim())){return true;}else{return false;}}/** * 格式化模糊查询 * @param str * @return */public static String formatLike(String str){if(isNotEmpty(str)){return "%"+str+"%";}else{return null;}}/** * 过滤掉集合里的空格 * @param list * @return */public static List<String> filterWhite(List<String> list){List<String> resultList=new ArrayList<String>();for(String l:list){if(isNotEmpty(l)){resultList.add(l);}}return resultList;}/** * 去除html标签 */public static String stripHtml(String content) { // 

段落替换为换行 content = content.replaceAll("

", "\r\n"); //

替换为换行
content = content.replaceAll("
"
, "\r\n"); // 去掉其它的之间的东西 content = content.replaceAll("\\", ""); // 去掉空格 content = content.replaceAll(" ", ""); return content; }/** * 生成六位随机数 * @return */public static String genSixRandomNum(){Random random = new Random();String result="";for (int i=0;i<6;i++){result+=random.nextInt(10);}return result;}/** * 生成由[A-Z,0-9]生成的随机字符串 * @param length欲生成的字符串长度 * @return */public static String getRandomString(int length){Random random = new Random();StringBuffer sb = new StringBuffer();for(int i = 0; i < length; ++i){int number = random.nextInt(2);long result = 0;switch(number){case 0:result = Math.round(Math.random() * 25 + 65);sb.append(String.valueOf((char)result));break;case 1:sb.append(String.valueOf(new Random().nextInt(10)));break;}}return sb.toString();}}

2.5、mapper和service接口

微信用户mapper —WxUserInfoMapper

package com.tigerhhzz.springbootvote.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.tigerhhzz.springbootvote.entity.WxUserInfo;import org.springframework.stereotype.Repository;/** * 微信用户mapper * * @author tigerhhzz * @date 2023/5/17 15:26 */@Repositorypublic interface WxUserInfoMapper extends BaseMapper<WxUserInfo> {}

微信用户Service接口 —WxUserInfoService

package com.tigerhhzz.springbootvote.service;import com.baomidou.mybatisplus.extension.service.IService;import com.tigerhhzz.springbootvote.entity.WxUserInfo;/** * 微信用户Service接口 * * @author tigerhhzz * @date 2023/5/17 15:34 */public interface WxUserInfoService extends IService<WxUserInfo> {}
2.5、Service实现类

WxUserInfoServiceImpl—- 实现WxUserInfoService 接口,并继承ServiceImpl实现类的泛型WxUserInfoMapper和WxUserInfo。

package com.tigerhhzz.springbootvote.service.impl;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;import com.tigerhhzz.springbootvote.entity.WxUserInfo;import com.tigerhhzz.springbootvote.mapper.WxUserInfoMapper;import com.tigerhhzz.springbootvote.service.WxUserInfoService;import org.springframework.stereotype.Service;/** * 微信用户Service实现类 * * @author tigerhhzz * @date 2023/5/17 15:34 */@Service //("wxUserInfoService")public class WxUserInfoServiceImpl extends ServiceImpl<WxUserInfoMapper, WxUserInfo> implements WxUserInfoService {//@Autowired//private WxUserInfoMapper wxUserInfoMapper;}
2.6、微信用户的控制层Controller

两个控制层请求接口

  1. /user/wxlogin

功能: 通过前端发送请求携带的参数code以及后端配置文件中的微信小程序appid和微信小程序密钥,后端拼接url向微信后台发送请求。

 String jscode2sessionUrl=weixinProperties.getJscode2sessionUrl()+"?appid="+weixinProperties.getAppid()+"&secret="+weixinProperties.getSecret()+"&js_code="+wxUserInfo.getCode()+"&grant_type=authorization_code";// https://api.weixin.qq.com/sns/jscode2session?appid=wxa4de78832ea93858&secret=a2efb3b602d96b2dee615b7a4dee451a&js_code=0b1JwPkl2xqHkb4VEjml2vVdua3JwPkq&grant_type=authorization_code
//后端向微信后台送发请求 获取openidString result = httpClientUtil.sendHttpGet(jscode2sessionUrl);System.out.println(result); //结果:{"session_key":"TPTXzC9MOe1owBJ8zrSWTw==","openid":"o2yqx5PBEW-ezFHA24ASqP0Lk1M0"}

通过拿到的openid,去数据库查询对应用户信息,如果没有openid的用户,进行新增操作;
如果存在openid的用户,进行更新操作。

最后利用jwt工具类生成token,返回前端

 // 利用jwt生成token返回到前端String token = JwtUtils.createJWT(openid, wxUserInfo.getNickName(), JwtConstant.JWT_TTL);
Map<String,Object> resultMap=new HashMap<>();resultMap.put("token",token);resultMap.put("openid",openid);return R.ok(resultMap);
  1. /user/getUserInfo

功能: 通过前端发送请求,请求头中携带token参数,后端接受到token,然后进行token验证,拿到openid,通过openid去数据库中查询用户信息,并返回前端

//token验证Claims claims = JwtUtils.validateJWT(token).getClaims();

获取当前微信登录用户信息:


WeixinUserController 完整代码:

package com.tigerhhzz.springbootvote.controller;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.JSONObject;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;import com.tigerhhzz.springbootvote.constant.JwtConstant;import com.tigerhhzz.springbootvote.entity.R;import com.tigerhhzz.springbootvote.entity.WxUserInfo;import com.tigerhhzz.springbootvote.properties.WeixinProperties;import com.tigerhhzz.springbootvote.service.WxUserInfoService;import com.tigerhhzz.springbootvote.util.DateUtil;import com.tigerhhzz.springbootvote.util.HttpClientUtil;import com.tigerhhzz.springbootvote.util.JwtUtils;import com.tigerhhzz.springbootvote.util.StringUtil;import io.jsonwebtoken.Claims;import org.apache.commons.io.FileUtils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestHeader;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import org.springframework.web.multipart.MultipartFile;import java.io.File;import java.util.Date;import java.util.HashMap;import java.util.List;import java.util.Map;/** * 微信用户Controller * * @author tigerhhzz * @date 2023/5/17 15:34 */@RequestMapping("/user")@RestControllerpublic class WeixinUserController {@Autowiredprivate WxUserInfoService wxUserInfoService;@Autowiredprivate WeixinProperties weixinProperties;@Autowiredprivate HttpClientUtil httpClientUtil;/** * 微信用户登录 * @return */@RequestMapping("/wxlogin")public R wxLogin(@RequestBody WxUserInfo wxUserInfo){//拼接后端发送请求的URL 例如 https://api.weixin.qq.com/sns/jscode2session" />String jscode2sessionUrl=weixinProperties.getJscode2sessionUrl()+"?appid="+weixinProperties.getAppid()+"&secret="+weixinProperties.getSecret()+"&js_code="+wxUserInfo.getCode()+"&grant_type=authorization_code";System.out.println(jscode2sessionUrl);//后端向微信后台送发请求 获取openidString result = httpClientUtil.sendHttpGet(jscode2sessionUrl);System.out.println(result); //结果:{"session_key":"TPTXzC9MOe1owBJ8zrSWTw==","openid":"o2yqx5PBEW-ezFHA24ASqP0Lk1M0"}//JSONObject jsonObject= JSON.parseObject(result);//转换成objectString openid = jsonObject.get("openid").toString();//获取object中openid字段的值;System.out.println(openid);// 插入用户到数据库假如说 用户不存在 我们插入用户如果用户存在 我们更新用户WxUserInfo resultWxUserInfo = wxUserInfoService.getOne(new QueryWrapper<WxUserInfo>().eq("openid", openid));if(resultWxUserInfo==null){System.out.println("不存在 插入用户");wxUserInfo.setOpenid(openid);wxUserInfo.setRegisterDate(new Date());wxUserInfo.setLastLoginDate(new Date());wxUserInfoService.save(wxUserInfo);}else{System.out.println("存在 更新用户");// resultWxUserInfo.setNickName(wxUserInfo.getNickName());// resultWxUserInfo.setAvatarUrl(wxUserInfo.getAvatarUrl());resultWxUserInfo.setLastLoginDate(new Date());wxUserInfoService.updateById(resultWxUserInfo);}if(resultWxUserInfo!=null && resultWxUserInfo.getStatus().equals("1")){ // 被禁用return R.error(400,"用户被禁用,具体请联系管理员!");}else{// 利用jwt生成token返回到前端String token = JwtUtils.createJWT(openid, wxUserInfo.getNickName(), JwtConstant.JWT_TTL);Map<String,Object> resultMap=new HashMap<>();resultMap.put("token",token);resultMap.put("openid",openid);return R.ok(resultMap);}}/** * 获取当前用户信息 * @return */@RequestMapping("/getUserInfo")public R getUserInfo(@RequestHeader String token){System.out.println("/getUserInfo----token="+token);Claims claims = JwtUtils.validateJWT(token).getClaims();System.out.println("openid="+claims.getId());WxUserInfo currentUser = wxUserInfoService.getOne(new QueryWrapper<WxUserInfo>().eq("openid", claims.getId()));Map<String,Object> map=new HashMap<>();map.put("currentUser",currentUser);return R.ok(map);}}

前端源码下载地址 https://download.csdn.net/download/weixin_43025151/87803315

后端源码下载地址:https://download.csdn.net/download/weixin_43025151/87803318