diff --git a/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/QrCodeCredentialDto.java b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/QrCodeCredentialDto.java new file mode 100644 index 000000000..811a37cff --- /dev/null +++ b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/QrCodeCredentialDto.java @@ -0,0 +1,42 @@ +package org.dromara.maxkey.authn; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; + +import java.io.Serial; +import java.io.Serializable; + +/** + * @description: + * @author: orangeBabu + * @time: 16/8/2024 AM10:54 + */ + + +public class QrCodeCredentialDto { + + + @NotEmpty(message = "jwtToken不能为空") + @Schema(name = "jwtToken", description = "token") + String jwtToken; + + @NotEmpty(message = "返回码不能为空") + @Schema(name = "code", description = "返回码") + String code; + + public @NotEmpty(message = "jwtToken不能为空") String getJwtToken() { + return jwtToken; + } + + public void setJwtToken(@NotEmpty(message = "jwtToken不能为空") String jwtToken) { + this.jwtToken = jwtToken; + } + + public @NotEmpty(message = "返回码不能为空") String getCode() { + return code; + } + + public void setCode(@NotEmpty(message = "返回码不能为空") String code) { + this.code = code; + } +} diff --git a/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/ScanCode.java b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/ScanCode.java new file mode 100644 index 000000000..b67dedd8e --- /dev/null +++ b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/ScanCode.java @@ -0,0 +1,33 @@ +package org.dromara.maxkey.authn; + +import jakarta.validation.constraints.NotEmpty; + +/** + * @description: + * @author: orangeBabu + * @time: 16/8/2024 PM4:28 + */ +public class ScanCode { + + @NotEmpty(message = "二维码内容不能为空") + String code; + + @NotEmpty(message = "登录方式不能为空") + String authType; + + public @NotEmpty(message = "二维码内容不能为空") String getCode() { + return code; + } + + public void setCode(@NotEmpty(message = "二维码内容不能为空") String code) { + this.code = code; + } + + public @NotEmpty(message = "登录方式不能为空") String getAuthType() { + return authType; + } + + public void setAuthType(@NotEmpty(message = "登录方式不能为空") String authType) { + this.authType = authType; + } +} diff --git a/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/session/SessionManager.java b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/session/SessionManager.java index 5015bbef5..232d815ac 100644 --- a/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/session/SessionManager.java +++ b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/session/SessionManager.java @@ -1,19 +1,19 @@ /* * 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. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ - + package org.dromara.maxkey.authn.session; @@ -27,16 +27,16 @@ public interface SessionManager { public void create(String sessionId, Session session); public Session remove(String sessionId); - + public Session get(String sessionId); - + public Session refresh(String sessionId ,LocalDateTime refreshTime); - + public Session refresh(String sessionId); - + public List querySessions(); - + public int getValiditySeconds(); - + public void terminate(String sessionId,String userId,String username); } diff --git a/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/web/AuthorizationUtils.java b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/web/AuthorizationUtils.java index b70264374..c622d2050 100644 --- a/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/web/AuthorizationUtils.java +++ b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/dromara/maxkey/authn/web/AuthorizationUtils.java @@ -1,25 +1,26 @@ /* * 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. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ - + package org.dromara.maxkey.authn.web; import java.text.ParseException; +import com.nimbusds.jwt.SignedJWT; import org.dromara.maxkey.authn.SignPrincipal; import org.dromara.maxkey.authn.jwt.AuthTokenService; import org.dromara.maxkey.authn.session.Session; @@ -37,14 +38,14 @@ import jakarta.servlet.http.HttpServletRequest; public class AuthorizationUtils { private static final Logger _logger = LoggerFactory.getLogger(AuthorizationUtils.class); - + public static final class BEARERTYPE{ - + public static final String CONGRESS = "congress"; - + public static final String AUTHORIZATION = "Authorization"; } - + public static void authenticateWithCookie( HttpServletRequest request, AuthTokenService authTokenService, @@ -55,12 +56,12 @@ public class AuthorizationUtils { String authorization = authCookie.getValue(); _logger.trace("Try congress authenticate ."); doJwtAuthenticate(BEARERTYPE.CONGRESS,authorization,authTokenService,sessionManager); - }else { + }else { _logger.debug("cookie is null , clear authentication ."); clearAuthentication(); } } - + public static void authenticate( HttpServletRequest request, AuthTokenService authTokenService, @@ -71,9 +72,9 @@ public class AuthorizationUtils { _logger.trace("Try Authorization authenticate ."); doJwtAuthenticate(BEARERTYPE.AUTHORIZATION,authorization,authTokenService,sessionManager); } - + } - + public static void doJwtAuthenticate( String bearerType, String authorization, @@ -99,17 +100,25 @@ public class AuthorizationUtils { } } - + public static Session getSession(SessionManager sessionManager, String authorization) throws ParseException { + _logger.debug("get session by authorization {}", authorization); + SignedJWT signedJWT = SignedJWT.parse(authorization); + String sessionId = signedJWT.getJWTClaimsSet().getJWTID(); + _logger.debug("sessionId {}", sessionId); + return sessionManager.get(sessionId); + } + + public static Authentication getAuthentication() { Authentication authentication = (Authentication) getAuthentication(WebContext.getRequest()); return authentication; } - + public static Authentication getAuthentication(HttpServletRequest request) { Authentication authentication = (Authentication) request.getSession().getAttribute(WebConstants.AUTHENTICATION); return authentication; } - + //set Authentication to http session public static void setAuthentication(Authentication authentication) { WebContext.setAttribute(WebConstants.AUTHENTICATION, authentication); @@ -118,24 +127,24 @@ public class AuthorizationUtils { public static void clearAuthentication() { WebContext.removeAttribute(WebConstants.AUTHENTICATION); } - + public static boolean isAuthenticated() { return getAuthentication() != null; } - + public static boolean isNotAuthenticated() { return ! isAuthenticated(); } - + public static SignPrincipal getPrincipal() { Authentication authentication = getAuthentication(); return getPrincipal(authentication); } - + public static SignPrincipal getPrincipal(Authentication authentication) { return authentication == null ? null : (SignPrincipal) authentication.getPrincipal(); } - + public static UserInfo getUserInfo(Authentication authentication) { UserInfo userInfo = null; SignPrincipal principal = getPrincipal(authentication); @@ -144,9 +153,9 @@ public class AuthorizationUtils { } return userInfo; } - + public static UserInfo getUserInfo() { return getUserInfo(getAuthentication()); } - + } diff --git a/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/authn/provider/AbstractAuthenticationProvider.java b/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/authn/provider/AbstractAuthenticationProvider.java index 5a9605335..f071789e0 100644 --- a/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/authn/provider/AbstractAuthenticationProvider.java +++ b/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/authn/provider/AbstractAuthenticationProvider.java @@ -1,19 +1,19 @@ /* * 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. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ - + package org.dromara.maxkey.authn.provider; @@ -45,37 +45,41 @@ import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.web.authentication.WebAuthenticationDetails; /** * login Authentication abstract class. - * + * * @author Crystal.Sea * */ public abstract class AbstractAuthenticationProvider { - private static final Logger _logger = + private static final Logger _logger = LoggerFactory.getLogger(AbstractAuthenticationProvider.class); public static String PROVIDER_SUFFIX = "AuthenticationProvider"; - + public class AuthType{ public static final String NORMAL = "normal"; public static final String TFA = "tfa"; public static final String MOBILE = "mobile"; public static final String TRUSTED = "trusted"; + /** + * 扫描认证 + */ + public static final String SCAN_CODE = "scancode"; } - + protected ApplicationConfig applicationConfig; protected AbstractAuthenticationRealm authenticationRealm; protected AbstractOtpAuthn tfaOtpAuthn; - + protected MailOtpAuthnService otpAuthnService; protected SessionManager sessionManager; - + protected AuthTokenService authTokenService; - + public static ArrayList grantedAdministratorsAuthoritys = new ArrayList(); - + static { grantedAdministratorsAuthoritys.add(new SimpleGrantedAuthority("ROLE_ADMINISTRATORS")); } @@ -83,7 +87,7 @@ public abstract class AbstractAuthenticationProvider { public abstract String getProviderName(); public abstract Authentication doAuthenticate(LoginCredential authentication); - + @SuppressWarnings("rawtypes") public boolean supports(Class authentication) { return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication)); @@ -92,13 +96,13 @@ public abstract class AbstractAuthenticationProvider { public Authentication authenticate(LoginCredential authentication){ return null; } - + public Authentication authenticate(LoginCredential authentication,boolean trusted) { return null; } - + /** - * createOnlineSession + * createOnlineSession * @param credential * @param userInfo * @return @@ -112,7 +116,7 @@ public abstract class AbstractAuthenticationProvider { List grantedAuthoritys = authenticationRealm.grantAuthority(userInfo); principal.setAuthenticated(true); - + for(GrantedAuthority administratorsAuthority : grantedAdministratorsAuthoritys) { if(grantedAuthoritys.contains(administratorsAuthority)) { principal.setRoleAdministrators(true); @@ -120,37 +124,37 @@ public abstract class AbstractAuthenticationProvider { } } _logger.debug("Granted Authority {}" , grantedAuthoritys); - + principal.setGrantedAuthorityApps(authenticationRealm.queryAuthorizedApps(grantedAuthoritys)); - + UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken( - principal, - "PASSWORD", + principal, + "PASSWORD", grantedAuthoritys ); - + authenticationToken.setDetails( new WebAuthenticationDetails(WebContext.getRequest())); - + /* * put Authentication to current session context */ session.setAuthentication(authenticationToken); - + //create session this.sessionManager.create(session.getId(), session); - + //set Authentication to http session AuthorizationUtils.setAuthentication(authenticationToken); - + return authenticationToken; } - + /** * login user by j_username and j_cname first query user by j_cname if first * step userinfo is null,query user from system. - * + * * @param username String * @param password String * @return @@ -164,7 +168,7 @@ public abstract class AbstractAuthenticationProvider { } else { _logger.debug("User Login. "); } - + } return userInfo; @@ -172,7 +176,7 @@ public abstract class AbstractAuthenticationProvider { /** * check input password empty. - * + * * @param password String * @return */ @@ -185,7 +189,7 @@ public abstract class AbstractAuthenticationProvider { /** * check input username or password empty. - * + * * @param email String * @return */ @@ -198,7 +202,7 @@ public abstract class AbstractAuthenticationProvider { /** * check input username empty. - * + * * @param username String * @return */ @@ -219,8 +223,8 @@ public abstract class AbstractAuthenticationProvider { loginUser.setDisplayName("not exist"); loginUser.setLoginCount(0); authenticationRealm.insertLoginHistory( - loginUser, - ConstsLoginType.LOCAL, + loginUser, + ConstsLoginType.LOCAL, "", i18nMessage, WebConstants.LOGIN_RESULT.USER_NOT_EXIST); @@ -228,22 +232,22 @@ public abstract class AbstractAuthenticationProvider { } return true; } - + protected boolean statusValid(LoginCredential loginCredential , UserInfo userInfo) { if(userInfo.getIsLocked()==ConstsStatus.LOCK) { - authenticationRealm.insertLoginHistory( - userInfo, - loginCredential.getAuthType(), - loginCredential.getProvider(), - loginCredential.getCode(), + authenticationRealm.insertLoginHistory( + userInfo, + loginCredential.getAuthType(), + loginCredential.getProvider(), + loginCredential.getCode(), WebConstants.LOGIN_RESULT.USER_LOCKED ); }else if(userInfo.getStatus()!=ConstsStatus.ACTIVE) { - authenticationRealm.insertLoginHistory( - userInfo, - loginCredential.getAuthType(), - loginCredential.getProvider(), - loginCredential.getCode(), + authenticationRealm.insertLoginHistory( + userInfo, + loginCredential.getAuthType(), + loginCredential.getProvider(), + loginCredential.getCode(), WebConstants.LOGIN_RESULT.USER_INACTIVE ); } diff --git a/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/authn/provider/impl/ScanCodeAuthenticationProvider.java b/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/authn/provider/impl/ScanCodeAuthenticationProvider.java new file mode 100644 index 000000000..27410f661 --- /dev/null +++ b/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/authn/provider/impl/ScanCodeAuthenticationProvider.java @@ -0,0 +1,88 @@ +package org.dromara.maxkey.authn.provider.impl; + +import org.dromara.maxkey.authn.LoginCredential; +import org.dromara.maxkey.authn.SignPrincipal; +import org.dromara.maxkey.authn.provider.AbstractAuthenticationProvider; +import org.dromara.maxkey.authn.provider.scancode.ScanCodeService; +import org.dromara.maxkey.authn.provider.scancode.ScanCodeState; + +import org.dromara.maxkey.authn.realm.AbstractAuthenticationRealm; +import org.dromara.maxkey.authn.session.SessionManager; + +import org.dromara.maxkey.constants.ConstsLoginType; +import org.dromara.maxkey.crypto.password.PasswordReciprocal; +import org.dromara.maxkey.entity.idm.UserInfo; +; +import org.dromara.maxkey.web.WebConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; + +import java.util.Objects; + +/** + * @description: + * @author: orangeBabu + * @time: 16/8/2024 PM4:54 + */ +public class ScanCodeAuthenticationProvider extends AbstractAuthenticationProvider { + + private static final Logger _logger = LoggerFactory.getLogger(ScanCodeAuthenticationProvider.class); + + @Autowired + ScanCodeService scanCodeService; + + public ScanCodeAuthenticationProvider() { + super(); + } + + public ScanCodeAuthenticationProvider( + AbstractAuthenticationRealm authenticationRealm, + SessionManager sessionManager) { + this.authenticationRealm = authenticationRealm; + this.sessionManager = sessionManager; + } + + @Override + public String getProviderName() { + return "scancode" + PROVIDER_SUFFIX; + } + + @Override + public Authentication doAuthenticate(LoginCredential loginCredential) { + UsernamePasswordAuthenticationToken authenticationToken = null; + + String encodeTicket = PasswordReciprocal.getInstance().decoder(loginCredential.getUsername()); + + ScanCodeState scanCodeState = scanCodeService.consume(encodeTicket); + + if (Objects.isNull(scanCodeState)) { + return null; + } + + SignPrincipal signPrincipal = (SignPrincipal) sessionManager.get(scanCodeState.getSessionId()).getAuthentication().getPrincipal(); + //获取用户信息 + UserInfo userInfo = signPrincipal.getUserInfo(); + + isUserExist(loginCredential , userInfo); + + statusValid(loginCredential , userInfo); + + + //创建登录会话 + authenticationToken = createOnlineTicket(loginCredential,userInfo); + // user authenticated + _logger.debug("'{}' authenticated successfully by {}.", + loginCredential.getPrincipal(), getProviderName()); + + authenticationRealm.insertLoginHistory(userInfo, + ConstsLoginType.LOCAL, + "", + "xe00000004", + WebConstants.LOGIN_RESULT.SUCCESS); + + return authenticationToken; + } +} diff --git a/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/authn/provider/scancode/ScanCodeService.java b/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/authn/provider/scancode/ScanCodeService.java new file mode 100644 index 000000000..7bd4b9189 --- /dev/null +++ b/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/authn/provider/scancode/ScanCodeService.java @@ -0,0 +1,106 @@ +package org.dromara.maxkey.authn.provider.scancode; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.dromara.maxkey.authn.session.Session; +import org.dromara.maxkey.authn.session.SessionManager; +import org.dromara.maxkey.exception.BusinessException; +import org.dromara.maxkey.persistence.cache.MomentaryService; +import org.dromara.maxkey.util.IdGenerator; +import org.dromara.maxkey.util.JsonUtils; +import org.dromara.maxkey.util.TimeJsonUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Repository; + +import java.time.Duration; +import java.util.Objects; + +/** + * @description: + * @author: orangeBabu + * @time: 15/8/2024 AM9:49 + */ + +@Repository +public class ScanCodeService { + private static final Logger _logger = LoggerFactory.getLogger(ScanCodeService.class); + + static final String SCANCODE_TICKET = "login:scancode:%s"; + + static final String SCANCODE_CONFIRM = "login:scancode:confirm:%s"; + + public static class STATE { + public static final String SCANED = "scaned"; + public static final String CONFIRMED = "confirmed"; + public static final String CANCELED = "canceled"; + + public static final String CANCEL = "cancel"; + public static final String CONFIRM = "confirm"; + } + + int validitySeconds = 60 * 3; //default 3 minutes. + + int cancelValiditySeconds = 60 * 1; //default 1 minutes. + + + @Autowired + IdGenerator idGenerator; + + @Autowired + MomentaryService momentaryService; + + private String getKey(String ticket) { + return SCANCODE_TICKET.formatted(ticket); + } + + private String getConfirmKey(Long sessionId) { + return SCANCODE_CONFIRM.formatted(sessionId); + } + + public String createTicket() { + String ticket = idGenerator.generate(); + ScanCodeState scanCodeState = new ScanCodeState(); + scanCodeState.setState("unscanned"); + + // 将对象序列化为 JSON 字符串 + String jsonString = TimeJsonUtils.gsonToString(scanCodeState); + momentaryService.put(getKey(ticket), "", jsonString); + _logger.info("Ticket {} , Duration {}", ticket, jsonString); + + return ticket; + } + + public boolean validateTicket(String ticket, Session session) { + String key = getKey(ticket); + Object value = momentaryService.get(key, ""); + if (Objects.isNull(value)) { + return false; + } + + ScanCodeState scanCodeState = new ScanCodeState(); + scanCodeState.setState("scanned"); + scanCodeState.setTicket(ticket); + scanCodeState.setSessionId(session.getId()); + momentaryService.put(key, "", TimeJsonUtils.gsonToString(scanCodeState)); + + return true; + } + + public ScanCodeState consume(String ticket){ + String key = getKey(ticket); + Object o = momentaryService.get(key, ""); + if (Objects.nonNull(o)) { + String redisObject = o.toString(); + ScanCodeState scanCodeState = TimeJsonUtils.gsonStringToObject(redisObject, ScanCodeState.class); + if ("scanned".equals(scanCodeState.getState())) { + momentaryService.remove(key, ""); + return scanCodeState; + } else { + return null; + } + } else { + throw new BusinessException(20004, "该二维码失效"); + } + } +} diff --git a/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/authn/provider/scancode/ScanCodeState.java b/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/authn/provider/scancode/ScanCodeState.java new file mode 100644 index 000000000..74ac7c68d --- /dev/null +++ b/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/authn/provider/scancode/ScanCodeState.java @@ -0,0 +1,53 @@ +package org.dromara.maxkey.authn.provider.scancode; + +import com.fasterxml.jackson.annotation.JsonFormat; +import org.dromara.maxkey.authn.session.Session; + +/** + * @description: + * @author: orangeBabu + * @time: 16/8/2024 PM5:42 + */ +public class ScanCodeState { + + String sessionId; + + String ticket; + + @JsonFormat(shape = JsonFormat.Shape.STRING) + Long confirmKey; + + String state; + + public String getSessionId() { + return sessionId; + } + + public void setSessionId(String sessionId) { + this.sessionId = sessionId; + } + + public String getTicket() { + return ticket; + } + + public void setTicket(String ticket) { + this.ticket = ticket; + } + + public Long getConfirmKey() { + return confirmKey; + } + + public void setConfirmKey(Long confirmKey) { + this.confirmKey = confirmKey; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } +} diff --git a/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/authn/provider/scancode/ScancodeSignInfo.java b/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/authn/provider/scancode/ScancodeSignInfo.java new file mode 100644 index 000000000..c2d2ccc3e --- /dev/null +++ b/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/authn/provider/scancode/ScancodeSignInfo.java @@ -0,0 +1,19 @@ +package org.dromara.maxkey.authn.provider.scancode; + +/** + * @description: + * @author: orangeBabu + * @time: 17/8/2024 PM5:08 + */ +public class ScancodeSignInfo { + + String username; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } +} diff --git a/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/autoconfigure/AuthnProviderAutoConfiguration.java b/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/autoconfigure/AuthnProviderAutoConfiguration.java index 586c4861e..3138e8d77 100644 --- a/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/autoconfigure/AuthnProviderAutoConfiguration.java +++ b/maxkey-authentications/maxkey-authentication-provider/src/main/java/org/dromara/maxkey/autoconfigure/AuthnProviderAutoConfiguration.java @@ -1,19 +1,19 @@ /* * 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. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ - + package org.dromara.maxkey.autoconfigure; @@ -22,6 +22,7 @@ import org.dromara.maxkey.authn.provider.AbstractAuthenticationProvider; import org.dromara.maxkey.authn.provider.AuthenticationProviderFactory; import org.dromara.maxkey.authn.provider.impl.MobileAuthenticationProvider; import org.dromara.maxkey.authn.provider.impl.NormalAuthenticationProvider; +import org.dromara.maxkey.authn.provider.impl.ScanCodeAuthenticationProvider; import org.dromara.maxkey.authn.provider.impl.TrustedAuthenticationProvider; import org.dromara.maxkey.authn.realm.AbstractAuthenticationRealm; import org.dromara.maxkey.authn.session.SessionManager; @@ -44,21 +45,23 @@ import org.springframework.jdbc.core.JdbcTemplate; @AutoConfiguration public class AuthnProviderAutoConfiguration { static final Logger _logger = LoggerFactory.getLogger(AuthnProviderAutoConfiguration.class); - + @Bean public AbstractAuthenticationProvider authenticationProvider( NormalAuthenticationProvider normalAuthenticationProvider, MobileAuthenticationProvider mobileAuthenticationProvider, - TrustedAuthenticationProvider trustedAuthenticationProvider + TrustedAuthenticationProvider trustedAuthenticationProvider, + ScanCodeAuthenticationProvider scanCodeAuthenticationProvider ) { AuthenticationProviderFactory authenticationProvider = new AuthenticationProviderFactory(); authenticationProvider.addAuthenticationProvider(normalAuthenticationProvider); authenticationProvider.addAuthenticationProvider(mobileAuthenticationProvider); authenticationProvider.addAuthenticationProvider(trustedAuthenticationProvider); - + authenticationProvider.addAuthenticationProvider(scanCodeAuthenticationProvider); + return authenticationProvider; } - + @Bean public NormalAuthenticationProvider normalAuthenticationProvider( AbstractAuthenticationRealm authenticationRealm, @@ -74,7 +77,18 @@ public class AuthnProviderAutoConfiguration { authTokenService ); } - + + @Bean + public ScanCodeAuthenticationProvider scanCodeAuthenticationProvider( + AbstractAuthenticationRealm authenticationRealm, + SessionManager sessionManager + ) { + return new ScanCodeAuthenticationProvider( + authenticationRealm, + sessionManager + ); + } + @Bean public MobileAuthenticationProvider mobileAuthenticationProvider( AbstractAuthenticationRealm authenticationRealm, @@ -104,22 +118,22 @@ public class AuthnProviderAutoConfiguration { sessionManager ); } - + @Bean public PasswordPolicyValidator passwordPolicyValidator(JdbcTemplate jdbcTemplate,MessageSource messageSource) { return new PasswordPolicyValidator(jdbcTemplate,messageSource); } - + @Bean public LoginRepository loginRepository(JdbcTemplate jdbcTemplate) { return new LoginRepository(jdbcTemplate); } - + @Bean public LoginHistoryRepository loginHistoryRepository(JdbcTemplate jdbcTemplate) { return new LoginHistoryRepository(jdbcTemplate); } - + /** * remeberMeService . * @return @@ -135,5 +149,5 @@ public class AuthnProviderAutoConfiguration { return new JdbcRemeberMeManager( jdbcTemplate,applicationConfig,authTokenService,validity); } - + } diff --git a/maxkey-common/build.gradle b/maxkey-common/build.gradle index cae00db64..531a2f3dd 100644 --- a/maxkey-common/build.gradle +++ b/maxkey-common/build.gradle @@ -3,5 +3,6 @@ description = "maxkey-common" dependencies { //local jars implementation fileTree(dir: '../maxkey-lib/', include: '*/*.jar') - -} \ No newline at end of file + + +} diff --git a/maxkey-common/src/main/java/org/dromara/maxkey/adapter/LocalDateTimeAdapter.java b/maxkey-common/src/main/java/org/dromara/maxkey/adapter/LocalDateTimeAdapter.java new file mode 100644 index 000000000..d3ebb0193 --- /dev/null +++ b/maxkey-common/src/main/java/org/dromara/maxkey/adapter/LocalDateTimeAdapter.java @@ -0,0 +1,23 @@ +package org.dromara.maxkey.adapter; + +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; + +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +public class LocalDateTimeAdapter extends TypeAdapter { + private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + @Override + public void write(JsonWriter out, LocalDateTime value) throws IOException { + out.value(value.format(formatter)); + } + + @Override + public LocalDateTime read(JsonReader in) throws IOException { + return LocalDateTime.parse(in.nextString(), formatter); + } +} diff --git a/maxkey-common/src/main/java/org/dromara/maxkey/util/RQCodeUtils.java b/maxkey-common/src/main/java/org/dromara/maxkey/util/RQCodeUtils.java index bf92e250f..9122399aa 100644 --- a/maxkey-common/src/main/java/org/dromara/maxkey/util/RQCodeUtils.java +++ b/maxkey-common/src/main/java/org/dromara/maxkey/util/RQCodeUtils.java @@ -1,19 +1,19 @@ /* * Copyright [2020] [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. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ - + package org.dromara.maxkey.util; @@ -27,58 +27,58 @@ import com.google.zxing.common.BitMatrix; public class RQCodeUtils { - + public static void write2File(String path,String rqCodeText,String format,int width, int height ){ - try { + try { BitMatrix byteMatrix=genRQCode(rqCodeText,width,height); - - File file = new File(path); - - QRCode.writeToPath(byteMatrix, format, file); - } catch (Exception e) { - e.printStackTrace(); - } + + File file = new File(path); + + QRCode.writeToPath(byteMatrix, format, file); + } catch (Exception e) { + e.printStackTrace(); + } } - - public static BufferedImage write2BufferedImage(String rqCodeText,String format,int width, int height ){ - try { - BitMatrix byteMatrix=genRQCode(rqCodeText,width,height); - - return QRCode.toBufferedImage(byteMatrix); - } catch (Exception e) { - e.printStackTrace(); - } + + public static BufferedImage write2BufferedImage(String rqCodeText,String format,int width, int height){ + try { + BitMatrix byteMatrix=genRQCode(rqCodeText,width,height); + + return QRCode.toBufferedImage(byteMatrix); + } catch (Exception e) { + e.printStackTrace(); + } return null; } - + public static void write2OutputStream(OutputStream stream,String rqCodeText,String format,int width, int height ){ - try { + try { BitMatrix byteMatrix=genRQCode(rqCodeText,width,height); - - QRCode.writeToStream(byteMatrix, format, stream); - } catch (Exception e) { - e.printStackTrace(); - } + + QRCode.writeToStream(byteMatrix, format, stream); + } catch (Exception e) { + e.printStackTrace(); + } } - - - public static BitMatrix genRQCode(String rqCodeText,int width, int height ){ + + + public static BitMatrix genRQCode(String rqCodeText,int width, int height){ if(width==0){ width=200; } if(height==0){ height=200; } - try { + try { return new MultiFormatWriter().encode( - rqCodeText, - BarcodeFormat.QR_CODE, - width, - height); - } catch (Exception e) { - e.printStackTrace(); - } + rqCodeText, + BarcodeFormat.QR_CODE, + width, + height); + } catch (Exception e) { + e.printStackTrace(); + } return null; } - + } diff --git a/maxkey-common/src/main/java/org/dromara/maxkey/util/TimeJsonUtils.java b/maxkey-common/src/main/java/org/dromara/maxkey/util/TimeJsonUtils.java new file mode 100644 index 000000000..972d113e0 --- /dev/null +++ b/maxkey-common/src/main/java/org/dromara/maxkey/util/TimeJsonUtils.java @@ -0,0 +1,37 @@ +package org.dromara.maxkey.util; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import org.dromara.maxkey.adapter.LocalDateTimeAdapter; + +import java.time.LocalDateTime; + +/** + * @description: + * @author: orangeBabu + * @time: 16/8/2024 PM6:23 + */ +public class TimeJsonUtils { + public static T gsonStringToObject(String json, Class cls) { + Gson gson = new GsonBuilder() + .registerTypeAdapter(LocalDateTime.class, new LocalDateTimeAdapter()) + .create(); + return gson.fromJson(json, cls); + } + + /** + * Gson Transform java bean object to json string . + * + * @param bean Object + * @return string + */ + public static String gsonToString(Object bean) { + String json = ""; + Gson gson = new GsonBuilder() + .registerTypeAdapter(LocalDateTime.class, new LocalDateTimeAdapter()) + .create(); + json = gson.toJson(bean); + + return json; + } +} diff --git a/maxkey-core/src/main/java/org/dromara/maxkey/exception/BusinessException.java b/maxkey-core/src/main/java/org/dromara/maxkey/exception/BusinessException.java new file mode 100644 index 000000000..daa0c7e15 --- /dev/null +++ b/maxkey-core/src/main/java/org/dromara/maxkey/exception/BusinessException.java @@ -0,0 +1,47 @@ +package org.dromara.maxkey.exception; + +import java.io.Serial; + +/** + * @description: + * @author: orangeBabu + * @time: 16/8/2024 PM3:03 + */ +public class BusinessException extends RuntimeException { + /** + * 异常编码 + */ + private Integer code; + + /** + * 异常消息 + */ + private String message; + + + public BusinessException() { + super(); + } + + public BusinessException(Integer code, String message) { + this.message = message; + this.code = code; + } + + public Integer getCode() { + return code; + } + + public void setCode(Integer code) { + this.code = code; + } + + @Override + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} diff --git a/maxkey-core/src/main/java/org/dromara/maxkey/persistence/redis/RedisConnectionFactory.java b/maxkey-core/src/main/java/org/dromara/maxkey/persistence/redis/RedisConnectionFactory.java index 40ee4af20..7edb93407 100644 --- a/maxkey-core/src/main/java/org/dromara/maxkey/persistence/redis/RedisConnectionFactory.java +++ b/maxkey-core/src/main/java/org/dromara/maxkey/persistence/redis/RedisConnectionFactory.java @@ -1,19 +1,19 @@ /* * Copyright [2020] [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. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ - + package org.dromara.maxkey.persistence.redis; @@ -26,7 +26,7 @@ import redis.clients.jedis.JedisPoolConfig; public class RedisConnectionFactory { private static final Logger _logger = LoggerFactory.getLogger(RedisConnectionFactory.class); - + public static class DEFAULT_CONFIG { /** * Redis默认服务器IP @@ -95,7 +95,7 @@ public class RedisConnectionFactory { timeOut = DEFAULT_CONFIG.DEFAULT_TIMEOUT; } - if (this.password == null || this.password.equals("") || this.password.equalsIgnoreCase("password")) { + if (this.password == null || this.password.equals("")) { this.password = null; } jedisPool = new JedisPool(poolConfig, hostName, port, timeOut, password); @@ -120,7 +120,7 @@ public class RedisConnectionFactory { Jedis jedis = jedisPool.getResource(); _logger.trace("return jedisPool Resource ."); return jedis; - + } public void close(Jedis conn) { @@ -130,7 +130,7 @@ public class RedisConnectionFactory { _logger.trace("closed conn ."); } - + public String getHostName() { return hostName; } @@ -170,5 +170,5 @@ public class RedisConnectionFactory { public JedisPoolConfig getPoolConfig() { return poolConfig; } - + } diff --git a/maxkey-core/src/main/java/org/dromara/maxkey/web/GlobalExceptionHandler.java b/maxkey-core/src/main/java/org/dromara/maxkey/web/GlobalExceptionHandler.java new file mode 100644 index 000000000..14fd40fd5 --- /dev/null +++ b/maxkey-core/src/main/java/org/dromara/maxkey/web/GlobalExceptionHandler.java @@ -0,0 +1,198 @@ +package org.dromara.maxkey.web; + +import com.fasterxml.jackson.databind.exc.InvalidFormatException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.UnexpectedTypeException; +import org.dromara.maxkey.entity.Message; +import org.dromara.maxkey.exception.BusinessException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.validation.BindException; +import org.springframework.validation.BindingResult; +import org.springframework.validation.ObjectError; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.MissingServletRequestParameterException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; + +import java.util.List; +import java.util.Objects; + +/** + * @description: + * @author: orangeBabu + * @time: 16/8/2024 PM3:02 + */ + +/** + * 全局异常处理器 + * + */ +@RestControllerAdvice +public class GlobalExceptionHandler { + private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); + + + /** + * 缺少请求体异常处理器 + * @param e 缺少请求体异常 使用get方式请求 而实体使用@RequestBody修饰 + */ + @ExceptionHandler(HttpMessageNotReadableException.class) + public Message parameterBodyMissingExceptionHandler(HttpMessageNotReadableException e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',请求体缺失'{}'", requestURI, e.getMessage(),e); + return new Message<>(Message.FAIL, "缺少请求体"); + } + + // get请求的对象参数校验异常 + @ExceptionHandler({MissingServletRequestParameterException.class}) + public Message bindExceptionHandler(MissingServletRequestParameterException e,HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',get方式请求参数'{}'必传", requestURI, e.getMessage(),e); + return new Message<>(Message.FAIL, "请求的对象参数校验异常"); + } + + /** + * 请求方式不支持 + */ + @ExceptionHandler(HttpRequestMethodNotSupportedException.class) + public Message handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求地址 '{}',不支持'{}' 请求", requestURI, e.getMethod(),e); + return new Message<>(HttpStatus.METHOD_NOT_ALLOWED.value(),HttpStatus.METHOD_NOT_ALLOWED.getReasonPhrase()); + } + + + + + /** + * 参数不正确 + */ + @ExceptionHandler(MethodArgumentTypeMismatchException.class) + public Message methodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + String error = String.format("%s 应该是 %s 类型", e.getName(), e.getRequiredType().getSimpleName()); + log.error("请求地址'{}',{},参数类型不正确", requestURI,error,e); + return new Message<>(Message.FAIL, "参数类型不正确"); + } + + /** + * 系统异常 + */ + @ExceptionHandler(Exception.class) + public Message handleException(Exception e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',发生系统异常.", requestURI, e); + return new Message<>(Message.FAIL, HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase()); + } + + /** + * 捕获转换类型异常 + * @param e + * @return + */ + @ExceptionHandler(UnexpectedTypeException.class) + public Message unexpectedTypeHandler(UnexpectedTypeException e) + { + log.error("类型转换错误:{}",e.getMessage(), e); + return new Message<>(HttpStatus.INTERNAL_SERVER_ERROR.value(),e.getMessage()); + } + + /** + * 捕获转换类型异常 + * @param e + * @return + */ + @ExceptionHandler(MethodArgumentNotValidException.class) + public Message methodArgumentNotValidException(MethodArgumentNotValidException e) + { + BindingResult bindingResult = e.getBindingResult(); + List errors = bindingResult.getAllErrors(); + log.error("参数验证异常:{}",e.getMessage(), e); + if (!errors.isEmpty()) { + // 只显示第一个错误信息 + return new Message<>(HttpStatus.BAD_REQUEST.value(), errors.get(0).getDefaultMessage()); + } + return new Message<>(HttpStatus.BAD_REQUEST.value(),"MethodArgumentNotValid"); + } + + // 运行时异常 + @ExceptionHandler(RuntimeException.class) + public Message runtimeExceptionHandler(RuntimeException e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',捕获运行时异常'{}'", requestURI, e.getMessage(),e); + return new Message<>(Message.FAIL, e.getMessage()); + } + // 系统级别异常 + @ExceptionHandler(Throwable.class) + public Message throwableExceptionHandler(Throwable e,HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',捕获系统级别异常'{}'", requestURI,e.getMessage(),e); + return new Message<>(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage()); + } + + /** + * IllegalArgumentException 捕获转换类型异常 + * @param e + * @return + */ + @ExceptionHandler(IllegalArgumentException.class) + public Message illegalArgumentException(IllegalArgumentException e) + { + String message = e.getMessage(); + log.error("IllegalArgumentException:{}",e.getMessage(),e); + if (Objects.nonNull(message)) { + //错误信息 + return new Message<>(HttpStatus.BAD_REQUEST.value(),message); + } + return new Message<>(HttpStatus.BAD_REQUEST.value(),"error"); + } + /** + * InvalidFormatException 捕获转换类型异常 + * @param e + * @return + */ + @ExceptionHandler(InvalidFormatException.class) + public Message invalidFormatException(InvalidFormatException e) + { + String message = e.getMessage(); + log.error("InvalidFormatException:{}",e.getMessage(),e); + if (Objects.nonNull(message)) { + //错误信息 + return new Message<>(HttpStatus.BAD_REQUEST.value(),message); + } + return new Message<>(HttpStatus.BAD_REQUEST.value(),"error"); + } + + + + /** + * 自定义验证异常 + */ + @ExceptionHandler(BindException.class) + public Message handleBindException(BindException e) { + BindingResult bindingResult = e.getBindingResult(); + List errors = bindingResult.getAllErrors(); + log.error("参数验证异常:{}",e.getMessage(), e); + if (!errors.isEmpty()) { + // 只显示第一个错误信息 + return new Message<>(HttpStatus.BAD_REQUEST.value(), errors.get(0).getDefaultMessage()); + } + return new Message<>(HttpStatus.BAD_REQUEST.value(),"MethodArgumentNotValid"); + } + + /** + * 业务异常处理 + * 业务自定义code 与 message + * + */ + @ExceptionHandler(BusinessException.class) + public Message handleBusinessException(BusinessException e) { + log.error("业务自定义异常:{},{}",e.getCode(),e.getMessage(),e); + return new Message<>(e.getCode(),e.getMessage()); + } +} diff --git a/maxkey-persistence/src/main/java/org/dromara/maxkey/persistence/repository/InstitutionsRepository.java b/maxkey-persistence/src/main/java/org/dromara/maxkey/persistence/repository/InstitutionsRepository.java index 6f9fdec27..8526c7056 100644 --- a/maxkey-persistence/src/main/java/org/dromara/maxkey/persistence/repository/InstitutionsRepository.java +++ b/maxkey-persistence/src/main/java/org/dromara/maxkey/persistence/repository/InstitutionsRepository.java @@ -1,19 +1,19 @@ /* * 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. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ - + package org.dromara.maxkey.persistence.repository; @@ -23,38 +23,38 @@ import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; +import org.apache.commons.lang3.ObjectUtils; import org.dromara.maxkey.entity.Institutions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; -import com.alibaba.nacos.common.utils.CollectionUtils; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; public class InstitutionsRepository { static final Logger _logger = LoggerFactory.getLogger(InstitutionsRepository.class); - - private static final String SELECT_STATEMENT = + + private static final String SELECT_STATEMENT = "select * from mxk_institutions where id = ? or domain = ? or consoledomain = ?" ; - + private static final String DEFAULT_INSTID = "1"; - protected static final Cache institutionsStore = + protected static final Cache institutionsStore = Caffeine.newBuilder() .expireAfterWrite(60, TimeUnit.MINUTES) .build(); - + //id domain mapping protected static final ConcurrentHashMap mapper = new ConcurrentHashMap<>(); - + protected JdbcTemplate jdbcTemplate; - + public InstitutionsRepository(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } - + public Institutions get(String instIdOrDomain) { _logger.trace(" instId {}" , instIdOrDomain); Institutions inst = getByInstIdOrDomain(instIdOrDomain); @@ -64,15 +64,15 @@ public class InstitutionsRepository { } return inst; } - + private Institutions getByInstIdOrDomain(String instIdOrDomain) { _logger.trace(" instId {}" , instIdOrDomain); Institutions inst = institutionsStore.getIfPresent(mapper.get(instIdOrDomain)==null ? DEFAULT_INSTID : mapper.get(instIdOrDomain) ); if(inst == null) { - List institutions = + List institutions = jdbcTemplate.query(SELECT_STATEMENT,new InstitutionsRowMapper(),instIdOrDomain,instIdOrDomain,instIdOrDomain); - - if (CollectionUtils.isNotEmpty(institutions)) { + + if (ObjectUtils.isNotEmpty(institutions)) { inst = institutions.get(0); } if(inst != null ) { @@ -81,10 +81,10 @@ public class InstitutionsRepository { mapper.put(inst.getId(), inst.getDomain()); } } - + return inst; } - + public class InstitutionsRowMapper implements RowMapper { @Override public Institutions mapRow(ResultSet rs, int rowNum) throws SQLException { diff --git a/maxkey-persistence/src/main/java/org/dromara/maxkey/persistence/repository/LoginRepository.java b/maxkey-persistence/src/main/java/org/dromara/maxkey/persistence/repository/LoginRepository.java index 238b76be2..d5752a9e4 100644 --- a/maxkey-persistence/src/main/java/org/dromara/maxkey/persistence/repository/LoginRepository.java +++ b/maxkey-persistence/src/main/java/org/dromara/maxkey/persistence/repository/LoginRepository.java @@ -1,19 +1,19 @@ /* * Copyright [2020] [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. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ - + package org.dromara.maxkey.persistence.repository; @@ -24,13 +24,12 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; -import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.dromara.maxkey.constants.ConstsRoles; import org.dromara.maxkey.constants.ConstsStatus; import org.dromara.maxkey.entity.idm.Groups; import org.dromara.maxkey.entity.idm.UserInfo; -import org.dromara.maxkey.util.StrUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jdbc.core.JdbcTemplate; @@ -57,28 +56,28 @@ public class LoginRepository { private static final String GROUPS_SELECT_STATEMENT = "select distinct g.id,g.groupcode,g.groupname from mxk_userinfo u,mxk_groups g,mxk_group_member gm where u.id = ? and u.id=gm.memberid and gm.groupid=g.id "; private static final String DEFAULT_USERINFO_SELECT_STATEMENT = "select * from mxk_userinfo where username = ? "; - + private static final String DEFAULT_USERINFO_SELECT_STATEMENT_USERNAME_MOBILE = "select * from mxk_userinfo where (username = ? or mobile = ?)"; - + private static final String DEFAULT_USERINFO_SELECT_STATEMENT_USERNAME_MOBILE_EMAIL = "select * from mxk_userinfo where (username = ? or mobile = ? or email = ?) "; - + private static final String DEFAULT_MYAPPS_SELECT_STATEMENT = "select distinct app.id,app.appname from mxk_apps app,mxk_access gp,mxk_groups g where app.id=gp.appid and app.status = 1 and gp.groupid=g.id and g.id in(%s)"; - + protected JdbcTemplate jdbcTemplate; - + /** * 1 (USERNAME) 2 (USERNAME | MOBILE) 3 (USERNAME | MOBILE | EMAIL) */ public static int LOGIN_ATTRIBUTE_TYPE = 2; - + public LoginRepository(){ - + } - + public LoginRepository(JdbcTemplate jdbcTemplate){ this.jdbcTemplate=jdbcTemplate; } - + public UserInfo find(String username, String password) { List listUserInfo = null ; if( LOGIN_ATTRIBUTE_TYPE == 1) { @@ -89,37 +88,37 @@ public class LoginRepository { listUserInfo = findByUsernameOrMobileOrEmail(username,password); } _logger.debug("load UserInfo : {}" , listUserInfo); - return (CollectionUtils.isNotEmpty(listUserInfo))? listUserInfo.get(0) : null; + return (ObjectUtils.isNotEmpty(listUserInfo))? listUserInfo.get(0) : null; } - + public List findByUsername(String username, String password) { return jdbcTemplate.query( - DEFAULT_USERINFO_SELECT_STATEMENT, + DEFAULT_USERINFO_SELECT_STATEMENT, new UserInfoRowMapper(), username ); } - + public List findByUsernameOrMobile(String username, String password) { return jdbcTemplate.query( - DEFAULT_USERINFO_SELECT_STATEMENT_USERNAME_MOBILE, + DEFAULT_USERINFO_SELECT_STATEMENT_USERNAME_MOBILE, new UserInfoRowMapper(), username,username ); } - + public List findByUsernameOrMobileOrEmail(String username, String password) { return jdbcTemplate.query( - DEFAULT_USERINFO_SELECT_STATEMENT_USERNAME_MOBILE_EMAIL, + DEFAULT_USERINFO_SELECT_STATEMENT_USERNAME_MOBILE_EMAIL, new UserInfoRowMapper(), username,username,username ); } - + /** * 閿佸畾鐢ㄦ埛锛歩slock锛�1 鐢ㄦ埛瑙i攣 2 鐢ㄦ埛閿佸畾 - * + * * @param userInfo */ public void updateLock(UserInfo userInfo) { @@ -137,7 +136,7 @@ public class LoginRepository { /** * 閿佸畾鐢ㄦ埛锛歩slock锛�1 鐢ㄦ埛瑙i攣 2 鐢ㄦ埛閿佸畾 - * + * * @param userInfo */ public void updateUnlock(UserInfo userInfo) { @@ -155,7 +154,7 @@ public class LoginRepository { /** * reset BadPasswordCount And Lockout - * + * * @param userInfo */ public void updateLockout(UserInfo userInfo) { @@ -173,7 +172,7 @@ public class LoginRepository { /** * if login password is error ,BadPasswordCount++ and set bad date - * + * * @param userInfo */ public void updateBadPasswordCount(UserInfo userInfo) { @@ -190,15 +189,15 @@ public class LoginRepository { _logger.error(e.getMessage()); } } - + public List queryAuthorizedApps(List grantedAuthoritys) { String grantedAuthorityString="'ROLE_ALL_USER'"; for(GrantedAuthority grantedAuthority : grantedAuthoritys) { grantedAuthorityString += ",'"+ grantedAuthority.getAuthority()+"'"; } - + ArrayList listAuthorizedApps = (ArrayList) jdbcTemplate.query( - String.format(DEFAULT_MYAPPS_SELECT_STATEMENT, grantedAuthorityString), + String.format(DEFAULT_MYAPPS_SELECT_STATEMENT, grantedAuthorityString), new RowMapper() { public GrantedAuthority mapRow(ResultSet rs, int rowNum) throws SQLException { return new SimpleGrantedAuthority(rs.getString("id")); @@ -208,7 +207,7 @@ public class LoginRepository { _logger.debug("list Authorized Apps {}" , listAuthorizedApps); return listAuthorizedApps; } - + public List queryGroups(UserInfo userInfo) { List listRoles = jdbcTemplate.query(GROUPS_SELECT_STATEMENT, new RowMapper() { public Groups mapRow(ResultSet rs, int rowNum) throws SQLException { @@ -222,7 +221,7 @@ public class LoginRepository { /** * grant Authority by userinfo - * + * * @param userInfo * @return ArrayList */ @@ -237,7 +236,7 @@ public class LoginRepository { grantedAuthority.add(ConstsRoles.ROLE_ORDINARY_USER); for (Groups group : listGroups) { grantedAuthority.add(new SimpleGrantedAuthority(group.getId())); - if(group.getGroupCode().startsWith("ROLE_") + if(group.getGroupCode().startsWith("ROLE_") && !grantedAuthority.contains(new SimpleGrantedAuthority(group.getGroupCode()))) { grantedAuthority.add(new SimpleGrantedAuthority(group.getGroupCode())); } @@ -246,19 +245,19 @@ public class LoginRepository { return grantedAuthority; } - - + + public void updateLastLogin(UserInfo userInfo) { jdbcTemplate.update(LOGIN_USERINFO_UPDATE_STATEMENT, - new Object[] { - userInfo.getLastLoginTime(), - userInfo.getLastLoginIp(), - userInfo.getLoginCount() + 1, - userInfo.getId() + new Object[] { + userInfo.getLastLoginTime(), + userInfo.getLastLoginIp(), + userInfo.getLoginCount() + 1, + userInfo.getId() }, new int[] { Types.TIMESTAMP, Types.VARCHAR, Types.INTEGER, Types.VARCHAR }); } - + public class UserInfoRowMapper implements RowMapper { @Override public UserInfo mapRow(ResultSet rs, int rowNum) throws SQLException { @@ -372,7 +371,7 @@ public class LoginRepository { if (userInfo.getTheme() == null || userInfo.getTheme().equalsIgnoreCase("")) { userInfo.setTheme("default"); } - + return userInfo; } } diff --git a/maxkey-web-frontend/maxkey-web-app/angular.json b/maxkey-web-frontend/maxkey-web-app/angular.json index df85f80cf..48d6752fe 100644 --- a/maxkey-web-frontend/maxkey-web-app/angular.json +++ b/maxkey-web-frontend/maxkey-web-app/angular.json @@ -127,6 +127,7 @@ }, "defaultProject": "ng-alain", "cli": { + "analytics": false, "packageManager": "yarn" } } diff --git a/maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/login/login.component.html b/maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/login/login.component.html index 6ee4d61df..2ae5ef7a7 100644 --- a/maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/login/login.component.html +++ b/maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/login/login.component.html @@ -14,7 +14,7 @@ {{ 'mxk.login.tab-mobile' | i18n }} -