前端uniapp+后端springboot 详细教程《实现微信小程序授权登录》(附完整前后端项目demo)

这篇具有很好参考价值的文章主要介绍了前端uniapp+后端springboot 详细教程《实现微信小程序授权登录》(附完整前后端项目demo)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

微信小程序官方登录流程图:
uniapp前后端完整项目,前端,uni-app,spring boot,微信小程序登录,前后端项目案例
参考微信小程序登录官网文档

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;
    
    // 定义公共的url
    const baseUrl = "http://localhost:8866";
    
    /**
     * 返回baseUrl
     */
    export const getBaseUrl = () => {
      return baseUrl;
    }
    
    /**
     * 后端请求工具类
     * @param {*} params 请求参数
     */
    export const requestUtil = (params) => {
    
      let header = {
        ...params.header
      };
    
      // 拼接header 带上token
      header["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项目

uniapp前后端完整项目,前端,uni-app,spring boot,微信小程序登录,前后端项目案例
项目结构:
uniapp前后端完整项目,前端,uni-app,spring boot,微信小程序登录,前后端项目案例

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)
        // 发送请求 获取用户的token
        const 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: 8866
  servlet:
    context-path: /

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/db-wxlogin?serverTimezone=Asia/Shanghai
    username: root
    password: 123456


mybatis-plus:
  global-config:
    db-config:
      id-type: auto
  configuration:
    map-underscore-to-camel-case: true
    auto-mapping-behavior: full
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  mapper-locations: classpath:mapper/*.xml


weixin:
  jscode2sessionUrl: https://api.weixin.qq.com/sns/jscode2session
  appid: wxa4aa78831ea93858  #修改自己的微信小程序 appId
  secret: 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")
@Data
public 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();
    }

    @Override
    public 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")
@Data
public class WeixinProperties {

    private String jscode2sessionUrl; // 登录凭证校验请求地址

    private String appid; // 小程序 appId

    private 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 block
			e.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
 */
@Component
public 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);// 创建httpPost  
		return sendHttpPost(httpPost);
	}
	
	/**
	 * 发送 post请求
	 * @param httpUrl 地址
	 * @param params 参数(格式:key1=value1&key2=value2)
	 */
	public String sendHttpPost(String httpUrl, String params) {
		HttpPost httpPost = new HttpPost(httpUrl);// 创建httpPost  
		try {
			//设置参数
			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 {
        //小明失效 10s
        String 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) { 
	    // <p>段落替换为换行 
	    content = content.replaceAll("<p .*?>", "\r\n"); 
	    // <br><br/>替换为换行 
	    content = content.replaceAll("<br\\s*/?>", "\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
 */
@Repository
public 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
        //后端向微信后台送发请求 获取openid
        String 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();

获取当前微信登录用户信息:
uniapp前后端完整项目,前端,uni-app,spring boot,微信小程序登录,前后端项目案例


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")
@RestController
public class WeixinUserController {

    @Autowired
    private WxUserInfoService wxUserInfoService;

    @Autowired
    private WeixinProperties weixinProperties;

    @Autowired
    private HttpClientUtil httpClientUtil;


    /**
     * 微信用户登录
     * @return
     */
    @RequestMapping("/wxlogin")
    public R wxLogin(@RequestBody WxUserInfo wxUserInfo){
        //拼接后端发送请求的URL 例如 https://api.weixin.qq.com/sns/jscode2session?appid=wxa4de78832ea93858&secret=a2efb3b602d96b2dee615b7a4dee451a&js_code=0b1JwPkl2xqHkb4VEjml2vVdua3JwPkq&grant_type=authorization_code
        String jscode2sessionUrl=weixinProperties.getJscode2sessionUrl()+"?appid="+weixinProperties.getAppid()+"&secret="+weixinProperties.getSecret()+"&js_code="+wxUserInfo.getCode()+"&grant_type=authorization_code";
        System.out.println(jscode2sessionUrl);

        //后端向微信后台送发请求 获取openid
        String result = httpClientUtil.sendHttpGet(jscode2sessionUrl);
        System.out.println(result); //结果:{"session_key":"TPTXzC9MOe1owBJ8zrSWTw==","openid":"o2yqx5PBEW-ezFHA24ASqP0Lk1M0"}

        //
        JSONObject jsonObject= JSON.parseObject(result);//转换成object
        String 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文章来源地址https://www.toymoban.com/news/detail-624224.html

到了这里,关于前端uniapp+后端springboot 详细教程《实现微信小程序授权登录》(附完整前后端项目demo)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 基于SpringBoot+uniapp微信小程序校园点餐平台详细设计和实现

    博主介绍 : ✌ 全网粉丝30W+,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战 ✌ 🍅 文末获取源码联系 🍅 👇🏻 精彩专栏 推荐订阅 👇🏻 不然下次找不到哟 2022-2024年

    2024年02月05日
    浏览(42)
  • 基于SpringBoot+Vue+uniapp微信小程序的校园跑腿的详细设计和实现

    💗 博主介绍 :✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌💗 👇🏻 精彩专栏 推荐订阅 👇🏻 2023-2024年最值得选的微信小程序毕业设

    2024年03月18日
    浏览(42)
  • 基于SpringBoot+Vue+uniapp微信小程序的相亲网站的详细设计和实现

    🌞 博主介绍 :✌全网粉丝15W+,CSDN特邀作者、211毕业、高级全栈开发程序员、大厂多年工作经验、码云/掘金/华为云/阿里云/InfoQ/StackOverflow/github等平台优质作者、专注于Java、小程序技术领域和毕业项目实战,以及程序定制化开发、全栈讲解、就业辅导✌🌞 👇🏻 精彩专栏

    2024年03月11日
    浏览(49)
  • 基于SpringBoot+Vue+uniapp微信小程序的订餐系统的详细设计和实现

    💗 博主介绍 :✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌💗 👇🏻 精彩专栏 推荐订阅 👇🏻 2023-2024年最值得选的微信小程序毕业设

    2024年02月22日
    浏览(62)
  • 基于SpringBoot+Vue+uniapp微信小程序的微信小程序书店的详细设计和实现

    💗 博主介绍 :✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌💗 👇🏻 精彩专栏 推荐订阅 👇🏻 2023-2024年最值得选的微信小程序毕业设

    2024年03月17日
    浏览(45)
  • 基于SpringBoot+Vue+uniapp微信小程序的宿舍报修系统的详细设计和实现

    💗 博主介绍 :✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌💗 👇🏻 精彩专栏 推荐订阅 👇🏻 2023-2024年最值得选的微信小程序毕业设

    2024年04月22日
    浏览(32)
  • 基于SpringBoot+Vue+uniapp微信小程序的投票评选系统的详细设计和实现

    💗 博主介绍 :✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌💗 👇🏻 精彩专栏 推荐订阅 👇🏻 2023-2024年最值得选的微信小程序毕业设

    2024年04月10日
    浏览(43)
  • 基于SpringBoot+Vue+uniapp微信小程序的学生签到系统的详细设计和实现

    💗 博主介绍 :✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌💗 👇🏻 精彩专栏 推荐订阅 👇🏻 2023-2024年最值得选的微信小程序毕业设

    2024年04月08日
    浏览(40)
  • 基于SpringBoot+Vue+uniapp微信小程序的快递管理平台的详细设计和实现

    💗 博主介绍 :✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌💗 👇🏻 精彩专栏 推荐订阅 👇🏻 2023-2024年最值得选的微信小程序毕业设

    2024年03月08日
    浏览(37)
  • 基于SpringBoot+Vue+uniapp微信小程序的校园反诈骗微信小程序的详细设计和实现

    💗 博主介绍 :✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌💗 👇🏻 精彩专栏 推荐订阅 👇🏻 2023-2024年最值得选的微信小程序毕业设

    2024年03月22日
    浏览(73)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包