uniapp+Springboot+jpa实现的微信小程序简单登录框架
uni-app是一个使用 Vue.js 开发所有前端应用的框架,开发者编写一套代码,可发布到iOS、Android、H5、以及各种小程序(微信/支付宝/百度/头条/QQ/钉钉/淘宝)、快应用等多个平台。
uniapp项目创建
- 下载HBuilderX:官方IDE下载地址
HBuilderX是通用的前端开发工具,但为uni-app做了特别强化。
下载App开发版,可开箱即用;如下载标准版,在运行或发行uni-app时,会提示安装uni-app插件,插件下载完成后方可使用。
如使用cli方式创建项目,可直接下载标准版,因为uni-app编译插件被安装到项目下了。
安装成功后打开HBuilderX
1.在点击工具栏里的文件 -> 新建 -> 项目:
2.选择uni-app类型,输入工程名,选择模板,点击创建,即可成功创建。
uni-app自带的模板有 Hello uni-app ,是官方的组件和API示例。还有一个重要模板是 uni ui项目模板,日常开发推荐使用该模板,已内置大量常用组件。
创建成功后,注册自己的一个账号填一个DCloudAppid,方便以后直接导入共享组件(可以不做,和本项目无关),进入manifest.json文件里修改
填自己的微信小程序APPID,微信小程序开发工具,在开发>>开发设置里查看
下面是目录结构:
unpackage是运行后输出的文件,不用理会,主要添加store目录login.vue和store/index.js文件,修改index.vue、main.js、pages.json
- store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);//在创建Vue实例之前
export default new Vuex.Store({
state: {
//存放组件之间共享的数据
userInfo: null,
hasLogin: null,
},
actions: {
// 退出
logout({
commit, state}) {
return new Promise((resolve, reject) => {
uni.request({
url: 'http://localhost:8081/loginout',
method: 'GET',
header: {
'content-type': 'application/x-www-form-urlencoded',
},
success: function(res) {
state.userInfo=null
state.hasLogin=null
uni.reLaunch({
url: '/pages/index/index'});
},
fail: function(error) {
console.log(error);
}
})
})
},
},
})
- login.vue
<template>
<view class="container">
<!-- 登录后使用open-data -->
<view class="userinfo">
<!-- 登录组件 https://developers.weixin.qq.com/miniprogram/dev/api/wx.getUserInfo.html -->
<button v-if="!hasUserInfo" open-type="getUserInfo" @getuserinfo="onGetUserInfo(userInfo)">授权登录</button>
<block v-else>
<image :src="userInfo.avatarUrl" class="userinfo-avatar"/>
<open-data class="userinfo-nickname" type="userNickName"></open-data>
</block>
</view>
</view>
</template>
<script>
export default {
data(){
return{
hasUserInfo: true,//是否授权登录
userInfo: null
}
},
onLoad() {
// 页面加载时使用用户授权逻辑,弹出确认的框
this.userAuthorized()
},
methods: {
userAuthorized() {
let that = this;
// 查看是否授权
uni.getSetting({
success: data => {
//判断是否授权
if (data.authSetting['scope.userInfo']) {
uni.getUserInfo({
success: data => {
that.hasUserInfo = true,
that.userInfo = data.userInfo,
that.onGetUserInfo(data.userInfo)
}
})
} else {
//null或者是false,表示没有授权,显示授权按钮
that.hasUserInfo=false
}
},
fail: function(error) {
console.log(error);
}
})
},
onGetUserInfo(userInfo) {
let that = this;
if (userInfo) {
//小程序通过uni.login()获取code
uni.login({
success: function(login_res) {
//获取用户信息
uni.getUserInfo({
success: function(info_res) {
// 小程序通过uni.request()发送code到开发者服务器
uni.request({
url: 'http://localhost:8081/login',
method: 'POST',
header: {
'content-type': 'application/x-www-form-urlencoded',
},
data: {
code: login_res.code, //临时登录凭证
rawData: info_res.rawData, //用户非敏感信息
signature: info_res.signature, //签名
encrypteData: info_res.encryptedData, //用户敏感信息
iv: info_res.iv //解密算法的向量
},
success: function(res) {
// 全局存储
that.$store.state.userInfo=userInfo
that.$store.state.hasLogin=true
uni.reLaunch({
url: '/pages/index/index'});
},
fail: function(error) {
//调用服务端登录接口失败
console.log(error);
}
})
that.hasUserInfo= true,
that.userInfo = userInfo
},
fail: function(error) {
//用户拒绝授权
uni.reLaunch({
url: '/pages/index/index'});
}
})
}
})
}else{
//第一次授权登录
uni.login({
success: function(login_res) {
//获取用户信息
uni.getUserInfo({
success: function(info_res) {
// 2. 小程序通过wx.request()发送code到开发者服务器
uni.request({
url: 'http://localhost:8081/login',
method: 'POST',
header: {
'content-type': 'application/x-www-form-urlencoded',
},
data: {
code: login_res.code, //临时登录凭证
rawData: info_res.rawData, //用户非敏感信息
signature: info_res.signature, //签名
encrypteData: info_res.encryptedData, //用户敏感信息
iv: info_res.iv //解密算法的向量
},
success: function(res) {
that.$store.state.userInfo=info_res.userInfo
that.$store.state.hasLogin=true
uni.reLaunch({
url: '/pages/index/index'});
},
fail: function(error) {
//调用服务端登录接口失败
console.log(error);
}
})
that.userInfo = info_res.userInfo
that.hasUserInfo= true
},
fail: function(error) {
//用户拒绝授权
uni.reLaunch({
url: '/pages/index/index'});
}
})
}
})
}
}
}
}
</script>
<style>
.container {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
padding: 200rpx 0;
box-sizing: border-box;
}
.userinfo {
display: flex;
flex-direction: column;
align-items: center;
}
.userinfo-avatar {
width: 128rpx;
height: 128rpx;
margin: 20rpx;
border-radius: 50%;
}
.userinfo-nickname {
color: #aaa;
}
</style>
- index/index.vue
<template>
<view class="content">
<view v-if="!hasLogin" class="userinfo">
<view class="btn-row">
<button type="default" @tap="bindLogin">登录</button>
</view>
</view>
<view v-else class="userinfo">
<image :src="userInfo.avatarUrl" class="userinfo-avatar"></image>
<view class="userinfo-nickname">姓名:{
{
userInfo.nickName}}</view>
<view v-if="userInfo.gender==0" class="userinfo-nickname">性别:未知</view>
<view v-if="userInfo.gender==1" class="userinfo-nickname">性别:男</view>
<view v-if="userInfo.gender==2" class="userinfo-nickname">性别:女</view>
<view class="btn-row">
<button type="default" @tap="bindLogout">退出</button>
</view>
</view>
</view>
</template>
<script>
import {
mapState,mapActions} from 'vuex';
export default {
computed: {
...mapState(['userInfo','hasLogin']),
},
data() {
return {
title: 'Hello'
}
},
onLoad() {
},
methods: {
...mapActions(['logout']),
bindLogin() {
uni.reLaunch({
url: '/pages/index/login'});
},
bindLogout() {
this.$store.dispatch('logout').then(() => {
uni.reLaunch({
url: '/pages/index/index'});
}).catch((error) => {
if (error !== 'error') {
uni.showToast({
title: error,icon: "none"});
}
})
},
}
}
</script>
<style>
.container {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
padding: 200rpx 0;
box-sizing: border-box;
}
.userinfo {
display: flex;
flex-direction: column;
align-items: center;
}
.userinfo-avatar {
width: 128rpx;
height: 128rpx;
margin: 20rpx;
border-radius: 50%;
}
.userinfo-nickname {
color: #aaa;
}
</style>
- main.js
import Vue from 'vue'
import App from './App'
import store from './store'
Vue.config.productionTip = false
App.mpType = 'app'
const app = new Vue({
store,
...App
})
app.$mount()
- pages.json
{
"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "uni-app"
}
}
,{
"path" : "pages/index/login",
"style" : {
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "uni-app",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
}
}
至此,前端部分完结
unidemo项目创建
创建一个新的后端项目,我用的工具是idea
![](/qrcode.jpg)
根据自己的需求导入包,目录结构如下:
主要修改pom.xml文件,新建CrosConfig.class、UserController.class、User.class、UserRepository.class、ClobalResult.class、HttpClientUtil.class、WechatUtil.class和配置文件application.yml
- pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.zspt</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--shiro与spring整合依赖-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.4.0</version>
</dependency>
<!--用阿里的json-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.44</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.6</version>
</dependency>
<!-- alibaba的druid数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.17</version>
</dependency>
<!-- http请求工具包依赖 -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.2</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk16</artifactId>
<version>1.46</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- application.yml
server:
port: 8081
spring:
datasource:
name: test
url: jdbc:mysql://localhost:3306/blog?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=CTT
username: root
password: admin77
type: com.alibaba.druid.pool.DruidDataSource
filters: stat
maxActive: 20
initialSize: 1
maxWait: 60000
minIdle: 1
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: select 'x'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxOpenPreparedStatements: 20
jpa:
show-sql: false
properties:
hibernate:
format_aql: true
open-in-view: false
- CrosConfig.class
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class CrosConfig implements WebMvcConfigurer {
//解决跨域请求问题
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET","HEAD","POST","PUT","DELETE","OPTIONS")
.allowCredentials(true)
.maxAge(3600)
.allowedHeaders("*");
}
}
- UserController.class
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.zspt.demo.entity.User;
import com.zspt.demo.repository.UserRepository;
import com.zspt.demo.util.GlobalResult;
import com.zspt.demo.util.WechatUtil;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Date;
import java.util.UUID;
/**
* @program: demo
* @description
* @author: chenshuofang
* @create: 2020-08-07 09:27
**/
@RestController
@RequestMapping
public class UserController {
@Autowired
private UserRepository userRepository;
@PostMapping("/login")
@ResponseBody
public GlobalResult user_login(@RequestParam(value = "code", required = false) String code,
@RequestParam(value = "rawData", required = false) String rawData,
@RequestParam(value = "signature", required = false) String signature,
@RequestParam(value = "encrypteData", required = false) String encrypteData,
@RequestParam(value = "iv", required = false) String iv) {
// 用户非敏感信息:rawData
// 签名:signature
JSONObject rawDataJson = JSON.parseObject(rawData);
// 1.接收小程序发送的code
// 2.开发者服务器 登录凭证校验接口 appi + appsecret + code
JSONObject SessionKeyOpenId = WechatUtil.getSessionKeyOrOpenId(code);
// 3.接收微信接口服务 获取返回的参数
String openid = SessionKeyOpenId.getString("openid");
String sessionKey = SessionKeyOpenId.getString("session_key");
// 4.校验签名 小程序发送的签名signature与服务器端生成的签名signature2 = sha1(rawData + sessionKey)
String signature2 = DigestUtils.sha1Hex(rawData + sessionKey);
if (!signature.equals(signature2)) {
return GlobalResult.build(500, "签名校验失败", null);
}
// 5.根据返回的User实体类,判断用户是否是新用户,是的话,将用户信息存到数据库;不是的话,更新最新登录时间
User user = userRepository.findByOpenId(openid);
// uuid生成唯一key,用于维护微信小程序用户与服务端的会话
String skey = UUID.randomUUID().toString();
if (user == null) {
// 用户信息入库
String nickName = rawDataJson.getString("nickName");
String avatarUrl = rawDataJson.getString("avatarUrl");
String gender = rawDataJson.getString("gender");
String city = rawDataJson.getString("city");
String country = rawDataJson.getString("country");
String province = rawDataJson.getString("province");
user = new User();
user.setOpenId(openid);
user.setSkey(skey);
user.setCreateTime(new Date());
user.setLastVisitTime(new Date());
user.setSessionKey(sessionKey);
user.setCity(city);
user.setProvince(province);
user.setCountry(country);
user.setAvatarUrl(avatarUrl);
user.setGender(Integer.parseInt(gender));
user.setNickName(nickName);
userRepository.save(user);
} else {
// 已存在,更新用户登录时间
user.setLastVisitTime(new Date());
// 重新设置会话skey
user.setSkey(skey);
userRepository.save(user);
}
//encrypteData比rowData多了appid和openid
//JSONObject userInfo = WechatUtil.getUserInfo(encrypteData, sessionKey, iv);
//6. 把新的skey返回给小程序
JSONObject jsonObject=new JSONObject();
jsonObject.put("skey", skey);
jsonObject.put("id", user.getId());
GlobalResult result = GlobalResult.build(200, null, jsonObject);
return result;
}
@GetMapping({
"/loginout"})
public GlobalResult logout() {
return GlobalResult.build(200, (String)null, (Object)null);
}
}
- User.class
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import javax.persistence.*;
import java.util.Date;
/**
* @program: demo
* @description
* @author: chenshuofang
* @create: 2020-08-07 09:28
**/
@Entity
@Data
@Table(name = "t_user")
public class User {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@JoinColumn(name = "open_id")
private String openId;
private String skey;
@JoinColumn(name = "create_time")
@JsonFormat( pattern="yyyy-MM-dd HH:mm:ss")
private Date createTime;
@JoinColumn(name = "last_visit_time")
@JsonFormat( pattern="yyyy-MM-dd HH:mm:ss")
private Date lastVisitTime;
@JoinColumn(name = "session_key")
private String sessionKey;
private String city;
private String province;
private String country;
@JoinColumn(name = "avatar_url")
private String avatarUrl;
private Integer gender;
@JoinColumn(name = "nick_name")
private String nickName;
}
- UserRepository.class
import com.zspt.demo.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
/**
* @program: demo
* @description
* @author: chenshuofang
* @create: 2020-08-07 09:30
**/
public interface UserRepository extends JpaRepository<User, Integer>, JpaSpecificationExecutor<User> {
User findByOpenId(String openid);
}
- ClobalResult.class
/**
* @Description: 自定义响应数据结构
* 这个类是提供给门户,ios,安卓,微信商城用的
* 门户接受此类数据后需要使用本类的方法转换成对于的数据类型格式(类,或者list)
* 其他自行处理
* 200:表示成功
* 500:表示错误,错误信息在msg字段中
* 501:bean验证错误,不管多少个错误都以map形式返回
* 502:拦截器拦截到用户token出错
* 555:异常抛出信息
*/
public class GlobalResult {
// 响应业务状态
private Integer status;
// 响应消息
private String msg;
// 响应中的数据
private Object data;
private String ok; // 不使用
public static GlobalResult build(Integer status, String msg, Object data) {
return new GlobalResult(status, msg, data);
}
public static GlobalResult ok(Object data) {
return new GlobalResult(data);
}
public static GlobalResult ok() {
return new GlobalResult(null);
}
public static GlobalResult errorMsg(String msg) {
return new GlobalResult(500, msg, null);
}
public static GlobalResult errorMap(Object data) {
return new GlobalResult(501, "error", data);
}
public static GlobalResult errorTokenMsg(String msg) {
return new GlobalResult(502, msg, null);
}
public static GlobalResult errorException(String msg) {
return new GlobalResult(555, msg, null);
}
public GlobalResult() {
}
public GlobalResult(Integer status, String msg, Object data) {
this.status = status;
this.msg = msg;
this.data = data;
}
public GlobalResult(Object data) {
this.status = 200;
this.msg = "OK";
this.data = data;
}
public Boolean isOK() {
return this.status == 200;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public String getOk() {
return ok;
}
public void setOk(String ok) {
this.ok = ok;
}
}
- HttpClientUtil.class
import org.apache.http.NameValuePair;
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.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
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 java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class HttpClientUtil {
public static String doGet(String url, Map<String, String> param) {
// 创建Httpclient对象
CloseableHttpClient httpclient = HttpClients.createDefault();
String resultString = "";
CloseableHttpResponse response = null;
try {
// 创建uri
URIBuilder builder = new URIBuilder(url);
if (param != null) {
for (String key : param.keySet()) {
builder.addParameter(key, param.get(key));
}
}
URI uri = builder.build();
// 创建http GET请求
HttpGet httpGet = new HttpGet(uri);
// 执行请求
response = httpclient.execute(httpGet);
// 判断返回状态是否为200
if (response.getStatusLine().getStatusCode() == 200) {
resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (response != null) {
response.close();
}
httpclient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
public static String doGet(String url) {
return doGet(url, null);
}
public static String doPost(String url, Map<String, String> param) {
// 创建Httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
// 创建Http Post请求
HttpPost httpPost = new HttpPost(url);
// 创建参数列表
if (param != null) {
List<NameValuePair> paramList = new ArrayList<>();
for (String key : param.keySet()) {
paramList.add(new BasicNameValuePair(key, param.get(key)));
}
// 模拟表单
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList);
httpPost.setEntity(entity);
}
// 执行http请求
response = httpClient.execute(httpPost);
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
public static String doPost(String url) {
return doPost(url, null);
}
public static String doPostJson(String url, String json) {
// 创建Httpclient对象
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse response = null;
String resultString = "";
try {
// 创建Http Post请求
HttpPost httpPost = new HttpPost(url);
// 创建请求内容
StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);
httpPost.setEntity(entity);
// 执行http请求
response = httpClient.execute(httpPost);
resultString = EntityUtils.toString(response.getEntity(), "utf-8");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return resultString;
}
}
- WechatUtil.class
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.shiro.codec.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.AlgorithmParameters;
import java.security.Security;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* @ClassName WechatUtil
* @Description TODO
* @Author eval
* @Date 9:44 2019/3/20
* @Version 1.0
*/
public class WechatUtil {
public static JSONObject getSessionKeyOrOpenId(String code) {
String requestUrl = "https://api.weixin.qq.com/sns/jscode2session";
Map<String, String> requestUrlParam = new HashMap<>();
// https://mp.weixin.qq.com/wxopen/devprofile?action=get_profile&token=164113089&lang=zh_CN
//小程序appId
requestUrlParam.put("appid", "你的小程序appId");
//小程序secret
requestUrlParam.put("secret", "你的小程序secret");
//小程序端返回的code
requestUrlParam.put("js_code", code);
//默认参数
requestUrlParam.put("grant_type", "authorization_code");
//发送post请求读取调用微信接口获取openid用户唯一标识
JSONObject jsonObject = JSON.parseObject(HttpClientUtil.doPost(requestUrl, requestUrlParam));
return jsonObject;
}
public static JSONObject getUserInfo(String encryptedData, String sessionKey, String iv) {
// 被加密的数据
byte[] dataByte = Base64.decode(encryptedData);
// 加密秘钥
byte[] keyByte = Base64.decode(sessionKey);
// 偏移量
byte[] ivByte = Base64.decode(iv);
try {
// 如果密钥不足16位,那么就补足. 这个if 中的内容很重要
int base = 16;
if (keyByte.length % base != 0) {
int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
byte[] temp = new byte[groups * base];
Arrays.fill(temp, (byte) 0);
System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
keyByte = temp;
}
// 初始化
Security.addProvider(new BouncyCastleProvider());
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
parameters.init(new IvParameterSpec(ivByte));
cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
byte[] resultByte = cipher.doFinal(dataByte);
if (null != resultByte && resultByte.length > 0) {
String result = new String(resultByte, "UTF-8");
return JSON.parseObject(result);
}
} catch (Exception e) {
}
return null;
}
}
注意:WechatUtil.class里的小程序appId和小程序secret需根据你自己实际填写,获取方法上文有提到
MySQL数据库创建
/*
Navicat Premium Data Transfer
Source Server : 本机
Source Server Type : MySQL
Source Server Version : 50562
Source Host : localhost:3306
Source Schema : blog
Target Server Type : MySQL
Target Server Version : 50562
File Encoding : 65001
Date: 07/08/2020 09:45:09
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for t_user
-- ----------------------------
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'id',
`open_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'open_id',
`skey` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'skey',
`create_time` timestamp NULL DEFAULT NULL COMMENT '创建时间',
`last_visit_time` timestamp NULL DEFAULT NULL COMMENT '最后登录时间',
`session_key` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'session_key',
`city` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '市',
`province` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '省',
`country` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '国',
`avatar_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '头像',
`gender` tinyint(11) NULL DEFAULT NULL COMMENT '性别',
`nick_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '网名',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Compact;
SET FOREIGN_KEY_CHECKS = 1;
成品效果图
项目我放我的GitHub了,需要的自己拉取,记得修改微信的AppID和secret,地址:https://github.com/zsptsf/uniapp
转载请声明来源