Vue+abp微信扫码登录
最近系统中要使用微信扫码登录,根据微信官方文档和网络搜索相关文献实现了。分享给需要的人,也作为自己的一个笔记。后端系统是基于 ABP 的,所以部分代码直接使用了 abp 的接口,直接拷贝代码编译不通过。
注册微信开放平台账号#
在微信开放平台注册,注意是开放平台不是公众平台,这里需要 300 元,然后申请网站应用。审核通过后获取到 AppID 和 AppSecret 以及登记的网站 url。只有此 url 下的地址微信扫码后才能回调。
具体申请条件见官方文档。
生成登录二维码#
在 vue 登录页面嵌入登录二维码,根据官方文档,在页面中放入一个 div 元素,二维码就放在此元素中,注意 var obj = new WxLogin 必须放在 mounted 方法中执行,此时 vue 才会把 dom 元素初始化挂载到 dom 树,可以参见 vue 官方文档生命周期介绍。
<template>
<div id="login" class="login"></div>
</template>
<script>
export default {
name: "WXLogin",
data: function() {
return {};
},
mounted() {
this.wechatHandleClick();
document.getElementsByTagName("iframe")[0].height="320";
document.getElementsByTagName("iframe")[0].style.marginLeft="30px";
},
methods: {
wechatHandleClick() {
let ba64Css =
"css 代码 base64 编码";// 微信需要 https 的样式路径,这里将样式内容加密 base64,可以避免使用 https,如果你的网站是 https 的可以直接使用安官方文档使用 css 文件路径
const appid = "你第一步申请的 Appid";
const redirect_uri = encodeURIComponent("http:///#/login");
var obj = new WxLogin({
id: "login", //div 的 id
appid: appid,
scope: "snsapi_login",// 固定内容
redirect_uri: redirect_uri, // 回调地址
// href: "http:///static/UserCss/WeChart.css" // 自定义样式链接,第三方可根据实际需求覆盖默认样式。
href: "data:text/css;base64," + ba64Css
// state: "", // 参数,可带可不带
// style: "", // 样式 提供"black"、"white" 可选,默认为黑色文字描述
});
}
}
};
</script>
注册回调事件#
用户扫码后微信会回调访问前一步提供的 redirect_uri,这里要监控微信回调,并用微信返回的 code 请求后端,在后端再去访问微信服务器获取 token 及用户 openID
在回调页面中监控路由改变事件以监控微信回调(因为我的二维码和回调在同一个路由页面),如果有其他更好的方法请告诉我。
@Watch("$route")
async RouteChange(newVal, oldVal) {
await this.weixinRedirect();
}
// 请求微信后台
async weixinRedirect() {
let code = this.$route.query.code;
let state = this.$route.query.state;
if (code) {
let wxTo = {
code,
state
};
// 请求后台
this.$http("*/WeixinRedirect",data:wxTo).then((token)=>{
// 登录成功,把 token 写入 cookie
// 跳转到主页
this.$router.replace({ path: "/", replace: true });
}).catch(error => {
// 保持当前页面
this.$router.replace({ path: "/login", replace: true });
});
}
}
}
后端接收 code 请求 token#
在 appsettings.json 中配置 AppId 和 AppSecret
[HttpPost]
public async Task<AuthenticateResultModel> WeixinRedirect(string code, string state)
{
if (code.IsNullOrEmpty())
{
throw new UserFriendlyException("微信授权失败,请重新授权");
}
var appid = configuration["Authentication:Wechat:AppId"];
var secret = configuration["Authentication:Wechat:AppSecret"];
var url = $"https://api.weixin.qq.com/sns/oauth2/access_token?appid={appid}&secret={secret}&code={code}&grant_type=authorization_code";
var httpClient = httpClientFactory.CreateClient();
httpClient.DefaultRequestHeaders.Add("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)");
httpClient.Timeout = TimeSpan.FromMinutes(3);
<span class="hljs-keyword">var</span> resstr = <span class="hljs-keyword">await</span> httpClient.GetStringAsync(url);
<span class="hljs-keyword">try</span>{
<span class="hljs-comment">//如果微信授权返回失败这里序列化不成功</span>
<span class="hljs-keyword">var</span> res = JsonSerializationHelper.DeserializeWithType<WeiXinAccess_tokenResponse>(resstr);
}<span class="hljs-keyword">catch</span> (Exception e)
{
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> UserFriendlyException(<span class="hljs-string">"获取微信access_token失败"</span>);
}
<span class="hljs-keyword">if</span> (res == <span class="hljs-literal">null</span> || res.openid.IsNullOrEmpty())
{
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> UserFriendlyException(<span class="hljs-string">"获取微信access_token失败"</span>);
}
<span class="hljs-keyword">var</span> userId = <span class="hljs-comment">//根据openID获取用户id,我们系统要求用户提前把微信和用户关联绑定,所以这里可以根据微信用户的openID获取到户农户id;</span>
<span class="hljs-comment">//使用用户直接登录</span>
<span class="hljs-keyword">if</span> (!userId.IsNullOrEmpty()&&<span class="hljs-built_in">long</span>.TryParse(userId, <span class="hljs-keyword">out</span> <span class="hljs-built_in">long</span> id))
{
<span class="hljs-keyword">var</span> user = <span class="hljs-keyword">await</span> _userManager.GetUserByIdAsync(id);
<span class="hljs-keyword">var</span> loginResult = <span class="hljs-keyword">await</span> _logInManager.LoginByUser(user);
<span class="hljs-built_in">string</span> accessToken = CreateAccessToken(CreateJwtClaims(loginResult.Identity));
<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> AuthenticateResultModel
{
AccessToken = accessToken,
EncryptedAccessToken = GetEncrpyedAccessToken(accessToken),
ExpireInSeconds = (<span class="hljs-built_in">int</span>)_tokenConfiguration.Expiration.TotalSeconds,
UserId = loginResult.User.Id
};
}
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> UserFriendlyException(<span class="hljs-string">"微信尚未绑定账号,请使用账号登录后绑定微信。"</span>);
}
WeiXinAccess_tokenResponse 类型
public class WeiXinAccess_tokenResponse
{
public string access_token { get; set; }
public int expires_in { get; set; }
public string refresh_token { get; set; }
public string openid { get; set; }
public string scope { get; set; }
public string unionid { get; set; }
}