微信小程序授权流程+说明
授权流程说明官方链接
授权流程图
我的理解(一得之见,如有错误,欢迎指出哈)
1.前端获取小程序的code,调用后端接口
2.后端获取code,结合写在配置文件里的appid和secret,用GET请求‘(https://api.weixin.qq.com/sns/jscode2session)’,获取sesson_key和openid;
3.后端用加密算法把返回的sesson_key和openid加密并写入httpsesson或者redis中,配置这个值的有效时间,返回保存体的id;
4.前端获取这个值保存,后续接口都增加这个ID,后端校验是否失效,失效的话就重新登录。
uniapp前端页面
代码结构
登录界面预览
登录界面前端代码
<template>
<view>
<view>
<view>
<view class="header">
<image src="/static/login-wx.jpg"></image>
</view>
<view class="content">
<view>申请获取以下权限</view>
<text>获得你的公开信息(昵称,头像、地区等)</text>
</view>
<button class="bottom" type="primary" :disabled="userInfoDisabled" @click="wxGetUserInfo">授权登录</button>
</view>
</view>
</view>
</template>
<script>
export default {
data(){
return {
userInfoDisabled: false
}
} ,
onLoad() {
},
methods :{
wxGetUserInfo(){
console.log("点击测试!")
uni.login({
"provider": "weixin",
"onlyAuthorize": true, // 微信登录仅请求授权认证
success: function(event){
console.log(event)
uni.showLoading({
title: '授权中...'
})
uni.getUserInfo({
provider: 'weixin',
success: function (infoRes) {
console.log(infoRes.userInfo)
console.log('用户昵称为:' + infoRes.userInfo );
}})
uni.request({
url:'http://localhost:8090/user/getWxInfoTest', //请求的地址,类型为String
data:event.code, //要求为Object或String类型的参数,发送到服务器的数据。如果已经不是字符串,将自动转换为字符串格式。get请求中将附加在url后
method:'post', //get、post、delete
success:res=>{
console.log(res.data)
uni.setStorage({
key: 'token',
data: 'res.data',
success: function () {
uni.showToast({
title: '授权成功',
icon: 'success',
duration: 1000,
})
uni.switchTab({
url: '../index/index'
});
}
})
} })
}
})
}
}
}
</script>
<style lang="scss" scoped>
.header {
margin: 160rpx 0 60rpx 50rpx;
text-align: center;
width: 650rpx;
image {
width: 180rpx;
height: 180rpx;
}
}
.content {
margin-bottom: 68rpx;
text-align: center;
text {
display: block;
color: #9d9d9d;
margin-top: 20rpx;
}
}
.bottom {
border-radius: 80rpx;
margin: 70rpx 50rpx;
font-size: 35rpx;
}
.login-wrapper{
height: 100%;
position: relative;
.login-bottom{
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 133rpx;
background-color: #fff;
button{
position: absolute;
top: 50%;
left: 50%;
background-color: #CA2915;
transform: translate(-50%, -50%);
border-radius: 30px;
width: 435rpx;
height: 68rpx;
line-height: 68rpx;
}
}
}
</style>
后端springboot配置及接口
后端代码结构
pom依赖
<?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>
<groupId>com.example</groupId>
<artifactId>wx</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>wx</name>
<description>wx</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.6.13</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.14</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.28</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.10</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</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-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<configuration>
<mainClass>com.example.wx.WxApplication</mainClass>
<skip>true</skip>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
配置文件 application.properties
#开启sesson缓存
spring.session.store-type=none
#设置缓存有效时间5分钟
server.servlet.session.timeout=5m
[微信官方平台扫码登录获取](https://mp.weixin.qq.com/)
wx.open.app_id=wx7941efe26a50548c
wx.open.secret=1063068c30ab233e160eeea31af17983
wx.open.token_url=https://api.weixin.qq.com/sns/jscode2session?
httpUtils.java
package com.example.wx.utils;
import lombok.extern.slf4j.Slf4j;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
@Slf4j
public class httpUtils {
public static String getRequest(String httpurl) {
log.info("完整的请求是{}", httpurl);
HttpURLConnection connection = null;
InputStream is = null;
BufferedReader br = null;
String result = null;// 返回结果字符串
try {
// 创建远程url连接对象
URL url = new URL(httpurl);
// 通过远程url连接对象打开一个连接,强转成httpURLConnection类
connection = (HttpURLConnection) url.openConnection();
// 设置连接方式:get
connection.setRequestMethod("GET");
// 设置连接主机服务器的超时时间:15000毫秒
connection.setConnectTimeout(15000);
// 设置读取远程返回的数据时间:60000毫秒
connection.setReadTimeout(60000);
// 发送请求
connection.connect();
// 通过connection连接,获取输入流
if (connection.getResponseCode() == 200) {
is = connection.getInputStream();
// 封装输入流is,并指定字符集
br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
// 存放数据
StringBuffer sbf = new StringBuffer();
String temp = null;
while ((temp = br.readLine()) != null) {
sbf.append(temp);
sbf.append("\r\n");
}
result = sbf.toString();
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭资源
if (null != br) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null != is) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
connection.disconnect();// 关闭远程连接
}
return result;
}
}
MD5Util.java
package com.example.wx.utils;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* MD5加密解密工具类
* @author admin
*
*/
public class MD5Util {
/**
* 使用 MD5算法加密生成32位md5码
* @param str
* @return
*/
public static String string2MD5(String str) {
byte[] secretBytes = null;
try {
secretBytes = MessageDigest.getInstance("md5").digest(
str.getBytes());
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("没有md5这个算法!");
}
String md5code = new BigInteger(1, secretBytes).toString(16);// 16进制数字
// 如果生成数字未满32位,需要前面补0
for (int i = 0; i < 32 - md5code.length(); i++) {
md5code = "0" + md5code;
}
return md5code;
}
/**
* 可逆的加密解密算法,执行一次加密,两次解密
* @param str
* @return
*/
public static String convertMD5(String str){
char[] a = str.toCharArray();
for (int i = 0; i < a.length; i++){
a[i] = (char) (a[i] ^ 't');
}
String s = new String(a);
return s;
}
}
SessionUtil.java
package com.example.wx.utils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class SessionUtil {
@Autowired
private HttpServletRequest request;
public String saveToSession(String key, Object value) {
HttpSession session = request.getSession();
session.setAttribute(key, value);
return session.getId();
}
}
WechatTools.java
package com.example.wx.utils;
import com.alibaba.fastjson.JSONObject;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
public class WechatTools {
public static String getOpenid(String code,String appid,String secret,String token_url) {
StringBuilder data=new StringBuilder();
data.append("appid="+ appid+"&");
data.append("secret="+ secret+"&");
data.append("js_code="+ code+"&");
data.append("grant_type="+ "authorization_code");
String response = httpUtils.getRequest(token_url + data);
JSONObject jo= JSONObject.parseObject(response);
jo.get("session_key");
jo.get("openid");
return response;
}
}
UserController.java
package com.example.wx.controller;
import com.example.wx.utils.MD5Util;
import com.example.wx.utils.SessionUtil;
import com.example.wx.utils.WechatTools;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {
@Value("${wx.open.app_id}")
private String appid;
@Value("${wx.open.secret}")
private String secret;
@Value("${wx.open.token_url}")
private String token_url;
@Autowired
SessionUtil sessionUtil;
/**
* 微信小程序的登陆,如果有账号,则返回登陆成功,如果没有则返回(首次用户,需要认证)
* @return
*/
@PostMapping("/getWxInfoTest")
public String getWxInfoTest(@RequestBody String obj) {
log.info("传入的参数{}", obj);
String finalStr= WechatTools.getOpenid(obj,appid,secret,token_url);
String md5= MD5Util.convertMD5(MD5Util.string2MD5(finalStr));
String sessonId= sessionUtil.saveToSession(md5,finalStr);
log.info("微信的返回值{}", finalStr);
log.info("后端接口返回的seasonID:{}", finalStr);
return sessonId;
}
}
运行截图
运行结果
2023-12-07 18:53:00.653 INFO 21354 --- [nio-8090-exec-1] c.example.wx.controller.UserController : 传入的参数0b1EO6ml20sGwc4F5kol2EQVG04EO6mp
2023-12-07 18:53:00.653 INFO 21354 --- [nio-8090-exec-1] com.example.wx.utils.httpUtils : 完整的请求是https://api.weixin.qq.com/sns/jscode2session?appid=wx7941efe26a50548c&secret=1063068c30ab233e160eeea31af17983&js_code=0b1EO6ml20sGwc4F5kol2EQVG04EO6mp&grant_type=authorization_code
2023-12-07 18:53:01.081 INFO 21354 --- [nio-8090-exec-1] c.example.wx.controller.UserController : 微信的返回值{"session_key":"rmFq7DZCy27OrztZLMNucg==","openid":"o4eNX0ikqYyiQAXSSBSMYMrPgN6E"}
2023-12-07 18:53:01.081 INFO 21354 --- [nio-8090-exec-1] c.example.wx.controller.UserController : 后端接口返回的seasonID:{"session_key":"rmFq7DZCy27OrztZLMNucg==","openid":"o4eNX0ikqYyiQAXSSBSMYMrPgN6E"}