Uniapp小程序微信一键登录

5,393 阅读3分钟

微信一键登录

微信小程序不需要用户下载安装,可以直接在微信内部使用,方便快捷。使用微信一键登录可以快速登录小程序,无需再进行注册或者输入账号密码登录,大大节省了时间和精力。

小程序登录

小程序可以通过微信官方提供的登录能力方便地获取微信提供的用户身份标识,快速建立小程序内的用户体系。

登录流程时序

image.png

微信官方文档

文章介绍

本文采用Uniapp进行微信小程序的开发,后端使用.NET开发的WebAPI

获取开发者ID和密钥

前往微信公众平台使用微信扫码登录

image.png

image.png

后端

创建一条登录api,接收微信获取到的code

image.png

我的LoginResult类实现

public class MessageResult
    {
        public int Code { get; set; } = 200;
        public string Message { get; set; } = "";
        public bool Successed { get; protected set; }
        public bool Failed { get; protected set; }


        public MessageResult(int code, string message, bool b)
        {
            Code = code;
            Message = message;
            if (b)
            {
                Successed = true;
            }
            else
            {
                Failed = true;
            }
        }
        public MessageResult(string message)
        {
            Message = message;
            Successed = true;
        }
        public MessageResult() { }
    }

 public class LoginResult: MessageResult
    {
        public string? Token { get; set; }
        public LoginResult(int code,string message)
        {
            base.Code = code;
            base.Message = message;
            base.Failed = true;
        }
        public LoginResult(string token) 
        {
            Token = token;
            base.Message = "登录成功";
            base.Successed = true;
        }
        public LoginResult(int code,string message,bool b):base(code,message,b) { }
    }

通过code去请求微信登录接口返回openid。

在表里查找有没有这个openid

有的话返回userId找到这个user然后获取token,没有就新建个user进数据库然后获取token。

可以根据你喜欢的方法去实现,表达的是我个人思路

private async Task<string> BuildTokenAsync(AppUser user)
        {
            var roles = await repository.GetRolesAsync(user);
            List<Claim> claims = new List<Claim>();
            claims.Add(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()));
            foreach (string role in roles)
            {
                claims.Add(new Claim(ClaimTypes.Role, role));
            }
            return tokenService.BuildToken(claims, optJWT.Value);
        }
        
public async Task<LoginResult> loginByWeChatOpenIdAsync(string code)
        {
            string authUrl = "https://api.weixin.qq.com/sns/jscode2session?grant_type=authorization_code";
            //这里通过环境变量获取Id和密钥,你也可以改成明文
            string appId = Environment.GetEnvironmentVariable("AppID");
            string appSecret = Environment.GetEnvironmentVariable("AppSecret");
            //最后的url拼接
            authUrl = authUrl + "&appid=" + appId + "&secret=" + appSecret + "&js_code=" + code;
            var httpClient = new HttpClient();
            //发起get请求
            using HttpResponseMessage response = await httpClient.GetAsync(authUrl);
            var jsonString = await response.Content.ReadAsStringAsync();
            //反序列化成对象
            WechatOptions? wechatOptions = JsonSerializer.Deserialize<WechatOptions>(jsonString);
            //没获取到openid就登录失败了
            if(wechatOptions.openid != null)
            {
                var user = new AppUser();
                var token = "";
                //查找数据库里有没有这个openid
                var chetWeChat = await weChatRepository.FindByOpenIdAsync(wechatOptions.openid);
                if(chetWeChat != null)
                {
                    //有的话通过userid查找这个用户
                    user = await repository.FindByIdAsync(chetWeChat.AppUserId);
                    
                }
                else
                {
                    //创建用户需要userName和password,为了不重复我就通过时间戳了
                    TimeSpan ts = DateTime.Now - new DateTime(1970, 1, 1, 0, 0, 0, 0);
                    var pwd  = Convert.ToInt64(ts.TotalSeconds).ToString();
                    user.UserName = "ChatGPT_" + pwd;
                    await repository.CreateAsync(user, pwd);
                    user = await repository.FindByNameAsync(user.UserName);
                    //用新建的用户id和openid添加一条数据
                    var newUser = new WeChatUser(user.Id, wechatOptions.openid);
                    var res = await weChatRepository.CreateWechatUserAsync(newUser);
                }
                //获取token
                token = await BuildTokenAsync(user);
                return new LoginResult(token);

            }
            return new LoginResult(404,"登录失败");
        }

WechatOptions请求后反序列化的类

public class WechatOptions
    {
        public string session_key { get; set; }
        public string openid { get; set; }

    }

WeChatUser实体类

public class WeChatUser
    {
        public int Id { get; set; }
        public string OpenId { get; set; }
        public Guid AppUserId { get; set; }

        public WeChatUser(Guid appUserId,string openId)
        {
            AppUserId= appUserId;
            OpenId= openId;
        }
    }

还有什么遗漏记得评论留言

前端

登录界面

image.png

在项目文件夹下的manifest.json文件输入获取到的AppId

image.png

封装请求

const baseURL = 'https://localhost:3010';

function get(url){
	return new Promise((resolve) => {
		uni.request({
			url: baseURL+url,
			data: {},
			method: "GET",
			success(res) {
				resolve(res.data)
			}
		});
	});
}
export default{
	async LoginByWeChat(code){
		return await get("/IdentityServer/Login/LoginByWeChat?code="+code)
	}
}

页面代码,我用了UviewUI框架,所以u-toast,u--image,u-button会报错,你可以换成你用的或者你也尝试一下UviewUI,虽然有时候会有许多烦恼

<template>
	<view class="body" :style="{height:screenHeight}">
		<u-toast ref="uToast"></u-toast>
		<view style="height: 40%;">
			<view class="logo">
				<u--image :src="logoSrc" width="120px" height="120px"></u--image>
			</view>
			<view class="title">登录 TC Message</view>
			<u-button text="微信一键登录" type="success" :hairline="false" throttleTime="2000" shape="circle"
				@click="getUserProfile"></u-button>
		</view>
	</view>
</template>

<script>
	import api from '../api.js'
	export default {
		data() {
			return {
				//屏幕高度
				screenHeight: "",
				logoSrc: "/static/logo.png"
			};
		},
		onLoad() {
			//获取屏幕高度,我的项目再store里已经取到了
			uni.getSystemInfo({
				success: (res) => {
					this.screenHeight = res.windowHeight + "px"
				}
			})
		},
		methods: {
			// 登录方法
			async login(code) {
                                let that = this
				//调用自己封装的方法
				var res = await api.LoginByWeChat(code)
				//登录成功跳转页面
				if (res.successed) {
					this.$refs.uToast.show({
						type: 'success',
						message: res.message,
						complete() {
							that.$store.state.token = res.token
							//为了用动画所以用了navigateTo
							uni.navigateTo({
								url: '/pages/home/home'
							})
						}
					})
				} else {
					this.$refs.uToast.show({
						type: 'error',
						message: res.message
					})
				}
			},
			getChatCode() {
				//因为作用域先提前赋值
				var that = this
				//使用uni封装的一键登录方法
				uni.login({
					provider: 'weixin',
					success(res) {
						//成功后带着微信登录返回的code去请求我们的后端
						that.login(res.code)
					}
				})
			},
			getUserProfile() {
				var that = this
				uni.getUserProfile({
					desc: "获取你的昵称、头像",
					success(res) {
						if (res.errMsg == "getUserProfile:ok" && res.userInfo != undefined) {
							//我用store来存储一些数据,你可以放到你要放的地方
							that.$store.state.userInfo.nickName = res.userInfo.nickName,
								that.$store.state.userInfo.avatarUrl = res.userInfo.avatarUrl
							that.getChatCode()
						}
					},
					complete(res) {
						console.log(res);
					}
				})
			}
		}
	}
</script>

<style lang="scss">
	.body {
		// width: 100%;
		background: linear-gradient(#415169, #1e2d43);

		.logo {
			padding-top: 200rpx;
			margin: auto;
			width: 120px;
			height: 120px;
		}

		.title {
			width: 100%;
			margin: 30px 0;
			text-align: center;
			font-size: 18px;
			color: white;
		}

		.u-button {
			width: 60% !important;
		}
	}
</style>

微信开发者工具需要微信登录,id也要对应

image.png

总结

  1. 在前端获取用户code
  2. 到后端通过code请求微信官方接口获得openid
  3. 在数据库里查找这个openid的用户,可以在用户类添加一条openid的属性或者新建个关联表,我新建多一个表
  4. 找到用户就获取token,没有就先创建再获取token返回,有关联表的话是添加两个表的数据了
  5. 返回token前端保存

仓库地址:gitee