Skip to content
Back to skills

Spring Session

ASecurity

Spring Session for distributed session management with Redis, JDBC, or Hazelcast. Covers session configuration, security integration, and session events. USE WHEN: user mentions "spring session", "distributed session", "session Redis", "session JDBC", "session cluster", "session management", "@SessionScope" DO NOT USE FOR: stateless JWT auth - use `jwt` skill, OAuth2 tokens - use `oauth2` skill

  • 31 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 8, 2026
ai-agentsgojavasqlnodespringtestingapibackendsecuritydocumentation

Works with

  • cli
  • api
  • mcp

Security analysis

A100/100

Scanned September 8, 2026

npx -y skills add claude-dev-suite/claude-dev-suite --skill spring-session --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Spring Session?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Spring Session
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/claude-dev-suite-spring-session/badge)](https://www.skillsdirectory.com/skills/claude-dev-suite-spring-session)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: spring-session
description: |
  Spring Session for distributed session management with Redis, JDBC, or Hazelcast.
  Covers session configuration, security integration, and session events.

  USE WHEN: user mentions "spring session", "distributed session", "session Redis",
  "session JDBC", "session cluster", "session management", "@SessionScope"

  DO NOT USE FOR: stateless JWT auth - use `jwt` skill,
  OAuth2 tokens - use `oauth2` skill
allowed-tools: Read, Grep, Glob, Write, Edit
---
# Spring Session - Quick Reference

> **Deep Knowledge**: Use `mcp__documentation__fetch_docs` with technology: `spring-session` for comprehensive documentation.

## Dependencies

```xml
<!-- Redis Backend -->
<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!-- JDBC Backend -->
<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-jdbc</artifactId>
</dependency>

<!-- Hazelcast Backend -->
<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-hazelcast</artifactId>
</dependency>
```

## Architecture

```
┌─────────────────────────────────────────────────────────────┐
│                    Load Balancer                            │
└─────────────────────────────────────────────────────────────┘
                            │
        ┌───────────────────┼───────────────────┐
        ▼                   ▼                   ▼
┌───────────────┐   ┌───────────────┐   ┌───────────────┐
│   Instance 1  │   │   Instance 2  │   │   Instance 3  │
│   App Server  │   │   App Server  │   │   App Server  │
└───────┬───────┘   └───────┬───────┘   └───────┬───────┘
        │                   │                   │
        └───────────────────┼───────────────────┘
                            ▼
              ┌─────────────────────────┐
              │   Session Store         │
              │   (Redis/JDBC/Hazelcast)│
              └─────────────────────────┘
```

## Redis Configuration

### application.yml
```yaml
spring:
  data:
    redis:
      host: localhost
      port: 6379
      password: ${REDIS_PASSWORD:}

  session:
    store-type: redis
    timeout: 30m
    redis:
      namespace: spring:session
      flush-mode: on_save  # immediate or on_save
      cleanup-cron: "0 * * * * *"  # Every minute
```

### Java Configuration
```java
@Configuration
@EnableRedisHttpSession(
    maxInactiveIntervalInSeconds = 1800,  // 30 minutes
    redisNamespace = "myapp:session",
    flushMode = FlushMode.ON_SAVE
)
public class SessionConfig {

    @Bean
    public LettuceConnectionFactory connectionFactory() {
        return new LettuceConnectionFactory();
    }

    @Bean
    public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
        return new GenericJackson2JsonRedisSerializer();
    }
}
```

## JDBC Configuration

### application.yml
```yaml
spring:
  session:
    store-type: jdbc
    timeout: 30m
    jdbc:
      initialize-schema: always  # always, embedded, never
      table-name: SPRING_SESSION
      cleanup-cron: "0 * * * * *"
```

### Schema
```sql
-- Auto-created with initialize-schema: always
-- Or create manually:

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 BYTEA 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
);
```

## Session Usage

### Controller
```java
@RestController
@RequestMapping("/api")
public class SessionController {

    @GetMapping("/session/info")
    public Map<String, Object> getSessionInfo(HttpSession session) {
        return Map.of(
            "sessionId", session.getId(),
            "creationTime", new Date(session.getCreationTime()),
            "lastAccessedTime", new Date(session.getLastAccessedTime()),
            "maxInactiveInterval", session.getMaxInactiveInterval()
        );
    }

    @PostMapping("/session/attribute")
    public void setAttribute(
            HttpSession session,
            @RequestParam String key,
            @RequestParam String value) {
        session.setAttribute(key, value);
    }

    @GetMapping("/session/attribute/{key}")
    public String getAttribute(HttpSession session, @PathVariable String key) {
        return (String) session.getAttribute(key);
    }

    @DeleteMapping("/session/invalidate")
    public void invalidateSession(HttpSession session) {
        session.invalidate();
    }
}
```

### @SessionAttributes
```java
@Controller
@SessionAttributes("cart")
public class CartController {

    @ModelAttribute("cart")
    public Cart createCart() {
        return new Cart();
    }

    @PostMapping("/cart/add")
    public String addToCart(
            @ModelAttribute("cart") Cart cart,
            @RequestParam Long productId) {
        cart.addItem(productId);
        return "redirect:/cart";
    }

    @PostMapping("/cart/checkout")
    public String checkout(
            @ModelAttribute("cart") Cart cart,
            SessionStatus status) {
        orderService.createOrder(cart);
        status.setComplete();  // Remove from session
        return "redirect:/orders";
    }
}
```

### @SessionScope Bean
```java
@Component
@SessionScope
public class UserPreferences implements Serializable {

    private String theme = "light";
    private String language = "en";
    private int pageSize = 20;

    // getters and setters
}

@RestController
public class PreferencesController {

    @Autowired
    private UserPreferences preferences;  // Session-scoped

    @PutMapping("/preferences/theme")
    public void setTheme(@RequestParam String theme) {
        preferences.setTheme(theme);
    }
}
```

## Security Integration

```java
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .sessionManagement(session -> session
                .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                .maximumSessions(1)  // One session per user
                .maxSessionsPreventsLogin(true)  // Prevent new login
                .sessionRegistry(sessionRegistry())
            )
            .logout(logout -> logout
                .invalidateHttpSession(true)
                .deleteCookies("SESSION")
            );
        return http.build();
    }

    @Bean
    public SpringSessionBackedSessionRegistry<?> sessionRegistry() {
        return new SpringSessionBackedSessionRegistry<>(sessionRepository);
    }
}
```

### Find Sessions by Principal
```java
@Service
@RequiredArgsConstructor
public class SessionManagementService {

    private final FindByIndexNameSessionRepository<?> sessionRepository;

    public Map<String, ?> findSessionsByUsername(String username) {
        return sessionRepository.findByPrincipalName(username);
    }

    public void invalidateUserSessions(String username) {
        Map<String, ?> sessions = sessionRepository.findByPrincipalName(username);
        sessions.keySet().forEach(sessionRepository::deleteById);
    }
}
```

## Custom Session Repository

```java
@Configuration
@EnableRedisHttpSession
public class CustomSessionConfig {

    @Bean
    public SessionRepositoryCustomizer<RedisIndexedSessionRepository> customizer() {
        return sessionRepository -> {
            sessionRepository.setDefaultMaxInactiveInterval(Duration.ofMinutes(30));
            sessionRepository.setRedisKeyNamespace("app:sessions");
        };
    }
}
```

## Session Events

```java
@Component
@Slf4j
public class SessionEventListener {

    @EventListener
    public void onSessionCreated(SessionCreatedEvent event) {
        log.info("Session created: {}", event.getSessionId());
    }

    @EventListener
    public void onSessionDeleted(SessionDeletedEvent event) {
        log.info("Session deleted: {}", event.getSessionId());
    }

    @EventListener
    public void onSessionExpired(SessionExpiredEvent event) {
        log.info("Session expired: {}", event.getSessionId());
    }
}
```

## Cookie Configuration

```java
@Configuration
public class CookieConfig {

    @Bean
    public CookieSerializer cookieSerializer() {
        DefaultCookieSerializer serializer = new DefaultCookieSerializer();
        serializer.setCookieName("SESSIONID");
        serializer.setCookiePath("/");
        serializer.setDomainNamePattern("^.+?\\.(\\w+\\.[a-z]+)$");
        serializer.setSameSite("Strict");
        serializer.setUseSecureCookie(true);
        serializer.setUseHttpOnlyCookie(true);
        serializer.setCookieMaxAge(3600);  // 1 hour
        return serializer;
    }
}
```

## Header-Based Session (REST APIs)

```java
@Configuration
@EnableRedisHttpSession
public class HeaderSessionConfig {

    @Bean
    public HttpSessionIdResolver httpSessionIdResolver() {
        return HeaderHttpSessionIdResolver.xAuthToken();
        // Or custom header:
        // return new HeaderHttpSessionIdResolver("X-Session-Id");
    }
}

// Client sends: X-Auth-Token: <session-id>
```

## WebSocket Session

```java
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureClientInboundChannel(ChannelRegistration registration) {
        registration.interceptors(new SessionChannelInterceptor());
    }
}

public class SessionChannelInterceptor implements ChannelInterceptor {

    @Override
    public Message<?> preSend(Message<?> message, MessageChannel channel) {
        StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
        if (StompCommand.CONNECT.equals(accessor.getCommand())) {
            // Access session attributes
            Map<String, Object> sessionAttributes = accessor.getSessionAttributes();
        }
        return message;
    }
}
```

## Testing

```java
@SpringBootTest
@AutoConfigureMockMvc
class SessionControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    void shouldMaintainSessionAcrossRequests() throws Exception {
        // First request - set attribute
        MvcResult result = mockMvc.perform(post("/api/session/attribute")
                .param("key", "user")
                .param("value", "john"))
            .andExpect(status().isOk())
            .andReturn();

        MockHttpSession session = (MockHttpSession) result.getRequest().getSession();

        // Second request - get attribute with same session
        mockMvc.perform(get("/api/session/attribute/user")
                .session(session))
            .andExpect(status().isOk())
            .andExpect(content().string("john"));
    }
}
```

## Best Practices

| Do | Don't |
|----|-------|
| Use Redis for distributed sessions | Use in-memory sessions in cluster |
| Configure proper timeout | Infinite session timeout |
| Serialize session data properly | Store non-serializable objects |
| Clean up expired sessions | Let sessions accumulate |
| Secure session cookies | Use insecure cookies |

## Production Checklist

- [ ] Distributed store configured (Redis/JDBC)
- [ ] Session timeout set appropriately
- [ ] Cookie security (Secure, HttpOnly, SameSite)
- [ ] Cleanup cron configured
- [ ] Maximum sessions per user
- [ ] Session fixation protection
- [ ] Serialization configured (JSON)
- [ ] Monitoring session count
- [ ] Redis cluster for HA
- [ ] Session events logging

## When NOT to Use This Skill

- **Stateless JWT auth** - Use `jwt` or `spring-security` skills
- **OAuth2 tokens** - Use `oauth2` skill
- **Simple single-instance apps** - In-memory sessions suffice
- **REST APIs** - Consider stateless design

## Anti-Patterns

| Anti-Pattern | Problem | Solution |
|--------------|---------|----------|
| In-memory sessions in cluster | Session lost on failover | Use Redis/JDBC store |
| Infinite session timeout | Resource exhaustion | Set appropriate timeout |
| Non-serializable objects | Session save fails | Ensure Serializable |
| No cleanup cron | Sessions accumulate | Configure cleanup |
| Insecure cookies | Session hijacking | Use Secure, HttpOnly |

## Quick Troubleshooting

| Problem | Diagnostic | Fix |
|---------|------------|-----|
| Session not persisted | Check store-type | Configure Redis/JDBC |
| Session lost between nodes | Check store connectivity | Verify Redis/DB connection |
| Serialization error | Check object types | Make objects Serializable |
| Cookie not sent | Check cookie config | Enable Secure for HTTPS |
| Session expires too fast | Check timeout | Increase session timeout |

## Reference Documentation
- [Spring Session Reference](https://docs.spring.io/spring-session/reference/)

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…