当前位置: 首页 > article >正文

yungouos微信扫码登录demo示例(支持个人免费)

学习链接

30分钟完成微信登录接口对接和原理掌握,支持个人

yungouos文档 - 个人开发者,可对接微信/支付宝

gitee代码地址

效果

在这里插入图片描述

后端

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>

http://www.kler.cn/a/588247.html

相关文章:

  • 使用 OpenSSL 生成的 RSA 私钥文件(如`prikey.pem`)可以用于加密和解密数据
  • 【Cadence软件技巧集萃】从Capture到Allergo——分布演示从原理符号导出到网络表
  • OrioleDB: 新一代PostgreSQL存储引擎
  • 增量数据同步怎么做
  • HTTPS 证书相关
  • 毕业设计程序调试部署反馈
  • 从零开始掌握接口测试:RESTful/WebSocket/gRPC实战宝典
  • 如何高效解决 Java 内存泄漏问题方法论
  • 【redis】reids 客户端的连接(Windows和mac)
  • 关系数据库设计基础:函数依赖、码与多值依赖详解
  • 机器语言基础
  • 单源最短路径问题的相关总结
  • Flask中的装饰器
  • PHP优化技术
  • Kotlin知识体系(二) : Kotlin的七个关键特性
  • Vue项目上传到GitHub,vscode拉取vue项目更新后推送到GitHub上
  • 给easygui添加字体设置功能(tyysoft增强版)
  • 五子棋小游戏-简单开发版
  • docker部署DVWA-暴力破解-难度从low到impossible
  • 前缀和的例题