diff --git a/samples/httpsession-redis-json/src/integration-test/java/samples/mixins/MixinsDeserilizeTest.java b/samples/httpsession-redis-json/src/integration-test/java/samples/mixins/MixinsDeserilizeTest.java deleted file mode 100644 index 04c4b82d..00000000 --- a/samples/httpsession-redis-json/src/integration-test/java/samples/mixins/MixinsDeserilizeTest.java +++ /dev/null @@ -1,134 +0,0 @@ -package samples.mixins; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.SpringApplicationConfiguration; -import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; -import org.springframework.data.redis.serializer.SerializationException; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.web.authentication.WebAuthenticationDetails; -import org.springframework.security.web.csrf.DefaultCsrfToken; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import sample.Application; - -import javax.servlet.http.Cookie; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author jitendra on 28/3/16. - */ -@RunWith(SpringJUnit4ClassRunner.class) -@SpringApplicationConfiguration(Application.class) -public class MixinsDeserilizeTest { - - @Autowired - GenericJackson2JsonRedisSerializer redisSerializer; - - @Test - public void defaultCsrfTokenMixin() { - String tokenJson = "{\"@class\": \"org.springframework.security.web.csrf.DefaultCsrfToken\", \"token\": \"123456\", \"parameterName\": \"_csrf\", \"headerName\": \"x-csrf-header\"}"; - DefaultCsrfToken token = redisSerializer.deserialize(tokenJson.getBytes(), DefaultCsrfToken.class); - assertThat(token) - .hasFieldOrPropertyWithValue("token", "123456") - .hasFieldOrPropertyWithValue("parameterName", "_csrf") - .hasFieldOrPropertyWithValue("headerName", "x-csrf-header"); - } - - @Test - public void httpCookieTest() { - String httpCookie = "{\"@class\": \"javax.servlet.http.Cookie\", \"name\": \"SESSION\", \"value\": \"123456789\", \"maxAge\": 1000, \"path\": \"/\", \"secure\": true, \"version\": 0, \"httpOnly\": true}"; - Cookie cookie = redisSerializer.deserialize(httpCookie.getBytes(), Cookie.class); - assertThat(cookie).hasFieldOrPropertyWithValue("name", "SESSION") - .hasFieldOrPropertyWithValue("value", "123456789") - .hasFieldOrPropertyWithValue("secure", true) - .hasFieldOrPropertyWithValue("comment", "") - .hasFieldOrPropertyWithValue("path", "/") - .hasFieldOrPropertyWithValue("maxAge", 1000) - .hasFieldOrPropertyWithValue("httpOnly", true); - } - - @Test(expected = SerializationException.class) - public void simpleGrantedAuthorityWithoutTypeIdTest() { - String authorityJson = "{\"authority\": \"ROLE_USER\"}"; - SimpleGrantedAuthority authority = redisSerializer.deserialize(authorityJson.getBytes(), SimpleGrantedAuthority.class); - assertThat(authority.getAuthority()).isEqualTo("ROLE_USER"); - } - - @Test - public void simpleGrantedAuthorityWithTypeIdTest() { - String authorityJson = "{\"@class\": \"org.springframework.security.core.authority.SimpleGrantedAuthority\", \"role\": \"ROLE_USER\"}"; - SimpleGrantedAuthority authority = redisSerializer.deserialize(authorityJson.getBytes(), SimpleGrantedAuthority.class); - assertThat(authority.getAuthority()).isEqualTo("ROLE_USER"); - } - - @Test - public void userTest() { - String userJson = "{\"@class\": \"org.springframework.security.core.userdetails.User\", \"username\": \"user\", \"password\": \"password\", \"authorities\": [\"java.util.Collections$UnmodifiableSet\", [{\"@class\": \"org.springframework.security.core.authority.SimpleGrantedAuthority\", \"role\": \"ROLE_USER\"}]], \"accountNonExpired\": true, \"accountNonLocked\": true, \"credentialsNonExpired\": true, \"enabled\": true}"; - User user = redisSerializer.deserialize(userJson.getBytes(), User.class); - assertThat(user.getUsername()).isEqualTo("user"); - assertThat(user.getPassword()).isEqualTo("password"); - assertThat(user.getAuthorities()).contains(new SimpleGrantedAuthority("ROLE_USER")); - assertThat(user.isEnabled()).isEqualTo(true); - assertThat(user.isAccountNonExpired()).isEqualTo(true); - assertThat(user.isAccountNonLocked()).isEqualTo(true); - assertThat(user.isCredentialsNonExpired()).isEqualTo(true); - } - - @Test - public void unauthenticatedUsernamePasswordAuthenticationTokenTest() { - String unauthenticatedTokenJson = "{\"@class\": \"org.springframework.security.authentication.UsernamePasswordAuthenticationToken\"," + - "\"principal\": \"user\", \"credentials\": \"password\", \"details\": null, \"authorities\": [\"java.util.ArrayList\", []]," + - "\"authenticated\": false}"; - UsernamePasswordAuthenticationToken token = redisSerializer.deserialize(unauthenticatedTokenJson.getBytes(), UsernamePasswordAuthenticationToken.class); - assertThat(token.getPrincipal()).isEqualTo("user"); - assertThat(token.getCredentials()).isEqualTo("password"); - assertThat(token.isAuthenticated()).isEqualTo(false); - assertThat(token.getAuthorities()).hasSize(0); - } - - @Test - public void unauthenticatedUsernamePasswordAuthenticationTokenWithUserAsPrincipalTest() { - String unauthenticatedTokenJson = "{\"@class\": \"org.springframework.security.authentication.UsernamePasswordAuthenticationToken\"," + - "\"principal\": {\"@class\": \"org.springframework.security.core.userdetails.User\", \"username\": \"user\", \"password\": \"password\", " + - "\"authorities\": [\"java.util.Collections$UnmodifiableSet\", [{\"@class\": \"org.springframework.security.core.authority.SimpleGrantedAuthority\"," + - " \"role\": \"ROLE_USER\"}]], \"accountNonExpired\": true, \"accountNonLocked\": true, \"credentialsNonExpired\": true, \"enabled\": true}, " + - "\"credentials\": \"password\", \"details\": null, \"authorities\": [\"java.util.ArrayList\", []], \"authenticated\": false}"; - UsernamePasswordAuthenticationToken token = redisSerializer.deserialize(unauthenticatedTokenJson.getBytes(), UsernamePasswordAuthenticationToken.class); - assertThat(token.getPrincipal()).isInstanceOf(User.class); - User user = (User) token.getPrincipal(); - assertThat(user.getUsername()).isEqualTo("user"); - assertThat(user.getPassword()).isEqualTo("password"); - assertThat(user.getAuthorities()).contains(new SimpleGrantedAuthority("ROLE_USER")); - assertThat(user.isEnabled()).isEqualTo(true); - assertThat(user.isAccountNonExpired()).isEqualTo(true); - assertThat(user.isAccountNonLocked()).isEqualTo(true); - assertThat(user.isCredentialsNonExpired()).isEqualTo(true); - } - - @Test - public void authenticatedUsernamePasswordAuthenticationTokenTest() { - String unauthenticatedTokenJson = "{\"@class\": \"org.springframework.security.authentication.UsernamePasswordAuthenticationToken\"," + - "\"principal\": \"user\", \"credentials\": \"password\", \"details\": null, \"authorities\": [\"java.util.ArrayList\", " + - "[{\"@class\": \"org.springframework.security.core.authority.SimpleGrantedAuthority\", \"role\": \"ROLE_USER\"}]]," + - "\"authenticated\": true}"; - UsernamePasswordAuthenticationToken authenticationToken = redisSerializer.deserialize(unauthenticatedTokenJson.getBytes(), UsernamePasswordAuthenticationToken.class); - assertThat(authenticationToken.getPrincipal()).isEqualTo("user"); - assertThat(authenticationToken.getCredentials()).isEqualTo("password"); - assertThat(authenticationToken.isAuthenticated()).isEqualTo(true); - assertThat(authenticationToken.getAuthorities()).hasSize(1); - assertThat(authenticationToken.getAuthorities()).contains(new SimpleGrantedAuthority("ROLE_USER")); - } - - @Test - public void webAuthenticationDetailTest() { - String authenticationDetailJson = "{\"@class\": \"org.springframework.security.web.authentication.WebAuthenticationDetails\"," + - "\"remoteAddress\": \"http://localhost/login\", \"sessionId\": \"123456789\"}"; - WebAuthenticationDetails details = redisSerializer.deserialize(authenticationDetailJson.getBytes(), WebAuthenticationDetails.class); - assertThat(details.getRemoteAddress()).isEqualTo("http://localhost/login"); - assertThat(details.getSessionId()).isEqualTo("123456789"); - } -} diff --git a/samples/httpsession-redis-json/src/integration-test/java/samples/mixins/MixinsSerializeTest.java b/samples/httpsession-redis-json/src/integration-test/java/samples/mixins/MixinsSerializeTest.java deleted file mode 100644 index 98faabb1..00000000 --- a/samples/httpsession-redis-json/src/integration-test/java/samples/mixins/MixinsSerializeTest.java +++ /dev/null @@ -1,104 +0,0 @@ -package samples.mixins; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.skyscreamer.jsonassert.JSONAssert; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.SpringApplicationConfiguration; -import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.mock.web.MockHttpSession; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.web.PortResolverImpl; -import org.springframework.security.web.authentication.WebAuthenticationDetails; -import org.springframework.security.web.csrf.DefaultCsrfToken; -import org.springframework.security.web.savedrequest.DefaultSavedRequest; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import sample.Application; - -import javax.servlet.http.Cookie; -import java.util.Arrays; - -/** - * @author jitendra on 28/3/16. - */ -@RunWith(SpringJUnit4ClassRunner.class) -@SpringApplicationConfiguration(Application.class) -public class MixinsSerializeTest { - - @Autowired - GenericJackson2JsonRedisSerializer springSessionDefaultRedisSerializer; - - MockHttpServletRequest request; - - @Before - public void setup() { - request = new MockHttpServletRequest("get", "/login"); - request.setCookies(new Cookie("SESSION", "123456789")); - request.setRemoteAddr("http://localhost:8080/login"); - request.setSession(new MockHttpSession(null, "123456789")); - } - - @Test - public void testDefaultTypingIdJson() { - User user = new User("user", "password", Arrays.asList(new SimpleGrantedAuthority("ROLE_USER"))); - String expectedJsonString = "{'@class': 'org.springframework.security.core.userdetails.User', 'username': 'user', 'password': 'password', 'enabled': true, 'accountNonExpired': true, 'credentialsNonExpired': true, 'accountNonLocked': true, 'authorities': ['java.util.Collections$UnmodifiableSet', [{'@class': 'org.springframework.security.core.authority.SimpleGrantedAuthority', 'role': 'ROLE_USER'}]]}"; - String serializedJson = new String(springSessionDefaultRedisSerializer.serialize(user)); - JSONAssert.assertEquals(expectedJsonString, serializedJson, true); - } - - @Test - public void persistFinalClass() { - SimpleGrantedAuthority authority = new SimpleGrantedAuthority("USER"); - String expectedJson = "{'@class': 'org.springframework.security.core.authority.SimpleGrantedAuthority', 'role': 'USER'}"; - String actualJson = new String(springSessionDefaultRedisSerializer.serialize(authority)); - JSONAssert.assertEquals(expectedJson, actualJson, true); - } - - @Test - public void testDefaultCsrfTokenMixin() { - DefaultCsrfToken token = new DefaultCsrfToken("CSRF_HEADER", "CSRF", "123456789"); - String expectedJson = "{'@class': 'org.springframework.security.web.csrf.DefaultCsrfToken', 'token': '123456789', 'parameterName': 'CSRF', 'headerName': 'CSRF_HEADER'}"; - String serializedString = new String(springSessionDefaultRedisSerializer.serialize(token)); - JSONAssert.assertEquals(expectedJson, serializedString, true); - } - - @Test - public void unauthenticatedUsernamePasswordAuthenticationTokenTest() { - UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("user", "password"); - String expectedJson = "{'@class': 'org.springframework.security.authentication.UsernamePasswordAuthenticationToken', 'principal': 'user', 'credentials': 'password', 'authenticated': false, 'authorities': ['java.util.ArrayList', []], 'details': null}"; - String actualJson = new String(springSessionDefaultRedisSerializer.serialize(token)); - JSONAssert.assertEquals(expectedJson, actualJson, true); - } - - @Test - public void authenticatedUsernamePasswordAuthenticationTokenTest() { - UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("user", "password", Arrays.asList(new SimpleGrantedAuthority("USER"))); - String expectedJson = "{'@class': 'org.springframework.security.authentication.UsernamePasswordAuthenticationToken', 'principal': 'user', 'credentials': 'password', 'authenticated': true, 'authorities': ['java.util.ArrayList', [{'@class': 'org.springframework.security.core.authority.SimpleGrantedAuthority', 'role': 'USER'}]], 'details': null}"; - String actualJson = new String(springSessionDefaultRedisSerializer.serialize(token)); - JSONAssert.assertEquals(expectedJson, actualJson, true); - } - - @Test - public void defaultSavedRequestTest() { - String savedRequestJson = "{ '@class': 'org.springframework.security.web.savedrequest.DefaultSavedRequest', 'serverPort': 80, 'servletPath': ''," + - "'serverName': 'localhost', 'scheme': 'http', 'requestURL': 'http://localhost/login', 'requestURI': '/login', 'queryString': null," + - "'pathInfo': null, 'method': 'get', 'contextPath': '', 'parameters': {'@class': 'java.util.TreeMap'}," + - "'headers': {'@class': 'java.util.TreeMap'}, 'locales': ['java.util.ArrayList', ['en']], 'cookies': ['java.util.ArrayList', " + - "[{'@class': 'org.springframework.security.web.savedrequest.SavedCookie', 'name': 'SESSION', 'value': '123456789', 'comment': null, domain: null, maxAge: -1, path: null, secure: false, version: 0}]]}"; - DefaultSavedRequest savedRequest = new DefaultSavedRequest(request, new PortResolverImpl()); - String actualJson = new String(springSessionDefaultRedisSerializer.serialize(savedRequest)); - JSONAssert.assertEquals(savedRequestJson, actualJson, true); - } - - @Test - public void webAuthenticationDetailsMixinTest() { - WebAuthenticationDetails details = new WebAuthenticationDetails(request); - String expectedJson = "{'@class': 'org.springframework.security.web.authentication.WebAuthenticationDetails', 'remoteAddress': 'http://localhost:8080/login', 'sessionId': '123456789'}"; - String actualJson = new String(springSessionDefaultRedisSerializer.serialize(details)); - JSONAssert.assertEquals(expectedJson, actualJson, true); - } -} diff --git a/samples/httpsession-redis-json/src/main/java/org/springframework/security/jackson2/AnonymousAuthenticationTokenMixin.java b/samples/httpsession-redis-json/src/main/java/org/springframework/security/jackson2/AnonymousAuthenticationTokenMixin.java new file mode 100644 index 00000000..b2218738 --- /dev/null +++ b/samples/httpsession-redis-json/src/main/java/org/springframework/security/jackson2/AnonymousAuthenticationTokenMixin.java @@ -0,0 +1,66 @@ +/* + * Copyright 2014-2016 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 + * + * 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.springframework.security.jackson2; + +import java.util.Collection; + +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.GrantedAuthority; + +/** + * This is a Jackson mixin class helps in serialize/deserialize + * {@link org.springframework.security.authentication.AnonymousAuthenticationToken} class. + * To use this class you need to register it with + * {@link com.fasterxml.jackson.databind.ObjectMapper} and + * {@link SimpleGrantedAuthorityMixin} because AnonymousAuthenticationToken contains + * SimpleGrantedAuthority.
+ *     ObjectMapper mapper = new ObjectMapper();
+ *     mapper.registerModule(new CoreJackson2Module());
+ * 
+ * + * Note: This class will save full class name into a property called @class + * + * @author Jitendra Singh + * @see CoreJackson2Module + * @see SecurityJacksonModules + * @since 4.2 + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY) +@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, isGetterVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.ANY) +@JsonIgnoreProperties(ignoreUnknown = true) +class AnonymousAuthenticationTokenMixin { + + /** + * Constructor used by Jackson to create object of + * {@link org.springframework.security.authentication.AnonymousAuthenticationToken}. + * + * @param keyHash hashCode of key provided at the time of token creation by using + * {@link org.springframework.security.authentication.AnonymousAuthenticationToken#AnonymousAuthenticationToken(String, Object, Collection)} + * @param principal the principal (typically a UserDetails) + * @param authorities the authorities granted to the principal + */ + @JsonCreator + AnonymousAuthenticationTokenMixin(@JsonProperty("keyHash") Integer keyHash, + @JsonProperty("principal") Object principal, + @JsonProperty("authorities") Collection authorities) { + } +} diff --git a/samples/httpsession-redis-json/src/main/java/org/springframework/security/jackson2/CoreJackson2Module.java b/samples/httpsession-redis-json/src/main/java/org/springframework/security/jackson2/CoreJackson2Module.java new file mode 100644 index 00000000..ff23bf5c --- /dev/null +++ b/samples/httpsession-redis-json/src/main/java/org/springframework/security/jackson2/CoreJackson2Module.java @@ -0,0 +1,71 @@ +/* + * Copyright 2014-2016 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 + * + * 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.springframework.security.jackson2; + +import java.util.Collections; + +import com.fasterxml.jackson.core.Version; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; + +import org.springframework.security.authentication.AnonymousAuthenticationToken; +import org.springframework.security.authentication.RememberMeAuthenticationToken; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.User; + +/** + * Jackson module for spring-security-core. This module register + * {@link AnonymousAuthenticationTokenMixin}, {@link RememberMeAuthenticationTokenMixin}, + * {@link SimpleGrantedAuthorityMixin}, {@link UnmodifiableSetMixin}, {@link UserMixin} + * and {@link UsernamePasswordAuthenticationTokenMixin}. If no default typing enabled by + * default then it'll enable it because typing info is needed to properly + * serialize/deserialize objects. In order to use this module just add this module into + * your ObjectMapper configuration. + * + *
+ *     ObjectMapper mapper = new ObjectMapper();
+ *     mapper.registerModule(new CoreJackson2Module());
+ * 
Note: use {@link SecurityJacksonModules#getModules(ClassLoader)} to get list + * of all security modules. + * + * @author Jitendra Singh. + * @see SecurityJacksonModules + */ +public class CoreJackson2Module extends SimpleModule { + + public CoreJackson2Module() { + super(CoreJackson2Module.class.getName(), new Version(1, 0, 0, null, null, null)); + } + + @Override + public void setupModule(SetupContext context) { + SecurityJacksonModules.enableDefaultTyping((ObjectMapper) context.getOwner()); + context.setMixInAnnotations(AnonymousAuthenticationToken.class, + AnonymousAuthenticationTokenMixin.class); + context.setMixInAnnotations(RememberMeAuthenticationToken.class, + RememberMeAuthenticationTokenMixin.class); + context.setMixInAnnotations(SimpleGrantedAuthority.class, + SimpleGrantedAuthorityMixin.class); + context.setMixInAnnotations( + Collections.unmodifiableSet(Collections.emptySet()).getClass(), + UnmodifiableSetMixin.class); + context.setMixInAnnotations(User.class, UserMixin.class); + context.setMixInAnnotations(UsernamePasswordAuthenticationToken.class, + UsernamePasswordAuthenticationTokenMixin.class); + } +} diff --git a/samples/httpsession-redis-json/src/main/java/org/springframework/security/jackson2/RememberMeAuthenticationTokenMixin.java b/samples/httpsession-redis-json/src/main/java/org/springframework/security/jackson2/RememberMeAuthenticationTokenMixin.java new file mode 100644 index 00000000..77ae0d51 --- /dev/null +++ b/samples/httpsession-redis-json/src/main/java/org/springframework/security/jackson2/RememberMeAuthenticationTokenMixin.java @@ -0,0 +1,72 @@ +/* + * Copyright 2014-2016 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 + * + * 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.springframework.security.jackson2; + +import java.util.Collection; + +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.GrantedAuthority; + +/** + * This mixin class helps in serialize/deserialize + * {@link org.springframework.security.authentication.RememberMeAuthenticationToken} + * class. To use this class you need to register it with + * {@link com.fasterxml.jackson.databind.ObjectMapper} and 2 more mixin classes. + * + *
    + *
  1. {@link SimpleGrantedAuthorityMixin}
  2. + *
  3. {@link UserMixin}
  4. + *
  5. {@link UnmodifiableSetMixin}
  6. + *
+ * + *
+ *     ObjectMapper mapper = new ObjectMapper();
+ *     mapper.registerModule(new CoreJackson2Module());
+ * 
+ * + * Note: This class will save TypeInfo (full class name) into a property + * called @class + * + * @author Jitendra Singh + * @see CoreJackson2Module + * @see SecurityJacksonModules + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY) +@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.ANY) +@JsonIgnoreProperties(ignoreUnknown = true) +class RememberMeAuthenticationTokenMixin { + + /** + * Constructor used by Jackson to create + * {@link org.springframework.security.authentication.RememberMeAuthenticationToken} + * object. + * + * @param keyHash hashCode of above given key. + * @param principal the principal (typically a UserDetails) + * @param authorities the authorities granted to the principal + */ + @JsonCreator + RememberMeAuthenticationTokenMixin(@JsonProperty("keyHash") Integer keyHash, + @JsonProperty("principal") Object principal, + @JsonProperty("authorities") Collection authorities) { + } +} diff --git a/samples/httpsession-redis-json/src/main/java/org/springframework/security/jackson2/SecurityJacksonModules.java b/samples/httpsession-redis-json/src/main/java/org/springframework/security/jackson2/SecurityJacksonModules.java new file mode 100644 index 00000000..b7e8d5fe --- /dev/null +++ b/samples/httpsession-redis-json/src/main/java/org/springframework/security/jackson2/SecurityJacksonModules.java @@ -0,0 +1,109 @@ +/* + * Copyright 2014-2016 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 + * + * 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.springframework.security.jackson2; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.databind.Module; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.util.ClassUtils; + +/** + * This utility class will find all the SecurityModules in classpath. + * + *

+ *

+ *     ObjectMapper mapper = new ObjectMapper();
+ *     mapper.registerModules(SecurityJacksonModules.getModules());
+ * 
Above code is equivalent to + *

+ *

+ *     ObjectMapper mapper = new ObjectMapper();
+ *     mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
+ *     mapper.registerModule(new CoreJackson2Module());
+ *     mapper.registerModule(new CasJackson2Module());
+ *     mapper.registerModule(new WebJackson2Module());
+ * 
+ * + * @author Jitendra Singh. + * @since 4.2 + */ +public final class SecurityJacksonModules { + + private static final Log logger = LogFactory.getLog(SecurityJacksonModules.class); + private static final List securityJackson2ModuleClasses = Arrays.asList( + "org.springframework.security.jackson2.CoreJackson2Module", + "org.springframework.security.cas.jackson2.CasJackson2Module", + "org.springframework.security.web.jackson2.WebJackson2Module"); + + private SecurityJacksonModules() { + } + + public static void enableDefaultTyping(ObjectMapper mapper) { + if (mapper != null) { + TypeResolverBuilder typeBuilder = mapper.getDeserializationConfig() + .getDefaultTyper(null); + if (typeBuilder == null) { + mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, + JsonTypeInfo.As.PROPERTY); + } + } + } + + @SuppressWarnings("unchecked") + private static Module loadAndGetInstance(String className, ClassLoader loader) { + Module instance = null; + try { + Class securityModule = (Class) ClassUtils + .forName(className, loader); + if (securityModule != null) { + if (logger.isDebugEnabled()) { + logger.debug("Loaded module " + className + ", now registering"); + } + instance = securityModule.newInstance(); + } + } + catch (Exception e) { + if (logger.isDebugEnabled()) { + logger.debug("Cannot load module " + className, e); + } + } + return instance; + } + + /** + * @param loader the ClassLoader to use + * @return List of available security modules in classpath. + */ + public static List getModules(ClassLoader loader) { + List modules = new ArrayList(); + for (String className : securityJackson2ModuleClasses) { + Module module = loadAndGetInstance(className, loader); + if (module != null) { + modules.add(module); + } + } + return modules; + } +} diff --git a/samples/httpsession-redis-json/src/main/java/org/springframework/security/jackson2/SimpleGrantedAuthorityMixin.java b/samples/httpsession-redis-json/src/main/java/org/springframework/security/jackson2/SimpleGrantedAuthorityMixin.java new file mode 100644 index 00000000..6ce2bc12 --- /dev/null +++ b/samples/httpsession-redis-json/src/main/java/org/springframework/security/jackson2/SimpleGrantedAuthorityMixin.java @@ -0,0 +1,50 @@ +/* + * Copyright 2014-2016 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 + * + * 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.springframework.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; + +/** + * Jackson Mixin class helps in serialize/deserialize + * {@link org.springframework.security.core.authority.SimpleGrantedAuthority}. + * + *
+ *     ObjectMapper mapper = new ObjectMapper();
+ *     mapper.registerModule(new CoreJackson2Module());
+ * 
+ * @author Jitendra Singh + * @see CoreJackson2Module + * @see SecurityJacksonModules + * @since 4.2 + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY) +@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY, isGetterVisibility = JsonAutoDetect.Visibility.NONE) +@JsonIgnoreProperties(ignoreUnknown = true) +public abstract class SimpleGrantedAuthorityMixin { + + /** + * Mixin Constructor. + * @param role the role + */ + @JsonCreator + public SimpleGrantedAuthorityMixin(@JsonProperty("authority") String role) { + } +} diff --git a/samples/httpsession-redis-json/src/main/java/org/springframework/security/jackson2/UnmodifiableSetMixin.java b/samples/httpsession-redis-json/src/main/java/org/springframework/security/jackson2/UnmodifiableSetMixin.java new file mode 100644 index 00000000..9f824ec7 --- /dev/null +++ b/samples/httpsession-redis-json/src/main/java/org/springframework/security/jackson2/UnmodifiableSetMixin.java @@ -0,0 +1,48 @@ +/* + * Copyright 2014-2016 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 + * + * 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.springframework.security.jackson2; + +import java.util.Set; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +/** + * This mixin class used to deserialize java.util.Collections$UnmodifiableSet and used + * with various AuthenticationToken implementation's mixin classes. + * + *
+ *     ObjectMapper mapper = new ObjectMapper();
+ *     mapper.registerModule(new CoreJackson2Module());
+ * 
+ * + * @author Jitendra Singh + * @see CoreJackson2Module + * @see SecurityJacksonModules + * @since 4.2 + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY) +class UnmodifiableSetMixin { + + /** + * Mixin Constructor + * @param s the Set + */ + @JsonCreator + UnmodifiableSetMixin(Set s) { + } +} diff --git a/samples/httpsession-redis-json/src/main/java/org/springframework/security/jackson2/UserDeserializer.java b/samples/httpsession-redis-json/src/main/java/org/springframework/security/jackson2/UserDeserializer.java new file mode 100644 index 00000000..f078664a --- /dev/null +++ b/samples/httpsession-redis-json/src/main/java/org/springframework/security/jackson2/UserDeserializer.java @@ -0,0 +1,81 @@ +/* + * Copyright 2014-2016 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 + * + * 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.springframework.security.jackson2; + +import java.io.IOException; +import java.util.Set; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.MissingNode; + +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.User; + +/** + * Custom Deserializer for {@link User} class. This is already registered with + * {@link UserMixin}. You can also use it directly with your mixin class. + * + * @author Jitendra Singh + * @see UserMixin + */ +class UserDeserializer extends JsonDeserializer { + + /** + * This method will create {@link User} object. It will ensure successful object + * creation even if password key is null in serialized json, because credentials may + * be removed from the {@link User} by invoking {@link User#eraseCredentials()}. In + * that case there won't be any password key in serialized json. + * + * @param jp the JsonParser + * @param ctxt the DeserializationContext + * @return the user + * @throws IOException if a exception during IO occurs + * @throws JsonProcessingException if an error during JSON processing occurs + */ + @Override + public User deserialize(JsonParser jp, DeserializationContext ctxt) + throws IOException, JsonProcessingException { + ObjectMapper mapper = (ObjectMapper) jp.getCodec(); + JsonNode jsonNode = mapper.readTree(jp); + Set authorities = mapper.convertValue( + jsonNode.get("authorities"), + new TypeReference>() { + }); + JsonNode password = readJsonNode(jsonNode, "password"); + User result = new User(readJsonNode(jsonNode, "username").asText(), + password.asText(""), readJsonNode(jsonNode, "enabled").asBoolean(), + readJsonNode(jsonNode, "accountNonExpired").asBoolean(), + readJsonNode(jsonNode, "credentialsNonExpired").asBoolean(), + readJsonNode(jsonNode, "accountNonLocked").asBoolean(), authorities); + + if (password.asText(null) == null) { + result.eraseCredentials(); + } + return result; + } + + private JsonNode readJsonNode(JsonNode jsonNode, String field) { + return jsonNode.has(field) ? jsonNode.get(field) : MissingNode.getInstance(); + } +} diff --git a/samples/httpsession-redis-json/src/main/java/org/springframework/security/jackson2/UserMixin.java b/samples/httpsession-redis-json/src/main/java/org/springframework/security/jackson2/UserMixin.java new file mode 100644 index 00000000..6d4cda3c --- /dev/null +++ b/samples/httpsession-redis-json/src/main/java/org/springframework/security/jackson2/UserMixin.java @@ -0,0 +1,50 @@ +/* + * Copyright 2014-2016 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 + * + * 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.springframework.security.jackson2; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; + +/** + * This mixin class helps in serialize/deserialize + * {@link org.springframework.security.core.userdetails.User}. This class also register a + * custom deserializer {@link UserDeserializer} to deserialize User object successfully. + * In order to use this mixin you need to register two more mixin classes in your + * ObjectMapper configuration. + *
    + *
  1. {@link SimpleGrantedAuthorityMixin}
  2. + *
  3. {@link UnmodifiableSetMixin}
  4. + *
+ *
+ *     ObjectMapper mapper = new ObjectMapper();
+ *     mapper.registerModule(new CoreJackson2Module());
+ * 
+ * + * @author Jitendra Singh + * @see UserDeserializer + * @see CoreJackson2Module + * @see SecurityJacksonModules + * @since 4.2 + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY) +@JsonDeserialize(using = UserDeserializer.class) +@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE) +@JsonIgnoreProperties(ignoreUnknown = true) +abstract class UserMixin { +} diff --git a/samples/httpsession-redis-json/src/main/java/org/springframework/security/jackson2/UsernamePasswordAuthenticationTokenDeserializer.java b/samples/httpsession-redis-json/src/main/java/org/springframework/security/jackson2/UsernamePasswordAuthenticationTokenDeserializer.java new file mode 100644 index 00000000..bf5bc5a2 --- /dev/null +++ b/samples/httpsession-redis-json/src/main/java/org/springframework/security/jackson2/UsernamePasswordAuthenticationTokenDeserializer.java @@ -0,0 +1,95 @@ +/* + * Copyright 2014-2016 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 + * + * 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.springframework.security.jackson2; + +import java.io.IOException; +import java.util.List; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.MissingNode; + +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.User; + +/** + * Custom deserializer for {@link UsernamePasswordAuthenticationToken}. At the time of + * deserialization it will invoke suitable constructor depending on the value of + * authenticated property. It will ensure that the token's state must not change. + *

+ * This deserializer is already registered with + * {@link UsernamePasswordAuthenticationTokenMixin} but you can also registered it with + * your own mixin class. + * + * @author Jitendra Singh + * @see UsernamePasswordAuthenticationTokenMixin + */ +class UsernamePasswordAuthenticationTokenDeserializer + extends JsonDeserializer { + + /** + * This method construct {@link UsernamePasswordAuthenticationToken} object from + * serialized json. + * @param jp the JsonParser + * @param ctxt the DeserializationContext + * @return the user + * @throws IOException if a exception during IO occurs + * @throws JsonProcessingException if an error during JSON processing occurs + */ + @Override + public UsernamePasswordAuthenticationToken deserialize(JsonParser jp, + DeserializationContext ctxt) throws IOException, JsonProcessingException { + UsernamePasswordAuthenticationToken token = null; + ObjectMapper mapper = (ObjectMapper) jp.getCodec(); + JsonNode jsonNode = mapper.readTree(jp); + Boolean authenticated = readJsonNode(jsonNode, "authenticated").asBoolean(); + JsonNode principalNode = readJsonNode(jsonNode, "principal"); + Object principal = null; + if (principalNode.isObject()) { + principal = mapper.readValue(principalNode.toString(), + new TypeReference() { + }); + } + else { + principal = principalNode.asText(); + } + Object credentials = readJsonNode(jsonNode, "credentials").asText(); + List authorities = mapper.readValue( + readJsonNode(jsonNode, "authorities").toString(), + new TypeReference>() { + }); + if (authenticated) { + token = new UsernamePasswordAuthenticationToken(principal, credentials, + authorities); + } + else { + token = new UsernamePasswordAuthenticationToken(principal, credentials); + } + token.setDetails(readJsonNode(jsonNode, "details")); + return token; + } + + private JsonNode readJsonNode(JsonNode jsonNode, String field) { + return jsonNode.has(field) ? jsonNode.get(field) : MissingNode.getInstance(); + } +} diff --git a/samples/httpsession-redis-json/src/main/java/org/springframework/security/jackson2/UsernamePasswordAuthenticationTokenMixin.java b/samples/httpsession-redis-json/src/main/java/org/springframework/security/jackson2/UsernamePasswordAuthenticationTokenMixin.java new file mode 100644 index 00000000..b323c1a0 --- /dev/null +++ b/samples/httpsession-redis-json/src/main/java/org/springframework/security/jackson2/UsernamePasswordAuthenticationTokenMixin.java @@ -0,0 +1,49 @@ +/* + * Copyright 2014-2016 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 + * + * 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.springframework.security.jackson2; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; + +/** + * This mixin class is used to serialize / deserialize + * {@link org.springframework.security.authentication.UsernamePasswordAuthenticationToken} + * . This class register a custom deserializer + * {@link UsernamePasswordAuthenticationTokenDeserializer}. + * + * In order to use this mixin you'll need to add 3 more mixin classes. + *

    + *
  1. {@link UnmodifiableSetMixin}
  2. + *
  3. {@link SimpleGrantedAuthorityMixin}
  4. + *
  5. {@link UserMixin}
  6. + *
+ * + *
+ *     ObjectMapper mapper = new ObjectMapper();
+ *     mapper.registerModule(new CoreJackson2Module());
+ * 
+ * @author Jitendra Singh + * @see CoreJackson2Module + * @see SecurityJacksonModules + * @since 4.2 + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class") +@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE) +@JsonDeserialize(using = UsernamePasswordAuthenticationTokenDeserializer.class) +abstract class UsernamePasswordAuthenticationTokenMixin { +} diff --git a/samples/httpsession-redis-json/src/main/java/org/springframework/security/jackson2/package-info.java b/samples/httpsession-redis-json/src/main/java/org/springframework/security/jackson2/package-info.java new file mode 100644 index 00000000..0c2a3a9f --- /dev/null +++ b/samples/httpsession-redis-json/src/main/java/org/springframework/security/jackson2/package-info.java @@ -0,0 +1,27 @@ +/* + * Copyright 2014-2016 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 + * + * 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. + */ + +/** + * Mix-in classes to add Jackson serialization support. + * + * @author Jitendra Singh + * @since 4.2 + */ +package org.springframework.security.jackson2; + +/** + * Package contains Jackson mixin classes. + */ diff --git a/samples/httpsession-redis-json/src/main/java/org/springframework/security/web/jackson2/CookieDeserializer.java b/samples/httpsession-redis-json/src/main/java/org/springframework/security/web/jackson2/CookieDeserializer.java new file mode 100644 index 00000000..ed31fe4f --- /dev/null +++ b/samples/httpsession-redis-json/src/main/java/org/springframework/security/web/jackson2/CookieDeserializer.java @@ -0,0 +1,64 @@ +/* + * Copyright 2014-2016 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 + * + * 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.springframework.security.web.jackson2; + +import java.io.IOException; + +import javax.servlet.http.Cookie; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.MissingNode; +import com.fasterxml.jackson.databind.node.NullNode; + +/** + * Jackson deserializer for {@link Cookie}. This is needed because in most cases we don't + * set {@link Cookie#getDomain()} property. So when jackson deserialize that json + * {@link Cookie#setDomain(String)} throws {@link NullPointerException}. This is + * registered with {@link CookieMixin} but you can also use it with your own mixin. + * + * @author Jitendra Singh + * @see CookieMixin + */ +class CookieDeserializer extends JsonDeserializer { + + @Override + public Cookie deserialize(JsonParser jp, DeserializationContext ctxt) + throws IOException, JsonProcessingException { + ObjectMapper mapper = (ObjectMapper) jp.getCodec(); + JsonNode jsonNode = mapper.readTree(jp); + Cookie cookie = new Cookie(readJsonNode(jsonNode, "name").asText(), + readJsonNode(jsonNode, "value").asText()); + cookie.setComment(readJsonNode(jsonNode, "comment").asText()); + cookie.setDomain(readJsonNode(jsonNode, "domain").asText()); + cookie.setMaxAge(readJsonNode(jsonNode, "maxAge").asInt(-1)); + cookie.setSecure(readJsonNode(jsonNode, "secure").asBoolean()); + cookie.setVersion(readJsonNode(jsonNode, "version").asInt()); + cookie.setPath(readJsonNode(jsonNode, "path").asText()); + cookie.setHttpOnly(readJsonNode(jsonNode, "httpOnly").asBoolean()); + return cookie; + } + + private JsonNode readJsonNode(JsonNode jsonNode, String field) { + return jsonNode.has(field) && !(jsonNode.get(field) instanceof NullNode) + ? jsonNode.get(field) : MissingNode.getInstance(); + } +} diff --git a/samples/httpsession-redis-json/src/main/java/org/springframework/security/web/jackson2/CookieMixin.java b/samples/httpsession-redis-json/src/main/java/org/springframework/security/web/jackson2/CookieMixin.java new file mode 100644 index 00000000..2ffb20f6 --- /dev/null +++ b/samples/httpsession-redis-json/src/main/java/org/springframework/security/web/jackson2/CookieMixin.java @@ -0,0 +1,40 @@ +/* + * Copyright 2014-2016 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 + * + * 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.springframework.security.web.jackson2; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; + +/** + * Mixin class to serialize/deserialize {@link javax.servlet.http.Cookie} + * + *
+ *     ObjectMapper mapper = new ObjectMapper();
+ *     mapper.registerModule(new WebJackson2Module());
+ * 
+ * + * @author Jitendra Singh + * @see WebJackson2Module + * @see org.springframework.security.jackson2.SecurityJacksonModules + * @since 4.2 + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY) +@JsonDeserialize(using = CookieDeserializer.class) +@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, isGetterVisibility = JsonAutoDetect.Visibility.NONE) +abstract class CookieMixin { +} diff --git a/samples/httpsession-redis-json/src/main/java/org/springframework/security/web/jackson2/DefaultCsrfTokenMixin.java b/samples/httpsession-redis-json/src/main/java/org/springframework/security/web/jackson2/DefaultCsrfTokenMixin.java new file mode 100644 index 00000000..7fb64292 --- /dev/null +++ b/samples/httpsession-redis-json/src/main/java/org/springframework/security/web/jackson2/DefaultCsrfTokenMixin.java @@ -0,0 +1,55 @@ +/* + * Copyright 2014-2016 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 + * + * 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.springframework.security.web.jackson2; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +/** + * Jackson mixin class to serialize/deserialize + * {@link org.springframework.security.web.csrf.DefaultCsrfToken} serialization support. + * + *
+ * 		ObjectMapper mapper = new ObjectMapper();
+ *		mapper.registerModule(new WebJackson2Module());
+ * 
+ * + * @author Jitendra Singh + * @see WebJackson2Module + * @see org.springframework.security.jackson2.SecurityJacksonModules + * @since 4.2 + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class") +@JsonIgnoreProperties(ignoreUnknown = true) +class DefaultCsrfTokenMixin { + + /** + * JsonCreator constructor needed by Jackson to create + * {@link org.springframework.security.web.csrf.DefaultCsrfToken} object. + * + * @param headerName the name of the header + * @param parameterName the parameter name + * @param token the CSRF token value + */ + @JsonCreator + DefaultCsrfTokenMixin(@JsonProperty("headerName") String headerName, + @JsonProperty("parameterName") String parameterName, + @JsonProperty("token") String token) { + } +} diff --git a/samples/httpsession-redis-json/src/main/java/org/springframework/security/web/jackson2/DefaultSavedRequestBuilder.java b/samples/httpsession-redis-json/src/main/java/org/springframework/security/web/jackson2/DefaultSavedRequestBuilder.java new file mode 100644 index 00000000..9c0966e1 --- /dev/null +++ b/samples/httpsession-redis-json/src/main/java/org/springframework/security/web/jackson2/DefaultSavedRequestBuilder.java @@ -0,0 +1,34 @@ +/* + * Copyright 2014-2016 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 + * + * 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.springframework.security.web.jackson2; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +import org.springframework.security.web.savedrequest.DefaultSavedRequest; + +/** + * Spring Security 4.2 will support saved request. + * + * @author Rob Winch + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class DefaultSavedRequestBuilder { + + public DefaultSavedRequest build() { + return null; + } +} diff --git a/samples/httpsession-redis-json/src/main/java/org/springframework/security/web/jackson2/DefaultSavedRequestMixin.java b/samples/httpsession-redis-json/src/main/java/org/springframework/security/web/jackson2/DefaultSavedRequestMixin.java new file mode 100644 index 00000000..16db2f96 --- /dev/null +++ b/samples/httpsession-redis-json/src/main/java/org/springframework/security/web/jackson2/DefaultSavedRequestMixin.java @@ -0,0 +1,45 @@ +/* + * Copyright 2014-2016 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 + * + * 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.springframework.security.web.jackson2; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; + +import org.springframework.security.web.savedrequest.DefaultSavedRequest; + +/** + * Jackson mixin class to serialize/deserialize {@link DefaultSavedRequest}. This mixin + * use {@link org.springframework.security.web.savedrequest.DefaultSavedRequest.Builder} + * to deserialized json.In order to use this mixin class you also need to register + * {@link CookieMixin}. + *

+ *

+ *     ObjectMapper mapper = new ObjectMapper();
+ *     mapper.registerModule(new WebJackson2Module());
+ * 
+ * + * @author Jitendra Singh + * @see WebJackson2Module + * @see org.springframework.security.jackson2.SecurityJacksonModules + * @since 4.2 + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY) +@JsonDeserialize(builder = DefaultSavedRequestBuilder.class) +@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE) +abstract class DefaultSavedRequestMixin { +} diff --git a/samples/httpsession-redis-json/src/main/java/org/springframework/security/web/jackson2/SavedCookieMixin.java b/samples/httpsession-redis-json/src/main/java/org/springframework/security/web/jackson2/SavedCookieMixin.java new file mode 100644 index 00000000..f0cdd7f5 --- /dev/null +++ b/samples/httpsession-redis-json/src/main/java/org/springframework/security/web/jackson2/SavedCookieMixin.java @@ -0,0 +1,53 @@ +/* + * Copyright 2014-2016 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 + * + * 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.springframework.security.web.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; + +/** + * Jackson mixin class to serialize/deserialize + * {@link org.springframework.security.web.savedrequest.SavedCookie} serialization + * support. + * + *
+ * 		ObjectMapper mapper = new ObjectMapper();
+ *		mapper.registerModule(new WebJackson2Module());
+ * 
+ * + * @author Jitendra Singh. + * @see WebJackson2Module + * @see org.springframework.security.jackson2.SecurityJacksonModules + * @since 4.2 + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY) +@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE) +@JsonIgnoreProperties(ignoreUnknown = true) +abstract class SavedCookieMixin { + + @JsonCreator + SavedCookieMixin(@JsonProperty("name") String name, + @JsonProperty("value") String value, @JsonProperty("comment") String comment, + @JsonProperty("domain") String domain, @JsonProperty("maxAge") int maxAge, + @JsonProperty("path") String path, @JsonProperty("secure") boolean secure, + @JsonProperty("version") int version) { + + } +} diff --git a/samples/httpsession-redis-json/src/main/java/org/springframework/security/web/jackson2/WebAuthenticationDetailsMixin.java b/samples/httpsession-redis-json/src/main/java/org/springframework/security/web/jackson2/WebAuthenticationDetailsMixin.java new file mode 100644 index 00000000..e65b9470 --- /dev/null +++ b/samples/httpsession-redis-json/src/main/java/org/springframework/security/web/jackson2/WebAuthenticationDetailsMixin.java @@ -0,0 +1,48 @@ +/* + * Copyright 2014-2016 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 + * + * 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.springframework.security.web.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; + +/** + * Jackson mixin class to serialize/deserialize + * {@link org.springframework.security.web.authentication.WebAuthenticationDetails}. + * + *
+ * 	ObjectMapper mapper = new ObjectMapper();
+ *	mapper.registerModule(new WebJackson2Module());
+ * 
+ * + * @author Jitendra Singh + * @see WebJackson2Module + * @see org.springframework.security.jackson2.SecurityJacksonModules + * @since 4.2 + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY) +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.ANY) +class WebAuthenticationDetailsMixin { + + @JsonCreator + WebAuthenticationDetailsMixin(@JsonProperty("remoteAddress") String remoteAddress, + @JsonProperty("sessionId") String sessionId) { + } +} diff --git a/samples/httpsession-redis-json/src/main/java/org/springframework/security/web/jackson2/WebJackson2Module.java b/samples/httpsession-redis-json/src/main/java/org/springframework/security/web/jackson2/WebJackson2Module.java new file mode 100644 index 00000000..5a86502d --- /dev/null +++ b/samples/httpsession-redis-json/src/main/java/org/springframework/security/web/jackson2/WebJackson2Module.java @@ -0,0 +1,65 @@ +/* + * Copyright 2014-2016 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 + * + * 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.springframework.security.web.jackson2; + +import javax.servlet.http.Cookie; + +import com.fasterxml.jackson.core.Version; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; + +import org.springframework.security.jackson2.SecurityJacksonModules; +import org.springframework.security.web.authentication.WebAuthenticationDetails; +import org.springframework.security.web.csrf.DefaultCsrfToken; +import org.springframework.security.web.savedrequest.DefaultSavedRequest; +import org.springframework.security.web.savedrequest.SavedCookie; + +/** + * Jackson module for spring-security-web. This module register {@link CookieMixin}, + * {@link DefaultCsrfTokenMixin}, {@link DefaultSavedRequestMixin} and + * {@link WebAuthenticationDetailsMixin}. If no default typing enabled by default then + * it'll enable it because typing info is needed to properly serialize/deserialize + * objects. In order to use this module just add this module into your ObjectMapper + * configuration. + * + *
+ *     ObjectMapper mapper = new ObjectMapper();
+ *     mapper.registerModule(new WebJackson2Module());
+ * 
Note: use {@link SecurityJacksonModules#getModules(ClassLoader)} to get list + * of all security modules. + * + * @author Jitendra Singh + * @see SecurityJacksonModules + */ +public class WebJackson2Module extends SimpleModule { + + public WebJackson2Module() { + super(WebJackson2Module.class.getName(), new Version(1, 0, 0, null, null, null)); + } + + @Override + public void setupModule(SetupContext context) { + SecurityJacksonModules.enableDefaultTyping((ObjectMapper) context.getOwner()); + context.setMixInAnnotations(Cookie.class, CookieMixin.class); + context.setMixInAnnotations(SavedCookie.class, SavedCookieMixin.class); + context.setMixInAnnotations(DefaultCsrfToken.class, DefaultCsrfTokenMixin.class); + context.setMixInAnnotations(DefaultSavedRequest.class, + DefaultSavedRequestMixin.class); + context.setMixInAnnotations(WebAuthenticationDetails.class, + WebAuthenticationDetailsMixin.class); + } +} diff --git a/samples/httpsession-redis-json/src/main/java/org/springframework/security/web/jackson2/package-info.java b/samples/httpsession-redis-json/src/main/java/org/springframework/security/web/jackson2/package-info.java new file mode 100644 index 00000000..f65eabb7 --- /dev/null +++ b/samples/httpsession-redis-json/src/main/java/org/springframework/security/web/jackson2/package-info.java @@ -0,0 +1,23 @@ +/* + * Copyright 2014-2016 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 + * + * 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. + */ + +/** + * Mix-in classes to provide Jackson serialization support. + * + * @author Jitendra Singh + * @since 4.2 + */ +package org.springframework.security.web.jackson2; diff --git a/samples/httpsession-redis-json/src/main/java/sample/mixins/BadCredentialsExceptionMixin.java b/samples/httpsession-redis-json/src/main/java/sample/mixins/BadCredentialsExceptionMixin.java deleted file mode 100644 index a126b3d3..00000000 --- a/samples/httpsession-redis-json/src/main/java/sample/mixins/BadCredentialsExceptionMixin.java +++ /dev/null @@ -1,15 +0,0 @@ -package sample.mixins; - -import com.fasterxml.jackson.annotation.*; - -/** - * @author jitendra on 15/3/16. - */ -@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="@id") -@JsonIgnoreProperties({"cause", "@id"}) -public class BadCredentialsExceptionMixin { - - @JsonCreator - public BadCredentialsExceptionMixin(@JsonProperty("detailMessage") String msg) { - } -} diff --git a/samples/httpsession-redis-json/src/main/java/sample/mixins/DefaultCsrfTokenMixin.java b/samples/httpsession-redis-json/src/main/java/sample/mixins/DefaultCsrfTokenMixin.java deleted file mode 100644 index 23153eb0..00000000 --- a/samples/httpsession-redis-json/src/main/java/sample/mixins/DefaultCsrfTokenMixin.java +++ /dev/null @@ -1,21 +0,0 @@ -package sample.mixins; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import org.springframework.security.web.PortResolver; - -import javax.servlet.http.HttpServletRequest; - -/** - * @author jitendra on 8/3/16. - */ -@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class") -public abstract class DefaultCsrfTokenMixin { - - @JsonCreator - public DefaultCsrfTokenMixin(@JsonProperty("headerName") String headerName, - @JsonProperty("parameterName") String parameterName, - @JsonProperty("token") String token) { - } -} diff --git a/samples/httpsession-redis-json/src/main/java/sample/mixins/DefaultSavedRequestDeserializer.java b/samples/httpsession-redis-json/src/main/java/sample/mixins/DefaultSavedRequestDeserializer.java deleted file mode 100644 index 7ed0369f..00000000 --- a/samples/httpsession-redis-json/src/main/java/sample/mixins/DefaultSavedRequestDeserializer.java +++ /dev/null @@ -1,262 +0,0 @@ -package sample.mixins; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.deser.std.StdDeserializer; -import com.sun.org.apache.xpath.internal.operations.Bool; -import org.springframework.security.web.PortResolverImpl; -import org.springframework.security.web.savedrequest.DefaultSavedRequest; -import org.springframework.security.web.savedrequest.SavedCookie; -import org.springframework.util.ObjectUtils; - -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletRequestWrapper; -import javax.servlet.http.HttpSession; -import java.io.IOException; -import java.lang.reflect.Proxy; -import java.util.*; - -/** - * @author jitendra on 8/3/16. - */ -public class DefaultSavedRequestDeserializer extends StdDeserializer { - - public DefaultSavedRequestDeserializer(Class requestClass) { - super(requestClass); - } - - @Override - public DefaultSavedRequest deserialize(JsonParser p, DeserializationContext context) throws IOException, JsonProcessingException { - ObjectMapper mapper = (ObjectMapper) p.getCodec(); - JsonNode jsonNode = mapper.readTree(p); - DummyServletRequest request = new DummyServletRequest(); - request.setContextPath(jsonNode.get("contextPath").asText()); - request.setMethod(jsonNode.get("method").asText()); - request.setPathInfo(jsonNode.get("pathInfo").asText()); - request.setQueryString(jsonNode.get("queryString").asText()); - request.setRequestURI(jsonNode.get("requestURI").asText()); - request.setRequestURL(jsonNode.get("requestURL").asText()); - request.setScheme(jsonNode.get("scheme").asText()); - request.setServerName(jsonNode.get("serverName").asText()); - request.setServletPath(jsonNode.get("servletPath").asText()); - request.setServerPort(jsonNode.get("serverPort").asInt()); - List cookies = mapper.readValue(jsonNode.get("cookies").toString(), new TypeReference>() { - }); - Map params = mapper.readValue(jsonNode.get("parameterMap").toString(), new TypeReference>() { - }); - ArrayList locales = mapper.readValue(jsonNode.get("locales").toString(), new TypeReference>() { - }); - Map> headers = mapper.readValue(jsonNode.get("headers").toString(), new TypeReference>>() { - }); - request.setCookies(cookies.toArray(new Cookie[]{})); - request.setParameters(params); - request.setLocales(locales); - request.setHeaders(headers); - return new DefaultSavedRequest(request, new PortResolverImpl()); - } - - protected static class DummyServletRequest extends HttpServletRequestWrapper { - private static final HttpServletRequest UNSUPPORTED_REQUEST = (HttpServletRequest) Proxy - .newProxyInstance(DummyServletRequest.class.getClassLoader(), - new Class[]{HttpServletRequest.class}, - new UnsupportedOperationExceptionInvocationHandler()); - - private String method; - private String pathInfo; - private String queryString; - private String requestURI; - private int serverPort; - private String requestURL; - private String scheme; - private String serverName; - private String contextPath; - private String servletPath; - private Cookie[] cookies; - private String remoteAddress; - private ArrayList locales = new ArrayList(); - private Map> headers = new TreeMap>(String.CASE_INSENSITIVE_ORDER); - private Map parameters = new TreeMap(); - private HttpSession session; - - public DummyServletRequest() { - super(UNSUPPORTED_REQUEST); - } - - public void setMethod(String method) { - this.method = method; - } - - public void setPathInfo(String pathInfo) { - this.pathInfo = pathInfo; - } - - public void setQueryString(String queryString) { - this.queryString = queryString; - } - - public void setRequestURI(String requestURI) { - this.requestURI = requestURI; - } - - public void setServerPort(int serverPort) { - this.serverPort = serverPort; - } - - public void setRequestURL(String requestURL) { - this.requestURL = requestURL; - } - - public void setScheme(String scheme) { - this.scheme = scheme; - } - - public void setServerName(String serverName) { - this.serverName = serverName; - } - - public void setContextPath(String contextPath) { - this.contextPath = contextPath; - } - - public void setServletPath(String servletPath) { - this.servletPath = servletPath; - } - - public void setCookies(Cookie[] cookies) { - this.cookies = cookies; - } - - @Override - public Cookie[] getCookies() { - return cookies; - } - - @Override - public Map getParameterMap() { - return parameters; - } - - public void setParameters(Map params) { - this.parameters = params; - } - - @Override - public Enumeration getParameterNames() { - return Collections.enumeration(parameters.keySet()); - } - - @Override - public String[] getParameterValues(String name) { - return super.getParameterValues(name); - } - - public void setLocales(ArrayList locales) { - this.locales = locales; - } - - public void setHeaders(Map> headers) { - this.headers = headers; - } - - @Override - public Enumeration getHeaders(String name) { - return Collections.enumeration(headers.get(name)); - } - - @Override - public Enumeration getHeaderNames() { - return Collections.enumeration(headers.keySet()); - } - - @Override - public String getMethod() { - return method; - } - - @Override - public String getPathInfo() { - return pathInfo; - } - - @Override - public String getQueryString() { - return (queryString == null || "null".equals(queryString)) ? "" : queryString; - } - - @Override - public String getRequestURI() { - return requestURI; - } - - @Override - public int getServerPort() { - return serverPort; - } - - @Override - public StringBuffer getRequestURL() { - return new StringBuffer(requestURL); - } - - @Override - public String getScheme() { - return scheme; - } - - @Override - public String getServerName() { - return serverName; - } - - @Override - public String getContextPath() { - return contextPath; - } - - @Override - public String getServletPath() { - return servletPath; - } - - @Override - public Enumeration getLocales() { - return Collections.enumeration(locales); - } - - public Map> getHeaders() { - return headers; - } - - public Map getParameters() { - return parameters; - } - - public String getRemoteAddr() { - return remoteAddress; - } - - public void setRemoteAddr(String remoteAddress) { - this.remoteAddress = remoteAddress; - } - - @Override - public HttpSession getSession() { - return session; - } - - @Override - public HttpSession getSession(boolean create) { - return session; - } - - public void setSession(HttpSession session) { - this.session = session; - } - } -} - diff --git a/samples/httpsession-redis-json/src/main/java/sample/mixins/HttpCookieDeserializer.java b/samples/httpsession-redis-json/src/main/java/sample/mixins/HttpCookieDeserializer.java deleted file mode 100644 index 4993ffd3..00000000 --- a/samples/httpsession-redis-json/src/main/java/sample/mixins/HttpCookieDeserializer.java +++ /dev/null @@ -1,34 +0,0 @@ -package sample.mixins; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; - -import javax.servlet.http.Cookie; -import java.io.IOException; - -import static sample.utils.JsonNodeExtractor.*; - -/** - * @author jitendra on 22/3/16. - */ -public class HttpCookieDeserializer extends JsonDeserializer { - - @Override - public Cookie deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { - ObjectMapper mapper = (ObjectMapper) p.getCodec(); - JsonNode jsonNode = mapper.readTree(p); - Cookie cookie = new Cookie(getStringValue(jsonNode, "name"), getStringValue(jsonNode, "value")); - cookie.setComment(getStringValue(jsonNode, "comment")); - cookie.setDomain(getStringValue(jsonNode, "domain", "")); - cookie.setMaxAge(getIntValue(jsonNode, "maxAge", -1)); - cookie.setSecure(getBooleanValue(jsonNode, "secure")); - cookie.setVersion(getIntValue(jsonNode, "version")); - cookie.setPath(getStringValue(jsonNode, "path")); - cookie.setHttpOnly(getBooleanValue(jsonNode, "httpOnly", false)); - return cookie; - } -} diff --git a/samples/httpsession-redis-json/src/main/java/sample/mixins/SavedCookieMixin.java b/samples/httpsession-redis-json/src/main/java/sample/mixins/SavedCookieMixin.java deleted file mode 100644 index 493584f9..00000000 --- a/samples/httpsession-redis-json/src/main/java/sample/mixins/SavedCookieMixin.java +++ /dev/null @@ -1,17 +0,0 @@ -package sample.mixins; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonTypeInfo; - -/** - * @author jitendra on 8/3/16. - */ -public abstract class SavedCookieMixin { - - @JsonCreator - SavedCookieMixin(@JsonProperty("name") String name, @JsonProperty("value") String value, @JsonProperty("comment") String comment, - @JsonProperty("domain") String domain, @JsonProperty("maxAge") int maxAge, @JsonProperty("path") String path, - @JsonProperty("secure") boolean secure, @JsonProperty("version") int version){ - } -} diff --git a/samples/httpsession-redis-json/src/main/java/sample/mixins/SimpleGrantedAuthorityMixin.java b/samples/httpsession-redis-json/src/main/java/sample/mixins/SimpleGrantedAuthorityMixin.java deleted file mode 100644 index 381ae0a0..00000000 --- a/samples/httpsession-redis-json/src/main/java/sample/mixins/SimpleGrantedAuthorityMixin.java +++ /dev/null @@ -1,15 +0,0 @@ -package sample.mixins; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * @author jitendra on 14/3/16. - */ -public abstract class SimpleGrantedAuthorityMixin { - - @JsonCreator - public SimpleGrantedAuthorityMixin(@JsonProperty("authority") String role) { - - } -} diff --git a/samples/httpsession-redis-json/src/main/java/sample/mixins/UnmodifiableSetMixin.java b/samples/httpsession-redis-json/src/main/java/sample/mixins/UnmodifiableSetMixin.java deleted file mode 100644 index 2c77f70d..00000000 --- a/samples/httpsession-redis-json/src/main/java/sample/mixins/UnmodifiableSetMixin.java +++ /dev/null @@ -1,16 +0,0 @@ -package sample.mixins; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.util.Set; - -/** - * @author jitendra on 14/3/16. - */ -public abstract class UnmodifiableSetMixin { - - @JsonCreator - UnmodifiableSetMixin(@JsonProperty("s") Set s) { - } -} diff --git a/samples/httpsession-redis-json/src/main/java/sample/mixins/UnsupportedOperationExceptionInvocationHandler.java b/samples/httpsession-redis-json/src/main/java/sample/mixins/UnsupportedOperationExceptionInvocationHandler.java deleted file mode 100644 index a3e50365..00000000 --- a/samples/httpsession-redis-json/src/main/java/sample/mixins/UnsupportedOperationExceptionInvocationHandler.java +++ /dev/null @@ -1,14 +0,0 @@ -package sample.mixins; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; - -/** - * @author jitendra on 8/3/16. - */ -public class UnsupportedOperationExceptionInvocationHandler implements InvocationHandler { - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - throw new UnsupportedOperationException(method + " is not supported"); - } -} diff --git a/samples/httpsession-redis-json/src/main/java/sample/mixins/UserDeserializer.java b/samples/httpsession-redis-json/src/main/java/sample/mixins/UserDeserializer.java deleted file mode 100644 index 327f1f70..00000000 --- a/samples/httpsession-redis-json/src/main/java/sample/mixins/UserDeserializer.java +++ /dev/null @@ -1,38 +0,0 @@ -package sample.mixins; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.userdetails.User; - -import java.io.IOException; -import java.util.Set; - -import static sample.utils.JsonNodeExtractor.*; - -/** - * @author jitendra on 14/3/16. - */ -public class UserDeserializer extends JsonDeserializer { - - @Override - public User deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { - ObjectMapper mapper = (ObjectMapper) p.getCodec(); - JsonNode jsonNode = mapper.readTree(p); - Set authorities = mapper.readValue(jsonNode.get("authorities").toString(), new TypeReference>() {}); - return new User( - getStringValue(jsonNode, "username"), - getStringValue(jsonNode, "password", ""), - getBooleanValue(jsonNode, "enabled"), - getBooleanValue(jsonNode, "accountNonExpired"), - getBooleanValue(jsonNode, "credentialsNonExpired"), - getBooleanValue(jsonNode, "accountNonLocked"), - authorities - ); - } -} diff --git a/samples/httpsession-redis-json/src/main/java/sample/mixins/UsernamePasswordAuthenticationTokenDeserializer.java b/samples/httpsession-redis-json/src/main/java/sample/mixins/UsernamePasswordAuthenticationTokenDeserializer.java deleted file mode 100644 index e9a5f1c9..00000000 --- a/samples/httpsession-redis-json/src/main/java/sample/mixins/UsernamePasswordAuthenticationTokenDeserializer.java +++ /dev/null @@ -1,46 +0,0 @@ -package sample.mixins; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.userdetails.User; - -import java.io.IOException; -import java.util.List; - -/** - * @author jitendra on 9/3/16. - */ -public class UsernamePasswordAuthenticationTokenDeserializer extends JsonDeserializer { - - @Override - public UsernamePasswordAuthenticationToken deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { - UsernamePasswordAuthenticationToken token = null; - ObjectMapper mapper = (ObjectMapper) p.getCodec(); - JsonNode jsonNode = mapper.readTree(p); - Boolean authenticated = jsonNode.get("authenticated").asBoolean(); - JsonNode principalNode = jsonNode.get("principal"); - Object principal = null; - if(principalNode.isObject()) { - principal = mapper.readValue(principalNode.toString(), new TypeReference() {}); - } else { - principal = principalNode.asText(); - } - Object credentials = jsonNode.get("credentials").asText(); - List authorities = mapper.readValue(jsonNode.get("authorities").toString(), new TypeReference>() { - }); - if (authenticated) { - token = new UsernamePasswordAuthenticationToken(principal, credentials, authorities); - } else { - token = new UsernamePasswordAuthenticationToken(principal, credentials); - } - token.setDetails(jsonNode.get("details")); - return token; - } -} diff --git a/samples/httpsession-redis-json/src/main/java/sample/mixins/WebAuthenticationDetailsDeserializer.java b/samples/httpsession-redis-json/src/main/java/sample/mixins/WebAuthenticationDetailsDeserializer.java deleted file mode 100644 index 6ea9602c..00000000 --- a/samples/httpsession-redis-json/src/main/java/sample/mixins/WebAuthenticationDetailsDeserializer.java +++ /dev/null @@ -1,127 +0,0 @@ -package sample.mixins; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.springframework.security.web.authentication.WebAuthenticationDetails; - -import javax.servlet.ServletContext; -import javax.servlet.http.HttpSession; -import javax.servlet.http.HttpSessionContext; -import java.io.IOException; -import java.util.Enumeration; - -/** - * @author jitendra on 14/3/16. - */ -public class WebAuthenticationDetailsDeserializer extends JsonDeserializer { - - @Override - public WebAuthenticationDetails deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { - ObjectMapper mapper = (ObjectMapper) p.getCodec(); - JsonNode jsonNode = mapper.readTree(p); - DummyHttpSession session = new DummyHttpSession(); - session.setId(jsonNode.get("sessionId").asText()); - DefaultSavedRequestDeserializer.DummyServletRequest request = new DefaultSavedRequestDeserializer.DummyServletRequest(); - request.setRemoteAddr(jsonNode.get("remoteAddress").asText()); - request.setSession(session); - return new WebAuthenticationDetails(request); - } - - protected static class DummyHttpSession implements HttpSession { - - private String id; - - @Override - public long getCreationTime() { - return 0; - } - - @Override - public String getId() { - return this.id; - } - - public void setId(String id) { - this.id = id; - } - - @Override - public long getLastAccessedTime() { - return 0; - } - - @Override - public ServletContext getServletContext() { - return null; - } - - @Override - public void setMaxInactiveInterval(int interval) { - - } - - @Override - public int getMaxInactiveInterval() { - return 0; - } - - @Override - public HttpSessionContext getSessionContext() { - return null; - } - - @Override - public Object getAttribute(String name) { - return null; - } - - @Override - public Object getValue(String name) { - return null; - } - - @Override - public Enumeration getAttributeNames() { - return null; - } - - @Override - public String[] getValueNames() { - return new String[0]; - } - - @Override - public void setAttribute(String name, Object value) { - - } - - @Override - public void putValue(String name, Object value) { - - } - - @Override - public void removeAttribute(String name) { - - } - - @Override - public void removeValue(String name) { - - } - - @Override - public void invalidate() { - - } - - @Override - public boolean isNew() { - return false; - } - } -} diff --git a/samples/httpsession-redis-json/src/main/java/sample/utils/JsonNodeExtractor.java b/samples/httpsession-redis-json/src/main/java/sample/utils/JsonNodeExtractor.java deleted file mode 100644 index 38fd04c5..00000000 --- a/samples/httpsession-redis-json/src/main/java/sample/utils/JsonNodeExtractor.java +++ /dev/null @@ -1,45 +0,0 @@ -package sample.utils; - -import com.fasterxml.jackson.databind.JsonNode; -import com.sun.org.apache.xpath.internal.operations.Bool; - -/** - * Created by jitendra on 28/3/16. - */ -public class JsonNodeExtractor { - - public static String getStringValue(JsonNode jsonNode, String field) { - return getStringValue(jsonNode, field, null); - } - - public static String getStringValue(JsonNode jsonNode, String field, String defaultValue) { - JsonNode node = getValue(jsonNode, field); - return (node != null) ? node.asText(defaultValue) : defaultValue; - } - - private static JsonNode getValue(JsonNode jsonNode, String field) { - if (jsonNode.has(field)) { - return jsonNode.get(field); - } else { - return null; - } - } - - public static Integer getIntValue(JsonNode jsonNode, String field) { - return getIntValue(jsonNode, field, 0); - } - - public static Integer getIntValue(JsonNode jsonNode, String field, Integer defaultValue) { - JsonNode node = getValue(jsonNode, field); - return (node != null) ? node.asInt(defaultValue) : defaultValue; - } - - public static Boolean getBooleanValue(JsonNode jsonNode, String field) { - return getBooleanValue(jsonNode, field, false); - } - - public static Boolean getBooleanValue(JsonNode jsonNode, String field, Boolean defaultValue) { - JsonNode node = getValue(jsonNode, field); - return (node != null) ? node.asBoolean(defaultValue) : defaultValue; - } -}