Jelajahi Sumber

ForgotPassword

MaxKey 3 tahun lalu
induk
melakukan
7ddde38019
25 mengubah file dengan 599 tambahan dan 160 penghapusan
  1. 12 7
      maxkey-authentications/maxkey-authentication-captcha/src/main/java/org/maxkey/web/contorller/ImageCaptcha.java
  2. 1 1
      maxkey-authentications/maxkey-authentication-captcha/src/main/java/org/maxkey/web/contorller/ImageCaptchaEndpoint.java
  3. 0 3
      maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/AbstractAuthenticationProvider.java
  4. 28 2
      maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/jwt/AuthJwtService.java
  5. 1 4
      maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/provider/MfaAuthenticationProvider.java
  6. 3 18
      maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/provider/NormalAuthenticationProvider.java
  7. 1 1
      maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/web/AuthorizationUtils.java
  8. 4 5
      maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/autoconfigure/AuthenticationAutoConfiguration.java
  9. 26 0
      maxkey-authentications/maxkey-authentication-otp/src/main/java/org/maxkey/password/onetimepwd/OtpAuthnService.java
  10. 5 0
      maxkey-core/src/main/java/org/maxkey/entity/ChangePassword.java
  11. 1 0
      maxkey-core/src/main/java/org/maxkey/persistence/repository/PasswordPolicyRepository.java
  12. 1 1
      maxkey-web-frontend/maxkey-web-app/src/app/app.module.ts
  13. 120 0
      maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/forgot/forgot.component.html
  14. 21 0
      maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/forgot/forgot.component.less
  15. 25 0
      maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/forgot/forgot.component.spec.ts
  16. 176 0
      maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/forgot/forgot.component.ts
  17. 1 1
      maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/login/login.component.html
  18. 6 0
      maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/passport-routing.module.ts
  19. 5 3
      maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/passport.module.ts
  20. 20 0
      maxkey-web-frontend/maxkey-web-app/src/app/service/forgot-password.service.ts
  21. 17 0
      maxkey-web-frontend/maxkey-web-app/src/assets/i18n/en-US.json
  22. 18 1
      maxkey-web-frontend/maxkey-web-app/src/assets/i18n/zh-CN.json
  23. 8 21
      maxkey-webs/maxkey-web-maxkey/src/main/java/org/maxkey/web/contorller/ChangePasswodController.java
  24. 99 89
      maxkey-webs/maxkey-web-maxkey/src/main/java/org/maxkey/web/contorller/ForgotPasswordContorller.java
  25. 0 3
      maxkey-webs/maxkey-web-maxkey/src/main/java/org/maxkey/web/contorller/SafeController.java

+ 12 - 7
maxkey-authentications/maxkey-authentication-captcha/src/main/java/org/maxkey/web/contorller/ImageCaptcha.java

@@ -17,21 +17,26 @@
 package org.maxkey.web.contorller;
 
 public class ImageCaptcha {
-	String id;
+	String state;
 	String image;
 	
-	public ImageCaptcha(String id, String image) {
+	public ImageCaptcha(String state, String image) {
 		super();
-		this.id = id;
+		this.state = state;
 		this.image = image;
 	}
 	
-	public String getId() {
-		return id;
+
+	public String getState() {
+		return state;
 	}
-	public void setId(String id) {
-		this.id = id;
+
+
+	public void setState(String state) {
+		this.state = state;
 	}
+
+
 	public String getImage() {
 		return image;
 	}

+ 1 - 1
maxkey-authentications/maxkey-authentication-captcha/src/main/java/org/maxkey/web/contorller/ImageCaptchaEndpoint.java

@@ -88,7 +88,7 @@ public class ImageCaptchaEndpoint {
             }else {
             	state = authJwtService.genJwt();
             }
-            kaptchaKey = authJwtService.resolveTicket(state);
+            kaptchaKey = authJwtService.resolveJWTID(state);
             _logger.trace("kaptchaKey {} , Captcha Text is {}" ,kaptchaKey, kaptchaValue);
            
             momentaryService.put("", kaptchaKey, kaptchaValue);

+ 0 - 3
maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/AbstractAuthenticationProvider.java

@@ -30,7 +30,6 @@ import org.maxkey.constants.ConstsStatus;
 import org.maxkey.entity.UserInfo;
 import org.maxkey.password.onetimepwd.AbstractOtpAuthn;
 import org.maxkey.password.onetimepwd.OtpAuthnService;
-import org.maxkey.persistence.MomentaryService;
 import org.maxkey.web.WebConstants;
 import org.maxkey.web.WebContext;
 import org.slf4j.Logger;
@@ -70,8 +69,6 @@ public abstract class AbstractAuthenticationProvider {
 
     protected OnlineTicketService onlineTicketServices;
     
-    protected MomentaryService momentaryService;
-    
     protected AuthJwtService authJwtService;
     
     public static  ArrayList<GrantedAuthority> grantedAdministratorsAuthoritys = new ArrayList<GrantedAuthority>();

+ 28 - 2
maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/jwt/AuthJwtService.java

@@ -19,11 +19,14 @@ package org.maxkey.authn.jwt;
 
 import java.text.ParseException;
 import java.util.Date;
+
+import org.apache.commons.lang3.StringUtils;
 import org.joda.time.DateTime;
 import org.maxkey.authn.SigninPrincipal;
 import org.maxkey.configuration.AuthJwkConfig;
 import org.maxkey.crypto.jwt.HMAC512Service;
 import org.maxkey.entity.UserInfo;
+import org.maxkey.persistence.MomentaryService;
 import org.maxkey.web.WebContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -44,6 +47,8 @@ public class AuthJwtService {
 	AuthJwkConfig authJwkConfig;
 	
 	CongressService congressService;
+	
+	MomentaryService momentaryService;
 
 	public AuthJwtService(AuthJwkConfig authJwkConfig) throws JOSEException {
 		this.authJwkConfig = authJwkConfig;
@@ -51,12 +56,16 @@ public class AuthJwtService {
 		this.hmac512Service = new HMAC512Service(authJwkConfig.getSecret());
 	}
 	
-	public AuthJwtService(AuthJwkConfig authJwkConfig,CongressService congressService) throws JOSEException {
+	public AuthJwtService(AuthJwkConfig authJwkConfig,CongressService congressService,MomentaryService momentaryService) throws JOSEException {
 		this.authJwkConfig = authJwkConfig;
 		
 		this.congressService = congressService;
 		
+		this.momentaryService = momentaryService;
+		
 		this.hmac512Service = new HMAC512Service(authJwkConfig.getSecret());
+		
+		
 	}
 	
 	/**
@@ -156,7 +165,7 @@ public class AuthJwtService {
 		return signedJWT.getJWTClaimsSet();
 	}
 	
-	public String resolveTicket(String authToken) throws ParseException {
+	public String resolveJWTID(String authToken) throws ParseException {
 		JWTClaimsSet claims = resolve(authToken); 
 		return claims.getJWTID();
 	}
@@ -177,4 +186,21 @@ public class AuthJwtService {
 		return authJwt;
 	}
 	
+	public boolean validateCaptcha(String state,String captcha) {
+    	try {
+			String jwtId = resolveJWTID(state);
+			if(StringUtils.isNotBlank(jwtId) &&StringUtils.isNotBlank(captcha)) {
+				Object momentaryCaptcha = momentaryService.get("", jwtId);
+		        _logger.debug("captcha : {}, momentary Captcha : {}" ,captcha, momentaryCaptcha);
+		        if (!StringUtils.isBlank(captcha) && captcha.equals(momentaryCaptcha.toString())) {
+		        	momentaryService.remove("", jwtId);
+		        	return true;
+		        }
+			}
+		} catch (ParseException e) {
+			 _logger.debug("Exception ",e);
+		}
+    	 return false;
+    }
+	
 }

+ 1 - 4
maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/provider/MfaAuthenticationProvider.java

@@ -26,7 +26,6 @@ import org.maxkey.configuration.ApplicationConfig;
 import org.maxkey.constants.ConstsLoginType;
 import org.maxkey.entity.Institutions;
 import org.maxkey.entity.UserInfo;
-import org.maxkey.persistence.MomentaryService;
 import org.maxkey.web.WebConstants;
 import org.maxkey.web.WebContext;
 import org.slf4j.Logger;
@@ -59,13 +58,11 @@ public class MfaAuthenticationProvider extends AbstractAuthenticationProvider {
     		AbstractAuthenticationRealm authenticationRealm,
     		ApplicationConfig applicationConfig,
     	    OnlineTicketService onlineTicketServices,
-    	    AuthJwtService authJwtService,
-    	    MomentaryService momentaryService) {
+    	    AuthJwtService authJwtService) {
 		this.authenticationRealm = authenticationRealm;
 		this.applicationConfig = applicationConfig;
 		this.onlineTicketServices = onlineTicketServices;
 		this.authJwtService = authJwtService;
-		this.momentaryService = momentaryService;
 	}
 
     @Override

+ 3 - 18
maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/provider/NormalAuthenticationProvider.java

@@ -18,7 +18,6 @@
 package org.maxkey.authn.provider;
 
 import java.text.ParseException;
-import org.apache.commons.lang3.StringUtils;
 import org.maxkey.authn.AbstractAuthenticationProvider;
 import org.maxkey.authn.LoginCredential;
 import org.maxkey.authn.jwt.AuthJwtService;
@@ -28,7 +27,6 @@ import org.maxkey.configuration.ApplicationConfig;
 import org.maxkey.constants.ConstsLoginType;
 import org.maxkey.entity.Institutions;
 import org.maxkey.entity.UserInfo;
-import org.maxkey.persistence.MomentaryService;
 import org.maxkey.web.WebConstants;
 import org.maxkey.web.WebContext;
 import org.slf4j.Logger;
@@ -60,13 +58,11 @@ public class NormalAuthenticationProvider extends AbstractAuthenticationProvider
     		AbstractAuthenticationRealm authenticationRealm,
     		ApplicationConfig applicationConfig,
     	    OnlineTicketService onlineTicketServices,
-    	    AuthJwtService authJwtService,
-    	    MomentaryService momentaryService) {
+    	    AuthJwtService authJwtService) {
 		this.authenticationRealm = authenticationRealm;
 		this.applicationConfig = applicationConfig;
 		this.onlineTicketServices = onlineTicketServices;
 		this.authJwtService = authJwtService;
-		this.momentaryService = momentaryService;
 	}
 
     @Override
@@ -138,19 +134,8 @@ public class NormalAuthenticationProvider extends AbstractAuthenticationProvider
      */
     protected void captchaValid(String state ,String captcha) throws ParseException {
         // for basic
-    	String ticket = authJwtService.resolveTicket(state);
-    	if(ticket == null) {
+    	if(!authJwtService.validateCaptcha(state,captcha)) {
     		throw new BadCredentialsException(WebContext.getI18nValue("login.error.captcha"));
-    	}
-    	Object momentaryCaptcha = momentaryService.get("", ticket);
-        _logger.info("captcha : {} , momentary Captcha : {} " ,captcha, momentaryCaptcha);
-        if (StringUtils.isBlank(captcha) || !captcha.equals(momentaryCaptcha.toString())) {
-            _logger.debug("login captcha valid error.");
-            throw new BadCredentialsException(WebContext.getI18nValue("login.error.captcha"));
-        }
-        momentaryService.remove("", ticket);
+    	}        
     }
-
-   
-  
 }

+ 1 - 1
maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/web/AuthorizationUtils.java

@@ -73,7 +73,7 @@ public class AuthorizationUtils {
 			AuthJwtService authJwtService,
 			OnlineTicketService onlineTicketService) throws ParseException {
 		if(authJwtService.validateJwtToken(authorization)) {
-			String ticket = authJwtService.resolveTicket(authorization);
+			String ticket = authJwtService.resolveJWTID(authorization);
 			OnlineTicket onlineTicket = onlineTicketService.get(ticket);
 			if(onlineTicket != null) {
 				setAuthentication(onlineTicket.getAuthentication());

+ 4 - 5
maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/autoconfigure/AuthenticationAutoConfiguration.java

@@ -87,16 +87,14 @@ public class AuthenticationAutoConfiguration  implements InitializingBean {
     		AbstractAuthenticationRealm authenticationRealm,
     		ApplicationConfig applicationConfig,
     	    OnlineTicketService onlineTicketServices,
-    	    AuthJwtService authJwtService,
-    	    MomentaryService momentaryService
+    	    AuthJwtService authJwtService
     		) {
     	_logger.debug("init authentication Provider .");
     	return new NormalAuthenticationProvider(
         		authenticationRealm,
         		applicationConfig,
         		onlineTicketServices,
-        		authJwtService,
-        		momentaryService
+        		authJwtService
         	);
     }
     
@@ -134,6 +132,7 @@ public class AuthenticationAutoConfiguration  implements InitializingBean {
     public AuthJwtService authJwtService(
     		AuthJwkConfig authJwkConfig,
     		RedisConnectionFactory redisConnFactory,
+    		MomentaryService  momentaryService,
     		@Value("${maxkey.server.persistence}") int persistence) throws JOSEException {
     	CongressService congressService;
     	if (persistence == ConstsPersistence.REDIS) {
@@ -142,7 +141,7 @@ public class AuthenticationAutoConfiguration  implements InitializingBean {
     		congressService = new InMemoryCongressService();
     	}
     	
-    	AuthJwtService authJwtService = new AuthJwtService(authJwkConfig,congressService);
+    	AuthJwtService authJwtService = new AuthJwtService(authJwkConfig,congressService,momentaryService);
     	
     	return authJwtService;
     }

+ 26 - 0
maxkey-authentications/maxkey-authentication-otp/src/main/java/org/maxkey/password/onetimepwd/OtpAuthnService.java

@@ -124,6 +124,32 @@ public class OtpAuthnService {
     	}
     	return otpAuthn;
     }
+	
+	public AbstractOtpAuthn getMailOtpAuthn(String instId) {
+		AbstractOtpAuthn otpAuthn = otpAuthnStore.getIfPresent(instId);
+    	if(otpAuthn == null) {
+			EmailSenders emailSender = 
+					emailSendersService.findOne("where instid = ? ", new Object[]{instId}, new int[]{Types.VARCHAR});
+			
+			String credentials = PasswordReciprocal.getInstance().decoder(emailSender.getCredentials());
+			EmailConfig emailConfig = 
+							new EmailConfig(
+									emailSender.getAccount(),
+									credentials,
+									emailSender.getSmtpHost(),
+									emailSender.getPort(),
+									ConstsBoolean.isTrue(emailSender.getSslSwitch()),
+									emailSender.getSender());
+			MailOtpAuthn mailOtpAuthn = new MailOtpAuthn(emailConfig);
+			mailOtpAuthn.setInterval(60 * 5);//5 minute
+			if(redisOptTokenStore != null) {
+				mailOtpAuthn.setOptTokenStore(redisOptTokenStore);
+			}
+			otpAuthn = mailOtpAuthn;
+    	}
+		otpAuthnStore.put(instId, otpAuthn);	
+		return otpAuthn;
+	}
 
 	public void setRedisOptTokenStore(RedisOtpTokenStore redisOptTokenStore) {
 		this.redisOptTokenStore = redisOptTokenStore;

+ 5 - 0
maxkey-core/src/main/java/org/maxkey/entity/ChangePassword.java

@@ -67,6 +67,11 @@ public class ChangePassword extends JpaBaseEntity{
 		this.setInstId(userInfo.getInstId());
 	}
 	
+	public void clearPassword() {
+		this.password ="";
+		this.decipherable = "";
+	}
+	
 	/**
 	 * @return the id
 	 */

+ 1 - 0
maxkey-core/src/main/java/org/maxkey/persistence/repository/PasswordPolicyRepository.java

@@ -152,6 +152,7 @@ public class PasswordPolicyRepository {
   
  
    public ArrayList<Rule> getPasswordPolicyRuleList() {
+	   getPasswordPolicy();
 		return passwordPolicyRuleList;
 	}
 

+ 1 - 1
maxkey-web-frontend/maxkey-web-app/src/app/app.module.ts

@@ -106,4 +106,4 @@ import { Observable } from 'rxjs';
   providers: [...LANG_PROVIDES, ...INTERCEPTOR_PROVIDES, ...I18NSERVICE_PROVIDES, ...APPINIT_PROVIDES],
   bootstrap: [AppComponent]
 })
-export class AppModule {}
+export class AppModule { }

+ 120 - 0
maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/forgot/forgot.component.html

@@ -0,0 +1,120 @@
+<h3>{{ 'mxk.forgot.forgot' | i18n }}</h3>
+<form nz-form [formGroup]="formGroup" role="form">
+  <nz-form-item style="width: 100%">
+    <nz-steps nzType="navigation" [nzCurrent]="step" style="width: 100%">
+      <nz-step nzTitle="{{ 'mxk.forgot.step1' | i18n }}" nzDescription=""> </nz-step>
+      <nz-step nzTitle="{{ 'mxk.forgot.step2' | i18n }}" nzDescription=""></nz-step>
+    </nz-steps>
+  </nz-form-item>
+  <nz-form-item style="width: 100%" *ngIf="step === 0">
+    <nz-form-item style="width: 100%">
+      <nz-radio-group
+        [(ngModel)]="forgotType"
+        [ngModelOptions]="{ standalone: true }"
+        style="margin-bottom: 8px; width: 100%"
+        nzSize="large"
+        nzButtonStyle="solid"
+        (ngModelChange)="ngModelChange()"
+      >
+        <label nz-radio-button [nzValue]="'mobile'" style="width: 50%">{{ 'mxk.forgot.type.mobile' | i18n }}</label>
+        <label nz-radio-button [nzValue]="'email'" style="width: 50%">{{ 'mxk.forgot.type.email' | i18n }}</label>
+      </nz-radio-group>
+    </nz-form-item>
+    <nz-form-item style="width: 100%" *ngIf="forgotType === 'email'">
+      <nz-form-control nzErrorTip="">
+        <nz-input-group nzSize="large" nzPrefixIcon="mail">
+          <input nz-input formControlName="email" placeholder="{{ 'mxk.forgot.email' | i18n }}" />
+        </nz-input-group>
+      </nz-form-control>
+    </nz-form-item>
+    <nz-form-item style="width: 100%" *ngIf="forgotType === 'mobile'">
+      <nz-form-control [nzErrorTip]="">
+        <nz-input-group nzSize="large" nzPrefixIcon="mobile">
+          <input nz-input formControlName="mobile" placeholder="{{ 'mxk.forgot.mobile' | i18n }}" />
+        </nz-input-group>
+        <ng-template #mobileErrorTip let-i>
+          <ng-container *ngIf="i.errors.required">
+            {{ 'validation.phone-number.required' | i18n }}
+          </ng-container>
+          <ng-container *ngIf="i.errors.pattern">
+            {{ 'validation.phone-number.wrong-format' | i18n }}
+          </ng-container>
+        </ng-template>
+      </nz-form-control>
+    </nz-form-item>
+    <nz-form-item style="width: 100%">
+      <nz-form-control nzErrorTip="">
+        <nz-input-group nzSearch nzSize="large" nzPrefixIcon="lock" nzSearch [nzAddOnAfter]="suffixImageCaptchaButton">
+          <input type="text" formControlName="captcha" nz-input placeholder="{{ 'mxk.forgot.captcha' | i18n }}" />
+        </nz-input-group>
+        <ng-template #suffixImageCaptchaButton>
+          <img src="{{ imageCaptcha }}" (click)="getImageCaptcha()" />
+        </ng-template>
+      </nz-form-control>
+    </nz-form-item>
+    <nz-form-item style="width: 100%">
+      <nz-form-control [nzErrorTip]="'' | i18n">
+        <nz-input-group nzSize="large" nzPrefixIcon="mail" nzSearch [nzAddOnAfter]="suffixSendOtpCodeButton">
+          <input nz-input formControlName="otpCaptcha" placeholder="{{ 'mxk.login.text.captcha' | i18n }}" />
+        </nz-input-group>
+        <ng-template #suffixSendOtpCodeButton>
+          <button type="button" nz-button nzSize="large" (click)="sendOtpCode()" [disabled]="count > 0" nzBlock [nzLoading]="loading">
+            {{ count ? count + 's' : ('mxk.forgot.sendCaptcha' | i18n) }}
+          </button>
+        </ng-template>
+      </nz-form-control>
+    </nz-form-item>
+    <nz-form-item style="width: 100%">
+      <button nz-button nzType="primary" nzSize="large" class="submit" (click)="onNextReset($event)">
+        {{ 'mxk.forgot.next' | i18n }}
+      </button>
+      <a class="login" routerLink="/passport/login">{{ 'mxk.forgot.login' | i18n }}</a>
+    </nz-form-item>
+  </nz-form-item>
+  <nz-form-item style="width: 100%" *ngIf="step === 1">
+    <nz-form-item style="width: 100%">
+      <nz-form-label nzRequired nzFor="password">{{ 'mxk.password.password' | i18n }}</nz-form-label>
+    </nz-form-item>
+    <nz-form-item style="width: 100%">
+      <nz-form-control nzErrorTip="The input is not valid password!">
+        <nz-input-group nzSize="large" [nzSuffix]="suffixPasswordTemplate" style="width: 100%">
+          <input
+            [type]="passwordVisible ? 'text' : 'password'"
+            nz-input
+            placeholder="new password"
+            [(ngModel)]="form.model.password"
+            formControlName="password"
+            id="password"
+          />
+        </nz-input-group>
+        <ng-template #suffixPasswordTemplate>
+          <i nz-icon [nzType]="passwordVisible ? 'eye-invisible' : 'eye'" (click)="passwordVisible = !passwordVisible"></i>
+        </ng-template>
+      </nz-form-control>
+    </nz-form-item>
+
+    <nz-form-item style="width: 100%">
+      <nz-form-label nzRequired nzFor="confirmPassword">{{ 'mxk.password.confirmPassword' | i18n }}</nz-form-label>
+    </nz-form-item>
+    <nz-form-item style="width: 100%">
+      <nz-form-control nzErrorTip="The input is not valid confirmPassword!">
+        <input
+          nz-input
+          type="password"
+          nzSize="large"
+          placeholder="confirm password"
+          [(ngModel)]="form.model.confirmPassword"
+          formControlName="confirmPassword"
+          name="confirmPassword"
+          id="confirmPassword"
+        />
+      </nz-form-control>
+    </nz-form-item>
+    <nz-form-item style="width: 100%">
+      <button nz-button nzType="primary" nzSize="large" class="submit" (click)="onSubmit($event)">
+        {{ 'mxk.forgot.submit' | i18n }}
+      </button>
+      <a class="login" routerLink="/passport/login">{{ 'mxk.forgot.login' | i18n }}</a>
+    </nz-form-item>
+  </nz-form-item>
+</form>

+ 21 - 0
maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/forgot/forgot.component.less

@@ -0,0 +1,21 @@
+@import '@delon/theme/index';
+
+:host {
+    display: block;
+    width: 460px;
+    margin: 0 auto;
+  
+  
+    ::ng-deep {
+      .ant-tabs .ant-tabs-bar {
+        margin-bottom: 24px;
+        text-align: center;
+        border-bottom: 0;
+      }
+    }
+
+    .login {
+      float: right;
+      line-height: @btn-height-lg;
+    }
+}

+ 25 - 0
maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/forgot/forgot.component.spec.ts

@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { ForgotComponent } from './forgot.component';
+
+describe('ForgotComponent', () => {
+  let component: ForgotComponent;
+  let fixture: ComponentFixture<ForgotComponent>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      declarations: [ ForgotComponent ]
+    })
+    .compileComponents();
+  });
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(ForgotComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 176 - 0
maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/forgot/forgot.component.ts

@@ -0,0 +1,176 @@
+import { Component, OnInit, ChangeDetectorRef } from '@angular/core';
+import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { Router, ActivatedRoute } from '@angular/router';
+import { NzMessageService } from 'ng-zorro-antd/message';
+import { NzStepsModule } from 'ng-zorro-antd/steps';
+
+import { ChangePassword } from '../../../entity/ChangePassword';
+import { ForgotPasswordService } from '../../../service/forgot-password.service';
+import { ImageCaptchaService } from '../../../service/image-captcha.service';
+import { PasswordService } from '../../../service/password.service';
+
+@Component({
+  selector: 'app-forgot',
+  templateUrl: './forgot.component.html',
+  styleUrls: ['./forgot.component.less']
+})
+export class ForgotComponent implements OnInit {
+  form: {
+    submitting: boolean;
+    model: ChangePassword;
+  } = {
+      submitting: false,
+      model: new ChangePassword()
+    };
+
+  imageCaptcha = '';
+  formGroup: FormGroup;
+  state = '';
+  step = 0;
+  forgotType = 'mobile';
+  passwordVisible = false;
+  loading = false;
+  count = 0;
+  interval$: any;
+  userId = '';
+  username = '';
+
+  constructor(
+    fb: FormBuilder,
+    private forgotPasswordService: ForgotPasswordService,
+    private imageCaptchaService: ImageCaptchaService,
+    private msg: NzMessageService,
+    private cdr: ChangeDetectorRef
+  ) {
+    this.formGroup = fb.group({
+      mobile: [null, [Validators.required, Validators.pattern(/^1\d{10}$/)]],
+      email: [null, [Validators.required]],
+      password: [null, [Validators.required]],
+      captcha: [null, [Validators.required]],
+      otpCaptcha: [null, [Validators.required]],
+      confirmPassword: [null, [Validators.required]]
+    });
+  }
+
+  get email(): AbstractControl {
+    return this.formGroup.get('email')!;
+  }
+  get password(): AbstractControl {
+    return this.formGroup.get('password')!;
+  }
+  get confirmPassword(): AbstractControl {
+    return this.formGroup.get('confirmPassword')!;
+  }
+  get mobile(): AbstractControl {
+    return this.formGroup.get('mobile')!;
+  }
+  get captcha(): AbstractControl {
+    return this.formGroup.get('captcha')!;
+  }
+
+  get otpCaptcha(): AbstractControl {
+    return this.formGroup.get('otpCaptcha')!;
+  }
+
+  ngOnInit(): void {
+    this.getImageCaptcha();
+  }
+
+  getImageCaptcha(): void {
+    this.imageCaptchaService.captcha({}).subscribe(res => {
+      this.imageCaptcha = res.data.image;
+      this.state = res.data.state;
+      this.cdr.detectChanges();
+    });
+  }
+
+  //send sms
+  sendOtpCode(): void {
+    if (this.forgotType == 'mobile' && this.mobile.invalid) {
+      this.mobile.markAsDirty({ onlySelf: true });
+      this.mobile.updateValueAndValidity({ onlySelf: true });
+      return;
+    }
+
+    if (this.forgotType == 'email' && this.email.invalid) {
+      this.email.markAsDirty({ onlySelf: true });
+      this.email.updateValueAndValidity({ onlySelf: true });
+      return;
+    }
+    if (this.forgotType == 'mobile') {
+      this.forgotPasswordService
+        .produceOtp({ mobile: this.mobile.value, state: this.state, captcha: this.captcha.value })
+        .subscribe(res => {
+          if (res.code !== 0) {
+            this.msg.success(`发送失败`);
+            this.getImageCaptcha();
+            this.cdr.detectChanges();
+          }
+          this.userId = res.data.userId;
+          this.username = res.data.username;
+          //console.log(res.data);
+        });
+    } else if (this.forgotType == 'email') {
+      this.forgotPasswordService
+        .produceEmailOtp({ email: this.email.value, state: this.state, captcha: this.captcha.value })
+        .subscribe(res => {
+          if (res.code !== 0) {
+            this.msg.success(`发送失败`);
+            this.getImageCaptcha();
+            this.cdr.detectChanges();
+          }
+          this.userId = res.data.userId;
+          this.username = res.data.username;
+          //console.log(res.data);
+        });
+    }
+    this.count = 59;
+    this.interval$ = setInterval(() => {
+      this.count -= 1;
+      if (this.count <= 0) {
+        clearInterval(this.interval$);
+      }
+      this.cdr.detectChanges();
+    }, 1000);
+  }
+
+  onNextReset(e: MouseEvent) {
+    if (this.otpCaptcha.invalid) {
+      this.otpCaptcha.markAsDirty({ onlySelf: true });
+      this.otpCaptcha.updateValueAndValidity({ onlySelf: true });
+      return;
+    }
+    this.step = 1;
+  }
+
+  onSubmit(e: MouseEvent) {
+    this.forgotPasswordService
+      .setPassWord({
+        forgotType: this.forgotType,
+        userId: this.userId,
+        username: this.username,
+        password: this.password.value,
+        confirmPassword: this.confirmPassword.value,
+        otpCaptcha: this.otpCaptcha.value,
+        state: this.state
+      })
+      .subscribe(res => {
+        if (res.code !== 0) {
+          this.msg.success(`密码修改失败`);
+          this.getImageCaptcha();
+          this.step = 0;
+          this.cdr.detectChanges();
+        }
+        this.msg.success(`密码修改成功`);
+      });
+  }
+
+  ngModelChange() {
+    if (this.forgotType == 'email') {
+      this.mobile.reset();
+    }
+    if (this.forgotType == 'mobile') {
+      this.email.reset();
+    }
+  }
+}

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

@@ -89,7 +89,7 @@
       <label nz-checkbox formControlName="remember">{{ 'mxk.login.remember-me' | i18n }}</label>
     </nz-col>
     <nz-col [nzSpan]="12" class="text-right">
-      <a class="forgot" routerLink="/passport/register">{{ 'mxk.login.forgot-password' | i18n }}</a></nz-col
+      <a class="forgot" routerLink="/passport/forgot">{{ 'mxk.login.forgot-password' | i18n }}</a></nz-col
     >
   </nz-form-item>
   <nz-form-item *ngIf="loginType == 'normal' || loginType == 'mobile'">

+ 6 - 0
maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/passport-routing.module.ts

@@ -3,6 +3,7 @@ import { RouterModule, Routes } from '@angular/router';
 
 import { LayoutPassportComponent } from '../../layout/passport/passport.component';
 import { CallbackComponent } from './callback.component';
+import { ForgotComponent } from './forgot/forgot.component';
 import { UserLockComponent } from './lock/lock.component';
 import { UserLoginComponent } from './login/login.component';
 import { UserRegisterResultComponent } from './register-result/register-result.component';
@@ -30,6 +31,11 @@ const routes: Routes = [
         data: { title: '注册结果', titleI18n: 'app.register.register' }
       },
       {
+        path: 'forgot',
+        component: ForgotComponent,
+        data: { title: '忘记密码', titleI18n: 'app.forgot.forgot' }
+      },
+      {
         path: 'lock',
         component: UserLockComponent,
         data: { title: '锁屏', titleI18n: 'app.lock' }

+ 5 - 3
maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/passport.module.ts

@@ -1,7 +1,9 @@
 import { NgModule } from '@angular/core';
 import { SharedModule } from '@shared';
+import { NzStepsModule } from 'ng-zorro-antd/steps';
 
 import { CallbackComponent } from './callback.component';
+import { ForgotComponent } from './forgot/forgot.component';
 import { UserLockComponent } from './lock/lock.component';
 import { UserLoginComponent } from './login/login.component';
 import { PassportRoutingModule } from './passport-routing.module';
@@ -11,7 +13,7 @@ import { UserRegisterComponent } from './register/register.component';
 const COMPONENTS = [UserLoginComponent, UserRegisterResultComponent, UserRegisterComponent, UserLockComponent, CallbackComponent];
 
 @NgModule({
-  imports: [SharedModule, PassportRoutingModule],
-  declarations: [...COMPONENTS]
+  imports: [SharedModule, PassportRoutingModule, NzStepsModule],
+  declarations: [...COMPONENTS, ForgotComponent]
 })
-export class PassportModule {}
+export class PassportModule { }

+ 20 - 0
maxkey-web-frontend/maxkey-web-app/src/app/service/forgot-password.service.ts

@@ -0,0 +1,20 @@
+import { Injectable, Inject } from '@angular/core';
+import { _HttpClient, User } from '@delon/theme';
+@Injectable({
+  providedIn: 'root'
+})
+export class ForgotPasswordService {
+  constructor(private http: _HttpClient) { }
+
+  produceOtp(param: any) {
+    return this.http.get('/forgotpassword/produceOtp?_allow_anonymous=true', param);
+  }
+
+  produceEmailOtp(param: any) {
+    return this.http.get(`/forgotpassword/produceEmailOtp?_allow_anonymous=true`, param);
+  }
+
+  setPassWord(param: any) {
+    return this.http.get('/forgotpassword/setpassword?_allow_anonymous=true', param);
+  }
+}

+ 17 - 0
maxkey-web-frontend/maxkey-web-app/src/assets/i18n/en-US.json

@@ -21,6 +21,22 @@
       "text.captcha": "Captcha",
       "text.smscode": "Code"
     },
+    "forgot":{
+      "forgot":"Forgot Password",
+      "step1":"Authentication",
+      "step2":"Resetting Password",
+      "type.mobile":"By Mobile",
+      "type.email":"By Email",
+      "mobile":"Mobile Phone Number",
+      "email":"Email",
+      "captcha":"Picture Captcha",
+      "sendCaptcha":"Send Captcha",
+      "password":"New Password",
+      "confirmPassword":"Confirm Password",
+      "login":"Back Login",
+      "next":"Next",
+      "submit":"Confirm"
+    },
     "menu": {
       "applist": "Apps",
       "sessions": "Sessions",
@@ -807,6 +823,7 @@
   "app.login.text.password": "Password",
   "app.login.text.captcha": "CAPTCHA",
   "app.login.text.smscode": "Code",
+  "app.forgot.forgot":"Forgot Password",
   "app.register.register": "Register",
   "app.register.get-verification-code": "Get code",
   "app.register.sign-in": "Already have an account?",

+ 18 - 1
maxkey-web-frontend/maxkey-web-app/src/assets/i18n/zh-CN.json

@@ -16,11 +16,27 @@
       "signup": "用户注册",
       "login": "登录",
       "text.username": "用户名",
-      "text.mobile": "手机号",
+      "text.mobile": "手机号",
       "text.password": "密码",
       "text.captcha": "验证码",
       "text.smscode": "验证码"
     },
+    "forgot":{
+      "forgot":"忘记密码",
+      "step1":"验证身份",
+      "step2":"设置新密码",
+      "type.mobile":"手机找回",
+      "type.email":"邮箱找回",
+      "mobile":"手机号码",
+      "email":"邮箱",
+      "captcha":"图形验证码",
+      "sendCaptcha":"发送验证码",
+      "password":"新密码",
+      "confirmPassword":"确认新密码",
+      "login":"返回登录",
+      "next":"下一步",
+      "submit":"提交"
+    },
     "menu": {
       "applist": "应用",
       "sessions": "会话",
@@ -805,6 +821,7 @@
   "app.login.text.password": "密码",
   "app.login.text.captcha": "验证码",
   "app.login.text.smscode": "验证码",
+  "app.forgot.forgot":"忘记密码",
   "app.register.register": "注册",
   "app.register.get-verification-code": "获取验证码",
   "app.register.sign-in": "使用已有账户登录",

+ 8 - 21
maxkey-webs/maxkey-web-maxkey/src/main/java/org/maxkey/web/contorller/ChangePasswodController.java

@@ -1,5 +1,5 @@
 /*
- * Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
+ * Copyright [2022] [MaxKey of copyright http://www.maxkey.top]
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -17,31 +17,21 @@
 
 package org.maxkey.web.contorller;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
 import org.maxkey.authn.annotation.CurrentUser;
-import org.maxkey.constants.ConstsOperateMessage;
 import org.maxkey.constants.ConstsPasswordSetType;
-import org.maxkey.constants.ConstsTimeInterval;
 import org.maxkey.entity.ChangePassword;
+import org.maxkey.entity.Message;
 import org.maxkey.entity.UserInfo;
-import org.maxkey.persistence.repository.PasswordPolicyValidator;
 import org.maxkey.persistence.service.UserInfoService;
-import org.maxkey.web.WebConstants;
-import org.maxkey.web.WebContext;
-import org.maxkey.web.message.Message;
-import org.maxkey.web.message.MessageType;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.ModelAttribute;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.ResponseBody;
-import org.springframework.web.servlet.ModelAndView;
 
 @Controller
 @RequestMapping(value={"/config"})
@@ -52,8 +42,8 @@ public class ChangePasswodController {
 	private UserInfoService userInfoService;
 	
 	@ResponseBody
-	@RequestMapping(value="/changePassword") 
-	public Message changePasswod(
+	@RequestMapping(value = { "/changePassword" }, produces = {MediaType.APPLICATION_JSON_VALUE})
+	public ResponseEntity<?> changePasswod(
 			@RequestBody ChangePassword changePassword,
 			@CurrentUser UserInfo currentUser) {
 		
@@ -62,12 +52,9 @@ public class ChangePasswodController {
 			changePassword.setInstId(currentUser.getInstId());
 			changePassword.setPasswordSetType(ConstsPasswordSetType.PASSWORD_NORMAL);
 			if(userInfoService.changePassword(changePassword)) {
-				return  new Message(WebContext.getI18nValue(ConstsOperateMessage.UPDATE_SUCCESS),MessageType.success);
+				return new Message<ChangePassword>().buildResponse();
 			}else {
-				return  new Message(
-				        WebContext.getI18nValue(ConstsOperateMessage.UPDATE_ERROR)+"<br>"
-				        +WebContext.getAttribute(PasswordPolicyValidator.PASSWORD_POLICY_VALIDATE_RESULT),
-				        MessageType.error);
+				return new Message<ChangePassword>(Message.ERROR).buildResponse();
 			}	
 	}
 

+ 99 - 89
maxkey-webs/maxkey-web-maxkey/src/main/java/org/maxkey/web/contorller/ForgotPasswordContorller.java

@@ -1,5 +1,5 @@
 /*
- * Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
+ * Copyright [2022] [MaxKey of copyright http://www.maxkey.top]
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,22 +19,26 @@ package org.maxkey.web.contorller;
 
 import java.util.regex.Pattern;
 
+import org.apache.commons.lang3.StringUtils;
+import org.maxkey.authn.jwt.AuthJwtService;
 import org.maxkey.configuration.EmailConfig;
+import org.maxkey.entity.ChangePassword;
+import org.maxkey.entity.Message;
 import org.maxkey.entity.UserInfo;
 import org.maxkey.password.onetimepwd.AbstractOtpAuthn;
 import org.maxkey.password.onetimepwd.OtpAuthnService;
-import org.maxkey.persistence.repository.PasswordPolicyValidator;
 import org.maxkey.persistence.service.UserInfoService;
-import org.maxkey.web.WebConstants;
-import org.maxkey.web.WebContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.ModelAttribute;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.bind.annotation.ResponseBody;
 
 @Controller
 @RequestMapping(value = { "/forgotpassword" })
@@ -45,7 +49,7 @@ public class ForgotPasswordContorller {
             "^\\s*\\w+(?:\\.{0,1}[\\w-]+)*@[a-zA-Z0-9]+(?:[-.][a-zA-Z0-9]+)*\\.[a-zA-Z]+\\s*$");
     
     Pattern mobileRegex = Pattern.compile(
-            "^(13[4,5,6,7,8,9]|15[0,8,9,1,7]|188|187)\\\\d{8}$");
+            "^[1][3,4,5,7,8][0-9]{9}$");
     
     @Autowired
     EmailConfig emailConfig;
@@ -64,101 +68,107 @@ public class ForgotPasswordContorller {
     }
     
     @Autowired
-    private UserInfoService userInfoService;
-
+	AuthJwtService authJwtService;
+    
     @Autowired
-    @Qualifier("mailOtpAuthn")
-    protected AbstractOtpAuthn mailOtpAuthn;
+    UserInfoService userInfoService;
     
     @Autowired
     @Qualifier("otpAuthnService")
     OtpAuthnService otpAuthnService;
     
-
-    @RequestMapping(value = { "/forward" })
-    public ModelAndView forwardreg() {
-        _logger.debug("forgotpassword  /forgotpassword/forward.");
-        return new ModelAndView("forgotpassword/findpwd");
-    }
-
-    @RequestMapping(value = { "/emailmobile" })
-    public ModelAndView email(@RequestParam String emailMobile,@RequestParam String captcha) {
-        _logger.debug("forgotpassword  /forgotpassword/emailmobile.");
-        _logger.debug("emailMobile : " + emailMobile);
-        int forgotType = ForgotType.NOTFOUND;
-        UserInfo userInfo = null;
-        if (captcha != null && captcha
-                .equals(WebContext.getSession().getAttribute(
-                                WebConstants.KAPTCHA_SESSION_KEY).toString())) {            
-            if(mobileRegex.matcher(emailMobile).matches()) {
-            	forgotType = ForgotType.MOBILE;
-            }else if(emailRegex.matcher(emailMobile).matches()) {
-            	forgotType = ForgotType.EMAIL;
-            }else {
-            	forgotType = ForgotType.EMAIL;
-            	emailMobile =emailMobile + "@" + emailConfig.getSmtpHost().substring(emailConfig.getSmtpHost().indexOf(".")+1);
-            }
-            
-            userInfo = userInfoService.findByEmailMobile(emailMobile);
-            
-            if(null != userInfo) {
-	            if (forgotType == ForgotType.EMAIL ) {
-	            	mailOtpAuthn.produce(userInfo);
-	            }else if (forgotType == ForgotType.MOBILE) {
-	            	AbstractOtpAuthn smsOtpAuthn = otpAuthnService.getByInstId(userInfo.getInstId());
-	            	smsOtpAuthn.produce(userInfo);
-	            }
-            }
-           
-        }else {
-            _logger.debug("login captcha valid error.");
-            forgotType = ForgotType.CAPTCHAERROR;
+ 
+    
+    
+    @ResponseBody
+	@RequestMapping(value = { "/produceOtp" }, produces = {MediaType.APPLICATION_JSON_VALUE})
+    public ResponseEntity<?> produceOtp(
+    			@RequestParam String mobile,
+    			@RequestParam String state,
+    			@RequestParam String captcha) {
+        _logger.debug("forgotpassword  /forgotpassword/produceOtp.");
+        _logger.debug(" Mobile {}: " ,mobile);
+        if (!authJwtService.validateCaptcha(state,captcha)) {    
+        	_logger.debug("login captcha valid error.");
+        	return new Message<ChangePassword>(Message.FAIL).buildResponse();
         }
         
-        ModelAndView modelAndView = new ModelAndView("forgotpassword/resetpwd");
-        modelAndView.addObject("userId", userInfo==null ?"":userInfo.getId());
-        modelAndView.addObject("username", userInfo==null ?"":userInfo.getUsername());
-        modelAndView.addObject("emailMobile", emailMobile);
-        modelAndView.addObject("forgotType", forgotType);
+    	ChangePassword change = null;
+    	_logger.debug("Mobile Regex matches {}",mobileRegex.matcher(mobile).matches());
+    	if(StringUtils.isNotBlank(mobile) && mobileRegex.matcher(mobile).matches()) {
+    		UserInfo userInfo = userInfoService.findByEmailMobile(mobile);
+    		if(userInfo != null) {
+	    		change = new ChangePassword(userInfo);
+	            change.clearPassword();
+	        	AbstractOtpAuthn smsOtpAuthn = otpAuthnService.getByInstId(userInfo.getInstId());
+	        	smsOtpAuthn.produce(userInfo);
+	        	return new Message<ChangePassword>(change).buildResponse();
+    		}
+        }
+            
+        return new Message<ChangePassword>(Message.FAIL).buildResponse();
+    }
+    
+    @ResponseBody
+	@RequestMapping(value = { "/produceEmailOtp" }, produces = {MediaType.APPLICATION_JSON_VALUE})
+    public ResponseEntity<?> produceEmailOtp(
+    			@RequestParam String email,
+    			@RequestParam String state,
+    			@RequestParam String captcha) {
+        _logger.debug("forgotpassword  /forgotpassword/produceEmailOtp.");
+        _logger.debug("Email {} : " , email);
+        if (!authJwtService.validateCaptcha(state,captcha)) {
+        	_logger.debug("login captcha valid error.");
+        	return new Message<ChangePassword>(Message.FAIL).buildResponse();
+        }
         
-        return modelAndView;
+    	ChangePassword change = null;
+    	if(StringUtils.isNotBlank(email) && emailRegex.matcher(email).matches()) {
+    		UserInfo userInfo = userInfoService.findByEmailMobile(email);
+    		if(userInfo != null) {
+	    		change = new ChangePassword(userInfo);
+	            change.clearPassword();
+	            AbstractOtpAuthn mailOtpAuthn =  otpAuthnService.getMailOtpAuthn(userInfo.getInstId());
+	            mailOtpAuthn.produce(userInfo);
+	        	return new Message<ChangePassword>(change).buildResponse();
+    		}
+    	}
+        return new Message<ChangePassword>(Message.FAIL).buildResponse();
     }
 
     @RequestMapping(value = { "/setpassword" })
-    public ModelAndView setPassWord(
-                        @RequestParam String userId, 
-                        @RequestParam String username, 
-                        @RequestParam int forgotType, 
-                        @RequestParam String password,
-                        @RequestParam String confirmpassword,
-                        @RequestParam String captcha) {
-        _logger.debug("forgotPassword  /forgotpassword/pwdreseted.");
-        ModelAndView modelAndView = new ModelAndView("forgotpassword/pwdreseted");
-        if (null != password && password.equals(confirmpassword)) {
-            UserInfo userInfo = new UserInfo();
-            userInfo.setId(userId);
-            userInfo.setUsername(username);
-            userInfo.setPassword(password);
-            userInfo.setDecipherable(password);
-            UserInfo loadedUserInfo = userInfoService.findByUsername(username);
-            AbstractOtpAuthn smsOtpAuthn = otpAuthnService.getByInstId(loadedUserInfo.getInstId());
-            if ((forgotType == ForgotType.EMAIL && mailOtpAuthn.validate(userInfo, captcha)) ||
-                    (forgotType == ForgotType.MOBILE && smsOtpAuthn.validate(userInfo, captcha))
-                ) {
-            	/**
-                if(userInfoService.changePassword(userInfo,true)) {
-                	modelAndView.addObject("passwordResetResult", PasswordResetResult.SUCCESS);
-                }else {
-                	;
-                	modelAndView.addObject("validate_result", WebContext.getAttribute(PasswordPolicyValidator.PASSWORD_POLICY_VALIDATE_RESULT));
-                	modelAndView.addObject("passwordResetResult", PasswordResetResult.PASSWORDERROR);
-                }*/
-            } else {
-                modelAndView.addObject("passwordResetResult", PasswordResetResult.CAPTCHAERROR);
-            }
-        } else {
-            modelAndView.addObject("passwordResetResult", PasswordResetResult.PASSWORDERROR);
+    public ResponseEntity<?> setPassWord(
+    					@ModelAttribute ChangePassword changePassword,
+    					@RequestParam String forgotType,
+                        @RequestParam String otpCaptcha,
+                        @RequestParam String state) {
+        _logger.debug("forgotPassword  /forgotpassword/setpassword.");
+        if (StringUtils.isNotBlank(changePassword.getPassword() )
+        		&& changePassword.getPassword().equals(changePassword.getConfirmPassword())) {
+            UserInfo loadedUserInfo = userInfoService.get(changePassword.getUserId());
+            if(loadedUserInfo != null) {
+	            AbstractOtpAuthn smsOtpAuthn = otpAuthnService.getByInstId(loadedUserInfo.getInstId());
+	            AbstractOtpAuthn mailOtpAuthn =  otpAuthnService.getMailOtpAuthn(loadedUserInfo.getInstId());
+	            if (
+	            		(forgotType.equalsIgnoreCase("email") 
+	            				&& mailOtpAuthn !=null 
+	            				&& mailOtpAuthn.validate(loadedUserInfo, otpCaptcha)) 
+	            		||
+	            		(forgotType.equalsIgnoreCase("mobile") 
+	            				&& smsOtpAuthn !=null 
+	            				&& smsOtpAuthn.validate(loadedUserInfo, otpCaptcha))
+	               ) {
+	            	
+	                if(userInfoService.changePassword(changePassword,true)) {
+	                	return new Message<ChangePassword>(Message.SUCCESS).buildResponse();
+	                }else {
+	                	return new Message<ChangePassword>(Message.FAIL).buildResponse();
+	                }
+	            } else {
+	            	return new Message<ChangePassword>(Message.FAIL).buildResponse();
+	            }
+	        } 
         }
-        return modelAndView;
+        return new Message<ChangePassword>(Message.FAIL).buildResponse();
     }
 }

+ 0 - 3
maxkey-webs/maxkey-web-maxkey/src/main/java/org/maxkey/web/contorller/SafeController.java

@@ -46,9 +46,6 @@ public class SafeController {
 	@Autowired
 	private UserInfoService userInfoService;
 	
-
-	
-	
 	@RequestMapping(value="/forward/setting") 
 	public ModelAndView fowardSetting(@CurrentUser UserInfo currentUser) {
 			ModelAndView modelAndView=new ModelAndView("safe/setting");