Skip to content

Commit c2a982c

Browse files
Add JDBC to Configurations section
Closes gh-2827 Closes gh-2829
1 parent 4920499 commit c2a982c

File tree

17 files changed

+966
-4
lines changed

17 files changed

+966
-4
lines changed

spring-session-docs/modules/ROOT/pages/configuration/jdbc.adoc

Lines changed: 411 additions & 0 deletions
Large diffs are not rendered by default.

spring-session-docs/spring-session-docs.gradle

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,20 +49,29 @@ tasks.named("generateAntoraYml") {
4949

5050

5151
def generateAttributes() {
52-
def springBootVersion = libs.versions.org.springframework.boot.get()
53-
springBootVersion = springBootVersion.contains("-")
54-
? springBootVersion.substring(0, springBootVersion.indexOf("-"))
55-
: springBootVersion
52+
def springBootVersion = getLibVersion(libs.versions.org.springframework.boot.get())
53+
def springSecurityVersion = getLibVersion(libs.org.springframework.security.spring.security.bom.get().version)
54+
def springFrameworkVersion = getLibVersion(libs.org.springframework.spring.framework.bom.get().version)
5655
def ghTag = snapshotBuild ? 'main' : project.version
5756
def docsUrl = 'https://docs.spring.io'
5857
def springBootRefDocs = "${docsUrl}/spring-boot/docs/${springBootVersion}/reference/html"
58+
def springSecurityRefDocs = "${docsUrl}/spring-security/reference/${springSecurityVersion}"
59+
def springFrameworkRefDocs = "${docsUrl}/spring-framework/reference/${springFrameworkVersion}"
5960
return ['gh-tag':ghTag,
6061
'spring-boot-version': springBootVersion,
6162
'spring-boot-ref-docs': springBootRefDocs.toString(),
6263
'spring-session-version': project.version,
64+
'spring-security-ref-docs': springSecurityRefDocs.toString(),
65+
'spring-framework-ref-docs': springFrameworkRefDocs.toString(),
6366
'docs-url': docsUrl]
6467
}
6568

69+
static def getLibVersion(String version) {
70+
return version.contains("-")
71+
? version.substring(0, version.indexOf("-"))
72+
: version
73+
}
74+
6675
sourceSets {
6776
test {
6877
java {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
apply plugin: 'io.spring.convention.spring-sample-boot'
2+
3+
dependencies {
4+
management platform(project(":spring-session-dependencies"))
5+
implementation project(':spring-session-jdbc')
6+
implementation "org.springframework.boot:spring-boot-starter-jdbc"
7+
implementation "org.springframework.boot:spring-boot-starter-web"
8+
implementation "org.springframework.boot:spring-boot-starter-thymeleaf"
9+
implementation "org.springframework.boot:spring-boot-starter-security"
10+
implementation "nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect"
11+
implementation "org.webjars:bootstrap"
12+
implementation "org.webjars:html5shiv"
13+
implementation "org.webjars:webjars-locator-core"
14+
15+
runtimeOnly 'org.postgresql:postgresql'
16+
17+
testImplementation 'org.springframework.boot:spring-boot-starter-test'
18+
testImplementation 'org.springframework.boot:spring-boot-testcontainers'
19+
testImplementation 'org.springframework.security:spring-security-test'
20+
testImplementation 'org.testcontainers:junit-jupiter'
21+
testImplementation 'org.testcontainers:postgresql'
22+
}
23+
24+
tasks.named('test') {
25+
useJUnitPlatform()
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2014-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package sample;
18+
19+
import org.springframework.stereotype.Controller;
20+
import org.springframework.web.bind.annotation.RequestMapping;
21+
22+
/**
23+
* Controller for sending the user to the login view.
24+
*
25+
* @author Rob Winch
26+
*
27+
*/
28+
@Controller
29+
public class IndexController {
30+
31+
@RequestMapping("/")
32+
public String index() {
33+
return "index";
34+
}
35+
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright 2014-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package sample;
18+
19+
import org.springframework.boot.SpringApplication;
20+
import org.springframework.boot.autoconfigure.SpringBootApplication;
21+
22+
/**
23+
* @author Rob Winch
24+
*/
25+
@SpringBootApplication
26+
public class JdbcJsonAttributeApplication {
27+
28+
public static void main(String[] args) {
29+
SpringApplication.run(JdbcJsonAttributeApplication.class, args);
30+
}
31+
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright 2014-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package sample;
18+
19+
import java.security.Principal;
20+
21+
import org.springframework.web.bind.annotation.ControllerAdvice;
22+
import org.springframework.web.bind.annotation.ModelAttribute;
23+
24+
/**
25+
* {@link ControllerAdvice} to expose security related attributes.
26+
*
27+
* @author Rob Winch
28+
*/
29+
@ControllerAdvice
30+
public class UserControllerAdvise {
31+
32+
@ModelAttribute("currentUserName")
33+
String currentUser(Principal principal) {
34+
return (principal != null) ? principal.getName() : null;
35+
}
36+
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright 2014-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package sample.config;
18+
19+
import org.springframework.context.annotation.Bean;
20+
import org.springframework.context.annotation.Configuration;
21+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
22+
import org.springframework.security.web.SecurityFilterChain;
23+
24+
/**
25+
* Spring Security configuration.
26+
*
27+
* @author Rob Winch
28+
* @author Vedran Pavic
29+
*/
30+
@Configuration
31+
public class SecurityConfig {
32+
33+
// @formatter:off
34+
// tag::config[]
35+
@Bean
36+
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
37+
return http
38+
.authorizeHttpRequests((authorize) -> authorize
39+
.anyRequest().authenticated()
40+
)
41+
.formLogin((formLogin) -> formLogin
42+
.permitAll()
43+
)
44+
.build();
45+
}
46+
// end::config[]
47+
// @formatter:on
48+
49+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package sample.config;
2+
3+
import java.io.IOException;
4+
import java.io.InputStream;
5+
import java.io.OutputStream;
6+
7+
import com.fasterxml.jackson.databind.ObjectMapper;
8+
9+
import org.springframework.beans.factory.BeanClassLoaderAware;
10+
import org.springframework.context.annotation.Bean;
11+
import org.springframework.context.annotation.Configuration;
12+
import org.springframework.core.convert.support.GenericConversionService;
13+
import org.springframework.core.serializer.Deserializer;
14+
import org.springframework.core.serializer.Serializer;
15+
import org.springframework.core.serializer.support.DeserializingConverter;
16+
import org.springframework.core.serializer.support.SerializingConverter;
17+
import org.springframework.security.jackson2.SecurityJackson2Modules;
18+
import org.springframework.session.config.SessionRepositoryCustomizer;
19+
import org.springframework.session.jdbc.JdbcIndexedSessionRepository;
20+
21+
@Configuration(proxyBeanMethods = false)
22+
public class SessionConfig implements BeanClassLoaderAware {
23+
24+
private static final String CREATE_SESSION_ATTRIBUTE_QUERY = """
25+
INSERT INTO %TABLE_NAME%_ATTRIBUTES (SESSION_PRIMARY_ID, ATTRIBUTE_NAME, ATTRIBUTE_BYTES)
26+
VALUES (?, ?, encode(?, 'escape')::jsonb)
27+
""";
28+
29+
private ClassLoader classLoader;
30+
31+
@Bean
32+
SessionRepositoryCustomizer<JdbcIndexedSessionRepository> customizer() {
33+
return (sessionRepository) -> sessionRepository.setCreateSessionAttributeQuery(CREATE_SESSION_ATTRIBUTE_QUERY);
34+
}
35+
36+
@Bean("springSessionConversionService")
37+
public GenericConversionService springSessionConversionService(ObjectMapper objectMapper) {
38+
ObjectMapper copy = objectMapper.copy();
39+
copy.registerModules(SecurityJackson2Modules.getModules(this.classLoader));
40+
GenericConversionService converter = new GenericConversionService();
41+
converter.addConverter(Object.class, byte[].class, new SerializingConverter(new JsonSerializer(copy)));
42+
converter.addConverter(byte[].class, Object.class, new DeserializingConverter(new JsonDeserializer(copy)));
43+
return converter;
44+
}
45+
46+
@Override
47+
public void setBeanClassLoader(ClassLoader classLoader) {
48+
this.classLoader = classLoader;
49+
}
50+
51+
static class JsonSerializer implements Serializer<Object> {
52+
53+
private final ObjectMapper objectMapper;
54+
55+
JsonSerializer(ObjectMapper objectMapper) {
56+
this.objectMapper = objectMapper;
57+
}
58+
59+
@Override
60+
public void serialize(Object object, OutputStream outputStream) throws IOException {
61+
this.objectMapper.writeValue(outputStream, object);
62+
}
63+
64+
}
65+
66+
static class JsonDeserializer implements Deserializer<Object> {
67+
68+
private final ObjectMapper objectMapper;
69+
70+
JsonDeserializer(ObjectMapper objectMapper) {
71+
this.objectMapper = objectMapper;
72+
}
73+
74+
@Override
75+
public Object deserialize(InputStream inputStream) throws IOException {
76+
return this.objectMapper.readValue(inputStream, Object.class);
77+
}
78+
79+
}
80+
81+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
spring.security.user.password=password
2+
spring.session.jdbc.schema=classpath:schema.sql
3+
spring.session.jdbc.initialize-schema=always
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
CREATE TABLE SPRING_SESSION
2+
(
3+
PRIMARY_ID CHAR(36) NOT NULL,
4+
SESSION_ID CHAR(36) NOT NULL,
5+
CREATION_TIME BIGINT NOT NULL,
6+
LAST_ACCESS_TIME BIGINT NOT NULL,
7+
MAX_INACTIVE_INTERVAL INT NOT NULL,
8+
EXPIRY_TIME BIGINT NOT NULL,
9+
PRINCIPAL_NAME VARCHAR(100),
10+
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
11+
);
12+
13+
CREATE UNIQUE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
14+
CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);
15+
CREATE INDEX SPRING_SESSION_IX3 ON SPRING_SESSION (PRINCIPAL_NAME);
16+
17+
CREATE TABLE SPRING_SESSION_ATTRIBUTES
18+
(
19+
SESSION_PRIMARY_ID CHAR(36) NOT NULL,
20+
ATTRIBUTE_NAME VARCHAR(200) NOT NULL,
21+
ATTRIBUTE_BYTES JSONB NOT NULL,
22+
CONSTRAINT SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_PRIMARY_ID, ATTRIBUTE_NAME),
23+
CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_PRIMARY_ID) REFERENCES SPRING_SESSION (PRIMARY_ID) ON DELETE CASCADE
24+
);

0 commit comments

Comments
 (0)