From 5f0f1fa7e03dfa827023ee6a5141e7cdca7aff04 Mon Sep 17 00:00:00 2001 From: MaxKey Date: Fri, 29 Apr 2022 11:58:05 +0800 Subject: [PATCH] rememberme --- .../java/org/maxkey/authn/jwt/AuthJwt.java | 8 ++ .../org/maxkey/authn/jwt/AuthJwtService.java | 2 - .../org/maxkey/authn/session/Session.java | 6 +- .../rememberme/AbstractRemeberMeService.java | 124 ++++++++++++++++++ .../rememberme/InMemoryRemeberMeService.java | 54 ++++++++ .../rememberme/JdbcRemeberMeService.java | 109 +++++++++++++++ .../authn/support/rememberme/RemeberMe.java | 97 ++++++++++++++ .../rememberme/RemeberMeServiceFactory.java | 47 +++++++ .../AuthenticationAutoConfiguration.java | 21 ++- .../java/org/maxkey/web/WebConstants.java | 24 +--- .../main/java/org/maxkey/web/WebContext.java | 6 - .../routes/passport/login/login.component.ts | 56 ++++---- .../src/app/service/authentication.service.ts | 5 +- .../maxkey-web-app/src/app/shared/consts.ts | 2 +- .../web/contorller/LoginEntryPoint.java | 57 ++++++-- .../resources/application-http.properties | 4 +- 16 files changed, 550 insertions(+), 72 deletions(-) create mode 100644 maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/support/rememberme/AbstractRemeberMeService.java create mode 100644 maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/support/rememberme/InMemoryRemeberMeService.java create mode 100644 maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/support/rememberme/JdbcRemeberMeService.java create mode 100644 maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/support/rememberme/RemeberMe.java create mode 100644 maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/support/rememberme/RemeberMeServiceFactory.java diff --git a/maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/jwt/AuthJwt.java b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/jwt/AuthJwt.java index e34741a37..92fc948f4 100644 --- a/maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/jwt/AuthJwt.java +++ b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/jwt/AuthJwt.java @@ -32,6 +32,7 @@ public class AuthJwt implements Serializable { private String ticket; private String token; private String type = "Bearer"; + private String remeberMe; private String id; private String name; private String username; @@ -157,6 +158,13 @@ public class AuthJwt implements Serializable { this.passwordSetType = passwordSetType; } + public String getRemeberMe() { + return remeberMe; + } + + public void setRemeberMe(String remeberMe) { + this.remeberMe = remeberMe; + } @Override public String toString() { diff --git a/maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/jwt/AuthJwtService.java b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/jwt/AuthJwtService.java index f4b2c520d..adc76c0fb 100644 --- a/maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/jwt/AuthJwtService.java +++ b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/jwt/AuthJwtService.java @@ -64,8 +64,6 @@ public class AuthJwtService { this.momentaryService = momentaryService; this.hmac512Service = new HMAC512Service(authJwkConfig.getSecret()); - - } /** diff --git a/maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/session/Session.java b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/session/Session.java index e2304b444..da4217fb6 100644 --- a/maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/session/Session.java +++ b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/session/Session.java @@ -69,11 +69,11 @@ public class Session implements Serializable{ } public String getFormattedId() { - return id; + return SESSION_PREFIX + id; } - public void setId(String ticketId) { - this.id = ticketId; + public void setId(String sessionId) { + this.id = sessionId; } diff --git a/maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/support/rememberme/AbstractRemeberMeService.java b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/support/rememberme/AbstractRemeberMeService.java new file mode 100644 index 000000000..f3ba83d82 --- /dev/null +++ b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/support/rememberme/AbstractRemeberMeService.java @@ -0,0 +1,124 @@ +/* + * 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.maxkey.authn.support.rememberme; + +import java.text.ParseException; +import java.util.Date; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.joda.time.DateTime; +import org.maxkey.authn.SignPrincipal; +import org.maxkey.authn.jwt.AuthJwtService; +import org.maxkey.configuration.ApplicationConfig; +import org.maxkey.crypto.jwt.HMAC512Service; +import org.maxkey.entity.UserInfo; +import org.maxkey.util.DateUtils; +import org.maxkey.web.WebContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.Authentication; + +import com.nimbusds.jwt.JWTClaimsSet; + +public abstract class AbstractRemeberMeService { + private static final Logger _logger = LoggerFactory.getLogger(AbstractRemeberMeService.class); + + protected Integer validity = 7; + + protected ApplicationConfig applicationConfig; + + AuthJwtService authJwtService; + + // follow function is for persist + public abstract void save(RemeberMe remeberMe); + + public abstract void update(RemeberMe remeberMe); + + public abstract RemeberMe read(RemeberMe remeberMe); + + public abstract void remove(String username); + // end persist + + public String createRemeberMe(Authentication authentication, + HttpServletRequest request, HttpServletResponse response) { + if (applicationConfig.getLoginConfig().isRemeberMe()) { + SignPrincipal principal = ((SignPrincipal)authentication.getPrincipal()); + UserInfo userInfo = principal.getUserInfo(); + _logger.debug("Remeber Me ..."); + RemeberMe remeberMe = new RemeberMe(); + remeberMe.setId(WebContext.genId()); + remeberMe.setUserId(userInfo.getId()); + remeberMe.setUsername(userInfo.getUsername()); + remeberMe.setLastLoginTime(DateUtils.getCurrentDate()); + remeberMe.setExpirationTime(DateTime.now().plusDays(validity).toDate()); + save(remeberMe); + _logger.debug("Remeber Me " + remeberMe); + return genRemeberMe(remeberMe); + } + return null; + } + + public String updateRemeberMe(RemeberMe remeberMe) { + remeberMe.setLastLoginTime(new Date()); + remeberMe.setExpirationTime(DateTime.now().plusDays(validity).toDate()); + update(remeberMe); + _logger.debug("update Remeber Me " + remeberMe); + + return genRemeberMe(remeberMe); + } + + public boolean removeRemeberMe(HttpServletResponse response,UserInfo currentUser) { + remove(currentUser.getUsername()); + + return true; + } + + public RemeberMe resolve(String rememberMeToken) throws ParseException { + JWTClaimsSet claims = authJwtService.resolve(rememberMeToken); + RemeberMe remeberMe = new RemeberMe(); + remeberMe.setId(claims.getJWTID()); + remeberMe.setUsername(claims.getSubject()); + return read(remeberMe); + } + + public String genRemeberMe(RemeberMe remeberMe ) { + _logger.debug("expiration Time : {}" , remeberMe.getExpirationTime()); + + JWTClaimsSet remeberMeJwtClaims =new JWTClaimsSet.Builder() + .issuer("") + .subject(remeberMe.getUsername()) + .jwtID(remeberMe.getId()) + .issueTime(remeberMe.getLastLoginTime()) + .expirationTime(remeberMe.getExpirationTime()) + .claim("kid", HMAC512Service.MXK_AUTH_JWK) + .build(); + + return authJwtService.signedJWT(remeberMeJwtClaims); + } + + public Integer getValidity() { + return validity; + } + + public void setValidity(Integer validity) { + this.validity = validity; + } + + +} diff --git a/maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/support/rememberme/InMemoryRemeberMeService.java b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/support/rememberme/InMemoryRemeberMeService.java new file mode 100644 index 000000000..7e66a7e48 --- /dev/null +++ b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/support/rememberme/InMemoryRemeberMeService.java @@ -0,0 +1,54 @@ +/* + * 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.maxkey.authn.support.rememberme; + +import java.util.concurrent.TimeUnit; + +import org.maxkey.constants.ConstsTimeInterval; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; + +public class InMemoryRemeberMeService extends AbstractRemeberMeService { + + protected static final Cache remeberMeStore = + Caffeine.newBuilder() + .expireAfterWrite(ConstsTimeInterval.TWO_WEEK, TimeUnit.SECONDS) + .build(); + + @Override + public void save(RemeberMe remeberMe) { + remeberMeStore.put(remeberMe.getUsername(), remeberMe); + } + + @Override + public void update(RemeberMe remeberMe) { + remeberMeStore.put(remeberMe.getUsername(), remeberMe); + } + + @Override + public RemeberMe read(RemeberMe remeberMe) { + return remeberMeStore.getIfPresent(remeberMe.getUsername()); + } + + @Override + public void remove(String username) { + remeberMeStore.invalidate(username); + } + +} diff --git a/maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/support/rememberme/JdbcRemeberMeService.java b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/support/rememberme/JdbcRemeberMeService.java new file mode 100644 index 000000000..a0eaf8764 --- /dev/null +++ b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/support/rememberme/JdbcRemeberMeService.java @@ -0,0 +1,109 @@ +/* + * 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.maxkey.authn.support.rememberme; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import java.util.List; + +import org.maxkey.authn.jwt.AuthJwtService; +import org.maxkey.configuration.ApplicationConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; + +public class JdbcRemeberMeService extends AbstractRemeberMeService { + private static final Logger _logger = LoggerFactory.getLogger(JdbcRemeberMeService.class); + + private static final String DEFAULT_DEFAULT_INSERT_STATEMENT = + "insert into mxk_remember_me(id, userid,username,lastlogintime,expirationtime)values( ? , ? , ? , ? , ?)"; + + private static final String DEFAULT_DEFAULT_SELECT_STATEMENT = + "select id, userid,username,lastlogintime,expirationtime from mxk_remember_me " + + " where id = ? and username = ?"; + + private static final String DEFAULT_DEFAULT_DELETE_STATEMENT = + "delete from mxk_remember_me where username = ?"; + + private static final String DEFAULT_DEFAULT_UPDATE_STATEMENT = + "update mxk_remember_me set lastlogintime = ? , expirationtime = ? where id = ?"; + + private final JdbcTemplate jdbcTemplate; + + public JdbcRemeberMeService( + JdbcTemplate jdbcTemplate, + ApplicationConfig applicationConfig, + AuthJwtService authJwtService) { + this.jdbcTemplate = jdbcTemplate; + this.applicationConfig = applicationConfig; + this.authJwtService = authJwtService; + } + + @Override + public void save(RemeberMe remeberMe) { + jdbcTemplate.update(DEFAULT_DEFAULT_INSERT_STATEMENT, + new Object[] { + remeberMe.getId(), + remeberMe.getUserId(), + remeberMe.getUsername(), + remeberMe.getLastLoginTime(), + remeberMe.getExpirationTime()}, + new int[] { + Types.VARCHAR, + Types.VARCHAR, + Types.VARCHAR, + Types.TIMESTAMP, + Types.TIMESTAMP + }); + } + + @Override + public void update(RemeberMe remeberMe) { + jdbcTemplate.update(DEFAULT_DEFAULT_UPDATE_STATEMENT, + new Object[] { + remeberMe.getLastLoginTime(), + remeberMe.getExpirationTime(), + remeberMe.getId() + }); + } + + @Override + public RemeberMe read(RemeberMe remeberMe) { + List listRemeberMe = jdbcTemplate.query(DEFAULT_DEFAULT_SELECT_STATEMENT, + new RowMapper() { + public RemeberMe mapRow(ResultSet rs, int rowNum) throws SQLException { + RemeberMe remeberMe = new RemeberMe(); + remeberMe.setId(rs.getString(1)); + remeberMe.setUserId(rs.getString(2)); + remeberMe.setUsername(rs.getString(3)); + remeberMe.setLastLoginTime(rs.getDate(4)); + return remeberMe; + } + }, remeberMe.getId(), remeberMe.getUsername()); + _logger.debug("listRemeberMe " + listRemeberMe); + return (listRemeberMe.size() > 0) ? listRemeberMe.get(0) : null; + } + + @Override + public void remove(String username) { + jdbcTemplate.update(DEFAULT_DEFAULT_DELETE_STATEMENT, username); + } + +} diff --git a/maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/support/rememberme/RemeberMe.java b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/support/rememberme/RemeberMe.java new file mode 100644 index 000000000..efc0aacab --- /dev/null +++ b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/support/rememberme/RemeberMe.java @@ -0,0 +1,97 @@ +/* + * 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.maxkey.authn.support.rememberme; + +import java.io.Serializable; +import java.util.Date; + +public class RemeberMe implements Serializable { + + private static final long serialVersionUID = 8010496585233991785L; + + String id; + + String userId; + + String username; + + Date lastLoginTime; + + Date expirationTime; + + public RemeberMe() { + super(); + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public Date getLastLoginTime() { + return lastLoginTime; + } + + public void setLastLoginTime(Date lastLoginTime) { + this.lastLoginTime = lastLoginTime; + } + + public Date getExpirationTime() { + return expirationTime; + } + + public void setExpirationTime(Date expirationTime) { + this.expirationTime = expirationTime; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("RemeberMe [id="); + builder.append(id); + builder.append(", userId="); + builder.append(userId); + builder.append(", username="); + builder.append(username); + builder.append(", lastLoginTime="); + builder.append(lastLoginTime); + builder.append(", expirationTime="); + builder.append(expirationTime); + builder.append("]"); + return builder.toString(); + } +} diff --git a/maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/support/rememberme/RemeberMeServiceFactory.java b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/support/rememberme/RemeberMeServiceFactory.java new file mode 100644 index 000000000..222d8e21a --- /dev/null +++ b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/authn/support/rememberme/RemeberMeServiceFactory.java @@ -0,0 +1,47 @@ +/* + * Copyright [2021] [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.maxkey.authn.support.rememberme; + +import org.maxkey.constants.ConstsPersistence; +import org.maxkey.persistence.redis.RedisConnectionFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.jdbc.core.JdbcTemplate; + +public class RemeberMeServiceFactory { + private static final Logger _logger = + LoggerFactory.getLogger(RemeberMeServiceFactory.class); + + public AbstractRemeberMeService getService( + int persistence, + JdbcTemplate jdbcTemplate, + RedisConnectionFactory redisConnFactory){ + + AbstractRemeberMeService remeberMeService = null; + if (persistence == ConstsPersistence.INMEMORY) { + remeberMeService = new InMemoryRemeberMeService(); + _logger.debug("InMemoryRemeberMeService"); + } else if (persistence == ConstsPersistence.JDBC) { + //remeberMeService = new JdbcRemeberMeService(jdbcTemplate); + _logger.debug("JdbcRemeberMeService not support "); + } else if (persistence == ConstsPersistence.REDIS) { + _logger.debug("RedisRemeberMeService not support "); + } + return remeberMeService; + } +} diff --git a/maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/autoconfigure/AuthenticationAutoConfiguration.java b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/autoconfigure/AuthenticationAutoConfiguration.java index 7685e2753..13b791503 100644 --- a/maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/autoconfigure/AuthenticationAutoConfiguration.java +++ b/maxkey-authentications/maxkey-authentication-core/src/main/java/org/maxkey/autoconfigure/AuthenticationAutoConfiguration.java @@ -30,6 +30,8 @@ import org.maxkey.authn.provider.TrustedAuthenticationProvider; import org.maxkey.authn.realm.AbstractAuthenticationRealm; import org.maxkey.authn.session.SessionManager; import org.maxkey.authn.session.SessionManagerFactory; +import org.maxkey.authn.support.rememberme.AbstractRemeberMeService; +import org.maxkey.authn.support.rememberme.JdbcRemeberMeService; import org.maxkey.authn.web.HttpSessionListenerAdapter; import org.maxkey.configuration.ApplicationConfig; import org.maxkey.configuration.AuthJwkConfig; @@ -181,7 +183,7 @@ public class AuthenticationAutoConfiguration implements InitializingBean { } - @Bean(name = "sessionManager") + @Bean public SessionManager sessionManager( @Value("${maxkey.server.persistence}") int persistence, JdbcTemplate jdbcTemplate, @@ -195,7 +197,22 @@ public class AuthenticationAutoConfiguration implements InitializingBean { return sessionManager; } - @Bean(name = "httpSessionListenerAdapter") + + /** + * remeberMeService . + * @return + */ + @Bean + public AbstractRemeberMeService remeberMeService( + @Value("${maxkey.server.persistence}") int persistence, + @Value("${maxkey.login.remeberme.validity}") int validity, + ApplicationConfig applicationConfig, + AuthJwtService authJwtService, + JdbcTemplate jdbcTemplate) { + return new JdbcRemeberMeService(jdbcTemplate,applicationConfig,authJwtService); + } + + @Bean public HttpSessionListenerAdapter httpSessionListenerAdapter() { return new HttpSessionListenerAdapter(); } diff --git a/maxkey-core/src/main/java/org/maxkey/web/WebConstants.java b/maxkey-core/src/main/java/org/maxkey/web/WebConstants.java index f233b87de..a589508d0 100644 --- a/maxkey-core/src/main/java/org/maxkey/web/WebConstants.java +++ b/maxkey-core/src/main/java/org/maxkey/web/WebConstants.java @@ -25,22 +25,6 @@ package org.maxkey.web; */ public class WebConstants { - public static final String USERNAME = "username"; - - public static final String REMOTE_USERNAME = "remote_username"; - - public static final String CURRENT_USER = "current_user"; - - public static final String CURRENT_COMPANY = "current_user_company"; - - public static final String CURRENT_DEPARTMENT = "current_user_department"; - - public static final String CURRENT_USER_NAVIGATIONS = "current_user_navigations"; - - public static final String CURRENT_USER_ROLES = "current_user_roles"; - - public static final String CURRENT_USER_SYSTEM_ROLES = "current_user_system_roles"; - public static final String CURRENT_USER_PASSWORD_SET_TYPE = "current_user_password_set_type"; @@ -65,15 +49,13 @@ public class WebConstants { public static final String AUTHORIZE_SIGN_ON_APP_SAMLV20_ADAPTER = "authorize_sign_on_app_samlv20_adapter"; - public static final String REMEBER_ME_SESSION = "remeber_me_session"; - public static final String KERBEROS_TOKEN_PARAMETER = "kerberosToken"; public static final String CAS_SERVICE_PARAMETER = "service"; public static final String KERBEROS_USERDOMAIN_PARAMETER = "kerberosUserDomain"; - public static final String REMEBER_ME_COOKIE = "sign_in_remeber_me"; + public static final String REMEBER_ME_COOKIE = "sign_remeber_me"; public static final String JWT_TOKEN_PARAMETER = "jwt"; @@ -88,10 +70,10 @@ public class WebConstants { public static final String LOGIN_ERROR_SESSION_MESSAGE = "login_error_session_message_key"; - public static final String ONLINE_TICKET_NAME = "online_ticket"; - public static final String ONLINE_TICKET_PREFIX = "OT"; + public static final String ONLINE_TICKET_NAME = "online_ticket"; + public static final String MXK_METADATA_PREFIX = "mxk_metadata_"; public static final class LOGIN_RESULT{ diff --git a/maxkey-core/src/main/java/org/maxkey/web/WebContext.java b/maxkey-core/src/main/java/org/maxkey/web/WebContext.java index a1302048b..e47aeac6e 100644 --- a/maxkey-core/src/main/java/org/maxkey/web/WebContext.java +++ b/maxkey-core/src/main/java/org/maxkey/web/WebContext.java @@ -79,29 +79,23 @@ public final class WebContext { sessionAttributeNameList.add(WebConstants.AUTHORIZE_SIGN_ON_APP); sessionAttributeNameList.add(WebConstants.AUTHORIZE_SIGN_ON_APP_SAMLV20_ADAPTER); - sessionAttributeNameList.add(WebConstants.CURRENT_USER); sessionAttributeNameList.add(WebConstants.CURRENT_USER_PASSWORD_SET_TYPE); sessionAttributeNameList.add(WebConstants.CURRENT_INST); sessionAttributeNameList.add(WebConstants.FIRST_SAVED_REQUEST_PARAMETER); - sessionAttributeNameList.add(WebConstants.REMEBER_ME_SESSION); - //logout logoutAttributeNameList.add(WebConstants.AUTHENTICATION); logoutAttributeNameList.add(WebConstants.AUTHORIZE_SIGN_ON_APP); logoutAttributeNameList.add(WebConstants.AUTHORIZE_SIGN_ON_APP_SAMLV20_ADAPTER); - logoutAttributeNameList.add(WebConstants.CURRENT_USER); logoutAttributeNameList.add(WebConstants.CURRENT_USER_PASSWORD_SET_TYPE); logoutAttributeNameList.add(WebConstants.FIRST_SAVED_REQUEST_PARAMETER); - logoutAttributeNameList.add(WebConstants.REMEBER_ME_SESSION); - } /** diff --git a/maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/login/login.component.ts b/maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/login/login.component.ts index 816c895da..2ab04545d 100644 --- a/maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/login/login.component.ts +++ b/maxkey-web-frontend/maxkey-web-app/src/app/routes/passport/login/login.component.ts @@ -79,27 +79,31 @@ export class UserLoginComponent implements OnInit, OnDestroy { this.congressLogin(this.route.snapshot.queryParams[CONSTS.CONGRESS]); } - if (localStorage.getItem(CONSTS.REMEMBER) && localStorage.getItem(CONSTS.REMEMBER)?.endsWith('true')) { - this.authenticationService.navigate({}); - //auto auth - } else { - //init socials,state - this.authenticationService.clear(); - this.authenticationService - .get({}) - .pipe( - finalize(() => { - this.loading = false; - this.cdr.detectChanges(); - }) - ) - .subscribe(res => { - this.loading = true; - if (res.code !== 0) { - this.error = res.msg; - } else { + //init socials,state + this.authenticationService.clear(); + this.authenticationService + .get({ remember_me: localStorage.getItem(CONSTS.REMEMBER) }) + .pipe( + finalize(() => { + this.loading = false; + this.cdr.detectChanges(); + }) + ) + .subscribe(res => { + this.loading = true; + if (res.code !== 0) { + this.error = res.msg; + } else { + // 清空路由复用信息 + //console.log(res.data); + //REMEMBER ME + if (res.data.token) { // 清空路由复用信息 - console.log(res.data); + this.reuseTabService.clear(); + // 设置用户Token信息 + this.authenticationService.auth(res.data); + this.authenticationService.navigate({}); + } else { this.socials = res.data.socials; this.state = res.data.state; this.captchaType = res.data.captchaType; @@ -109,8 +113,8 @@ export class UserLoginComponent implements OnInit, OnDestroy { this.cdr.detectChanges(); }); } - }); - } + } + }); this.cdr.detectChanges(); } @@ -156,6 +160,11 @@ export class UserLoginComponent implements OnInit, OnDestroy { get otpCaptcha(): AbstractControl { return this.form.get('otpCaptcha')!; } + + get remember(): AbstractControl { + return this.form.get('remember')!; + } + // #endregion // #region get captcha @@ -224,7 +233,8 @@ export class UserLoginComponent implements OnInit, OnDestroy { password: this.password.value, captcha: this.captcha.value, mobile: this.mobile.value, - otpCaptcha: this.otpCaptcha.value + otpCaptcha: this.otpCaptcha.value, + remeberMe: this.remember.value }) .pipe( finalize(() => { diff --git a/maxkey-web-frontend/maxkey-web-app/src/app/service/authentication.service.ts b/maxkey-web-frontend/maxkey-web-app/src/app/service/authentication.service.ts index 38e446eaa..dfef2b58b 100644 --- a/maxkey-web-frontend/maxkey-web-app/src/app/service/authentication.service.ts +++ b/maxkey-web-frontend/maxkey-web-app/src/app/service/authentication.service.ts @@ -47,6 +47,7 @@ export class AuthenticationService { clear() { this.tokenService.clear(); + localStorage.setItem(CONSTS.REMEMBER, ''); } clearUser() { @@ -73,7 +74,9 @@ export class AuthenticationService { this.cookieService.set(CONSTS.CONGRESS, authJwt.token); this.cookieService.set(CONSTS.CONGRESS, authJwt.ticket, { domain: subHostName }); - + if (authJwt.remeberMe) { + localStorage.setItem(CONSTS.REMEMBER, authJwt.remeberMe); + } this.settingsService.setUser(user); this.tokenService.set(authJwt); this.tokenService.get()?.expired; diff --git a/maxkey-web-frontend/maxkey-web-app/src/app/shared/consts.ts b/maxkey-web-frontend/maxkey-web-app/src/app/shared/consts.ts index b579cde24..71288f47f 100644 --- a/maxkey-web-frontend/maxkey-web-app/src/app/shared/consts.ts +++ b/maxkey-web-frontend/maxkey-web-app/src/app/shared/consts.ts @@ -1,5 +1,5 @@ export const CONSTS = { CONGRESS: 'congress', REDIRECT_URI: 'redirect_uri', - REMEMBER: 'remember' + REMEMBER: 'remember_me' }; diff --git a/maxkey-webs/maxkey-web-maxkey/src/main/java/org/maxkey/web/contorller/LoginEntryPoint.java b/maxkey-webs/maxkey-web-maxkey/src/main/java/org/maxkey/web/contorller/LoginEntryPoint.java index 18f89a163..ddab5b502 100644 --- a/maxkey-webs/maxkey-web-maxkey/src/main/java/org/maxkey/web/contorller/LoginEntryPoint.java +++ b/maxkey-webs/maxkey-web-maxkey/src/main/java/org/maxkey/web/contorller/LoginEntryPoint.java @@ -17,15 +17,21 @@ package org.maxkey.web.contorller; +import java.text.ParseException; import java.util.HashMap; import java.util.regex.Pattern; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + import org.apache.commons.lang3.StringUtils; import org.maxkey.authn.AbstractAuthenticationProvider; import org.maxkey.authn.LoginCredential; import org.maxkey.authn.jwt.AuthJwt; import org.maxkey.authn.jwt.AuthJwtService; import org.maxkey.authn.support.kerberos.KerberosService; +import org.maxkey.authn.support.rememberme.AbstractRemeberMeService; +import org.maxkey.authn.support.rememberme.RemeberMe; import org.maxkey.authn.support.socialsignon.service.SocialSignOnProviderService; import org.maxkey.configuration.ApplicationConfig; import org.maxkey.entity.Institutions; @@ -47,6 +53,8 @@ import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -60,12 +68,12 @@ import io.swagger.v3.oas.annotations.tags.Tag; public class LoginEntryPoint { private static Logger _logger = LoggerFactory.getLogger(LoginEntryPoint.class); + Pattern mobileRegex = Pattern.compile("^(13[4,5,6,7,8,9]|15[0,8,9,1,7]|188|187)\\\\d{8}$"); + @Autowired - @Qualifier("authJwtService") AuthJwtService authJwtService; @Autowired - @Qualifier("applicationConfig") ApplicationConfig applicationConfig; @Autowired @@ -77,11 +85,9 @@ public class LoginEntryPoint { SocialSignOnProviderService socialSignOnProviderService; @Autowired - @Qualifier("kerberosService") KerberosService kerberosService; @Autowired - @Qualifier("userInfoService") UserInfoService userInfoService; @Autowired @@ -92,8 +98,8 @@ public class LoginEntryPoint { @Qualifier("otpAuthnService") protected OtpAuthnService otpAuthnService; - Pattern mobileRegex = Pattern.compile( - "^(13[4,5,6,7,8,9]|15[0,8,9,1,7]|188|187)\\\\d{8}$"); + @Autowired + AbstractRemeberMeService remeberMeService; /** * init login @@ -101,8 +107,28 @@ public class LoginEntryPoint { */ @Operation(summary = "登录接口", description = "用户登录地址",method="GET") @RequestMapping(value={"/get"}, produces = {MediaType.APPLICATION_JSON_VALUE}) - public ResponseEntity get() { - _logger.debug("LoginController /get."); + public ResponseEntity get( + @RequestParam(value = "remember_me", required = false) String rememberMeToken) { + _logger.debug("/get."); + //Remember Me + if(StringUtils.isNotBlank(rememberMeToken) + && authJwtService.validateJwtToken(rememberMeToken)) { + try { + RemeberMe remeberMe = remeberMeService.resolve(rememberMeToken); + if(remeberMe != null) { + LoginCredential credential = new LoginCredential(); + String remeberMeJwt = remeberMeService.updateRemeberMe(remeberMe); + credential.setUsername(remeberMe.getUsername()); + Authentication authentication = authenticationProvider.authenticate(credential,true); + if(authentication != null) { + AuthJwt authJwt = authJwtService.genAuthJwt(authentication); + authJwt.setRemeberMe(remeberMeJwt); + return new Message(authJwt).buildResponse(); + } + } + } catch (ParseException e) { + } + } //for normal login HashMap model = new HashMap(); model.put("isRemeberMe", applicationConfig.getLoginConfig().isRemeberMe()); @@ -149,19 +175,26 @@ public class LoginEntryPoint { * @return */ @RequestMapping(value={"/signin"}, produces = {MediaType.APPLICATION_JSON_VALUE}) - public ResponseEntity signin( @RequestBody LoginCredential loginCredential) { + public ResponseEntity signin( HttpServletRequest request, HttpServletResponse response, + @RequestBody LoginCredential credential) { Message authJwtMessage = new Message(Message.FAIL); - if(authJwtService.validateJwtToken(loginCredential.getState())){ - String authType = loginCredential.getAuthType(); + if(authJwtService.validateJwtToken(credential.getState())){ + String authType = credential.getAuthType(); _logger.debug("Login AuthN Type " + authType); if (StringUtils.isNotBlank(authType)){ - Authentication authentication = authenticationProvider.authenticate(loginCredential); + Authentication authentication = authenticationProvider.authenticate(credential); if(authentication != null) { AuthJwt authJwt = authJwtService.genAuthJwt(authentication); + if(StringUtils.isNotBlank(credential.getRemeberMe()) + &&credential.getRemeberMe().equalsIgnoreCase("true")) { + String remeberMe = remeberMeService.createRemeberMe(authentication, request, response); + authJwt.setRemeberMe(remeberMe); + } if(WebContext.getAttribute(WebConstants.CURRENT_USER_PASSWORD_SET_TYPE)!=null) authJwt.setPasswordSetType( (Integer)WebContext.getAttribute(WebConstants.CURRENT_USER_PASSWORD_SET_TYPE)); authJwtMessage = new Message(authJwt); + } }else { _logger.error("Login AuthN type must eq normal , tfa or mobile . "); diff --git a/maxkey-webs/maxkey-web-maxkey/src/main/resources/application-http.properties b/maxkey-webs/maxkey-web-maxkey/src/main/resources/application-http.properties index 8c1c48d08..bc8b01b06 100644 --- a/maxkey-webs/maxkey-web-maxkey/src/main/resources/application-http.properties +++ b/maxkey-webs/maxkey-web-maxkey/src/main/resources/application-http.properties @@ -50,9 +50,11 @@ maxkey.server.message.queue =${SERVER_MESSAGE_QUEUE:none} #issuer name maxkey.app.issuer =CN=ConSec,CN=COM,CN=SH -maxkey.auth.jwt.expire =86400 maxkey.auth.jwt.issuer =${maxkey.server.uri} +maxkey.auth.jwt.expire =86400 maxkey.auth.jwt.secret =7heM-14BtxjyKPuH3ITIm7q2-ps5MuBirWCsrrdbzzSAOuSPrbQYiaJ54AeA0uH2XdkYy3hHAkTFIsieGkyqxOJZ_dQzrCbaYISH9rhUZAKYx8tUY0wkE4ArOC6LqHDJarR6UIcMsARakK9U4dhoOPO1cj74XytemI-w6ACYfzRUn_Rn4e-CQMcnD1C56oNEukwalf06xVgXl41h6K8IBEzLVod58y_VfvFn-NGWpNG0fy_Qxng6dg8Dgva2DobvzMN2eejHGLGB-x809MvC4zbG7CKNVlcrzMYDt2Gt2sOVDrt2l9YqJNfgaLFjrOEVw5cuXemGkX1MvHj6TAsbLg +maxkey.auth.jwt.refresh.expire =86400 +maxkey.auth.jwt.refresh.secret =7heM-14BtxjyKPuH3ITIm7q2-ps5MuBirWCsrrdbzzSAOuSPrbQYiaJ54AeA0uH2XdkYy3hHAkTFIsieGkyqxOJZ_dQzrCbaYISH9rhUZAKYx8tUY0wkE4ArOC6LqHDJarR6UIcMsARakK9U4dhoOPO1cj74XytemI-w6ACYfzRUn_Rn4e-CQMcnD1C56oNEukwalf06xVgXl41h6K8IBEzLVod58y_VfvFn-NGWpNG0fy_Qxng6dg8Dgva2DobvzMN2eejHGLGB-x809MvC4zbG7CKNVlcrzMYDt2Gt2sOVDrt2l9YqJNfgaLFjrOEVw5cuXemGkX1MvHj6TAsbLg ############################################################################ #Login configuration # ############################################################################