You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Redis缓存Principal对象致用户属性无法修改的问题排查求助

Alright, let's tackle this problem step by step. The core issue here is that after enabling Redis, your changes to the currentPropertyid in CustomUserDetails aren't persisting across requests—this is almost certainly because Spring Security's SecurityContext (which holds the Principal) is being cached/stored in Redis, most likely via Spring Session. Even if you commented out your custom RedisTemplate, Spring Session might still be active if it's on your classpath.

1. Confirm if Spring Session is the culprit

First, check your project dependencies. If you have spring-session-data-redis included, Spring Session will automatically store the entire HttpSession (including the SecurityContext containing your CustomUserDetails) in Redis. When you modify the currentPropertyid in the in-memory CustomUserDetails object, that change isn't synced back to Redis, so the next request loads the old, cached version from Redis.

To verify this temporarily, comment out the spring-session-data-redis dependency and restart your app. If the changeProperty endpoint works as expected again, you've confirmed the issue is Spring Session's Redis storage.

2. Fix 1: Manually sync the updated SecurityContext to Redis

When you modify the currentPropertyid, you need to explicitly update the SecurityContext stored in the session (which Spring Session will then sync to Redis). Here's how to adjust your endpoint:

@Autowired
private HttpSession httpSession;

@PutMapping(value="change/property")
public void changeProperty(Long propertyid) {
    CustomUserDetails user = ((CustomUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal());
    user.setCurrentPropertyid(propertyid);
    
    // Update the SecurityContext in the session so Spring Session syncs it to Redis
    httpSession.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext());
}

This ensures that the modified CustomUserDetails is saved back to Redis, so subsequent requests will load the updated version.

3. Fix 2: Store currentPropertyid separately in Redis (avoid modifying Principal)

Instead of tying currentPropertyid to the CustomUserDetails object (which is cached), store it in a dedicated Redis key tied to the user. This bypasses the Principal caching issue entirely:

First, adjust your CustomUserDetails to fetch the value from Redis:

@Component
public class CustomUserDetails implements UserDetails {
    private Long propertyid;
    // Remove the transient currentPropertyid field
    
    @Autowired
    private StringRedisTemplate redisTemplate;

    public Long getPropertyid() {
        // Use the username as part of the Redis key to ensure uniqueness
        String redisKey = "user:current-property:" + getUsername();
        String currentPropertyStr = redisTemplate.opsForValue().get(redisKey);
        
        if (currentPropertyStr != null) {
            return Long.parseLong(currentPropertyStr);
        }
        return propertyid;
    }

    // ... other UserDetails methods and getters/setters for propertyid
}

Then update your endpoint to write directly to Redis:

@Autowired
private StringRedisTemplate redisTemplate;

@PutMapping(value="change/property")
public void changeProperty(Long propertyid) {
    CustomUserDetails user = ((CustomUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal());
    String redisKey = "user:current-property:" + user.getUsername();
    redisTemplate.opsForValue().set(redisKey, propertyid.toString());
}

This approach keeps your user's base properties separate from their temporary current property, avoiding conflicts with cached Principal objects.

4. Fix 3: Store currentPropertyid in the HttpSession

Another option is to store currentPropertyid directly in the HttpSession instead of the Principal. Spring Session will automatically sync session attributes to Redis, so changes will persist across requests:

Update your endpoint:

@PutMapping(value="change/property")
public void changeProperty(Long propertyid, HttpSession session) {
    session.setAttribute("currentPropertyid", propertyid);
}

Then adjust CustomUserDetails to read from the session:

public Long getPropertyid() {
    // Get the current request's session
    ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    if (attributes != null) {
        HttpSession session = attributes.getRequest().getSession();
        Long currentPropertyid = (Long) session.getAttribute("currentPropertyid");
        if (currentPropertyid != null) {
            return currentPropertyid;
        }
    }
    return propertyid;
}

5. Check for accidental UserDetails caching

Double-check if you or a framework component is using @Cacheable on methods that load UserDetails (like your user service). Even if you didn't configure a custom RedisTemplate, Spring's auto-configuration might have wired up a cache manager using Redis if the right dependencies are present. Look for any @Cacheable annotations on user-loading methods and either remove them or update the cache when changeProperty is called.

内容的提问来源于stack exchange,提问作者ytWho

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 13:22:39