Skip to content

Commit

Permalink
Merge branch '3.1.x' into 3.2.x
Browse files Browse the repository at this point in the history
Closes gh-2827 in 3.2.x
Closes gh-2831
  • Loading branch information
marcusdacoregio committed Mar 1, 2024
2 parents 57860f7 + c2a982c commit ea14c33
Show file tree
Hide file tree
Showing 17 changed files with 966 additions and 4 deletions.
411 changes: 411 additions & 0 deletions spring-session-docs/modules/ROOT/pages/configuration/jdbc.adoc

Large diffs are not rendered by default.

17 changes: 13 additions & 4 deletions spring-session-docs/spring-session-docs.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -49,20 +49,29 @@ tasks.named("generateAntoraYml") {


def generateAttributes() {
def springBootVersion = libs.versions.org.springframework.boot.get()
springBootVersion = springBootVersion.contains("-")
? springBootVersion.substring(0, springBootVersion.indexOf("-"))
: springBootVersion
def springBootVersion = getLibVersion(libs.versions.org.springframework.boot.get())
def springSecurityVersion = getLibVersion(libs.org.springframework.security.spring.security.bom.get().version)
def springFrameworkVersion = getLibVersion(libs.org.springframework.spring.framework.bom.get().version)
def ghTag = snapshotBuild ? 'main' : project.version
def docsUrl = 'https://docs.spring.io'
def springBootRefDocs = "${docsUrl}/spring-boot/docs/${springBootVersion}/reference/html"
def springSecurityRefDocs = "${docsUrl}/spring-security/reference/${springSecurityVersion}"
def springFrameworkRefDocs = "${docsUrl}/spring-framework/reference/${springFrameworkVersion}"
return ['gh-tag':ghTag,
'spring-boot-version': springBootVersion,
'spring-boot-ref-docs': springBootRefDocs.toString(),
'spring-session-version': project.version,
'spring-security-ref-docs': springSecurityRefDocs.toString(),
'spring-framework-ref-docs': springFrameworkRefDocs.toString(),
'docs-url': docsUrl]
}

static def getLibVersion(String version) {
return version.contains("-")
? version.substring(0, version.indexOf("-"))
: version
}

sourceSets {
test {
java {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
apply plugin: 'io.spring.convention.spring-sample-boot'

dependencies {
management platform(project(":spring-session-dependencies"))
implementation project(':spring-session-jdbc')
implementation "org.springframework.boot:spring-boot-starter-jdbc"
implementation "org.springframework.boot:spring-boot-starter-web"
implementation "org.springframework.boot:spring-boot-starter-thymeleaf"
implementation "org.springframework.boot:spring-boot-starter-security"
implementation "nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect"
implementation "org.webjars:bootstrap"
implementation "org.webjars:html5shiv"
implementation "org.webjars:webjars-locator-core"

runtimeOnly 'org.postgresql:postgresql'

testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.boot:spring-boot-testcontainers'
testImplementation 'org.springframework.security:spring-security-test'
testImplementation 'org.testcontainers:junit-jupiter'
testImplementation 'org.testcontainers:postgresql'
}

tasks.named('test') {
useJUnitPlatform()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright 2014-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package sample;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/**
* Controller for sending the user to the login view.
*
* @author Rob Winch
*
*/
@Controller
public class IndexController {

@RequestMapping("/")
public String index() {
return "index";
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright 2014-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package sample;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
* @author Rob Winch
*/
@SpringBootApplication
public class JdbcJsonAttributeApplication {

public static void main(String[] args) {
SpringApplication.run(JdbcJsonAttributeApplication.class, args);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright 2014-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package sample;

import java.security.Principal;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ModelAttribute;

/**
* {@link ControllerAdvice} to expose security related attributes.
*
* @author Rob Winch
*/
@ControllerAdvice
public class UserControllerAdvise {

@ModelAttribute("currentUserName")
String currentUser(Principal principal) {
return (principal != null) ? principal.getName() : null;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright 2014-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package sample.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;

/**
* Spring Security configuration.
*
* @author Rob Winch
* @author Vedran Pavic
*/
@Configuration
public class SecurityConfig {

// @formatter:off
// tag::config[]
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated()
)
.formLogin((formLogin) -> formLogin
.permitAll()
)
.build();
}
// end::config[]
// @formatter:on

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package sample.config;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import com.fasterxml.jackson.databind.ObjectMapper;

import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.core.serializer.Deserializer;
import org.springframework.core.serializer.Serializer;
import org.springframework.core.serializer.support.DeserializingConverter;
import org.springframework.core.serializer.support.SerializingConverter;
import org.springframework.security.jackson2.SecurityJackson2Modules;
import org.springframework.session.config.SessionRepositoryCustomizer;
import org.springframework.session.jdbc.JdbcIndexedSessionRepository;

@Configuration(proxyBeanMethods = false)
public class SessionConfig implements BeanClassLoaderAware {

private static final String CREATE_SESSION_ATTRIBUTE_QUERY = """
INSERT INTO %TABLE_NAME%_ATTRIBUTES (SESSION_PRIMARY_ID, ATTRIBUTE_NAME, ATTRIBUTE_BYTES)
VALUES (?, ?, encode(?, 'escape')::jsonb)
""";

private ClassLoader classLoader;

@Bean
SessionRepositoryCustomizer<JdbcIndexedSessionRepository> customizer() {
return (sessionRepository) -> sessionRepository.setCreateSessionAttributeQuery(CREATE_SESSION_ATTRIBUTE_QUERY);
}

@Bean("springSessionConversionService")
public GenericConversionService springSessionConversionService(ObjectMapper objectMapper) {
ObjectMapper copy = objectMapper.copy();
copy.registerModules(SecurityJackson2Modules.getModules(this.classLoader));
GenericConversionService converter = new GenericConversionService();
converter.addConverter(Object.class, byte[].class, new SerializingConverter(new JsonSerializer(copy)));
converter.addConverter(byte[].class, Object.class, new DeserializingConverter(new JsonDeserializer(copy)));
return converter;
}

@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}

static class JsonSerializer implements Serializer<Object> {

private final ObjectMapper objectMapper;

JsonSerializer(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}

@Override
public void serialize(Object object, OutputStream outputStream) throws IOException {
this.objectMapper.writeValue(outputStream, object);
}

}

static class JsonDeserializer implements Deserializer<Object> {

private final ObjectMapper objectMapper;

JsonDeserializer(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}

@Override
public Object deserialize(InputStream inputStream) throws IOException {
return this.objectMapper.readValue(inputStream, Object.class);
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
spring.security.user.password=password
spring.session.jdbc.schema=classpath:schema.sql
spring.session.jdbc.initialize-schema=always
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
CREATE TABLE SPRING_SESSION
(
PRIMARY_ID CHAR(36) NOT NULL,
SESSION_ID CHAR(36) NOT NULL,
CREATION_TIME BIGINT NOT NULL,
LAST_ACCESS_TIME BIGINT NOT NULL,
MAX_INACTIVE_INTERVAL INT NOT NULL,
EXPIRY_TIME BIGINT NOT NULL,
PRINCIPAL_NAME VARCHAR(100),
CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
);

CREATE UNIQUE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);
CREATE INDEX SPRING_SESSION_IX3 ON SPRING_SESSION (PRINCIPAL_NAME);

CREATE TABLE SPRING_SESSION_ATTRIBUTES
(
SESSION_PRIMARY_ID CHAR(36) NOT NULL,
ATTRIBUTE_NAME VARCHAR(200) NOT NULL,
ATTRIBUTE_BYTES JSONB NOT NULL,
CONSTRAINT SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_PRIMARY_ID, ATTRIBUTE_NAME),
CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_PRIMARY_ID) REFERENCES SPRING_SESSION (PRIMARY_ID) ON DELETE CASCADE
);
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<html xmlns:th="https://www.thymeleaf.org" xmlns:layout="https://github.com/ultraq/thymeleaf-layout-dialect" layout:decorate="~{layout}">
<head>
<title>Secured Content</title>
</head>
<body>
<div layout:fragment="content">
<h1>Secured Page</h1>
<p>This page is secured using Spring Boot, Spring Session, and Spring Security.</p>
</div>
</body>
</html>
Loading

0 comments on commit ea14c33

Please sign in to comment.