errorHttpResponseConverter = new JwtErrorHttpMessageConverter();
+
+}
diff --git a/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/authentication/JwtAuthenticationToken.java b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/authentication/JwtAuthenticationToken.java
index 4d702670..a9aff40d 100644
--- a/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/authentication/JwtAuthenticationToken.java
+++ b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/authentication/JwtAuthenticationToken.java
@@ -18,6 +18,7 @@
package cn.topiam.employee.protocol.jwt.authentication;
import java.util.ArrayList;
+import java.util.UUID;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.Authentication;
@@ -25,6 +26,8 @@ import org.springframework.security.core.Authentication;
import cn.topiam.employee.application.jwt.model.JwtProtocolConfig;
import cn.topiam.employee.protocol.jwt.token.IdToken;
+import lombok.Getter;
+
/**
*
* @author TopIAM
@@ -32,6 +35,9 @@ import cn.topiam.employee.protocol.jwt.token.IdToken;
*/
public class JwtAuthenticationToken extends AbstractAuthenticationToken {
+ @Getter
+ private final String id;
+
/**
* principal
*/
@@ -40,11 +46,13 @@ public class JwtAuthenticationToken extends AbstractAuthenticationToken {
/**
* idToken
*/
+ @Getter
private final IdToken idToken;
/**
* 协议配置
*/
+ @Getter
private final JwtProtocolConfig config;
/**
@@ -60,6 +68,7 @@ public class JwtAuthenticationToken extends AbstractAuthenticationToken {
this.principal = principal;
this.idToken = idToken;
this.config = config;
+ this.id = UUID.randomUUID().toString();
setAuthenticated(true);
}
@@ -93,11 +102,4 @@ public class JwtAuthenticationToken extends AbstractAuthenticationToken {
return this.principal;
}
- public IdToken getIdToken() {
- return idToken;
- }
-
- public JwtProtocolConfig getConfig() {
- return config;
- }
}
diff --git a/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/authentication/JwtLogoutAuthenticationToken.java b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/authentication/JwtLogoutAuthenticationToken.java
new file mode 100644
index 00000000..c0c91cbe
--- /dev/null
+++ b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/authentication/JwtLogoutAuthenticationToken.java
@@ -0,0 +1,68 @@
+package cn.topiam.employee.protocol.jwt.authentication;
+
+import java.util.ArrayList;
+
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+import org.springframework.security.authentication.AnonymousAuthenticationToken;
+import org.springframework.security.core.Authentication;
+
+import lombok.Getter;
+
+/**
+ *
+ * @author SanLi
+ * Created by qinggang.zuo@gmail.com / 2689170096@qq.com on 2023/9/4 13:43
+ */
+public class JwtLogoutAuthenticationToken extends AbstractAuthenticationToken {
+
+ private final Authentication principal;
+
+ @Getter
+ private final String sessionId;
+
+ public JwtLogoutAuthenticationToken(Authentication principal, String sessionId) {
+ super(new ArrayList<>());
+ this.principal = principal;
+ this.sessionId = sessionId;
+ }
+
+ /**
+ * The credentials that prove the principal is correct. This is usually a password,
+ * but could be anything relevant to the AuthenticationManager
. Callers
+ * are expected to populate the credentials.
+ *
+ * @return the credentials that prove the identity of the Principal
+ */
+ @Override
+ public Object getCredentials() {
+ return "";
+ }
+
+ /**
+ * The identity of the principal being authenticated. In the case of an authentication
+ * request with username and password, this would be the username. Callers are
+ * expected to populate the principal for an authentication request.
+ *
+ * The AuthenticationManager implementation will often return an
+ * Authentication containing richer information as the principal for use by
+ * the application. Many of the authentication providers will create a
+ * {@code UserDetails} object as the principal.
+ *
+ * @return the Principal
being authenticated or the authenticated
+ * principal after authentication.
+ */
+ @Override
+ public Object getPrincipal() {
+ return principal;
+ }
+
+ /**
+ * Returns {@code true} if {@link #getPrincipal()} is authenticated, {@code false} otherwise.
+ *
+ * @return {@code true} if {@link #getPrincipal()} is authenticated, {@code false} otherwise
+ */
+ public boolean isPrincipalAuthenticated() {
+ return !AnonymousAuthenticationToken.class.isAssignableFrom(this.principal.getClass()) &&
+ this.principal.isAuthenticated();
+ }
+}
diff --git a/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/authentication/JwtRequestAuthenticationToken.java b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/authentication/JwtRequestAuthenticationToken.java
index a842e39a..904ad50b 100644
--- a/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/authentication/JwtRequestAuthenticationToken.java
+++ b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/authentication/JwtRequestAuthenticationToken.java
@@ -25,6 +25,8 @@ import org.springframework.security.core.Authentication;
import cn.topiam.employee.application.jwt.model.JwtProtocolConfig;
+import lombok.Getter;
+
/**
*
* @author TopIAM
@@ -39,25 +41,21 @@ public class JwtRequestAuthenticationToken extends AbstractAuthenticationToken {
/**
* 目标URL
*/
+ @Getter
private String targetUrl;
/**
* 协议配置
*/
+ @Getter
private final JwtProtocolConfig config;
/**
* 额外参数
*/
+ @Getter
private final Map additionalParameters;
- public JwtRequestAuthenticationToken(Authentication principal, JwtProtocolConfig config,
- Map additionalParameters) {
- super(new ArrayList<>());
- this.principal = principal;
- this.config = config;
- this.additionalParameters = additionalParameters;
- }
public JwtRequestAuthenticationToken(Authentication principal, String targetUrl,
JwtProtocolConfig config,
@@ -99,19 +97,8 @@ public class JwtRequestAuthenticationToken extends AbstractAuthenticationToken {
return principal;
}
- public JwtProtocolConfig getConfig() {
- return config;
- }
-
public void setTargetUrl(String targetUrl) {
this.targetUrl = targetUrl;
}
- public String getTargetUrl() {
- return targetUrl;
- }
-
- public Map getAdditionalParameters() {
- return additionalParameters;
- }
}
diff --git a/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/authentication/JwtRequestAuthenticationTokenProvider.java b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/authentication/JwtRequestAuthenticationTokenProvider.java
index 59b3234b..0eeba04a 100644
--- a/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/authentication/JwtRequestAuthenticationTokenProvider.java
+++ b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/authentication/JwtRequestAuthenticationTokenProvider.java
@@ -37,6 +37,7 @@ import cn.topiam.employee.protocol.jwt.token.IdToken;
import cn.topiam.employee.protocol.jwt.token.IdTokenContext;
import cn.topiam.employee.protocol.jwt.token.IdTokenGenerator;
import cn.topiam.employee.protocol.jwt.token.JwtIdTokenGenerator;
+import cn.topiam.employee.support.security.authentication.WebAuthenticationDetails;
import cn.topiam.employee.support.security.userdetails.UserDetails;
import static cn.topiam.employee.common.constant.ProtocolConstants.APP_CODE_VARIABLE;
import static cn.topiam.employee.common.constant.ProtocolConstants.JwtEndpointConstants.JWT_SSO_PATH;
@@ -80,11 +81,12 @@ public final class JwtRequestAuthenticationTokenProvider implements Authenticati
JwtProtocolConfig config = requestAuthenticationToken.getConfig();
String issuer = ServerHelp.getPortalPublicBaseUrl() + JWT_SSO_PATH.replace(APP_CODE_VARIABLE, config.getAppCode());
String subject = getSubject(config,(UserDetails) principal.getPrincipal());
-
+ WebAuthenticationDetails details = (WebAuthenticationDetails) requestAuthenticationToken.getDetails();
IdTokenContext tokenContext = IdTokenContext.builder()
.issuer(issuer)
.subject(subject)
.audience(config.getAppCode())
+ .sessionId(details.getSessionId())
.idTokenTimeToLive(config.getIdTokenTimeToLive())
.privateKey(config.getJwtPrivateKey())
.build();
diff --git a/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/authentication/OidcLogoutAuthenticationProvider.java b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/authentication/OidcLogoutAuthenticationProvider.java
new file mode 100644
index 00000000..c0e30136
--- /dev/null
+++ b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/authentication/OidcLogoutAuthenticationProvider.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2020-2023 the original author or authors.
+ *
+ * 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
+ *
+ * https://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 cn.topiam.employee.protocol.jwt.authentication;
+
+import org.springframework.security.authentication.AuthenticationProvider;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.session.SessionInformation;
+import org.springframework.security.core.session.SessionRegistry;
+
+/**
+ *
+ * @author SanLi
+ * Created by qinggang.zuo@gmail.com / 2689170096@qq.com on 2023/9/4 16:11
+ */
+public final class OidcLogoutAuthenticationProvider implements AuthenticationProvider {
+
+
+
+
+ @Override
+ public Authentication authenticate(Authentication authentication) throws AuthenticationException {
+ JwtLogoutAuthenticationToken logoutAuthenticationToken= (JwtLogoutAuthenticationToken) authentication;
+ SessionInformation sessionInformation=sessionRegistry.getSessionInformation(logoutAuthenticationToken.getSessionId());
+ if (sessionInformation.isExpired()){
+
+ }
+ return null;
+ }
+
+
+ @Override
+ public boolean supports(Class> authentication) {
+ return JwtLogoutAuthenticationToken.class.isAssignableFrom(authentication);
+ }
+
+ private final SessionRegistry sessionRegistry;
+
+ public OidcLogoutAuthenticationProvider(SessionRegistry sessionRegistry) {
+ this.sessionRegistry = sessionRegistry;
+ }
+}
diff --git a/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/authorization/InMemoryJwtAuthorizationService.java b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/authorization/InMemoryJwtAuthorizationService.java
index 8a181f77..577c190e 100644
--- a/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/authorization/InMemoryJwtAuthorizationService.java
+++ b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/authorization/InMemoryJwtAuthorizationService.java
@@ -17,5 +17,37 @@
*/
package cn.topiam.employee.protocol.jwt.authorization;
+import cn.topiam.employee.protocol.jwt.authentication.JwtAuthenticationToken;
+
+/**
+ * 内存
+ *
+ * @author TopIAM
+ * Created by support@topiam.cn
+ */
public class InMemoryJwtAuthorizationService implements JwtAuthorizationService {
+ /**
+ * save
+ *
+ * @param token {@link JwtAuthenticationToken}
+ */
+ @Override
+ public void save(JwtAuthenticationToken token) {
+
+ }
+
+ @Override
+ public void remove(JwtAuthenticationToken authorization) {
+
+ }
+
+ @Override
+ public JwtAuthenticationToken findById(String id) {
+ return null;
+ }
+
+ @Override
+ public JwtAuthenticationToken findByToken(String token) {
+ return null;
+ }
}
diff --git a/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/authorization/JwtAuthorizationService.java b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/authorization/JwtAuthorizationService.java
index 058c54e5..8f930a94 100644
--- a/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/authorization/JwtAuthorizationService.java
+++ b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/authorization/JwtAuthorizationService.java
@@ -17,9 +17,25 @@
*/
package cn.topiam.employee.protocol.jwt.authorization;
+import org.springframework.lang.Nullable;
+
+import cn.topiam.employee.protocol.jwt.authentication.JwtAuthenticationToken;
+
/**
+ *
* @author TopIAM
* Created by support@topiam.cn on 2023/7/8 00:23
*/
public interface JwtAuthorizationService {
+
+ void save(JwtAuthenticationToken token);
+
+ void remove(JwtAuthenticationToken authorization);
+
+ @Nullable
+ JwtAuthenticationToken findById(String id);
+
+ @Nullable
+ JwtAuthenticationToken findByToken(String token);
+
}
diff --git a/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/authorization/RedisJwtAuthorizationService.java b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/authorization/RedisJwtAuthorizationService.java
new file mode 100644
index 00000000..5be90b64
--- /dev/null
+++ b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/authorization/RedisJwtAuthorizationService.java
@@ -0,0 +1,184 @@
+/*
+ * eiam-protocol-jwt - Employee Identity and Access Management
+ * Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+package cn.topiam.employee.protocol.jwt.authorization;
+
+import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.time.Duration;
+import java.time.temporal.ChronoUnit;
+import java.util.*;
+
+import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
+import org.springframework.data.redis.core.RedisOperations;
+import org.springframework.http.converter.json.SpringHandlerInstantiator;
+import org.springframework.util.Assert;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import cn.topiam.employee.protocol.jwt.authentication.JwtAuthenticationToken;
+import cn.topiam.employee.protocol.jwt.jackson.JwtAuthorizationModule;
+import cn.topiam.employee.support.jackjson.SupportJackson2Module;
+
+import lombok.Setter;
+import static cn.topiam.employee.protocol.jwt.constant.JwtProtocolConstants.JWT_PROTOCOL_CACHE_PREFIX;
+
+/**
+ * redis
+ *
+ * @author TopIAM
+ *
+ * Created by support@topiam.cn / 2689170096@qq.com on 2023/9/1 12:51
+ */
+public class RedisJwtAuthorizationService implements JwtAuthorizationService {
+ private static final String CID_TO_AUTHORIZATIONS = "cid_to_authorizations:";
+ private static final String UID_TO_AUTHORIZATIONS = "uid_to_authorizations:";
+ private static final String ID_TO_AUTHORIZATION = "id_to_authorization:";
+ private static final String ID_TO_CORRELATIONS = "id_to_correlations:";
+
+ public RedisJwtAuthorizationService(RedisOperations redisOperations,
+ AutowireCapableBeanFactory beanFactory) {
+ Assert.notNull(redisOperations, "redisOperations mut not be null");
+ this.redisOperations = redisOperations;
+ ClassLoader classLoader = this.getClass().getClassLoader();
+ objectMapper.registerModules(SupportJackson2Module.getModules(classLoader));
+ objectMapper.registerModule(new JwtAuthorizationModule());
+ objectMapper.setHandlerInstantiator(new SpringHandlerInstantiator(beanFactory));
+ }
+
+ /**
+ * save
+ *
+ * @param token {@link JwtAuthenticationToken}
+ */
+ @Override
+ public void save(JwtAuthenticationToken token) {
+ String authorizationId = token.getId();
+ String uidToAuthorizationsKey = getIdTokenToAuthorization(authorizationId);
+ String idToAuthorizationKey = getIdToAuthorizationKey(authorizationId);
+ String idToCorrelationsKey = getIdToCorrelations(authorizationId);
+ String cidToAuthorizationsKey = getCidToAuthorizations(token.getConfig().getClientId());
+ //过期时间
+ Duration timeToLive = Duration.of(token.getConfig().getIdTokenTimeToLive(),
+ ChronoUnit.SECONDS);
+ Set correlationValues = new HashSet<>();
+ //add client authorizations
+ correlationValues.add(cidToAuthorizationsKey);
+ redisOperations.opsForSet().add(cidToAuthorizationsKey, authorizationId);
+ redisOperations.expire(cidToAuthorizationsKey, timeToLive);
+ //save authorization
+ correlationValues.add(idToAuthorizationKey);
+ redisOperations.opsForValue().set(idToAuthorizationKey, write(token));
+ redisOperations.expire(idToAuthorizationKey, timeToLive);
+ //save id_token
+ correlationValues.add(uidToAuthorizationsKey);
+ redisOperations.opsForValue().set(uidToAuthorizationsKey,
+ token.getIdToken().getTokenValue());
+ redisOperations.expire(uidToAuthorizationsKey, timeToLive);
+ //save correlations
+ correlationValues.add(idToCorrelationsKey);
+ redisOperations.opsForSet().add(idToCorrelationsKey,
+ correlationValues.toArray(String[]::new));
+ redisOperations.expire(idToCorrelationsKey, timeToLive);
+ }
+
+ @Override
+ public void remove(JwtAuthenticationToken authorization) {
+ String authorizationId = authorization.getId();
+ String uidToAuthorizationsKey = getIdTokenToAuthorization(authorizationId);
+ String idToAuthorizationKey = getIdToAuthorizationKey(authorizationId);
+ String idToCorrelationsKey = getIdToCorrelations(authorizationId);
+ String cidToAuthorizationsKey = getCidToAuthorizations(
+ authorization.getConfig().getClientId());
+ redisOperations.delete(idToAuthorizationKey);
+ redisOperations.delete(idToCorrelationsKey);
+ redisOperations.delete(uidToAuthorizationsKey);
+ redisOperations.opsForSet().remove(cidToAuthorizationsKey, authorization.getId());
+ }
+
+ @Override
+ public JwtAuthenticationToken findById(String id) {
+ return Optional.ofNullable(redisOperations.opsForValue().get(getIdToAuthorizationKey(id)))
+ .map(this::parse).orElse(null);
+ }
+
+ @Override
+ public JwtAuthenticationToken findByToken(String token) {
+ return Optional
+ .ofNullable(redisOperations.opsForValue().get(getIdTokenToAuthorization(token)))
+ .map(this::parse).orElse(null);
+ }
+
+ private String getIdToCorrelations(String authorizationId) {
+ return prefix + ID_TO_CORRELATIONS + authorizationId;
+ }
+
+ private String write(Object data) {
+ try {
+ return this.objectMapper.writeValueAsString(data);
+ } catch (Exception ex) {
+ throw new IllegalArgumentException(ex.getMessage(), ex);
+ }
+ }
+
+ private JwtAuthenticationToken parse(String data) {
+ try {
+ return this.objectMapper.readValue(data, new TypeReference<>() {
+ });
+ } catch (Exception ex) {
+ throw new IllegalArgumentException(ex.getMessage(), ex);
+ }
+ }
+
+ public String getCidToAuthorizations(String clientId) {
+ return prefix + CID_TO_AUTHORIZATIONS + clientId;
+ }
+
+ private String getIdToAuthorizationKey(String authorizationId) {
+ return prefix + ID_TO_AUTHORIZATION + authorizationId;
+ }
+
+ private String getIdTokenToAuthorization(String idToken) {
+ return prefix + UID_TO_AUTHORIZATIONS + generateKey(idToken);
+ }
+
+ protected static String generateKey(String rawKey) {
+ byte[] bytes = DIGEST.digest(rawKey.getBytes(StandardCharsets.UTF_8));
+ return String.format("%032x", new BigInteger(1, bytes));
+ }
+
+ private final RedisOperations redisOperations;
+
+ @Setter
+ private String prefix = JWT_PROTOCOL_CACHE_PREFIX;
+
+ @Setter
+ private ObjectMapper objectMapper = new ObjectMapper();
+
+ private static final MessageDigest DIGEST;
+
+ static {
+ try {
+ DIGEST = MessageDigest.getInstance("SHA-256");
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/configurers/JwtAuthorizationEndpointConfigurer.java b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/configurers/JwtAuthorizationEndpointConfigurer.java
index 73ab604e..9df739ed 100644
--- a/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/configurers/JwtAuthorizationEndpointConfigurer.java
+++ b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/configurers/JwtAuthorizationEndpointConfigurer.java
@@ -27,6 +27,7 @@ import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import cn.topiam.employee.protocol.code.configurer.AbstractConfigurer;
+import cn.topiam.employee.protocol.code.util.ProtocolConfigUtils;
import cn.topiam.employee.protocol.jwt.authentication.JwtRequestAuthenticationTokenProvider;
import cn.topiam.employee.protocol.jwt.authorization.JwtAuthorizationService;
import cn.topiam.employee.protocol.jwt.endpoint.JwtAuthenticationEndpointFilter;
@@ -73,9 +74,11 @@ public class JwtAuthorizationEndpointConfigurer extends AbstractConfigurer {
.getSharedObject(AuthenticationManager.class);
JwtAuthorizationService authorizationService = JwtAuthenticationUtils
.getAuthorizationService(httpSecurity);
- JwtAuthenticationEndpointFilter initSingleSignOnEndpointFilter = new JwtAuthenticationEndpointFilter(
+ JwtAuthenticationEndpointFilter jwtAuthenticationEndpointFilter = new JwtAuthenticationEndpointFilter(
requestMatcher, authenticationManager, authorizationService);
- httpSecurity.addFilterBefore(postProcess(initSingleSignOnEndpointFilter),
+ jwtAuthenticationEndpointFilter.setAuthenticationDetailsSource(
+ ProtocolConfigUtils.getAuthenticationDetailsSource(httpSecurity));
+ httpSecurity.addFilterBefore(postProcess(jwtAuthenticationEndpointFilter),
AuthorizationFilter.class);
}
diff --git a/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/constant/JwtProtocolConstants.java b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/constant/JwtProtocolConstants.java
index 7f45ac86..05704573 100644
--- a/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/constant/JwtProtocolConstants.java
+++ b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/constant/JwtProtocolConstants.java
@@ -17,8 +17,8 @@
*/
package cn.topiam.employee.protocol.jwt.constant;
-import static cn.topiam.employee.common.constant.AuthorizeConstants.AUTHORIZE_PATH;
-import static cn.topiam.employee.common.constant.ProtocolConstants.APP_CODE_VARIABLE;
+import static cn.topiam.employee.protocol.code.constant.ProtocolConstants.PROTOCOL_CACHE_PREFIX;
+import static cn.topiam.employee.support.constant.EiamConstants.COLON;
/**
* 协议常量
@@ -29,18 +29,19 @@ import static cn.topiam.employee.common.constant.ProtocolConstants.APP_CODE_VARI
public class JwtProtocolConstants {
/**
- * JWT IDP SSO 发起
+ * 协议缓存前缀
*/
- public static final String IDP_JWT_SSO_INITIATOR = AUTHORIZE_PATH + "/jwt/" + APP_CODE_VARIABLE
- + "/initiator";
+ public static final String JWT_PROTOCOL_CACHE_PREFIX = PROTOCOL_CACHE_PREFIX + "jwt" + COLON;
- public static final String TARGET_URL = "target_url";
- public static final String ID_TOKEN = "id_token";
- public static final String NONCE = "nonce";
+ public static final String TARGET_URL = "target_url";
+ public static final String ID_TOKEN = "id_token";
+ public static final String NONCE = "nonce";
- public static final String URL = "url";
- public static final String BINDING_TYPE = "binding_type";
+ public static final String URL = "url";
+ public static final String BINDING_TYPE = "binding_type";
- public static final String JWT_ERROR_URI = "https://eiam.topiam.cn";
+ public static final String JWT_ERROR_URI = "https://eiam.topiam.cn";
+
+ public static final String S_ID = "sid";
}
diff --git a/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/context/JwtAuthorizationServerContextFilter.java b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/context/JwtAuthorizationServerContextFilter.java
index c270b59c..7940c41c 100644
--- a/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/context/JwtAuthorizationServerContextFilter.java
+++ b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/context/JwtAuthorizationServerContextFilter.java
@@ -46,8 +46,6 @@ import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
-import static org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames.GRANT_TYPE;
-
import static cn.topiam.employee.common.constant.ProtocolConstants.APP_CODE;
import static cn.topiam.employee.support.util.HttpRequestUtils.getRequestHeaders;
@@ -85,7 +83,6 @@ public final class JwtAuthorizationServerContextFilter extends OncePerRequestFil
return;
}
try {
- request.getParameterValues(GRANT_TYPE);
//@formatter:off
Map variables = matcher.getVariables();
String appCode = variables.get(APP_CODE);
diff --git a/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/endpoint/JwtAuthenticationEndpointFilter.java b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/endpoint/JwtAuthenticationEndpointFilter.java
index 5ba84e44..c71812e9 100644
--- a/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/endpoint/JwtAuthenticationEndpointFilter.java
+++ b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/endpoint/JwtAuthenticationEndpointFilter.java
@@ -24,15 +24,11 @@ import org.apache.commons.compress.utils.CharsetNames;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.entity.ContentType;
import org.springframework.core.log.LogMessage;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.converter.HttpMessageConverter;
-import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.lang.NonNull;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
-import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
@@ -44,6 +40,7 @@ import org.springframework.web.filter.OncePerRequestFilter;
import cn.topiam.employee.application.jwt.model.JwtProtocolConfig;
import cn.topiam.employee.protocol.code.exception.TemplateNotExistException;
+import cn.topiam.employee.protocol.jwt.authentication.JwtAuthenticationFailureHandler;
import cn.topiam.employee.protocol.jwt.authentication.JwtAuthenticationToken;
import cn.topiam.employee.protocol.jwt.authentication.JwtRequestAuthenticationToken;
import cn.topiam.employee.protocol.jwt.authorization.JwtAuthorizationService;
@@ -51,7 +48,6 @@ import cn.topiam.employee.protocol.jwt.endpoint.authentication.JwtRequestAuthent
import cn.topiam.employee.protocol.jwt.exception.JwtAuthenticationException;
import cn.topiam.employee.protocol.jwt.exception.JwtError;
import cn.topiam.employee.protocol.jwt.exception.JwtErrorCodes;
-import cn.topiam.employee.protocol.jwt.http.converter.JwtErrorHttpMessageConverter;
import cn.topiam.employee.protocol.jwt.token.IdToken;
import freemarker.cache.ClassTemplateLoader;
@@ -62,6 +58,7 @@ import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import static cn.topiam.employee.protocol.jwt.constant.JwtProtocolConstants.*;
+import static cn.topiam.employee.protocol.jwt.endpoint.JwtAuthenticationEndpointUtils.throwError;
/**
* @author TopIAM
@@ -89,11 +86,6 @@ public final class JwtAuthenticationEndpointFilter extends OncePerRequestFilter
*/
private final JwtAuthorizationService authorizationService;
- /**
- * 错误响应处理器
- */
- private final HttpMessageConverter errorHttpResponseConverter = new JwtErrorHttpMessageConverter();
-
/**
* 授权端点匹配器
*/
@@ -107,7 +99,7 @@ public final class JwtAuthenticationEndpointFilter extends OncePerRequestFilter
/**
* 身份验证失败处理程序
*/
- private AuthenticationFailureHandler authenticationFailureHandler = this::sendErrorResponse;
+ private AuthenticationFailureHandler authenticationFailureHandler = new JwtAuthenticationFailureHandler();
/**
* 会话身份策略
@@ -198,6 +190,13 @@ public final class JwtAuthenticationEndpointFilter extends OncePerRequestFilter
LogMessage.format("Authorization request failed: %s", ex.getError()), ex);
}
this.authenticationFailureHandler.onAuthenticationFailure(request, response, ex);
+ } catch (Exception ex) {
+ JwtError error = new JwtError(JwtErrorCodes.SERVER_ERROR,ex.getMessage(),JWT_ERROR_URI);
+ if (this.logger.isTraceEnabled()) {
+ this.logger.trace(error, ex);
+ }
+ this.authenticationFailureHandler.onAuthenticationFailure(request, response,
+ new JwtAuthenticationException(error));
}
}
@@ -229,36 +228,15 @@ public final class JwtAuthenticationEndpointFilter extends OncePerRequestFilter
data.put(ID_TOKEN, idToken.getTokenValue());
data.put(TARGET_URL, targetUri);
template.process(data, response.getWriter());
-
+ //save
+ authorizationService.save(authenticationToken);
} catch (Exception e) {
JwtError error = new JwtError(JwtErrorCodes.SERVER_ERROR,e.getMessage(),JWT_ERROR_URI);
- throw new JwtAuthenticationException(error);
+ throwError(error);
}
//@formatter:on
}
- /**
- * 发送异常响应
- *
- * @param request {@link HttpServletRequest}
- * @param response {@link HttpServletResponse}
- * @param exception {@link AuthenticationException}
- */
- private void sendErrorResponse(HttpServletRequest request, HttpServletResponse response,
- AuthenticationException exception) throws IOException {
- if (exception instanceof JwtAuthenticationException) {
- ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
- JwtError error = ((JwtAuthenticationException) exception).getError();
- if (error.getErrorCode().equals(JwtErrorCodes.SERVER_ERROR)) {
- httpResponse.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
- }
- if (error.getErrorCode().equals(JwtErrorCodes.INVALID_REQUEST)) {
- httpResponse.setStatusCode(HttpStatus.BAD_REQUEST);
- }
- errorHttpResponseConverter.write(error, null, httpResponse);
- }
- }
-
private void configFreemarkerTemplate() {
try {
//模板存放路径
diff --git a/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/endpoint/JwtAuthenticationEndpointUtils.java b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/endpoint/JwtAuthenticationEndpointUtils.java
new file mode 100644
index 00000000..48320313
--- /dev/null
+++ b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/endpoint/JwtAuthenticationEndpointUtils.java
@@ -0,0 +1,16 @@
+package cn.topiam.employee.protocol.jwt.endpoint;
+
+import cn.topiam.employee.protocol.jwt.exception.JwtAuthenticationException;
+import cn.topiam.employee.protocol.jwt.exception.JwtError;
+
+/**
+ *
+ * @author SanLi
+ * Created by qinggang.zuo@gmail.com / 2689170096@qq.com on 2023/9/4 13:05
+ */
+public class JwtAuthenticationEndpointUtils {
+
+ public static void throwError(JwtError jwtError) {
+ throw new JwtAuthenticationException(jwtError);
+ }
+}
diff --git a/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/endpoint/JwtLogoutAuthenticationEndpointFilter.java b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/endpoint/JwtLogoutAuthenticationEndpointFilter.java
new file mode 100644
index 00000000..be91a204
--- /dev/null
+++ b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/endpoint/JwtLogoutAuthenticationEndpointFilter.java
@@ -0,0 +1,196 @@
+/*
+ * eiam-protocol-jwt - Employee Identity and Access Management
+ * Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+package cn.topiam.employee.protocol.jwt.endpoint;
+
+import java.io.IOException;
+
+import org.jetbrains.annotations.NotNull;
+import org.springframework.core.log.LogMessage;
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+import org.springframework.security.authentication.AuthenticationDetailsSource;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.session.SessionRegistry;
+import org.springframework.security.web.authentication.AuthenticationConverter;
+import org.springframework.security.web.authentication.AuthenticationFailureHandler;
+import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
+import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
+import org.springframework.security.web.authentication.logout.LogoutHandler;
+import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
+import org.springframework.security.web.util.matcher.RequestMatcher;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import cn.topiam.employee.protocol.jwt.authentication.JwtAuthenticationFailureHandler;
+import cn.topiam.employee.protocol.jwt.authentication.JwtLogoutAuthenticationToken;
+import cn.topiam.employee.protocol.jwt.endpoint.authentication.JwtLogoutAuthenticationConverter;
+import cn.topiam.employee.protocol.jwt.exception.JwtAuthenticationException;
+import cn.topiam.employee.protocol.jwt.exception.JwtError;
+import cn.topiam.employee.protocol.jwt.exception.JwtErrorCodes;
+
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import static cn.topiam.employee.protocol.jwt.constant.JwtProtocolConstants.JWT_ERROR_URI;
+
+/**
+ *
+ * @author TopIAM
+ * Created by support@topiam.cn on 2023/9/4 20:14
+ */
+public final class JwtLogoutAuthenticationEndpointFilter extends OncePerRequestFilter {
+
+ /**
+ * 端点匹配器
+ */
+ private final RequestMatcher requestMatcher;
+
+ /**
+ * 身份验证失败处理程序
+ */
+ private AuthenticationFailureHandler authenticationFailureHandler = new JwtAuthenticationFailureHandler();
+
+ /**
+ * AuthenticationSuccessHandler
+ */
+ private AuthenticationSuccessHandler authenticationSuccessHandler=this::sendAuthorizationResponse;
+
+ /**
+ * LogoutHandler
+ */
+ private final LogoutHandler logoutHandler;
+
+ /**
+ * 认证转换器
+ */
+ private AuthenticationConverter authenticationConverter;
+
+ /**
+ * AuthenticationDetailsSource
+ */
+ private AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource();
+
+ /**
+ * 认证管理器
+ */
+ private final AuthenticationManager authenticationManager;
+
+
+ public JwtLogoutAuthenticationEndpointFilter(RequestMatcher requestMatcher,
+ SessionRegistry sessionRegistry, AuthenticationManager authenticationManager) {
+ Assert.notNull(requestMatcher, "requestMatcher cannot be empty");
+ Assert.notNull(sessionRegistry, "sessionRegistry cannot be empty");
+ Assert.notNull(sessionRegistry, "authenticationManager cannot be empty");
+ this.authenticationManager = authenticationManager;
+ this.logoutHandler = new SecurityContextLogoutHandler();
+ this.requestMatcher = requestMatcher;
+ authenticationConverter=new JwtLogoutAuthenticationConverter();
+ }
+
+ public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {
+ Assert.notNull(authenticationFailureHandler, "authenticationFailureHandler cannot be null");
+ this.authenticationFailureHandler = authenticationFailureHandler;
+ }
+
+ public void setAuthenticationFailureHandler(AuthenticationSuccessHandler authenticationSuccessHandler) {
+ Assert.notNull(authenticationFailureHandler, "authenticationSuccessHandler cannot be null");
+ this.authenticationSuccessHandler = authenticationSuccessHandler;
+ }
+
+ public void setAuthenticationConverter(AuthenticationConverter authenticationConverter) {
+ Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
+ this.authenticationConverter = authenticationConverter;
+ }
+
+ /**
+ * Sets the {@link AuthenticationDetailsSource} used for building an authentication details instance from {@link HttpServletRequest}.
+ *
+ * @param authenticationDetailsSource the {@link AuthenticationDetailsSource} used for building an authentication details instance from {@link HttpServletRequest}
+ */
+ public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) {
+ Assert.notNull(authenticationDetailsSource, "authenticationDetailsSource cannot be null");
+ this.authenticationDetailsSource = authenticationDetailsSource;
+ }
+
+ /**
+ * Same contract as for {@code doFilter}, but guaranteed to be
+ * just invoked once per request within a single request thread.
+ * See {@link #shouldNotFilterAsyncDispatch()} for details.
+ * Provides HttpServletRequest and HttpServletResponse arguments instead of the
+ * default ServletRequest and ServletResponse ones.
+ *
+ * @param request {@link HttpServletRequest}
+ * @param response {@link HttpServletResponse}
+ * @param filterChain {@link FilterChain}
+ */
+ @Override
+ protected void doFilterInternal(@NotNull HttpServletRequest request,
+ @NotNull HttpServletResponse response,
+ @NotNull FilterChain filterChain) throws ServletException,
+ IOException {
+ if (!requestMatcher.matches(request)) {
+ doFilter(request, response, filterChain);
+ return;
+ }
+ try {
+ Authentication authentication= authenticationConverter.convert(request);
+ if (authentication instanceof AbstractAuthenticationToken) {
+ ((AbstractAuthenticationToken) authentication)
+ .setDetails(this.authenticationDetailsSource.buildDetails(request));
+ }
+ Authentication authenticationResult= authenticationManager.authenticate(authentication);
+ authenticationSuccessHandler.onAuthenticationSuccess(request,response,authenticationResult);
+ } catch (JwtAuthenticationException ex) {
+ if (this.logger.isTraceEnabled()) {
+ this.logger.trace(LogMessage.format("JWT logout request failed: %s", ex.getError()),
+ ex);
+ }
+ this.authenticationFailureHandler.onAuthenticationFailure(request, response, ex);
+ } catch (Exception ex) {
+ JwtError error = new JwtError(JwtErrorCodes.SERVER_ERROR,ex.getMessage(),JWT_ERROR_URI);
+ if (this.logger.isTraceEnabled()) {
+ this.logger.trace(error, ex);
+ }
+ this.authenticationFailureHandler.onAuthenticationFailure(request, response,
+ new JwtAuthenticationException(error));
+ }
+ }
+
+
+ /**
+ * 发送成功响应
+ *
+ * @param request {@link HttpServletRequest}
+ * @param response {@link HttpServletResponse}
+ * @param authentication {@link Authentication}
+ */
+ private void sendAuthorizationResponse(HttpServletRequest request, HttpServletResponse response,
+ Authentication authentication) {
+ JwtLogoutAuthenticationToken jwtLogoutAuthentication= (JwtLogoutAuthenticationToken) authentication;
+ // Check for active user session
+ if (jwtLogoutAuthentication.isPrincipalAuthenticated() &&
+ StringUtils.hasText(jwtLogoutAuthentication.getSessionId())) {
+ // Perform logout
+ this.logoutHandler.logout(request, response,
+ (Authentication) jwtLogoutAuthentication.getPrincipal());
+ }
+ }
+
+}
diff --git a/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/endpoint/authentication/JwtLogoutAuthenticationConverter.java b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/endpoint/authentication/JwtLogoutAuthenticationConverter.java
new file mode 100644
index 00000000..059c913d
--- /dev/null
+++ b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/endpoint/authentication/JwtLogoutAuthenticationConverter.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2020-2023 the original author or authors.
+ *
+ * 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
+ *
+ * https://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 cn.topiam.employee.protocol.jwt.endpoint.authentication;
+
+import org.springframework.security.authentication.AnonymousAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.authority.AuthorityUtils;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
+import org.springframework.security.web.authentication.AuthenticationConverter;
+import org.springframework.util.StringUtils;
+
+import cn.topiam.employee.protocol.jwt.authentication.JwtLogoutAuthenticationToken;
+import cn.topiam.employee.protocol.jwt.exception.JwtError;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpSession;
+import static cn.topiam.employee.protocol.jwt.constant.JwtProtocolConstants.S_ID;
+import static cn.topiam.employee.protocol.jwt.endpoint.JwtAuthenticationEndpointUtils.throwError;
+
+/**
+ *
+ * @author SanLi
+ * Created by qinggang.zuo@gmail.com / 2689170096@qq.com on 2023/9/4 16:01
+ */
+public final class JwtLogoutAuthenticationConverter implements AuthenticationConverter {
+ private static final Authentication ANONYMOUS_AUTHENTICATION = new AnonymousAuthenticationToken(
+ "anonymous", "anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
+
+ @Override
+ public Authentication convert(HttpServletRequest request) {
+
+ if (request.getParameterValues(S_ID).length != 1) {
+ throwError(new JwtError(OAuth2ErrorCodes.INVALID_REQUEST,
+ "JWT Logout Request Parameter: " + S_ID));
+ }
+
+ String sessionId = request.getParameter(S_ID);
+ if (!StringUtils.hasText(sessionId)) {
+ HttpSession session = request.getSession(false);
+ if (session != null) {
+ sessionId = session.getId();
+ }
+ }
+
+ Authentication principal = SecurityContextHolder.getContext().getAuthentication();
+ if (principal == null) {
+ principal = ANONYMOUS_AUTHENTICATION;
+ }
+
+ return new JwtLogoutAuthenticationToken(principal,sessionId);
+ }
+
+}
diff --git a/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/jackson/JwtAuthenticationTokenMixin.java b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/jackson/JwtAuthenticationTokenMixin.java
new file mode 100644
index 00000000..57f8ae48
--- /dev/null
+++ b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/jackson/JwtAuthenticationTokenMixin.java
@@ -0,0 +1,33 @@
+/*
+ * eiam-protocol-jwt - Employee Identity and Access Management
+ * Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+package cn.topiam.employee.protocol.jwt.jackson;
+
+import com.fasterxml.jackson.annotation.*;
+
+/**
+ * JwtAuthenticationTokenMixin
+ *
+ * @author TopIAM
+ * Created by support@topiam.cn on 2023/6/30 21:07
+ */
+@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
+@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE)
+@JsonIgnoreProperties(ignoreUnknown = true)
+abstract class JwtAuthenticationTokenMixin {
+
+}
diff --git a/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/jackson/JwtAuthorizationModule.java b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/jackson/JwtAuthorizationModule.java
new file mode 100644
index 00000000..0ad52f76
--- /dev/null
+++ b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/jackson/JwtAuthorizationModule.java
@@ -0,0 +1,46 @@
+/*
+ * eiam-protocol-jwt - Employee Identity and Access Management
+ * Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+package cn.topiam.employee.protocol.jwt.jackson;
+
+import org.springframework.security.jackson2.SecurityJackson2Modules;
+
+import com.fasterxml.jackson.core.Version;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+
+import cn.topiam.employee.protocol.jwt.authentication.JwtAuthenticationToken;
+
+/**
+ * JwtAuthorizationModule
+ *
+ * @author TopIAM
+ * Created by support@topiam.cn on 2023/6/30 21:07
+ */
+public class JwtAuthorizationModule extends SimpleModule {
+
+ public JwtAuthorizationModule() {
+ super(JwtAuthorizationModule.class.getName(), new Version(1, 0, 0, null, null, null));
+ }
+
+ @Override
+ public void setupModule(SetupContext context) {
+ SecurityJackson2Modules.enableDefaultTyping(context.getOwner());
+ context.setMixInAnnotations(JwtAuthenticationToken.class,
+ JwtAuthenticationTokenMixin.class);
+ }
+
+}
diff --git a/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/token/IdToken.java b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/token/IdToken.java
index e8ed4e80..42e3ee08 100644
--- a/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/token/IdToken.java
+++ b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/token/IdToken.java
@@ -23,6 +23,7 @@ import java.util.Map;
import lombok.Builder;
import lombok.Data;
import lombok.NonNull;
+import lombok.extern.jackson.Jacksonized;
/**
*
@@ -31,6 +32,7 @@ import lombok.NonNull;
*/
@Data
@Builder
+@Jacksonized
public class IdToken {
@NonNull
diff --git a/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/token/IdTokenContext.java b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/token/IdTokenContext.java
index 498500e4..40f48a73 100644
--- a/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/token/IdTokenContext.java
+++ b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/token/IdTokenContext.java
@@ -38,6 +38,9 @@ public class IdTokenContext {
@NonNull
private String audience;
+ @NonNull
+ private String sessionId;
+
/**
* Token 过期时间(秒)
*/
diff --git a/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/token/JwtIdTokenGenerator.java b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/token/JwtIdTokenGenerator.java
index bb7f7a82..bb79caea 100644
--- a/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/token/JwtIdTokenGenerator.java
+++ b/eiam-protocol/eiam-protocol-jwt/src/main/java/cn/topiam/employee/protocol/jwt/token/JwtIdTokenGenerator.java
@@ -27,6 +27,7 @@ import cn.topiam.employee.protocol.jwt.exception.IdTokenGenerateException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
+import static cn.topiam.employee.protocol.jwt.constant.JwtProtocolConstants.S_ID;
/**
*
@@ -50,6 +51,7 @@ public class JwtIdTokenGenerator implements IdTokenGenerator {
.setAudience(context.getAudience())
.setExpiration(new Date(expiresAt.toEpochMilli()))
.signWith(rsaPrivateKey, SignatureAlgorithm.RS256)
+ .claim(S_ID,context.getSessionId())
.compact();
return IdToken.builder().tokenValue(tokenValue)
.issuedAt(issuedAt)
diff --git a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/eiam/protocol/oidc/context/OidcAuthorizationServerContextFilter.java b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/eiam/protocol/oidc/context/OidcAuthorizationServerContextFilter.java
index f5bab3c0..4d8f0584 100644
--- a/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/eiam/protocol/oidc/context/OidcAuthorizationServerContextFilter.java
+++ b/eiam-protocol/eiam-protocol-oidc/src/main/java/cn/topiam/eiam/protocol/oidc/context/OidcAuthorizationServerContextFilter.java
@@ -50,7 +50,6 @@ import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
-import static org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames.GRANT_TYPE;
import static org.springframework.security.oauth2.server.authorization.settings.ConfigurationSettingNames.Token.ACCESS_TOKEN_FORMAT;
import static cn.topiam.employee.common.constant.ProtocolConstants.APP_CODE;
@@ -91,7 +90,6 @@ public final class OidcAuthorizationServerContextFilter extends OncePerRequestFi
return;
}
try {
- request.getParameterValues(GRANT_TYPE);
//@formatter:off
Map variables = matcher.getVariables();
String appCode = variables.get(APP_CODE);