使用WMT框架开发移动应用的过程中遇到了第三方登录的问题,于是我就自己给WTM应用添加了第三方登录。这里以github未例子。我参照了天上木有OvO大神的博客 SPA+.NET Core3.1 GitHub第三方授权登录 使用AspNet.Security.OAuth.GitHub。做了属于WTM框架的第三方登录扩展。
一、GitHub授权登录前你必须获取client_id,client_secret如何获取和测试GitHub,请参照GitHub 第三方登录这篇博客。
二、在appsettings.json添加配置信息
"Authentication": {
"GitHub": {
"ClientId": "d08c83abe14c9f4e20f8",
"ClientSecret": "392b888c3926d4dd0cdb212829ec836d1eba41a2",
"redirectUrlMobile": "http://localhost:8080/#/pages/withAccount/login-result",
"redirectUrlPC": "https://localhost:5001/Login/LoginResult"
}
}
三、在WTM项目的model层添加用户类WebUser继承自FrameworkUserBase,添加OpenID字段存储,第三方登录返回的openid一类的身份标识信息。并在DataAccess层数据库上下文进行注册。
/// <summary>
/// 登录用户
/// </summary>
[Table("FrameworkUsers")]
public class WebUser : FrameworkUserBase
{
[Display(Name = "部门名称")]
[Required(ErrorMessage = "{0}是必填项")]
public Guid UnitWorkID { get; set; }
[Display(Name = "部门名称")]
public UnitWork UnitWork { get; set; }
/// <summary>
/// 外部登录
/// </summary>
[Display(Name = "外部登录")]
public string OpenID { get; set; }
}
四、添加AuthenticationApi控制器,将配置IConfiguration注入到控制器中,用来获取appsettings的配置信息。并添加相应的处理方法
public class AuthenticationApiController : BaseApiController
{
private readonly IConfiguration _configuration;
public AuthenticationApiController(IConfiguration configuration)
{
_configuration = configuration;
}
}
五、添加ExternalLogin处理方法,识别是从手机端还是PCWEB端发起的外部登录请求,并携带client_id到github获取code值。
[HttpGet("~/ExternalLogin")]
public IActionResult ExternalLogin(string provider, string terminal)
{
HttpContext.Session.SetString("terminal", terminal);
switch (provider)
{
case ("github"):
return Redirect("https://github.com/login/oauth/authorize?client_id=d08c83abe14c9f4e20f8");
default:
break;
}
return Content("参数错误");
}
六、添加SignIn方法,从github上获取access_token,并返回给SPA或者PCWEB的回调页面进行处理。
[HttpGet("~/signin-github")]
public IActionResult SignIn(string code)
{
string terminal = HttpContext.Session.GetString("terminal");
string redirectUrl = "";
if (terminal == "mobile")
{
redirectUrl = _configuration["Authentication:GitHub:redirectUrlMobile"];
}
else
{
redirectUrl = _configuration["Authentication:GitHub:redirectUrlPC"];
}
string clientId = _configuration["Authentication:GitHub:ClientId"];
string clientSecret = _configuration["Authentication:GitHub:ClientSecret"];
//利用coede获取access_token
string url = $"https://github.com/login/oauth/access_token?client_id={clientId}&client_secret={clientSecret}&code={code}";
string re = HttpHelper.SendHttp("post", url);
string[] reArr = re.Split("&");
return Redirect(redirectUrl + "?acc=" + reArr[0]);
}
七、添加AutoLogin处理方法,根据前端回调页面发给后台的openid在WebUser 中扩展的openid字段中进行检索,如果没有则返回空值,让前端页面调到登录页进行注册。如果有则给SPA应用下发JWT Token,WTM MVC调用框架生成的DoLogin进行登录操作。
[HttpPost]
public async Task<IActionResult> AutoLogin(string openid)
{
var user = await DC.Set<WebUser>().AsNoTracking().SingleOrDefaultAsync(x => x.OpenID.Contains(openid));
if (user == null)
{
var temp = new
{
access_token = "no",
openid = openid
};
return Content(JsonConvert.SerializeObject(temp), "application/json");
}
LoginVM vm = CreateVM<LoginVM>();
vm.ITCode = user.ITCode;
vm.Password = user.Password;
var userwtm = vm.DoLogin();
LoginUserInfo = userwtm;
string terminal = HttpContext.Session.GetString("terminal");
if (terminal == "mobile")
{
var authService = HttpContext.RequestServices.GetService(typeof(ITokenService)) as ITokenService;
var token = await authService.IssueTokenAsync(LoginUserInfo);
return Content(JsonConvert.SerializeObject(token), "application/json");
}
else
{
AuthenticationProperties properties = null;
properties = new AuthenticationProperties
{
IsPersistent = true,
ExpiresUtc = DateTimeOffset.UtcNow.Add(TimeSpan.FromDays(30))
};
var principal = LoginUserInfo.CreatePrincipal();
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal, properties);
return Redirect("/Home/");
}
}
控制器业务逻辑就完成了,接下来,咱们分为别WTM MVC和SPA应用添加登录逻辑。
八、WTM MVC登录
1.1、在login视图中添加登录按钮
<li>
<div class="layui-row layui-col-space10" style="text-align:center">
<div class="layui-col-md4">
<img src="~/images/WX.jpg" class="layui-nav-img">
</div>
<div class="layui-col-md4">
<img src="~/images/QQ.jpg" class="layui-nav-img">
</div>
<div class="layui-col-md4">
<img src="~/images/GITHUB.jpg" class="layui-nav-img" onclick="githublogin()">
</div>
</div>
</li>
添加登录js方法
function githublogin() {
var url = window.location.href;
var urlBase = url.replace('Login/Login', "");
window.location.href = urlBase + 'ExternalLogin?provider=github&terminal=PC';
}
1.2、WTM MVC在login控制器中添加LoginResult方法,返回视图,作为WTM MVC的回调页面。
[Public]
public IActionResult LoginResult(string acc)
{
ViewBag.acc = acc;
return View();
}
@{
Layout = null;
}
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="/layui/css/layui.css">
<script src="/jquery.min.js"></script>
<script src="/jquery.cookie.js"></script>
<script src="/layui/layui.js"></script>
<script src="/_js/framework_layui.js?time=@DateTime.Now.Ticks"></script>
<link rel="stylesheet" href="~/sitecss/logincss.css">
<title>WTM</title>
<script type="text/javascript">
$(document).ready(function () {
//利用access_token获取openid,如果成功则调用后台AutoLogin方法,完成登录。
$.ajax({
url: 'https://api.github.com/user?' + '@ViewBag.acc',
success: function (res) {
var openid = res.login;
autoLogin(openid);
},
error: function (xhr) {
myFunction();
},
})
});
//发生错误弹出窗口
function myFunction() {
if (confirm("网络错误,稍后重试")) {
window.location.href = "/Login/Login"
} else {
window.location.href = "/Login/Login"
}
}
//调用后台AutoLogin
function autoLogin(id) {
$.ajax({
url: '/api/AuthenticationApi/AutoLogin?openid='+id,
type: 'post',
success: function (res) {
console.log(res)
if (res.access_token == 'no') {
window.location.href = '/Login/Reg';
} else {
window.location.href = "/"
}
},
error: function (xhr) {
myFunction();
},
})
};
layui.use('layer');
</script>
</head>
<body class="loginBody">
<div class="loginDiv">
<h4>
登录中...
</h4>
</div>
</body>
</html>
WTM MVC端完成设置。接下来我们继续这只SAP应用端。
九、SAP应用端设置
1.1、在login.vue页面添加GitHubLogin方法,添加到你需要@click的按钮和图片上面
GitHubLogin(){
const urlBase = this.Common.urlBase;
//window.location.href = 'https://github.com/login/oauth/authorize?client_id=d08c83abe14c9f4e20f8';
window.location.href = urlBase + "ExternalLogin?provider=github&terminal=mobile";
}
1.2、添加login-result页面,地址要和在appsettings.json文件中能对应上的地址。其中在onLoad中接收,后台返回的access_token,并在github请求openid发到后台autoLogin,并接收JWT Token。
<template>
<view>
<scroll-view scroll-y class="page">
<view style="margin-top: 200px;">
<view class="image-content" style="text-align: center; width: 100%;">
<image style="width: 200px; height: 200px; background-color: #eeeeee;margin-top:100;" src="../../static/刷新.png"></image>
</view>
<view class="solids-bottom flex align-center">
<view class="flex-sub text-center">
<view class="solid-bottom text-xl padding">
<text class="text-black text-bold">登录中......</text>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
data() {
return {
}
},
onLoad(op) {
console.log(op);
const acc = op.acc;
var that = this;
uni.request({
url: 'https://api.github.com/user?' + acc,
success: (res) => {
console.log(res.data);
if (res.statusCode === 200) {
let openid = res.data.login;
that.autoLogin(openid);
} else {
uni.showModal({
title: '提示',
content: '网络问题,稍后重试',
showCancel: false,
success: function(res) {
if (res.confirm) {
uni.navigateTo({
url: '../withAccount/Login'
});
}
}
});
}
},
fail() {
uni.showModal({
title: '提示',
content: '网络问题,稍后重试',
showCancel: false,
success: function(res) {
if (res.confirm) {
uni.navigateTo({
url: '../withAccount/Login'
});
}
}
});
}
});
},
methods: {
registered() {
uni.navigateTo({
url: '../withAccount/registered'
})
},
autoLogin(id) {
var that = this;
console.log(id)
const urlBase = this.Common.urlBase;
uni.request({
url: urlBase + "api/AuthenticationApi/AutoLogin?openid=" + id,
method: "POST",
success: (res) => {
console.log(res.data);
if (res.statusCode === 200) {
let access_token = res.data.access_token;
if (access_token === 'no') {
that.registered();
} else {
uni.setStorageSync('access_token', res.data.access_token);
uni.setStorageSync('refresh_token', res.data.refresh_token);
uni.showToast({
title: '登录成功',
duration: 2000,
icon: 'none',
success() {
uni.navigateTo({
url: '../withAccount/testlist'
});
}
});
}
} else {
uni.showToast({
title: '登录失败',
duration: 2000,
icon: 'none',
success() {
uni.navigateTo({
url: '../withAccount/Login'
});
}
});
}
}
});
}
}
}
</script>
<style>
</style>
就此,完成了github扩展登录。值得一提的是,WTM里很有很多好东西是文档里没写的,例如咱们这里用的JWT令牌生成。
var authService = HttpContext.RequestServices.GetService(typeof(ITokenService)) as ITokenService;
var token = await authService.IssueTokenAsync(LoginUserInfo);