Browse Source

⬆️ 升级abp5.2.1

 集成Github第三方登录
pull/43/head
王军 4 years ago
parent
commit
c6fb9316a0
  1. 2
      aspnet-core/Directory.Build.props
  2. 2
      aspnet-core/global.json
  3. 36
      aspnet-core/services/host/Lion.AbpPro.HttpApi.Host/AbpProHttpApiHostModule.cs
  4. 11
      aspnet-core/services/host/Lion.AbpPro.HttpApi.Host/appsettings.json
  5. 24
      aspnet-core/services/src/Lion.AbpPro.Application.Contracts/Users/Dtos/GithubAccessTokenResponse.cs
  6. 99
      aspnet-core/services/src/Lion.AbpPro.Application.Contracts/Users/Dtos/LoginGithubResponse.cs
  7. 2
      aspnet-core/services/src/Lion.AbpPro.Application.Contracts/Users/Dtos/LoginStsResponse.cs
  8. 18
      aspnet-core/services/src/Lion.AbpPro.Application.Contracts/Users/IAccountAppService.cs
  9. 6
      aspnet-core/services/src/Lion.AbpPro.Application/HttpClientNameConsts.cs
  10. 90
      aspnet-core/services/src/Lion.AbpPro.Application/Users/AccountAppService.cs
  11. 13
      aspnet-core/services/src/Lion.AbpPro.HttpApi/Controllers/Systems/AccountController.cs
  12. 15
      vben271/src/api/sys/user.ts
  13. 9
      vben271/src/router/routes/mainOut.ts
  14. 365
      vben271/src/services/ServiceProxies.ts
  15. 6
      vben271/src/services/ServiceProxyBase.ts
  16. 56
      vben271/src/store/modules/user.ts
  17. 46
      vben271/src/views/sys/login/GithubOidcSignIn.vue
  18. 11
      vben271/src/views/sys/login/LoginForm.vue
  19. 2
      vben271/src/views/sys/login/OidcSignIn.vue

2
aspnet-core/Directory.Build.props

@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<AbpPackageVersion>5.2.0</AbpPackageVersion>
<AbpPackageVersion>5.2.1</AbpPackageVersion>
<DotNetCoreCAPVersion>6.0.0</DotNetCoreCAPVersion>
<MicrosoftVersion>6.0.0</MicrosoftVersion>
<SystemComponentModelAnnotationsVersion>6.0.0-preview.4.21253.7</SystemComponentModelAnnotationsVersion>

2
aspnet-core/global.json

@ -1,6 +1,6 @@
{
"sdk": {
"version": "6.0.201",
"version": "6.0.202",
"rollForward": "latestFeature"
}
}

36
aspnet-core/services/host/Lion.AbpPro.HttpApi.Host/AbpProHttpApiHostModule.cs

@ -43,6 +43,7 @@ using Volo.Abp.Modularity;
using Microsoft.AspNetCore.Mvc;
using Magicodes.ExporterAndImporter.Core;
using Magicodes.ExporterAndImporter.Excel;
using Microsoft.AspNetCore.Identity;
using Volo.Abp.AspNetCore.ExceptionHandling;
namespace Lion.AbpPro
@ -83,6 +84,7 @@ namespace Lion.AbpPro
ConfigurationMiniProfiler(context);
ConfigureMagicodes(context);
ConfigureAbpExceptions(context);
ConfigureIdentity(context);
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
@ -127,6 +129,7 @@ namespace Lion.AbpPro
app.UseConsul();
}
}
/// <summary>
/// 异常处理
/// </summary>
@ -135,15 +138,10 @@ namespace Lion.AbpPro
{
//开启后通过ErrorCode抛本地化异常,message不会显示本地化词条
var SendExceptionsDetails = context.Services.GetHostingEnvironment().IsDevelopment();
context.Services.Configure<AbpExceptionHandlingOptions>(options =>
{
options.SendExceptionsDetailsToClients = SendExceptionsDetails;
});
context.Services.AddMvc(options =>
{
options.Filters.Add(typeof(ResultExceptionFilter));
});
context.Services.Configure<AbpExceptionHandlingOptions>(options => { options.SendExceptionsDetailsToClients = SendExceptionsDetails; });
context.Services.AddMvc(options => { options.Filters.Add(typeof(ResultExceptionFilter)); });
}
/// <summary>
/// 配置Magicodes.IE
/// Excel导入导出
@ -299,8 +297,30 @@ namespace Lion.AbpPro
new Uri(context.Services.GetConfiguration().GetSection("HttpClient:Sts:Url")
.Value);
});
context.Services.AddHttpClient(context.Services.GetConfiguration().GetSection("HttpClient:Github:Name").Value,
options =>
{
options.BaseAddress =
new Uri(context.Services.GetConfiguration().GetSection("HttpClient:Github:Url")
.Value);
});
context.Services.AddHttpClient(context.Services.GetConfiguration().GetSection("HttpClient:GithubApi:Name").Value,
options =>
{
options.BaseAddress =
new Uri(context.Services.GetConfiguration().GetSection("HttpClient:GithubApi:Url")
.Value);
});
}
/// <summary>
/// 配置Identity
/// </summary>
private void ConfigureIdentity(ServiceConfigurationContext context)
{
context.Services.Configure<IdentityOptions>(options => { options.Lockout = new LockoutOptions() { AllowedForNewUsers = false }; });
}
private void ConfigureConventionalControllers()
{
Configure<AbpAspNetCoreMvcOptions>(options =>

11
aspnet-core/services/host/Lion.AbpPro.HttpApi.Host/appsettings.json

@ -64,6 +64,17 @@
"Sts": {
"Name": "Sts",
"Url": "http://localhost:44354"
},
"Github": {
"Name": "Github",
"Url": "https://github.com",
"ClientId": "127fc528f611879fba03",
"ClientSecret": "fd0914e9e8e28b51dd5efe381121429279e43973"
},
"GithubApi": {
"Name": "GithubApi",
"Url": "https://api.github.com",
"ClientName": "AbpPro"
}
},
"Consul": {

24
aspnet-core/services/src/Lion.AbpPro.Application.Contracts/Users/Dtos/GithubAccessTokenResponse.cs

@ -0,0 +1,24 @@
using Newtonsoft.Json;
namespace Lion.AbpPro.Users.Dtos;
public class GithubAccessTokenResponse
{
/// <summary>
/// access_token
/// </summary>
[JsonProperty("access_token")]
public string Access_token { get; set; }
/// <summary>
/// scope
/// </summary>
[JsonProperty("scope")]
public string Scope { get; set; }
/// <summary>
/// token_type
/// </summary>
[JsonProperty("token_type")]
public string TokenType { get; set; }
}

99
aspnet-core/services/src/Lion.AbpPro.Application.Contracts/Users/Dtos/LoginGithubResponse.cs

@ -0,0 +1,99 @@
namespace Lion.AbpPro.Users.Dtos;
public class LoginGithubResponse
{
public string login { get; set; }
public int id { get; set; }
public string node_id { get; set; }
public string avatar_url { get; set; }
public string gravatar_id { get; set; }
public string url { get; set; }
public string html_url { get; set; }
public string followers_url { get; set; }
public string following_url { get; set; }
public string gists_url { get; set; }
public string starred_url { get; set; }
public string subscriptions_url { get; set; }
public string organizations_url { get; set; }
public string repos_url { get; set; }
public string events_url { get; set; }
public string received_events_url { get; set; }
public string type { get; set; }
public string site_admin { get; set; }
public string name { get; set; }
public string company { get; set; }
public string blog { get; set; }
public string location { get; set; }
public string email { get; set; }
public string hireable { get; set; }
public string bio { get; set; }
public string twitter_username { get; set; }
public int public_repos { get; set; }
public int public_gists { get; set; }
public int followers { get; set; }
public int following { get; set; }
public string created_at { get; set; }
public string updated_at { get; set; }
}

2
aspnet-core/services/src/Lion.AbpPro.Application.Contracts/Users/Dtos/LoginStsOutput.cs → aspnet-core/services/src/Lion.AbpPro.Application.Contracts/Users/Dtos/LoginStsResponse.cs

@ -3,7 +3,7 @@ using System.Threading.Tasks;
namespace Lion.AbpPro.Users.Dtos
{
public class LoginStsOutput
public class LoginStsResponse
{
public string name { get; set; }

18
aspnet-core/services/src/Lion.AbpPro.Application.Contracts/Users/IAccountAppService.cs

@ -9,9 +9,25 @@ namespace Lion.AbpPro.Users
{
public interface IAccountAppService: IApplicationService
{
/// <summary>
/// 用户名密码登录
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task<LoginOutput> LoginAsync(LoginInput input);
Task<LoginOutput> StsLoginAsync(string accessToken);
/// <summary>
/// identityServer4第三方登录
/// </summary>
/// <returns></returns>
Task<LoginOutput> Id4LoginAsync(string accessToken);
/// <summary>
/// github第三方登录
/// </summary>
/// <param name="code">授权码</param>
/// <returns></returns>
Task<LoginOutput> GithubLoginAsync(string code);
}
}

6
aspnet-core/services/src/Lion.AbpPro.Application/HttpClientNameConsts.cs

@ -9,5 +9,11 @@ namespace Lion.AbpPro
/// 身份验证中心
/// </summary>
public static string Sts = "Sts";
/// <summary>
/// Github
/// </summary>
public static string Github = "Github";
public static string GithubApi = "GithubApi";
}
}

90
aspnet-core/services/src/Lion.AbpPro.Application/Users/AccountAppService.cs

@ -9,44 +9,41 @@ using System.Threading.Tasks;
using Lion.AbpPro.ConfigurationOptions;
using Lion.AbpPro.Users.Dtos;
using IdentityModel;
using Lion.AbpPro.Extension.Customs.Dtos;
using Lion.AbpPro.Extension.Customs.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Volo.Abp;
using Volo.Abp.Identity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Security.Claims;
namespace Lion.AbpPro.Users
{
public class AccountAppService : AbpProAppService, IAccountAppService
{
private readonly IdentityUserManager _userManager;
private readonly JwtOptions _jwtOptions;
private readonly Microsoft.AspNetCore.Identity.SignInManager<Volo.Abp.Identity.IdentityUser>
_signInManager;
private readonly Microsoft.AspNetCore.Identity.SignInManager<IdentityUser> _signInManager;
private readonly IHttpClientFactory _httpClientFactory;
private readonly ICurrentTenant _currentTenant;
private readonly IHttpContextAccessor _contextAccessor;
private readonly IConfiguration _configuretion;
private readonly Volo.Abp.Domain.Repositories.IRepository<IdentityRole> _identityRoleRepository;
public AccountAppService(
IdentityUserManager userManager,
IOptionsSnapshot<JwtOptions> jwtOptions,
Microsoft.AspNetCore.Identity.SignInManager<IdentityUser> signInManager,
IHttpClientFactory httpClientFactory, ICurrentTenant currentTenant,
IHttpContextAccessor contextAccessor)
IHttpClientFactory httpClientFactory,
IConfiguration configuretion,
Volo.Abp.Domain.Repositories.IRepository<IdentityRole> identityRoleRepository)
{
_userManager = userManager;
_jwtOptions = jwtOptions.Value;
_signInManager = signInManager;
_httpClientFactory = httpClientFactory;
_currentTenant = currentTenant;
_contextAccessor = contextAccessor;
_configuretion = configuretion;
_identityRoleRepository = identityRoleRepository;
}
@ -67,23 +64,78 @@ namespace Lion.AbpPro.Users
return await BuildResult(user);
}
public async Task<LoginOutput> StsLoginAsync(string accessToken)
/// <summary>
/// identityServer4第三方登录
/// </summary>
/// <returns></returns>
public async Task<LoginOutput> Id4LoginAsync(string accessToken)
{
// 通过access token 获取用户信息
Dictionary<string, string> headers = new Dictionary<string, string>
{ { "Authorization", $"Bearer {accessToken}" } };
var response =
await _httpClientFactory.GetAsync<LoginStsOutput>(HttpClientNameConsts.Sts,
"connect/userinfo", headers);
var response = await _httpClientFactory.GetAsync<LoginStsResponse>(HttpClientNameConsts.Sts, "connect/userinfo", headers);
var user = await _userManager.FindByNameAsync(response.name);
if (!user.IsActive)
{
throw new UserFriendlyException("当前用户已锁定");
}
return await BuildResult(user);
}
/// <summary>
/// github第三方登录
/// </summary>
/// <param name="code">授权码</param>
/// <returns></returns>
public async Task<LoginOutput> GithubLoginAsync(string code)
{
var headers = new Dictionary<string, string> { { "Accept", $"application/json" } };
// 通过code获取access token
var accessTokenUrl =
$"login/oauth/access_token?client_id={_configuretion.GetValue<string>("HttpClient:Github:ClientId")}&client_secret={_configuretion.GetValue<string>("HttpClient:Github:ClientSecret")}&code={code}";
var accessTokenResponse = await _httpClientFactory.GetAsync<GithubAccessTokenResponse>(HttpClientNameConsts.Github, accessTokenUrl, headers);
// 获取github用户信息
headers.Add("Authorization", $"token {accessTokenResponse.Access_token}");
headers.Add("User-Agent", _configuretion.GetValue<string>("HttpClient:GithubApi:ClientName"));
var userResponse = await _httpClientFactory.GetAsync<LoginGithubResponse>(HttpClientNameConsts.GithubApi, "/user", headers);
var user = await _userManager.FindByEmailAsync(userResponse.email);
if (user != null) return await BuildResult(user);
return await RegisterAsync(userResponse.name, userResponse.email);
}
#region 私有方法
/// <summary>
/// 注册用户
/// </summary>
/// <returns></returns>
private async Task<LoginOutput> RegisterAsync(string userName, string email, string password = "1q2w3E*")
{
var result = new LoginOutput();
var roles = await _identityRoleRepository.GetListAsync(e => e.IsDefault);
if (roles == null || roles.Count == 0) throw new UserFriendlyException("系统未配置默认角色");
var userId = GuidGenerator.Create();
var user = new IdentityUser(userId, userName, email)
{
Name = userName
};
roles.ForEach(e => { user.AddRole(e.Id); });
user.SetIsActive(true);
await _userManager.CreateAsync(user, password);
result.Token = GenerateJwt(user.Id, user.UserName, user.Name, user.Email,
user.TenantId.ToString(), roles.Select(e => e.Name).ToList());
result.Id = user.Id;
result.UserName = userName;
result.Name = userName;
result.Roles = roles.Select(e => e.Name).ToList();
return result;
}
private async Task<LoginOutput> BuildResult(IdentityUser user)
{
@ -136,5 +188,7 @@ namespace Lion.AbpPro.Users
var token = handler.CreateToken(tokenDescriptor);
return handler.WriteToken(token);
}
#endregion
}
}

13
aspnet-core/services/src/Lion.AbpPro.HttpApi/Controllers/Systems/AccountController.cs

@ -23,10 +23,17 @@ namespace Lion.AbpPro.Controllers.Systems
}
[SwaggerOperation(summary: "登录", Tags = new[] {"Account"})]
[HttpPost("/api/app/account/login/Sts")]
public Task<LoginOutput> StsLoginAsync(string accessToken)
[HttpPost("/api/app/account/login/id4")]
public Task<LoginOutput> Id4LoginAsync(string accessToken)
{
return _accountAppService.StsLoginAsync(accessToken);
return _accountAppService.Id4LoginAsync(accessToken);
}
[SwaggerOperation(summary: "登录", Tags = new[] {"Account"})]
[HttpPost("/api/app/account/login/github")]
public Task<LoginOutput> GithubLoginAsync(string code)
{
return _accountAppService.GithubLoginAsync(code);
}
}
}

15
vben271/src/api/sys/user.ts

@ -36,13 +36,20 @@ export function login(input: LoginInput): Promise<LoginOutput> {
* @param token
* @returns
*/
export function stsLogin(token: string): Promise<LoginOutput> {
export function id4(token: string): Promise<LoginOutput> {
const _accountServiceProxy = new AccountServiceProxy();
return _accountServiceProxy.sts(token);
return _accountServiceProxy.id4(token);
}
export function stsLogout() {
/**
* sts登录
* @param token
* @returns
*/
export function github(code: string): Promise<LoginOutput> {
debugger;
const _accountServiceProxy = new AccountServiceProxy();
return _accountServiceProxy.logout();
return _accountServiceProxy.github(code);
}
/**

9
vben271/src/router/routes/mainOut.ts

@ -26,6 +26,15 @@ export const mainOutRoutes: AppRouteModule[] = [
ignoreAuth: true,
},
},
{
path: '/githubSignIn',
name: 'GithubOidcSign',
component: () => import('/@/views/sys/login/GithubOidcSignIn.vue'),
meta: {
title: 'GithubOidcSign',
ignoreAuth: true,
},
},
];
export const mainOutRouteNames = mainOutRoutes.map((item) => item.name);

365
vben271/src/services/ServiceProxies.ts

File diff suppressed because it is too large

6
vben271/src/services/ServiceProxyBase.ts

@ -64,10 +64,8 @@ export class ServiceProxyBase {
if (url == '/Tenants/find') {
return true;
}
if (url == '/api/app/account/login') {
return true;
}
if (url.startsWith('/api/app/account/login/Sts')) {
if (url.startsWith('/api/app/account')) {
return true;
}

56
vben271/src/store/modules/user.ts

@ -13,7 +13,7 @@ import {
} from '/@/enums/cacheEnum';
import { getAuthCache, setAuthCache } from '/@/utils/auth';
import { GetUserInfoByUserIdModel, LoginParams } from '/@/api/sys/model/userModel';
import { login, getAbpApplicationConfiguration, stsLogin } from '/@/api/sys/user';
import { login, getAbpApplicationConfiguration, id4, github } from '/@/api/sys/user';
import { useI18n } from '/@/hooks/web/useI18n';
import { useMessage } from '/@/hooks/web/useMessage';
import { router } from '/@/router';
@ -148,10 +148,8 @@ export const useUserStore = defineStore({
goHome && (await router.replace(PageEnum.BASE_HOME));
return null;
} catch (error) {
//console.log(error);
router.replace(PageEnum.BASE_LOGIN);
return null;
//throw new Error(error);
}
},
async getAbpApplicationConfigurationAsync() {
@ -165,7 +163,7 @@ export const useUserStore = defineStore({
permissionStore.setPermCodeList(grantPolicy);
},
async stsLogin(token: string, id_token: string) {
async id4Login(token: string, id_token: string) {
try {
// 获取token中的租户信息
const decoded: any = jwt_decode(token);
@ -175,7 +173,7 @@ export const useUserStore = defineStore({
this.setTenant(decoded.tenantid);
}
const data = await stsLogin(token);
const data = await id4(token);
this.setToken(data.token as string);
this.setUserInfo({
@ -195,6 +193,54 @@ export const useUserStore = defineStore({
}
},
async githubLogin(code: string) {
try {
const data = await github(code);
this.setToken(data.token as string);
this.setTenant('');
this.setUserInfo({
userId: data.id as string,
username: data.userName as string,
realName: data.name as string,
roles: data.roles as [],
avatar: '',
isSts: false,
idToken: '',
});
await this.getAbpApplicationConfigurationAsync();
await router.replace(PageEnum.BASE_HOME);
} catch (error) {
this.setTenant('');
router.replace(PageEnum.BASE_LOGIN);
}
},
// async githubLogin(code: string) {
// try {
// debugger;
// await githubLogin(code).then((res) => {
// console.log(res);
// });
// // this.setToken(data.token as string);
// // this.setUserInfo({
// // userId: decoded.sub as string,
// // username: data.userName as string,
// // realName: data.name as string,
// // roles: data.roles as [],
// // avatar: '',
// // isSts: true,
// // idToken: id_token,
// // });
// // await this.getAbpApplicationConfigurationAsync();
// // await router.replace(PageEnum.BASE_HOME);
// } catch (error) {
// console.log(error);
// //this.setTenant('');
// router.replace(PageEnum.BASE_LOGIN);
// }
// },
/**
* @description: logout
*/

46
vben271/src/views/sys/login/GithubOidcSignIn.vue

@ -0,0 +1,46 @@
<template>
<Loading :loading="loading" :absolute="absolute" :tip="tip" />
</template>
<script lang="ts">
import { defineComponent, reactive, toRefs } from 'vue';
import { Loading } from '/@/components/Loading';
import { useUserStore } from '/@/store/modules/user';
import { useRouter } from 'vue-router';
import { router } from '/@/router';
import { PageEnum } from '/@/enums/pageEnum';
import { message } from 'ant-design-vue';
export default defineComponent({
components: { Loading },
setup() {
const compState = reactive({
absolute: false,
loading: false,
tip: '登录中',
});
const userStore = useUserStore();
async function openLoading(absolute: boolean) {
compState.absolute = absolute;
compState.loading = true;
try {
debugger;
const { currentRoute } = useRouter();
const code = currentRoute.value.fullPath.split('=')[1].split('&')[0];
if (code) {
await userStore.githubLogin(code);
}
} catch {
message.error('登陆失败');
router.replace(PageEnum.BASE_HOME);
} finally {
compState.loading = false;
}
}
openLoading(true);
return {
...toRefs(compState),
};
},
});
</script>

11
vben271/src/views/sys/login/LoginForm.vue

@ -43,7 +43,15 @@
<Divider class="enter-x">{{ t('sys.login.otherSignIn') }}</Divider>
<div class="flex justify-evenly enter-x" :class="`${prefixCls}-sign-in-way`">
<a-button type="link" @click="useOidcLogin">IdentityServer登录 </a-button>
<a-button type="link" @click="useOidcLogin" aria-placeholder="sf"
><LoginOutlined />
</a-button>
<a-button
type="link"
href="https://github.com/login/oauth/authorize?client_id=127fc528f611879fba03&state=abp"
><GithubFilled />
</a-button>
</div>
</Form>
</template>
@ -57,6 +65,7 @@
AlipayCircleFilled,
GoogleCircleFilled,
TwitterCircleFilled,
LoginOutlined,
} from '@ant-design/icons-vue';
import LoginFormTitle from './LoginFormTitle.vue';

2
vben271/src/views/sys/login/OidcSignIn.vue

@ -27,7 +27,7 @@
const token = currentRoute.value.fullPath.split('=')[2].split('&')[0];
if (token) {
await userStore.stsLogin(token, id_token);
await userStore.id4Login(token, id_token);
}
} catch {
message.error('登陆失败');

Loading…
Cancel
Save