【问题标题】:HttpServletRequest request.getSession().setAttribute not workingHttpServletRequest request.getSession().setAttribute 不起作用
【发布时间】:2021-03-01 13:59:16
【问题描述】:

我正在使用 RedisHttpSession,我的基本目标是在成功登录时将人员对象保存在会话对象中,在需要的地方检索它并在注销时销毁会话。

成功登录后,这就是我正在做的事情:

Staff staff = staffService.getEmailInstance(body.getEmailId());
request.getSession(true).setAttribute("staff", staff);

而注销就是这样:

request.getSession().invalidate();

在不同的控制器中,我正在调用此实用程序方法来检查员工是否已登录:util.isStaffLoggedIn(request, response, StaffRole.EDITOR); 如果员工已登录,则 API 继续执行,否则用户将被重定向到登录页面。

@Service
public class Util {

    public boolean isStaffLoggedIn(HttpServletRequest request, HttpServletResponse response, StaffRole staffRole)
            throws PaperTrueInvalidCredentialsException, PaperTrueJavaException {
        Staff staff = (Staff) request.getSession().getAttribute("staff");
        if (!isObjectNull(staff) && staff.getStaffRole().equals(staffRole)) {
            return true;
        }
        invalidateSessionAndRedirect(request, response);
        return false;
    }


    public void invalidateSessionAndRedirect(HttpServletRequest request, HttpServletResponse response)
            throws PaperTrueJavaException, PaperTrueInvalidCredentialsException {
        request.getSession().invalidate();
        try {
            response.sendRedirect(ProjectConfigurations.configMap.get("staff_logout_path"));
        } catch (IOException e) {
            throw new PaperTrueJavaException(e.getMessage());
        }
        throw new PaperTrueInvalidCredentialsException("Staff not loggedIn");
    }
}

现在,当应用程序运行时,成功登录后会立即调用 get-jobs API。大多数时候request.getSession().getAttribute("staff") 方法工作正常并返回'staff' 对象,但偶尔会返回null。这不会经常发生,但确实会发生。我打印了会话 ID 以查看它们在注销后是否不同,并且确实如此。每次注销后,我都有一个新的会话 ID。我什至检查了我从数据库中检索到的人员对象是否为空,但它不是。

staff 对象已成功保存在会话中,但我无法在其他 API 中检索它。这是我的会话配置的样子:

@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 10800)
public class SessionConfig {

    HashMap<String, String> configMap = ProjectConfigurations.configMap;

    @Bean
    public LettuceConnectionFactory connectionFactory() {
        int redisPort = Integer.parseInt(configMap.get("redis_port"));
        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(
                configMap.get("redis_host"), redisPort);
        redisStandaloneConfiguration.setPassword(configMap.get("redis_password"));
        return new LettuceConnectionFactory(redisStandaloneConfiguration);
    }

    @Bean
    public CookieSerializer cookieSerializer() {
        DefaultCookieSerializer serializer = new DefaultCookieSerializer();
        serializer.setCookieName("PTSESSIONID");
        serializer.setSameSite("none");
        serializer.setUseSecureCookie(!configMap.get("staff_logout_path").contains("localhost"));
        return serializer;
    }
}

如果我错过了什么,请告诉我。提前致谢。

更新 1

我不再使会话无效,我已将 request.getSession(true).setAttribute("staff", staff); 替换为 request.getSession().setAttribute("staff", staff);

我在 StaffController 中设置了“员工”并在 EditorController 中获取它。我是这样设置的:

@RestController
@RequestMapping(path = { "/staff" }, produces = "application/json")
public class StaffApiController {

    private final HttpServletRequest request;
    private final HttpSession httpSession;

    @Autowired
    private StaffService staffService;

    @Autowired
    StaffApiController(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
        this.request = request;
        this.httpSession = session;
    }

    @PostMapping("/login")
    public ResponseEntity<StaffLoginResponse> login(@Valid @RequestBody StaffLoginBody body) {
        StaffLoginResponse staffLoginResponse = new StaffLoginResponse();
        try {
            if (!staffService.isValidLogin(body.getEmailId(), body.getPassword())) {
                throw new PaperTrueInvalidCredentialsException("Invalid Credentials");
            }

            Staff staff = staffService.getEmailInstance(body.getEmailId());
            httpSession.setAttribute("staff", staff);

            staffLoginResponse.setEmail(staff.getEmail()).setRole(staff.getStaffRole().getValue())
                    .setStaffID(staff.getId()).setStatus(new Status("Staff Login Successful"));
        } catch (PaperTrueException e) {
            httpSession.removeAttribute("staff");
            staffLoginResponse.setStatus(new Status(e.getCode(), e.getMessage()));
        }
        return ResponseEntity.ok(staffLoginResponse);
    }

    @PostMapping("/logout")
    public ResponseEntity<Status> logout() {
        httpSession.removeAttribute("staff");
        return ResponseEntity.ok(new Status("Staff Logged Out Successfully"));
    }

}

【问题讨论】:

  • 让我印象深刻的一件事是,您拨打的是request.getSession(true)。如果不存在,这将创建一个新会话。您是要在这里创建一个新会话,还是希望它此时已经存在?
  • @EleftheriaStein-Kousathana 我不想创建新会话。我只想将 Staff 对象保存到 Session 并在注销时将其删除。我已经用getSession() 替换了getSession(true),并且我没有在注销时使会话无效,而是在做removeAttribute("staff")。目前,如果我在会话中设置一个属性然后立即检索它,我会得到空值。但是,如果我刷新页面,我会在会话中获得该属性。我认为会话不会立即更新。
  • 您在哪里获取和设置人员属性?例如在自定义过滤器或控制器中?
  • 我在 StaffController 中设置它并在 EditorController 中获取它
  • 对 StaffController 和 EditorController 的请求是异步的吗?还是先调用 StaffController,一旦调用成功,再调用 EditorController?

标签: java spring-boot spring-data-redis httpsession spring-session


【解决方案1】:

如果你使用 Spring Security,你可以创建一个自定义的“/login”端点,通过设置SecurityContext来验证用户。
您可以使用 Spring Security 提供的默认注销行为。
如果您不需要在正文中提供凭据,则可以使用 Spring Security 提供的默认登录行为并完全省略此 Controller。

这是一个起点。
它不提供全面的安全性,例如它可能是易受攻击的会话固定攻击。

@RestController
public class LoginController {

    private AuthenticationManager authenticationManager;

    public LoginController(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }

    @PostMapping("/login")
    public void login(@RequestBody StaffLoginBody body, HttpServletRequest request) {
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(body.getUsername(), body.getPassword());
        Authentication auth = authenticationManager.authenticate(token);
        SecurityContextHolder.getContext().setAuthentication(auth);
        HttpSession session = request.getSession();
        session.setAttribute("staff", "staff_value");
    }

    @GetMapping("/jobs")
    public String getStaffJobs(HttpServletRequest request) {
        return request.getSession().getAttribute("staff").toString();
    }
}
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    // expose AuthenticationManager bean to be used in Controller
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests(authorize -> authorize
                .antMatchers("/login").permitAll()
                .anyRequest().authenticated()
            )
            // use built in logout
            .logout(logout -> logout
                .deleteCookies("PTSESSIONID")
            );
    }
}

您需要添加 Spring Security 依赖项才能使用此代码 org.springframework.boot:spring-boot-starter-security

【讨论】:

  • 嘿,我尝试了这个解决方案,但我收到了403 Forbidden
  • @KeshavramKuduwa 确保您提供了 CSRF 令牌。如果您的客户端应用程序不在浏览器中并且您不易受到 CSRF 攻击,那么您可以在 configure(HttpSecurity http) 方法中禁用 CSRF 保护。 http.csrf().disable()
  • 我在尝试登录时在循环中收到此错误:drive.google.com/file/d/1LAk3PgR0kRz_NxxHZS7dyiASnNHNy8Tl/…
猜你喜欢
  • 2015-06-23
  • 1970-01-01
  • 1970-01-01
  • 2014-09-15
  • 2013-03-08
  • 1970-01-01
  • 2013-06-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多