学习链接
yungouos文档 - 个人开发者,可对接微信/支付宝
效果
后端
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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.zzhua</groupId>
<artifactId>demo-web-backend</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.yungouos.pay</groupId>
<artifactId>yungouos-pay-sdk</artifactId>
<version>2.0.34</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.68</version>
</dependency>
</dependencies>
</project>
YunGouController
@Slf4j
@RestController
@RequestMapping("yungou")
public class YunGouController {
@Autowired
private YunGouProperties yunGouProperties;
// 获取授权Url链接
@GetMapping("/wx/getAuthorizationUrl")
public Result<String> getAuthorizationUrl() {
log.info("getAuthorizationUrl---->");
JSONObject params = new JSONObject();
String url = WxApi.getWxOauthUrl(yunGouProperties.getMchId(),
yunGouProperties.getCallbackUrl(),
"open-url",
params,
yunGouProperties.getMchKey()
);
return Result.ok(url);
}
// 根据code获取用户信息
@GetMapping("/wx/loginByCode")
public Result<Map<String, Object>> loginByCode(@RequestParam("code") String code) {
WxOauthInfo wxOauthInfo = WxApi.getWxOauthInfo(
yunGouProperties.getMchId(),
code,
yunGouProperties.getMchKey()
);
String jwtToken = genToken(wxOauthInfo);
Map<String, Object> data = MapBuilder.newHashMap()
.put("wxOauthInfo", wxOauthInfo)
.put("token", jwtToken)
.build();
return Result.ok(data);
}
private String genToken(WxOauthInfo wxOauthInfo) {
log.info("wxOauthInfo: {}", wxOauthInfo);
// simply back
return wxOauthInfo.getOpenId();
}
// 获取内嵌二维码信息
@GetMapping("/wx/innerQrCodeInfo")
public Result<Object> qrCodeInfo() {
JSONObject params = new JSONObject();
WxWebLoginBiz loginBiz = WxApi.getWebLogin(
yunGouProperties.getMchId(),
yunGouProperties.getCallbackUrl(),
params,
yunGouProperties.getMchKey()
);
loginBiz.getState();
return Result.ok(loginBiz);
}
}
/*
window.parent.postMessage({ type: 'wechatLoginSuccess' })
window.addEventListener('message', function (event) {
console.log('收到消息', event);
if (event.data.type == 'wechatLoginSuccess') {
console.log('wechatLoginSuccesswechatLoginSuccesswechatLoginSuccess');
location.href = '/home'
}
});
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
// 检测当前页面是否在iframe中
if (window.self !== window.top) {
// 如果在iframe中,则让父窗口跳转到首页(或目标页面)
window.top.location.href = '/home'; // 替换为你的实际目标URL
} else {
// 如果直接访问回调页面,也进行跳转
window.location.href = '/home';
}
</script>
</head>
<body>
<!-- 可添加加载提示 -->
登录成功,正在跳转...
</body>
</html>*/
前端
路由
import { createRouter,createWebHistory} from "vue-router";
// 路由信息
const routes = [
{
path: '/',
redirect: '/login'
},
{
path: '/login',
name: 'login',
component: () => import('@/views/Login.vue')
},
{
path: '/login/wx',
name: 'wxLogin',
component: () => import('@/views/WxLogin.vue')
},
{
path: '/home',
name: 'home',
component: () => import('@/views/Home.vue')
}
]
const router = createRouter({
history: createWebHistory(),
routes
});
router.beforeEach((to,from,next)=>{
next()
})
router.afterEach((to,from,next)=>{
})
// 导出路由
export default router;
Login.vue
<template>
<div style="width: 600px; margin: auto; text-align: center;">
<h2>
Login
</h2>
<div style="display: flex">
<div style="width:0;flex-grow: 1;">
<button @click="login">微信登录(跳转微信扫码)</button>
</div>
<div style="width:0;flex-grow: 1;min-width: 300px;">
<button @click="loginInner">微信登录(内嵌微信扫码)</button>
<div id="login_container"></div>
</div>
</div>
</div>
</template>
<script setup>
import { getAuthorizationUrl, innerQrCodeInfo } from '@/api/login.js'
import { useRouter } from 'vue-router'
const router = useRouter()
const login = async () => {
let authorizationUrl = await getAuthorizationUrl()
location.href = authorizationUrl
}
const loginInner = async () => {
/*
{
"code": 0,
"msg": "成功",
"data": {
"appId": "wx7a04e843a99eb4c1",
"scope": "snsapi_login",
"state": "9773F9350C8E4A1B8A49584C1B251366",
"redirect_uri": "https://api.wx.yungouos.com/callback/wxmp/oauth"
}
}
*/
let qrCodeInfo = await innerQrCodeInfo()
var obj = new WxLogin({
self_redirect: true,
id: "login_container",
appid: qrCodeInfo.appId,
scope: qrCodeInfo.scope,
redirect_uri: qrCodeInfo.redirect_uri,
state: qrCodeInfo.state,
style: "",
href: ""
});
}
</script>
<style scoped>
button {
border: none;
border-radius: 6px;
height: 40px;
background-color: #1da5ff;
color: aliceblue;
cursor: pointer;
}
</style>
WxLogin.vue
<template>
<div class="loader" v-show="showLoadingCircle">
<div class="loader-inner">
<div class="loader-line-wrap">
<div class="loader-line"></div>
</div>
<div class="loader-line-wrap">
<div class="loader-line"></div>
</div>
<div class="loader-line-wrap">
<div class="loader-line"></div>
</div>
<div class="loader-line-wrap">
<div class="loader-line"></div>
</div>
<div class="loader-line-wrap">
<div class="loader-line"></div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { loginByCode } from '@/api/login'
const route = useRoute()
const router = useRouter()
const showLoadingCircle = ref(false)
onMounted(() => {
console.log(window == window.parent, window.self == window.top);
// 是否是内嵌iframe
if (window.self !== window.top) {
console.log('在iframe');
// 如果在iframe中,则让父窗口跳转到首页(或目标页面)
window.top.location.href = '/login/wx?code=' + route.query.code; // 替换为你的实际目标URL
} else {
console.log('不在iframe');
showLoadingCircle.value = true
loginByCode({code: route.query.code}).then(res=>{
localStorage.setItem('userInfo', JSON.stringify(res))
router.push('/home')
})
}
})
</script>
<style scoped>
.loader {
background: #fff;
background: radial-gradient(#fff, #fff);
bottom: 0;
left: 0;
overflow: hidden;
position: fixed;
right: 0;
top: 0;
z-index: 99999;
}
.loader-inner {
bottom: 0;
height: 60px;
left: 0;
margin: auto;
position: absolute;
right: 0;
top: 0;
width: 100px;
}
.loader-line-wrap {
animation:
spin 2000ms cubic-bezier(.175, .885, .32, 1.275) infinite;
box-sizing: border-box;
height: 50px;
left: 0;
overflow: hidden;
position: absolute;
top: 0;
transform-origin: 50% 100%;
width: 100px;
}
.loader-line {
border: 4px solid transparent;
border-radius: 100%;
box-sizing: border-box;
height: 100px;
left: 0;
margin: 0 auto;
position: absolute;
right: 0;
top: 0;
width: 100px;
}
.loader-line-wrap:nth-child(1) {
animation-delay: -50ms;
}
.loader-line-wrap:nth-child(2) {
animation-delay: -100ms;
}
.loader-line-wrap:nth-child(3) {
animation-delay: -150ms;
}
.loader-line-wrap:nth-child(4) {
animation-delay: -200ms;
}
.loader-line-wrap:nth-child(5) {
animation-delay: -250ms;
}
.loader-line-wrap:nth-child(1) .loader-line {
border-color: hsl(0, 80%, 60%);
height: 90px;
width: 90px;
top: 7px;
}
.loader-line-wrap:nth-child(2) .loader-line {
border-color: hsl(60, 80%, 60%);
height: 76px;
width: 76px;
top: 14px;
}
.loader-line-wrap:nth-child(3) .loader-line {
border-color: hsl(120, 80%, 60%);
height: 62px;
width: 62px;
top: 21px;
}
.loader-line-wrap:nth-child(4) .loader-line {
border-color: hsl(180, 80%, 60%);
height: 48px;
width: 48px;
top: 28px;
}
.loader-line-wrap:nth-child(5) .loader-line {
border-color: hsl(240, 80%, 60%);
height: 34px;
width: 34px;
top: 35px;
}
@keyframes spin {
0%,
15% {
transform: rotate(0);
}
100% {
transform: rotate(360deg);
}
}
</style>
Home.vue
<template>
<div style="width: 500px; margin: auto; text-align: center;">
<h2>主页</h2>
<div>
<img :src="userInfo.wxOauthInfo.wxUserInfo.headimgurl" style="border-radius: 50%;width: 80px;">
<h3>姓名: <span style="color: orange;">{{ userInfo.wxOauthInfo.wxUserInfo.nickname }}</span></h3>
</div>
<button @click="logout">注销</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
const router = useRouter()
const userInfo = ref(JSON.parse(localStorage.getItem('userInfo')))
function logout() {
localStorage.removeItem("userInfo")
router.push('/login')
}
</script>
<style lang="scss"></style>