diff --git a/application/src/main/java/run/halo/app/security/jackson2/HaloSecurityJackson2Module.java b/application/src/main/java/run/halo/app/security/jackson2/HaloSecurityJackson2Module.java
index 511438694..096d19cfd 100644
--- a/application/src/main/java/run/halo/app/security/jackson2/HaloSecurityJackson2Module.java
+++ b/application/src/main/java/run/halo/app/security/jackson2/HaloSecurityJackson2Module.java
@@ -3,6 +3,7 @@ package run.halo.app.security.jackson2;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.springframework.security.jackson2.SecurityJackson2Modules;
+import org.springframework.security.web.authentication.switchuser.SwitchUserGrantedAuthority;
import run.halo.app.security.authentication.login.HaloUser;
import run.halo.app.security.authentication.oauth2.HaloOAuth2AuthenticationToken;
import run.halo.app.security.authentication.twofactor.TwoFactorAuthentication;
@@ -28,6 +29,9 @@ public class HaloSecurityJackson2Module extends SimpleModule {
context.setMixInAnnotations(
HaloOAuth2AuthenticationToken.class, HaloOAuth2AuthenticationTokenMixin.class
);
+ context.setMixInAnnotations(
+ SwitchUserGrantedAuthority.class, SwitchUserGrantedAuthorityMixIn.class
+ );
}
}
diff --git a/application/src/main/java/run/halo/app/security/jackson2/SwitchUserGrantedAuthorityMixIn.java b/application/src/main/java/run/halo/app/security/jackson2/SwitchUserGrantedAuthorityMixIn.java
new file mode 100644
index 000000000..cbf440c11
--- /dev/null
+++ b/application/src/main/java/run/halo/app/security/jackson2/SwitchUserGrantedAuthorityMixIn.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2002-2024 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 run.halo.app.security.jackson2;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.web.authentication.switchuser.SwitchUserGrantedAuthority;
+import org.springframework.security.web.jackson2.WebServletJackson2Module;
+
+/**
+ * Jackson mixin class to serialize/deserialize {@link SwitchUserGrantedAuthority}.
+ * This class is copied from repository spring-projects/spring-security.
+ *
+ * @author Markus Heiden
+ * @see WebServletJackson2Module
+ * @see org.springframework.security.jackson2.SecurityJackson2Modules
+ * @since 6.3
+ */
+@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
+@JsonAutoDetect(
+ fieldVisibility = JsonAutoDetect.Visibility.ANY,
+ getterVisibility = JsonAutoDetect.Visibility.NONE,
+ isGetterVisibility = JsonAutoDetect.Visibility.NONE
+)
+@JsonIgnoreProperties(ignoreUnknown = true)
+abstract class SwitchUserGrantedAuthorityMixIn {
+
+ @JsonCreator
+ SwitchUserGrantedAuthorityMixIn(
+ @JsonProperty("role") String role,
+ @JsonProperty("source") Authentication source
+ ) {
+ }
+
+}
diff --git a/application/src/test/java/run/halo/app/security/jackson2/HaloSecurityJacksonModuleTest.java b/application/src/test/java/run/halo/app/security/jackson2/HaloSecurityJacksonModuleTest.java
index a548c25c6..6ab6d07fd 100644
--- a/application/src/test/java/run/halo/app/security/jackson2/HaloSecurityJacksonModuleTest.java
+++ b/application/src/test/java/run/halo/app/security/jackson2/HaloSecurityJacksonModuleTest.java
@@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
@@ -18,6 +19,7 @@ import org.springframework.security.core.userdetails.User;
import org.springframework.security.jackson2.SecurityJackson2Modules;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
+import org.springframework.security.web.authentication.switchuser.SwitchUserGrantedAuthority;
import run.halo.app.security.authentication.login.HaloUser;
import run.halo.app.security.authentication.oauth2.HaloOAuth2AuthenticationToken;
import run.halo.app.security.authentication.twofactor.TwoFactorAuthentication;
@@ -61,6 +63,23 @@ class HaloSecurityJacksonModuleTest {
});
}
+ @Test
+ void shouldReadSwitchUserGrantedAuthority() throws JsonProcessingException {
+ codecAssert(haloUser -> {
+ var authentication = UsernamePasswordAuthenticationToken.authenticated(
+ haloUser.getUsername(), haloUser.getPassword(), haloUser.getAuthorities()
+ );
+ var switchUserGrantedAuthority =
+ new SwitchUserGrantedAuthority("ADMIN", authentication);
+ var extendedAuthorities = new ArrayList<>(authentication.getAuthorities());
+ extendedAuthorities.add(switchUserGrantedAuthority);
+ authentication = UsernamePasswordAuthenticationToken.authenticated(
+ authentication.getPrincipal(), authentication.getCredentials(), extendedAuthorities
+ );
+ return authentication;
+ });
+ }
+
void codecAssert(Function authenticationConverter)
throws JsonProcessingException {
var userDetails = User.withUsername("faker")