TokenLifeTime ve RefreshTokenLifeTime tek yerden yönetilir.

This commit is contained in:
Sedat ÖZTÜRK 2026-06-19 08:58:13 +03:00
parent 76621b24d8
commit cb6b38f141
15 changed files with 125 additions and 58 deletions

View file

@ -19,9 +19,7 @@
},
"Platform_App": {
"ClientId": "Platform_App",
"RootUrl": "https://dev.sozsoft.com",
"TokenLifeTime": 2,
"RefreshTokenLifeTime": 8760
"RootUrl": "https://dev.sozsoft.com"
},
"Platform_Swagger": {
"ClientId": "Platform_Swagger",
@ -29,9 +27,7 @@
},
"Platform_PublicApi": {
"ClientId": "Platform_PublicApi",
"RootUrl": "https://dev.sozsoft.com",
"TokenLifeTime": 2,
"RefreshTokenLifeTime": 8760
"RootUrl": "https://dev.sozsoft.com"
}
}
}

View file

@ -19,9 +19,7 @@
},
"Platform_App": {
"ClientId": "Platform_App",
"RootUrl": "https://sozsoft.com",
"TokenLifeTime": 2,
"RefreshTokenLifeTime": 8760
"RootUrl": "https://sozsoft.com"
},
"Platform_Swagger": {
"ClientId": "Platform_Swagger",
@ -29,9 +27,7 @@
},
"Platform_PublicApi": {
"ClientId": "Platform_PublicApi",
"RootUrl": "https://sozsoft.com",
"TokenLifeTime": 2,
"RefreshTokenLifeTime": 8760
"RootUrl": "https://sozsoft.com"
}
}
}

View file

@ -20,9 +20,7 @@
},
"Platform_App": {
"ClientId": "Platform_App",
"RootUrl": "http://localhost:3000",
"TokenLifeTime": 2,
"RefreshTokenLifeTime": 8760
"RootUrl": "http://localhost:3000"
},
"Platform_Swagger": {
"ClientId": "Platform_Swagger",
@ -30,9 +28,7 @@
},
"Platform_PublicApi": {
"ClientId": "Platform_PublicApi",
"RootUrl": "http://localhost:3000",
"TokenLifeTime": 2,
"RefreshTokenLifeTime": 8760
"RootUrl": "http://localhost:3000"
}
}
}

View file

@ -4,7 +4,6 @@ using System.Linq;
using Volo.Abp.Uow;
using Volo.Abp.Data;
using System.Text.Json;
using System.Globalization;
using JetBrains.Annotations;
using System.Threading.Tasks;
using OpenIddict.Abstractions;
@ -108,9 +107,7 @@ public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDep
scopes: commonScopes,
redirectUri: consoleAndAngularClientRootUrl,
clientUri: consoleAndAngularClientRootUrl,
postLogoutRedirectUri: consoleAndAngularClientRootUrl,
tokenLifeTime: configurationSection["Platform_App:TokenLifeTime"]?.To<int>(),
refreshTokenLifeTime: configurationSection["Platform_App:RefreshTokenLifeTime"]?.To<int>()
postLogoutRedirectUri: consoleAndAngularClientRootUrl
);
}
@ -132,9 +129,7 @@ public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDep
},
scopes: commonScopes,
redirectUri: $"{swaggerRootUrl}/swagger/oauth2-redirect.html",
clientUri: swaggerRootUrl,
tokenLifeTime: configurationSection["Platform_Swagger:TokenLifeTime"]?.To<int>(),
refreshTokenLifeTime: configurationSection["Platform_Swagger:RefreshTokenLifeTime"]?.To<int>()
clientUri: swaggerRootUrl
);
}
@ -157,9 +152,7 @@ public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDep
scopes: commonScopes,
redirectUri: publicApiClientRootUrl,
clientUri: publicApiClientRootUrl,
postLogoutRedirectUri: publicApiClientRootUrl,
tokenLifeTime: configurationSection["Platform_PublicApi:TokenLifeTime"]?.To<int>(),
refreshTokenLifeTime: configurationSection["Platform_PublicApi:RefreshTokenLifeTime"]?.To<int>()
postLogoutRedirectUri: publicApiClientRootUrl
);
}
@ -176,9 +169,7 @@ public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDep
string? clientUri = null,
string? redirectUri = null,
string? postLogoutRedirectUri = null,
List<string>? permissions = null,
int? tokenLifeTime = 1,
int? refreshTokenLifeTime = 336
List<string>? permissions = null
)
{
if (!string.IsNullOrEmpty(secret) && string.Equals(type, OpenIddictConstants.ClientTypes.Public, StringComparison.OrdinalIgnoreCase))
@ -200,13 +191,7 @@ public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDep
ClientSecret = secret,
ConsentType = consentType,
DisplayName = displayName,
ClientUri = clientUri,
Settings = {
[OpenIddictConstants.Settings.TokenLifetimes.AccessToken] =
TimeSpan.FromHours(tokenLifeTime ?? 1).ToString("c", CultureInfo.InvariantCulture), // 1 saat
[OpenIddictConstants.Settings.TokenLifetimes.RefreshToken] =
TimeSpan.FromHours(refreshTokenLifeTime ?? 336).ToString("c", CultureInfo.InvariantCulture), // 336 saat = 14 gün
}
ClientUri = clientUri
};
Check.NotNullOrEmpty(grantTypes, nameof(grantTypes));
@ -382,6 +367,34 @@ public class OpenIddictDataSeedContributor : IDataSeedContributor, ITransientDep
client.Permissions = JsonSerializer.Serialize(application.Permissions.Select(q => q.ToString()));
await _applicationManager.UpdateAsync(client.ToModel());
}
await RemoveTokenLifetimeSettingsAsync(client);
}
private async Task RemoveTokenLifetimeSettingsAsync(OpenIddictApplication client)
{
if (client.Settings.IsNullOrWhiteSpace())
{
return;
}
var settings = JsonSerializer.Deserialize<Dictionary<string, string>>(client.Settings);
if (settings == null)
{
return;
}
var changed = false;
changed |= settings.Remove(OpenIddictConstants.Settings.TokenLifetimes.AccessToken);
changed |= settings.Remove(OpenIddictConstants.Settings.TokenLifetimes.RefreshToken);
if (!changed)
{
return;
}
client.Settings = settings.Count == 0 ? null : JsonSerializer.Serialize(settings);
await _applicationManager.UpdateAsync(client.ToModel());
}
private bool HasSameRedirectUris(OpenIddictApplication existingClient, AbpApplicationDescriptor application)

View file

@ -83,12 +83,14 @@ public class PlatformHttpApiHostModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
PreConfigure<OpenIddictBuilder>(builder =>
{
builder.AddServer(server =>
{
server.SetAccessTokenLifetime(TimeSpan.FromMinutes(60));
server.SetRefreshTokenLifetime(TimeSpan.FromMinutes(90));
server.SetAccessTokenLifetime(TimeSpan.FromMinutes(configuration.GetValue("OpenIddict:TokenLifetimes:AccessTokenMinutes", 60)));
server.SetRefreshTokenLifetime(TimeSpan.FromMinutes(configuration.GetValue("OpenIddict:TokenLifetimes:RefreshTokenMinutes", 90)));
server.AddEventHandler<HandleRevocationRequestContext>(builder =>
builder.UseScopedHandler<PlatformSessionRevocationHandler>());
});

View file

@ -78,6 +78,7 @@ public class DynamicFormReport : XtraReport
var localizer = scope.ServiceProvider.GetRequiredService<IStringLocalizer<PlatformResource>>();
var configuration = scope.ServiceProvider.GetRequiredService<IConfiguration>();
var currentUser = scope.ServiceProvider.GetRequiredService<ICurrentUser>();
var isSubReport = DynamicReportImageHelper.IsSubReport(parameters);
if (!await authManager.CanAccess(listFormCode))
{
@ -119,7 +120,7 @@ public class DynamicFormReport : XtraReport
ConfigurePage();
ConfigureDataSource(selectQuery, connectionString, dataSourceType);
await BuildLayoutAsync(listForm, reportFields, row, localizer, DynamicReportImageHelper.GetImageBaseUrl(configuration), currentUser.UserName);
await BuildLayoutAsync(listForm, reportFields, row, localizer, DynamicReportImageHelper.GetImageBaseUrl(configuration), currentUser.UserName, isSubReport);
RequestParameters = false;
}
@ -158,7 +159,8 @@ public class DynamicFormReport : XtraReport
IDictionary<string, object> row,
IStringLocalizer<PlatformResource> localizer,
string imageBaseUrl,
string currentUserName)
string currentUserName,
bool isSubReport)
{
Bands.Clear();
var formGroups = GetFormGroups(listForm, fields).ToList();
@ -179,7 +181,10 @@ public class DynamicFormReport : XtraReport
Font = new DXFont("Arial", 14F, DXFontStyle.Bold),
TextAlignment = TextAlignment.MiddleLeft
});
DynamicReportImageHelper.AddGeneratedAtToHeader(reportHeader.Controls, pageWidth);
if (!isSubReport)
{
DynamicReportImageHelper.AddGeneratedAtToHeader(reportHeader.Controls, pageWidth);
}
DynamicReportImageHelper.AddFooterTopLine(bottomMargin.Controls, pageWidth);
DynamicReportImageHelper.AddCurrentUserNameToFooter(bottomMargin.Controls, currentUserName, pageWidth);
@ -667,7 +672,8 @@ public class DynamicFormReport : XtraReport
return new Dictionary<string, string>
{
["listFormCode"] = subForm.Code,
["filter"] = filter
["filter"] = filter,
[DynamicReportImageHelper.IsSubReportParameterName] = bool.TrueString
};
}

View file

@ -71,6 +71,7 @@ public class DynamicGridReport : XtraReport
var localizer = scope.ServiceProvider.GetRequiredService<IStringLocalizer<PlatformResource>>();
var configuration = scope.ServiceProvider.GetRequiredService<IConfiguration>();
var currentUser = scope.ServiceProvider.GetRequiredService<ICurrentUser>();
var isSubReport = DynamicReportImageHelper.IsSubReport(parameters);
if (!await authManager.CanAccess(listFormCode))
{
@ -156,7 +157,7 @@ public class DynamicGridReport : XtraReport
selectQuery,
connectionString,
dataSourceType);
BuildLayout(listForm, reportColumns, localizer, imageBaseUrl, currentUser.UserName, criteriaText);
BuildLayout(listForm, reportColumns, localizer, imageBaseUrl, currentUser.UserName, criteriaText, isSubReport);
RequestParameters = false;
}
@ -245,7 +246,8 @@ public class DynamicGridReport : XtraReport
IStringLocalizer<PlatformResource> localizer,
string imageBaseUrl,
string currentUserName,
string criteriaText)
string criteriaText,
bool isSubReport)
{
Bands.Clear();
@ -271,7 +273,10 @@ public class DynamicGridReport : XtraReport
Font = new DXFont("Arial", 14F, DXFontStyle.Bold),
TextAlignment = TextAlignment.MiddleLeft
});
DynamicReportImageHelper.AddGeneratedAtToHeader(reportHeader.Controls, pageWidth);
if (!isSubReport)
{
DynamicReportImageHelper.AddGeneratedAtToHeader(reportHeader.Controls, pageWidth);
}
DynamicReportImageHelper.AddReportCriteriaToHeader(reportHeader.Controls, criteriaText, pageWidth);
DynamicReportImageHelper.AddFooterTopLine(bottomMargin.Controls, pageWidth);

View file

@ -25,6 +25,7 @@ namespace Sozsoft.Reports.PredefinedReports;
internal static class DynamicReportImageHelper
{
public const string IsSubReportParameterName = "isSubReport";
private const float DefaultGridImageHeight = 82F;
private const float DefaultGridImageWidth = 86F;
private const float DefaultFormImageHeight = 86F;
@ -394,6 +395,13 @@ internal static class DynamicReportImageHelper
return parameters.TryGetValue(name, out var value) ? value : null;
}
public static bool IsSubReport(IDictionary<string, string> parameters)
{
var value = GetParameter(parameters, IsSubReportParameterName);
return string.Equals(value, bool.TrueString, StringComparison.OrdinalIgnoreCase) ||
string.Equals(value, "1", StringComparison.OrdinalIgnoreCase);
}
public static string GetImageBaseUrl(IConfiguration configuration)
{
return configuration["App:CdnUrl"] ?? configuration["App:SelfUrl"];

View file

@ -75,6 +75,7 @@ public class DynamicTreeReport : XtraReport
var localizer = scope.ServiceProvider.GetRequiredService<IStringLocalizer<PlatformResource>>();
var configuration = scope.ServiceProvider.GetRequiredService<IConfiguration>();
var currentUser = scope.ServiceProvider.GetRequiredService<ICurrentUser>();
var isSubReport = DynamicReportImageHelper.IsSubReport(parameters);
if (!await authManager.CanAccess(listFormCode))
{
@ -166,7 +167,7 @@ public class DynamicTreeReport : XtraReport
ConfigurePage(reportColumns);
ConfigureDataSource(orderedRows, reportColumns);
BuildLayout(listForm, reportColumns, localizer, imageBaseUrl, currentUser.UserName, criteriaText);
BuildLayout(listForm, reportColumns, localizer, imageBaseUrl, currentUser.UserName, criteriaText, isSubReport);
RequestParameters = false;
}
@ -417,7 +418,8 @@ public class DynamicTreeReport : XtraReport
IStringLocalizer<PlatformResource> localizer,
string imageBaseUrl,
string currentUserName,
string criteriaText)
string criteriaText,
bool isSubReport)
{
Bands.Clear();
@ -443,7 +445,10 @@ public class DynamicTreeReport : XtraReport
Font = new DXFont("Arial", 14F, DXFontStyle.Bold),
TextAlignment = TextAlignment.MiddleLeft
});
DynamicReportImageHelper.AddGeneratedAtToHeader(reportHeader.Controls, pageWidth);
if (!isSubReport)
{
DynamicReportImageHelper.AddGeneratedAtToHeader(reportHeader.Controls, pageWidth);
}
DynamicReportImageHelper.AddReportCriteriaToHeader(reportHeader.Controls, criteriaText, pageWidth);
DynamicReportImageHelper.AddFooterTopLine(bottomMargin.Controls, pageWidth);

View file

@ -23,6 +23,12 @@
"RequireHttpsMetadata": false,
"SwaggerClientId": "Platform_Swagger"
},
"OpenIddict": {
"TokenLifetimes": {
"AccessTokenMinutes": 60,
"RefreshTokenMinutes": 90
}
},
"StringEncryption": {
"DefaultPassPhrase": "UQpiYfT79zRZ3yYH"
},

View file

@ -23,6 +23,12 @@
"RequireHttpsMetadata": false,
"SwaggerClientId": "Platform_Swagger"
},
"OpenIddict": {
"TokenLifetimes": {
"AccessTokenMinutes": 60,
"RefreshTokenMinutes": 90
}
},
"StringEncryption": {
"DefaultPassPhrase": "UQpiYfT79zRZ3yYH"
},
@ -35,4 +41,4 @@
"Default": "Information"
}
}
}
}

View file

@ -6,7 +6,7 @@
"RedirectAllowedUrls": "http://localhost:4200,http://localhost:4200/authentication/callback",
"AttachmentsPath": "C:\\Private\\Projects\\sozsoft-platform\\configs\\mail-queue\\attachments",
"CdnUrl": "http://localhost:4005",
"CdnPath": "C:\\Private\\Projects\\sozsoft-platform\\configs\\docker\\data\\cdn",
"CdnPath": "D:\\Sozsoft\\sozsoft-platform\\configs\\docker\\data\\cdn",
"Version": "1.0.5",
"BackupPath": "/var/opt/mssql/backup"
},
@ -23,6 +23,12 @@
"RequireHttpsMetadata": false,
"SwaggerClientId": "Platform_Swagger"
},
"OpenIddict": {
"TokenLifetimes": {
"AccessTokenMinutes": 60,
"RefreshTokenMinutes": 90
}
},
"StringEncryption": {
"DefaultPassPhrase": "UQpiYfT79zRZ3yYH"
},

View file

@ -47,17 +47,31 @@ export const refreshToken = (refresh_token: string) =>
authConfig,
)
export const signOut = (data: any) =>
const revokeToken = (token: string, tokenTypeHint: 'access_token' | 'refresh_token') =>
apiService.fetchData(
{
method: 'POST',
url: '/connect/revocat',
headers: { 'content-type': 'application/x-www-form-urlencoded' },
data: new URLSearchParams({
token: data.token,
token,
client_id: 'Platform_App',
token_type_hint: 'access_token',
token_type_hint: tokenTypeHint,
}).toString(),
},
authConfig,
)
export const signOut = (data: { token?: string; refreshToken?: string }) => {
const requests = []
if (data.token) {
requests.push(revokeToken(data.token, 'access_token'))
}
if (data.refreshToken) {
requests.push(revokeToken(data.refreshToken, 'refresh_token'))
}
return Promise.all(requests)
}

View file

@ -2,7 +2,7 @@ import axios from 'axios'
import { store } from '../store'
import { jwtDecode } from 'jwt-decode'
import appConfig from '../proxy/configs/app.config'
import { refreshToken } from './auth.service'
import { refreshToken, signOut as revokeTokens } from './auth.service'
import { isLoginSuccess } from '../proxy/account/models'
const unauthorizedCode = [401]
@ -103,11 +103,19 @@ platformApiService.interceptors.response.use(
error.config.headers['Authorization'] = `Bearer ${access_token}`
return axios.request(error.config)
} else {
await revokeTokens({
token: auth.session.token,
refreshToken: auth.session.refreshToken,
}).catch(() => undefined)
setIsRefreshing(false)
signOut()
error.silent = true
}
} catch (error2) {
await revokeTokens({
token: auth.session.token,
refreshToken: auth.session.refreshToken,
}).catch(() => undefined)
setIsRefreshing(false)
signOut()
error.silent = true

View file

@ -23,7 +23,7 @@ function useAuth() {
const query = useQuery()
const { token, signedIn } = useStoreState((state) => state.auth.session)
const { token, refreshToken, signedIn } = useStoreState((state) => state.auth.session)
const handleToken = ({
token,
@ -135,7 +135,7 @@ function useAuth() {
}
const _signOut = async () => {
await signOut({ token })
await signOut({ token, refreshToken })
signOutStore()
navigate(appConfig.unAuthenticatedEntryPath)
}