Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit10c5b46

Browse files
committed
passkey优化
1 parenta5f8d15 commit10c5b46

File tree

6 files changed

+91
-54
lines changed

6 files changed

+91
-54
lines changed

‎maxkey-starter/maxkey-starter-passkey/src/main/java/org/dromara/maxkey/passkey/config/PasskeyProperties.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
/**
2222
* Passkey配置属性
2323
*/
24-
@ConfigurationProperties(prefix ="maxkey.passkey")
24+
@ConfigurationProperties(prefix ="maxkey.login.passkey")
2525
publicclassPasskeyProperties {
2626

2727
/**

‎maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/login/login.component.html‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -101,12 +101,12 @@
101101
{{ 'app.login.login' | i18n }}
102102
</button>
103103
</nz-form-item>
104-
<nz-form-item*ngIf="loginType == 'normal'">
105-
<buttonnz-buttontype="button"nzType="default"nzSize="large"(click)="passkeyLogin()"nzBlock>
106-
<inz-iconnzType="safety-certificate"nzTheme="outline"></i>
107-
{{ 'mxk.login.passkey-login' | i18n }}
108-
</button>
109-
</nz-form-item>
104+
<nz-form-item*ngIf="passkeyEnabled">
105+
<buttonnz-buttontype="button"nzType="default"nzSize="large"(click)="passkeyLogin()"nzBlock>
106+
<inz-iconnzType="safety-certificate"nzTheme="outline"></i>
107+
{{ 'mxk.login.passkey-login' | i18n }}
108+
</button>
109+
</nz-form-item>
110110
</form>
111111
<divclass="other"*ngIf="loginType == 'normal'">
112112
{{ 'app.login.sign-in-with' | i18n }}

‎maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/login/login.component.ts‎

Lines changed: 70 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ export class UserLoginComponent implements OnInit, OnDestroy {
5656
loading=false;
5757
passwordVisible=false;
5858
qrexpire=false;
59+
passkeyEnabled=false;
60+
passkeyAllowedOrigins=[];
5961
imageCaptcha='';
6062
captchaType='';
6163
state='';
@@ -136,6 +138,25 @@ export class UserLoginComponent implements OnInit, OnDestroy {
136138
this.socials=res.data.socials;
137139
this.state=res.data.state;
138140
this.captchaType=res.data.captcha;
141+
this.passkeyEnabled=res.data.passkeyEnabled;
142+
this.passkeyAllowedOrigins=res.data.passkeyAllowedOrigins;
143+
letpasskeyAllowedOriginsMatch=false;
144+
for(letallowedOriginofthis.passkeyAllowedOrigins){
145+
console.log(`passkey allowedOrigin${allowedOrigin}`);
146+
console.log(`location${window.location.href}`);
147+
if(
148+
window.location.href.startsWith('http://localhost')||
149+
(window.location.href.startsWith('https')&&window.location.href.indexOf(allowedOrigin)>-1)
150+
){
151+
console.log(window.location.href.indexOf(allowedOrigin)>-1);
152+
passkeyAllowedOriginsMatch=true;
153+
}
154+
}
155+
if(window.PublicKeyCredential&&this.passkeyEnabled&&passkeyAllowedOriginsMatch){
156+
this.passkeyEnabled=true;
157+
}else{
158+
this.passkeyEnabled=false;
159+
}
139160
if(this.captchaType==='NONE'){
140161
//清除校验规则
141162
this.form.get('captcha')?.clearValidators();
@@ -508,7 +529,7 @@ export class UserLoginComponent implements OnInit, OnDestroy {
508529
asyncpasskeyLogin():Promise<void>{
509530
console.log('=== PASSKEY LOGIN DEBUG START ===');
510531
console.log('Passkey usernameless login clicked at:',newDate().toISOString());
511-
532+
512533
try{
513534
// 检查浏览器是否支持 WebAuthn
514535
if(!window.PublicKeyCredential){
@@ -520,7 +541,7 @@ export class UserLoginComponent implements OnInit, OnDestroy {
520541

521542
this.loading=true;
522543
this.cdr.detectChanges();
523-
544+
524545
// 1. 调用后端 API 获取认证选项(不传递任何用户信息)
525546
console.log('Step 1: Requesting authentication options from backend...');
526547
letauthOptionsResponse;
@@ -535,47 +556,51 @@ export class UserLoginComponent implements OnInit, OnDestroy {
535556
}elseif(httpError.message){
536557
errorMessage=httpError.message;
537558
}
538-
559+
539560
// 检查是否是没有注册 Passkey 的错误
540-
if(errorMessage.includes('没有注册任何 Passkey')||
541-
errorMessage.includes('No Passkeys registered')||
542-
errorMessage.includes('还没有注册任何 Passkey')||
543-
errorMessage.includes('系统中还没有注册任何 Passkey')){
561+
if(
562+
errorMessage.includes('没有注册任何 Passkey')||
563+
errorMessage.includes('No Passkeys registered')||
564+
errorMessage.includes('还没有注册任何 Passkey')||
565+
errorMessage.includes('系统中还没有注册任何 Passkey')
566+
){
544567
// 直接显示友好提示并返回,不抛出错误避免被全局拦截器捕获
545568
this.msg.warning('还未注册 Passkey,请注册 Passkey');
546569
console.log('=== PASSKEY LOGIN DEBUG END ===');
547570
return;
548571
}
549572
thrownewError(errorMessage);
550573
}
551-
574+
552575
console.log('Backend auth options response:',authOptionsResponse);
553-
576+
554577
if(!authOptionsResponse||authOptionsResponse.code!==0){
555578
console.error('Failed to get auth options:',authOptionsResponse);
556579
// 检查是否是没有注册 Passkey 的错误
557580
consterrorMessage=authOptionsResponse?.message||'获取认证选项失败';
558-
if(errorMessage.includes('没有注册任何 Passkey')||
559-
errorMessage.includes('No Passkeys registered')||
560-
errorMessage.includes('还没有注册任何 Passkey')||
561-
errorMessage.includes('系统中还没有注册任何 Passkey')){
581+
if(
582+
errorMessage.includes('没有注册任何 Passkey')||
583+
errorMessage.includes('No Passkeys registered')||
584+
errorMessage.includes('还没有注册任何 Passkey')||
585+
errorMessage.includes('系统中还没有注册任何 Passkey')
586+
){
562587
// 直接显示友好提示并返回,不抛出错误避免被全局拦截器捕获
563588
this.msg.warning('还未注册 Passkey,请注册 Passkey');
564589
console.log('=== PASSKEY LOGIN DEBUG END ===');
565590
return;
566591
}
567592
thrownewError(errorMessage);
568593
}
569-
594+
570595
constauthOptions=authOptionsResponse.data;
571596
console.log('Auth options received:',authOptions);
572-
597+
573598
// 检查返回的数据是否有效
574599
if(!authOptions||!authOptions.challenge){
575600
console.error('Invalid auth options:',authOptions);
576601
thrownewError('服务器返回的认证选项无效');
577602
}
578-
603+
579604
// 2. 转换认证选项格式
580605
console.log('Step 2: Converting authentication options...');
581606
constconvertedOptions:PublicKeyCredentialRequestOptions={
@@ -596,30 +621,30 @@ export class UserLoginComponent implements OnInit, OnDestroy {
596621
// 3. 调用 WebAuthn API 进行认证
597622
console.log('Step 3: Calling WebAuthn API navigator.credentials.get()...');
598623
console.log('Available authenticators will be queried automatically');
599-
600-
constcredential=awaitnavigator.credentials.get({
624+
625+
constcredential=(awaitnavigator.credentials.get({
601626
publicKey:convertedOptions
602-
})asPublicKeyCredential;
627+
}))asPublicKeyCredential;
603628

604629
if(!credential){
605630
console.error('No credential returned from WebAuthn API');
606631
thrownewError('认证失败');
607632
}
608-
633+
609634
console.log('=== CREDENTIAL DEBUG INFO ===');
610635
console.log('Credential ID:',credential.id);
611636
console.log('Credential ID length:',credential.id.length);
612637
console.log('Credential type:',credential.type);
613638
console.log('Credential rawId length:',credential.rawId.byteLength);
614639
console.log('Credential rawId as base64:',this.arrayBufferToBase64(credential.rawId));
615-
640+
616641
// 验证 credential.id 和 rawId 的一致性
617642
constrawIdBase64=this.arrayBufferToBase64(credential.rawId);
618643
console.log('ID consistency check:');
619644
console.log(' credential.id:',credential.id);
620645
console.log(' rawId as base64:',rawIdBase64);
621646
console.log(' IDs match:',credential.id===rawIdBase64);
622-
647+
623648
constcredentialResponse=credential.responseasAuthenticatorAssertionResponse;
624649
console.log('Authenticator response type:',credentialResponse.constructor.name);
625650
console.log('User handle:',credentialResponse.userHandle ?this.arrayBufferToBase64(credentialResponse.userHandle) :'null');
@@ -644,10 +669,10 @@ export class UserLoginComponent implements OnInit, OnDestroy {
644669
signatureLength:requestPayload.signature.length,
645670
userHandle:requestPayload.userHandle
646671
});
647-
672+
648673
constfinishResponse=awaitthis.http.post<any>('/passkey/authentication/finish?_allow_anonymous=true',requestPayload).toPromise();
649674
console.log('Backend finish response:',finishResponse);
650-
675+
651676
if(!finishResponse||finishResponse.code!==0){
652677
console.error('Backend verification failed:',finishResponse);
653678
thrownewError(finishResponse?.message||'Passkey认证失败');
@@ -657,13 +682,13 @@ export class UserLoginComponent implements OnInit, OnDestroy {
657682
console.log('Step 5: Authentication successful, setting user info...');
658683
constauthResult=finishResponse.data;
659684
console.log('Auth result received:',authResult);
660-
685+
661686
this.msg.success(`Passkey 登录成功!欢迎${authResult.username||'用户'}`);
662-
687+
663688
// 清空路由复用信息
664689
console.log('Clearing reuse tab service...');
665690
this.reuseTabService.clear();
666-
691+
667692
// 设置用户Token信息
668693
if(authResult&&authResult.userId){
669694
console.log('Valid auth result with userId:',authResult.userId);
@@ -684,12 +709,12 @@ export class UserLoginComponent implements OnInit, OnDestroy {
684709
passwordSetType:authResult.passwordSetType||'normal',
685710
authorities:authResult.authorities||[]
686711
};
687-
712+
688713
console.log('Setting auth info:',userInfo);
689-
714+
690715
// 设置认证信息
691716
this.authnService.auth(userInfo);
692-
717+
693718
// 使用 navigate 方法进行跳转,它会处理 StartupService 的重新加载
694719
console.log('Navigating with auth result...');
695720
this.authnService.navigate(authResult);
@@ -698,28 +723,30 @@ export class UserLoginComponent implements OnInit, OnDestroy {
698723
console.error('Invalid auth result - missing userId:',authResult);
699724
thrownewError('认证成功但用户数据无效');
700725
}
701-
702726
}catch(error:any){
703727
console.error('=== PASSKEY LOGIN ERROR ===');
704728
console.error('Error type:',error.constructor.name);
705729
console.error('Error message:',error.message);
706730
console.error('Error stack:',error.stack);
707731
console.error('Full error object:',error);
708-
732+
709733
// 检查是否是没有注册 Passkey 的错误
710-
if(error.message&&(error.message.includes('PASSKEY_NOT_REGISTERED')||
711-
error.message.includes('没有找到可用的凭据')||
712-
error.message.includes('No credentials available')||
713-
error.message.includes('用户未注册')||
714-
error.message.includes('credential not found')||
715-
error.message.includes('没有注册任何 Passkey')||
716-
error.message.includes('No Passkeys registered')||
717-
error.message.includes('还没有注册任何 Passkey'))){
734+
if(
735+
error.message&&
736+
(error.message.includes('PASSKEY_NOT_REGISTERED')||
737+
error.message.includes('没有找到可用的凭据')||
738+
error.message.includes('No credentials available')||
739+
error.message.includes('用户未注册')||
740+
error.message.includes('credential not found')||
741+
error.message.includes('没有注册任何 Passkey')||
742+
error.message.includes('No Passkeys registered')||
743+
error.message.includes('还没有注册任何 Passkey'))
744+
){
718745
this.msg.warning('还未注册 Passkey,请注册 Passkey');
719746
console.log('=== PASSKEY LOGIN DEBUG END ===');
720747
return;
721748
}
722-
749+
723750
// 如果是 WebAuthn 相关错误,提供更详细的信息
724751
if(error.name){
725752
console.error('WebAuthn error name:',error.name);
@@ -747,10 +774,10 @@ export class UserLoginComponent implements OnInit, OnDestroy {
747774
break;
748775
default:
749776
console.error('Unknown WebAuthn error');
750-
this.msg.error('Passkey 登录失败:'+(error.message||'请重试或使用其他登录方式'));
777+
this.msg.error(`Passkey 登录失败:${error.message||'请重试或使用其他登录方式'}`);
751778
}
752779
}else{
753-
this.msg.error('Passkey 登录失败:'+(error.message||'请重试或使用其他登录方式'));
780+
this.msg.error(`Passkey 登录失败:${error.message||'请重试或使用其他登录方式'}`);
754781
}
755782
console.log('=== PASSKEY LOGIN DEBUG END ===');
756783
}finally{

‎maxkey-web-frontend/maxkey-web-app/src/style-icons-auto.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ import {
107107
FileProtectOutline,
108108
HistoryOutline,
109109
UserAddOutline,
110+
SafetyCertificateOutline,
111+
PlusCircleOutline,
110112
AuditOutline
111113
}from'@ant-design/icons-angular/icons';
112114
import{QR_DEFULAT_CONFIG}from'@delon/abc/qr';
@@ -199,5 +201,7 @@ export const ICONS_AUTO = [
199201
FileProtectOutline,
200202
HistoryOutline,
201203
UserAddOutline,
204+
SafetyCertificateOutline,
205+
PlusCircleOutline,
202206
AuditOutline
203207
];

‎maxkey-webs/maxkey-web-maxkey/src/main/java/org/dromara/maxkey/web/contorller/LoginEntryPoint.java‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
importorg.dromara.maxkey.constants.ConstsLoginType;
3434
importorg.dromara.maxkey.entity.*;
3535
importorg.dromara.maxkey.entity.idm.UserInfo;
36+
importorg.dromara.maxkey.passkey.config.PasskeyProperties;
3637
importorg.dromara.maxkey.password.onetimepwd.AbstractOtpAuthn;
3738
importorg.dromara.maxkey.password.sms.SmsOtpAuthnService;
3839
importorg.dromara.maxkey.persistence.service.SocialsAssociatesService;
@@ -72,6 +73,9 @@ public class LoginEntryPoint {
7273

7374
@Autowired
7475
ApplicationConfigapplicationConfig;
76+
77+
@Autowired
78+
PasskeyPropertiespasskeyProperties;
7579

7680
@Autowired
7781
AbstractAuthenticationProviderauthenticationProvider ;
@@ -134,6 +138,8 @@ public Message<?> get(@RequestParam(value = "remember_me", required = false) Str
134138
model.put("otpType",tfaOtpAuthn.getOtpType());
135139
model.put("otpInterval",tfaOtpAuthn.getInterval());
136140
}
141+
model.put("passkeyEnabled",passkeyProperties.isEnabled());
142+
model.put("passkeyAllowedOrigins",passkeyProperties.getRelyingParty().getAllowedOrigins());
137143

138144
if(applicationConfig.getLoginConfig().isKerberos()){
139145
model.put("userDomainUrlJson",kerberosService.buildKerberosProxys());

‎maxkey-webs/maxkey-web-maxkey/src/main/resources/application-maxkey.properties‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,10 @@ maxkey.notices.visible =false
9090
############################################################################
9191
# Passkey Configuration #
9292
############################################################################
93-
maxkey.passkey.enabled=true
94-
maxkey.passkey.relying-party.name=MaxKey
95-
maxkey.passkey.relying-party.id=localhost
96-
maxkey.passkey.relying-party.allowed-origins=http://localhost:8527,http://localhost:8080,http://localhost
93+
maxkey.login.passkey.enabled=true
94+
maxkey.login.passkey.relying-party.name=MaxKey
95+
maxkey.login.passkey.relying-party.id=localhost
96+
maxkey.login.passkey.relying-party.allowed-origins=http://localhost
9797
############################################################################
9898
#ssl configuration #
9999
############################################################################

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp